Compare commits

..

8 Commits

11 changed files with 3942 additions and 128 deletions

View File

@ -121,7 +121,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@ -165,18 +165,15 @@
<ItemGroup>
<ClInclude Include="..\bo2plugin_test - Copy\framework.h" />
<ClInclude Include="functions.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="SimpleIni.h" />
<ClInclude Include="utils\hook.hpp" />
<ClInclude Include="utils\memory.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="functions.cpp" />
<ClCompile Include="utils\hook.cpp" />
<ClCompile Include="utils\memory.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@ -18,27 +18,33 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\bo2plugin_test - Copy\framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="functions.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SimpleIni.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="utils\hook.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="utils\memory.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<ClCompile Include="functions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="functions.cpp">
<ClCompile Include="utils\hook.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="utils\memory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

View File

@ -10,19 +10,13 @@ This C++ Plutonium t6 plugin and C# iw4m-admin plugin are used together to grab
# Installation:
Download the latest ClanTagRank.dll from the release page, and place it in your t6 server under ```./t6r/data/plugins```, and ClanTagRankApi.dll (from https://gitea.insanemode.org/INSANEMODE/ClanTagRankApi ) in your Iw4m-admin folder under ```./Plugins```
- Download the latest ClanTagRank.dll from the release page, and place it in your t6 server under ```./t6r/data/plugins```, and ClanTagRankCommands.dll (from https://gitea.insanemode.org/INSANEMODE/ClanTagRankCommands ) in your Iw4m-admin folder under ```./Plugins```
# Limitations:
Currently, these plugins require iw4m-admin to be running locally, on the same machine as your servers, and also needs the webfront to be running on the default port, 1624.
if either of these aren't followed, or iw4m-admin is not running, you will see an error in your server's console window that looks similar to this
```curl_easy_perform() failed: Couldn't connect to server```
- everything is now handled through rcon, so there is no longer a need to set your iw4m-admin url in the config file. it can be safely deleted.
# Additional required files/programs
https://github.com/RaidMax/IW4M-Admin/releases
https://forum.plutonium.pw/topic/2819/how-to-install-update-plutonium-t6
https://gitea.insanemode.org/INSANEMODE/ClanTagRankApi
https://gitea.insanemode.org/INSANEMODE/ClanTagRankCommands

3474
SimpleIni.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
#include "pch.h"
#include <Windows.h>
#include <iostream>
#include <string>
@ -7,15 +7,17 @@
#include <cstdlib>
#include <chrono>
#include <thread>
HMODULE LogHandle;
void init()
{
std::cout << "ClanTagRank (1.0.4) by INSANEMODE\n";
std::thread rank(Game::clanTagThread);
rank.detach();
std::cout << "ClanTagRank (1.3) by INSANEMODE\n";
std::thread addcommands(Game::addCommands);
addcommands.detach();
}
BOOL APIENTRY DllMain(HMODULE hModule,

View File

@ -1,4 +1,4 @@
#include "pch.h"
#include <Windows.h>
#include <iostream>
#include <string>
@ -8,125 +8,86 @@
#include <thread>
#include <algorithm>
#include "curl\curl.h"
#include "simpleini.h"
#include <filesystem>
#include "utils/hook.hpp"
#include "utils/memory.hpp"
#include <functional>
#include "functions.h"
utils::memory::allocator allocator;
std::function<void(const std::string& code)> callback;
int clientAddr{ 0x02347D10 - 0x57F8 };// check for client GUID instead of name
int clientNameAddr{ 0x02347cd4 - 0x57F8 };
int clanTagAddr{ 0x30 };
int clientOffset{ 0x57F8 };
namespace Game
{
static Cmd_AddCommand_t Cmd_AddCommand = Cmd_AddCommand_t(0x004DC2A0);
static Cmd_Argv_t Cmd_Argv = (Cmd_Argv_t)0x006B3D40;
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
void clanTagChanger_guid(unsigned int guid, std::string rank)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
std::string testcurl(unsigned int clientguid)
{
std::string guidstring = std::to_string(clientguid);
//std::cout << "guid string: " + guidstring + '\n';
CURL* curl;
CURLcode res;
std::string readBuffer;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:1624/api/gsc/clientguid/" + guidstring);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
/* example.com is redirected, so we tell libcurl to follow redirection */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
return readBuffer;
}
std::string ClanTagRequest(unsigned int clientguid)
{
std::string readBuffer;
//const char* constClanTag;
if (clientguid != 0)
{
readBuffer = testcurl(clientguid);
}
readBuffer.erase(std::remove(readBuffer.begin(), readBuffer.end(), '\n'), readBuffer.end());
readBuffer.erase(std::remove(readBuffer.begin(), readBuffer.end(), '\r'), readBuffer.end());
//readBuffer contains the rank that will be put into the clan tag of the client with the requested username
return readBuffer;
}
void clanTagChanger()
{
unsigned int clientguid{};
char emptyname[16]{};
char currentname[16]{};
std::string currentname_str;
char currentClanTag[8]{};
const char* changeClanTag{};
std::string changeClanTag_str;
int clientAddr{ 0x02347D10 - 0x57F8 };// check for client GUID instead of name
int clientNameAddr{ 0x02347cd4 - 0x57F8 };
int clanTagAddr{ 0x30 };
int clientOffset{ 0x57F8 };
//loop through clients to get their name and current clan tag.
//loop through clients to get their guid
for (int i = 1; i < 9; i++)
{
memcpy(&clientguid, (void*)((clientAddr + (clientOffset * i))), 4);
memcpy(&currentname, (void*)((clientNameAddr + (clientOffset * i))), 16);
//currentname_str = std::string(currentname, 16);
memcpy((void*)currentClanTag, (void*)(((clientAddr +(clientOffset * i)) + clanTagAddr)), 8);
//std::cout << "guid uint: " + clientguid << '\n';
//if ((strstr(currentClanTag, "Creator") == NULL) && (strstr(currentClanTag, "Admin") == NULL) && (strstr(currentClanTag, "SrAdmin") == NULL) && (strstr(currentClanTag, "Trusted") == NULL) && (strstr(currentClanTag, "Mod") == NULL) && (strstr(currentClanTag, "Owner") == NULL) && (strstr(currentClanTag, "3arc") == NULL) && (clientguid > 0) && (clientguid != NULL))
if (!*currentClanTag && (clientguid > 0) && (clientguid != NULL))
if (clientguid == guid)
{
changeClanTag_str = ClanTagRequest(clientguid);
changeClanTag = changeClanTag_str.c_str();
if ((strstr(currentClanTag, changeClanTag) == NULL) || (strstr(changeClanTag, "Error: Client info is null") == NULL))
{
if(!(changeClanTag_str.find("Error:") != std::string::npos)) //&& !(changeClanTag_str.find("User") != std::string::npos))
{
std::cout << "ClanTag Rank: [" + changeClanTag_str + "]" + currentname + '\n';
memcpy((void*)(clientAddr + (clientOffset * i) + clanTagAddr), changeClanTag, 8);
}
changeClanTag = rank.c_str();
memcpy((void*)(clientAddr + (clientOffset * i) + clanTagAddr), changeClanTag, 8);
}
}
}
}
void clanTagThread()
//
void clanTagChanger_slot(int slot, std::string rank)
{
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
clanTagChanger();
}
const char* changeClanTag{rank.c_str()};
slot += 1;
//loop through clients to get their guid
memcpy((void*)(clientAddr + (clientOffset * slot) + clanTagAddr), changeClanTag, 8);
}
void setrank_guid()
{
std::string::size_type sz; // alias of size_t
unsigned int guid = std::stoi(Cmd_Argv(1), &sz);
clanTagChanger_guid(guid, Cmd_Argv(2));
}
void setrank()
{
std::string::size_type sz; // alias of size_t
int client_slot = std::stoi(Cmd_Argv(1), &sz);
clanTagChanger_slot(client_slot, Cmd_Argv(2));
}
void addCommands()
{
//std::this_thread::sleep_for(std::chrono::milliseconds(15000));
const auto cmd_function = allocator.allocate<cmd_function_t>();
const auto cmd_function_2 = allocator.allocate<cmd_function_t>();
Cmd_AddCommand("setrank", setrank, cmd_function);
Cmd_AddCommand("setrank_guid", setrank_guid, cmd_function_2);
//Cmd_AddCommand("commandtest", commandtest);
}
}

View File

@ -1,9 +1,26 @@
#pragma once
#include <Windows.h>
HMODULE LogHandle;
namespace Game
{
//void Testprint();
void clanTagThread();
void addCommands();
struct cmd_function_t
{
cmd_function_t* next;
const char* name;
const char* autoCompleteDir;
const char* autoCompleteExt;
void(__cdecl* function)();
int flags;
};
typedef void (*Cmd_AddCommand_t)(const char* cmdName, void (*function)(), cmd_function_t* allocedCmd);
extern Cmd_AddCommand_t Cmd_AddCommand;
typedef char* (*Cmd_Argv_t)(int arg);
}

15
utils/hook.cpp Normal file
View File

@ -0,0 +1,15 @@
//#include "../pch.h"
#include <windows.h>
#include <vector>
#include <cassert>
#include <mutex>
namespace utils::hook
{
void* allocate_stub_memory(size_t size)
{
return VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
}
}

150
utils/hook.hpp Normal file
View File

@ -0,0 +1,150 @@
#pragma once
// stolen from plutoscript
namespace utils::hook
{
void* allocate_stub_memory(size_t size);
template<typename ValueType, typename AddressType>
inline ValueType get(AddressType address)
{
return *static_cast<ValueType*>(address);
}
template<typename ValueType, typename AddressType>
inline void set(AddressType address, ValueType value)
{
std::memcpy((void*)address, &value, sizeof(value));
}
template<typename ValueType, typename AddressType>
inline void set(AddressType address, const ValueType* data, size_t length)
{
std::memcpy((void*)address, data, sizeof(ValueType) * length);
}
template<typename AddressType>
inline void nop(AddressType address, size_t length)
{
std::memset((void*)address, 0x90, length);
}
template<typename FuncType, typename AddressType>
inline void jump(AddressType address, FuncType func)
{
set<uint8_t>(address, 0xE9);
set<uintptr_t>((uintptr_t)address + 1, (uintptr_t)func - (uintptr_t)address - 5);
}
template<typename FuncType, typename AddressType>
inline void call(AddressType address, FuncType func)
{
set<uint8_t>(address, 0xE8);
set<int>((uintptr_t)address + 1, (intptr_t)func - (intptr_t)address - 5);
}
template<typename FuncType, typename AddressType>
FuncType detour(AddressType target, FuncType hook, const int len)
{
uintptr_t tampoline = (uintptr_t)allocate_stub_memory(len + 5);
std::memcpy((void*)tampoline, (void*)target, len);
set<uint8_t>(tampoline + len, 0xE9);
set<int>((uintptr_t)tampoline + len + 1, (intptr_t)target - (intptr_t)tampoline - 5);
set<uint8_t>(target, 0xE9);
set<int>((uintptr_t)target + 1, (intptr_t)hook - (intptr_t)target - 5);
int pos = len - 5;
if (pos > 0) nop((uintptr_t)target + 5, pos);
return reinterpret_cast<FuncType>(tampoline);
}
namespace vp
{
template<typename ValueType, typename AddressType>
inline void set(AddressType address, ValueType value)
{
DWORD oldProtect;
VirtualProtect((void*)address, sizeof(value), PAGE_EXECUTE_READWRITE, &oldProtect);
std::memcpy((void*)address, &value, sizeof(value));
VirtualProtect((void*)address, sizeof(value), oldProtect, &oldProtect);
}
template<typename ValueType, typename AddressType>
inline void set(AddressType address, const ValueType* data, size_t length)
{
DWORD oldProtect;
VirtualProtect((void*)address, sizeof(ValueType) * length, PAGE_EXECUTE_READWRITE, &oldProtect);
std::memcpy((void*)address, data, sizeof(ValueType) * length);
VirtualProtect((void*)address, sizeof(ValueType) * length, oldProtect, &oldProtect);
}
template<typename AddressType>
inline void nop(AddressType address, size_t length)
{
DWORD oldProtect;
VirtualProtect((void*)address, length, PAGE_EXECUTE_READWRITE, &oldProtect);
std::memset((void*)address, 0x90, length);
VirtualProtect((void*)address, length, oldProtect, &oldProtect);
}
template<typename FuncType, typename AddressType>
inline void jump(AddressType address, FuncType func)
{
DWORD oldProtect;
VirtualProtect((void*)address, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
hook::set<uint8_t>(address, 0xE9);
hook::set<int>((uintptr_t)address + 1, (intptr_t)func - (intptr_t)address - 5);
VirtualProtect((void*)address, 5, oldProtect, &oldProtect);
}
template<typename FuncType, typename AddressType>
inline void call(AddressType address, FuncType func)
{
DWORD oldProtect;
VirtualProtect((void*)address, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
hook::set<uint8_t>(address, 0xE8);
hook::set<int>((uintptr_t)address + 1, (intptr_t)func - (intptr_t)address - 5);
VirtualProtect((void*)address, 5, oldProtect, &oldProtect);
}
template<typename FuncType, typename AddressType>
FuncType detour(AddressType target, FuncType hook, const int len)
{
uintptr_t tampoline = (uintptr_t)allocate_stub_memory(len + 5);
std::memcpy((void*)tampoline, (void*)target, len);
hook::set<uint8_t>(tampoline + len, 0xE9);
hook::set<int>((uintptr_t)tampoline + len + 1, (intptr_t)target - (intptr_t)tampoline - 5);
DWORD oldProtect;
VirtualProtect((void*)target, len, PAGE_EXECUTE_READWRITE, &oldProtect);
hook::set<uint8_t>(target, 0xE9);
hook::set<int>((uintptr_t)target + 1, (intptr_t)hook - (intptr_t)target - 5);
int pos = len - 5;
if (pos > 0) hook::nop((uintptr_t)target + 5, pos);
VirtualProtect((void*)target, len, oldProtect, &oldProtect);
return reinterpret_cast<FuncType>(tampoline);
}
}
}

122
utils/memory.cpp Normal file
View File

@ -0,0 +1,122 @@
/*
https://github.com/momo5502/open-iw5
*/
//#include "../pch.h"
#include <windows.h>
#include <vector>
#include <cassert>
#include <mutex>
#include "memory.hpp"
namespace utils
{
memory::allocator memory::mem_allocator_;
memory::allocator::~allocator()
{
this->clear();
}
void memory::allocator::clear()
{
std::lock_guard _(this->mutex_);
for (auto& data : this->pool_)
{
memory::free(data);
}
this->pool_.clear();
}
void memory::allocator::free(void* data)
{
std::lock_guard _(this->mutex_);
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
if (j != this->pool_.end())
{
memory::free(data);
this->pool_.erase(j);
}
}
void memory::allocator::free(const void* data)
{
this->free(const_cast<void*>(data));
}
void* memory::allocator::allocate(const size_t length)
{
std::lock_guard _(this->mutex_);
const auto data = memory::allocate(length);
this->pool_.push_back(data);
return data;
}
bool memory::allocator::empty() const
{
return this->pool_.empty();
}
/*char* memory::allocator::duplicate_string(const std::string& string)
{
std::lock_guard _(this->mutex_);
const auto data = memory::duplicate_string(string);
this->pool_.push_back(data);
return data;
}*/
void* memory::allocate(const size_t length)
{
const auto data = calloc(length, 1);
assert(data != nullptr);
return data;
}
/*char* memory::duplicate_string(const std::string& string)
{
const auto new_string = allocate_array<char>(string.size() + 1);
std::memcpy(new_string, string.data(), string.size());
return new_string;
}*/
void memory::free(void* data)
{
if (data)
{
::free(data);
}
}
void memory::free(const void* data)
{
free(const_cast<void*>(data));
}
bool memory::is_set(const void* mem, const char chr, const size_t length)
{
const auto mem_arr = static_cast<const char*>(mem);
for (size_t i = 0; i < length; ++i)
{
if (mem_arr[i] != chr)
{
return false;
}
}
return true;
}
memory::allocator* memory::get_allocator()
{
return &memory::mem_allocator_;
}
}

76
utils/memory.hpp Normal file
View File

@ -0,0 +1,76 @@
/*
https://github.com/momo5502/open-iw5
*/
#pragma once
#include <mutex>
#include <vector>
namespace utils
{
class memory final
{
public:
class allocator final
{
public:
~allocator();
void clear();
void free(void* data);
void free(const void* data);
void* allocate(size_t length);
template <typename T>
T* allocate()
{
return this->allocate_array<T>(1);
}
template <typename T>
T* allocate_array(const size_t count = 1)
{
return static_cast<T*>(this->allocate(count * sizeof(T)));
}
bool empty() const;
//char* duplicate_string(const std::string& string);
private:
std::mutex mutex_;
std::vector<void*> pool_;
};
static void* allocate(size_t length);
template <typename T>
static inline T* allocate()
{
return allocate_array<T>(1);
}
template <typename T>
static inline T* allocate_array(const size_t count = 1)
{
return static_cast<T*>(allocate(count * sizeof(T)));
}
//static char* duplicate_string(const std::string& string);
static void free(void* data);
static void free(const void* data);
static bool is_set(const void* mem, char chr, size_t length);
static allocator* get_allocator();
private:
static allocator mem_allocator_;
};
}