- Поддерживаемые игры
- CS: GO
Первое что хотелось бы сказать, что философия данного inc состоит в том, чтобы все манипуляции воспроизводились без создания и нужды дополнительных файлов на сервере.
Данный INC находится в разработке и из-за этого - WINDOWS ONLY. Он был выложен для показа возможностей библиотеки, а так же возможной обратной связи
С версии 2.0 - добавлена поддержка linux
Данное "оружие" было создано для удобной работы с другими .dll, ведь в функциях SourcePawn можно работать только с 3-мя => server.dll/engine.dll/matchmaking_ds
Что же из этого вышло? - Давайте рассмотрим его структуру =>
С версии 2.0 структура разделена по файлами
MemoryEx/ASM_Instruction.inc - Вспомогающий файл, который позволяет использовать самые частые ASM функции в SP [Подключает за собою MemoryEx/BaseMemory.inc]
2) MemoryEx/BaseMemory.inc - Реализовывает базовые функции для более удобной работы с памятью
3) MemoryEx/DynamicLibrary.inc - Реализует функции для работы с динамическими библиотеками [
4) MemoryEx/ServerLibrary.inc - Работа с server[.so]/[.dll] - а именно получения базового адреса [Windows Only] (Для Linux не нужен) / Получение OS сервера
5) MemoryEx/LinuxFunction.inc - Вспомогательные функции для linux => Загрузка списка .so библиотек
6) MemoryEx/WindowsFunction.inc - Функции для Windows => Получения размера DLL из PEB заголовка/Получения версии Windows
Плагины работающие с MemoryEx
1) Bypass -nobots/-nohltv
2) SM Plugins Block [Путем патча самого SourceMod]
Самая интересная функция как по мне -> Pointer GetModuleHandle(const char[] name) которая вызывает и возвращает результат WINAPI GetModuleHandleW через SourcePawn
Описании некоторых функций:
Данную функцию вы должны вызывать при инициализации MemoryEx, в ней определяется OS сервера, base address + size server.dll/.so.
Это позволяет использовать все функции из данной библиотеки.
Если вы попробуйте найтм какую-то строку, но не сделаете MemoryEx::Init() то ваш плагин будет вызывать подобную ошибку
Примеры:
1) Внедряем .dll через SourcePawn [GetProcAddress + вызов WINAPI LoadLibraryA]
Первая претензия была в том, что геймдату нужно изредко обновлять. Теперь мы просто ищем данное слово и "уничтожаем его"
Но если посмотрим на претензию до конца, то видно, что если снять сжатие, было видно строки, что упрощает поимку плагина, что же делать? - Попробуем другой способ - через MemoryEx::FindPattern
С версии 2.0 - добавлена поддержка linux
Данное "оружие" было создано для удобной работы с другими .dll, ведь в функциях SourcePawn можно работать только с 3-мя => server.dll/engine.dll/matchmaking_ds
Что же из этого вышло? - Давайте рассмотрим его структуру =>
С версии 2.0 структура разделена по файлами
MemoryEx/ASM_Instruction.inc - Вспомогающий файл, который позволяет использовать самые частые ASM функции в SP [Подключает за собою MemoryEx/BaseMemory.inc]
Код:
enum ASMRegister
{
ASMRegister_EAX,
ASMRegister_ECX,
ASMRegister_EDX,
ASMRegister_EBX,
ASMRegister_ESP,
ASMRegister_EBP,
ASMRegister_ESI,
ASMRegister_EDI
}
enum struct ASMInstructions
{
BaseMemory mem;
void Set(Pointer adr)
{
this.mem.pAddrBase = adr;
}
Pointer Get()
{
return this.mem.pAddrBase;
}
void PushRegister(ASMRegister reg)
{
this.mem.WriteByte(0x50 + view_as<int>(reg), _, MemoryEx_AddAfterWrite);
}
void Push(any value)
{
this.mem.WriteByte(0x68, _, MemoryEx_AddAfterWrite);
this.mem.WriteInt(value, _, MemoryEx_AddAfterWrite);
}
void PopRegister(ASMRegister reg)
{
this.mem.WriteByte(0x58 + view_as<int>(reg), _, MemoryEx_AddAfterWrite);
}
void Call (any value)
{
this.mem.WriteWord(0x15FF, _, MemoryEx_AddAfterWrite);
this.mem.WriteInt(value, _, MemoryEx_AddAfterWrite);
}
void Nop()
{
this.mem.WriteByte(0x90, _, MemoryEx_AddAfterWrite);
}
void Xchg(ASMRegister reg)
{
this.mem.WriteByte(0x90 + view_as<int>(reg), _, MemoryEx_AddAfterWrite);
}
void Retn()
{
this.mem.WriteByte(0xC3, _, MemoryEx_AddAfterWrite);
}
}
stock ASMInstructions g_ASM;
#define ASM g_ASM
#define ASMHELP_SET(%0) g_ASM.Set(%0)
#define ASMHELP_GET() g_ASM.Get()
#define PUSH_REGISTER(%0) g_ASM.PushRegister(ASMRegister_%0)
#define PUSH(%0) g_ASM.Push(%0)
#define POP_REGISTER(%0) g_ASM.PopRegister(ASMRegister_%0)
#define CALL(%0) g_ASM.Call(%0)
#define NOP() g_ASM.Nop()
#define XCHG(%0) g_ASM.Xchg(ASMRegister_%0)
#define RETN() g_ASM.Retn()
3) MemoryEx/DynamicLibrary.inc - Реализует функции для работы с динамическими библиотеками [
4) MemoryEx/ServerLibrary.inc - Работа с server[.so]/[.dll] - а именно получения базового адреса [Windows Only] (Для Linux не нужен) / Получение OS сервера
5) MemoryEx/LinuxFunction.inc - Вспомогательные функции для linux => Загрузка списка .so библиотек
6) MemoryEx/WindowsFunction.inc - Функции для Windows => Получения размера DLL из PEB заголовка/Получения версии Windows
Плагины работающие с MemoryEx
1) Bypass -nobots/-nohltv
2) SM Plugins Block [Путем патча самого SourceMod]
Самая интересная функция как по мне -> Pointer GetModuleHandle(const char[] name) которая вызывает и возвращает результат WINAPI GetModuleHandleW через SourcePawn
Данный INC вызывает WINAPI функцию GetModuleHandleW для получения Base Address любой библиотеки. Что же она делает?
Как можно заметить - первым аргументом она принимает название библиотеки, но для использования GetModuleHandleW - нужна unicode строка? - По этому и была создана функция MemoryEx::WriteUnicodeString.
В свободной памяти мы генерируем данную строку, делаем небольшой отступ [0x10] и мы должны создать функцию, которая вызывает ее, псевдокод на ASM =>
Что же inc делает?
GetModuleHandleW в server.dll используется в двух случаях и во всех - использует kernel32. По этому мы ищем адрес данной строки, и можно использовать MemoryEx::FindString, но т.к это unicode - используем MemoryEx::FindUnicodeString. После того, как мы нашли адрес данной строки - ищем где используется данная строка через MemoryEx::FindValue, так же можно заметить, что последним аргументом в данной функции вспомогательный байт 0x
Если проанализировать где используется данная строка, всегда после нее идет call ds:GetModuleHandleW т.е первый байт = 0xFF.
И так, узнали где используется kernel32.dll - теперь делаем смещение + 0x6 и тем самым загружаем адрес GetModuleHandleW. И так, как же это выглядит в библиотеке? =>
Дальнейшие действия такие:
Чтобы было более понятно, в итоге это привело к этому:
Далее просто вызываем ее через SDKCall и получаем Base Address любой интересующей нас dll
Как можно заметить - первым аргументом она принимает название библиотеки, но для использования GetModuleHandleW - нужна unicode строка? - По этому и была создана функция MemoryEx::WriteUnicodeString.
В свободной памяти мы генерируем данную строку, делаем небольшой отступ [0x10] и мы должны создать функцию, которая вызывает ее, псевдокод на ASM =>
Код:
push [string address]
call dword ptr [GetModuleHandleW]
retn
GetModuleHandleW в server.dll используется в двух случаях и во всех - использует kernel32. По этому мы ищем адрес данной строки, и можно использовать MemoryEx::FindString, но т.к это unicode - используем MemoryEx::FindUnicodeString. После того, как мы нашли адрес данной строки - ищем где используется данная строка через MemoryEx::FindValue, так же можно заметить, что последним аргументом в данной функции вспомогательный байт 0x
Код:
.text:1072C378 56 push esi
.text:1072C379 68 EC D5 79 10 push offset aKernel32Dll_0 ; "kernel32.dll"
.text:1072C37E FF 15 F4 E0 78 10 call ds:GetModuleHandleW
И так, узнали где используется kernel32.dll - теперь делаем смещение + 0x6 и тем самым загружаем адрес GetModuleHandleW. И так, как же это выглядит в библиотеке? =>
Код:
Pointer pKernelStr = this.FindUnicodeString("server", "kernel32.dll");
Pointer module = this.FindValue("server", pKernelStr, 0xFF) + PTR(0x06);
if(pKernelStr == nullptr || module == nullptr)
{
this.ChangeSettings(false);
LogStackTrace("GetModuleHandles failed -> Base = 0x%X pKernelStr 0x%X module 0x%X end = 0x%X", g_ServerDLL.base, pKernelStr, module, g_ServerDLL.base + PTR(g_ServerDLL.size) );
return nullptr;
}
module = PTR(LoadFromAddress(module, NumberType_Int32));
Код:
static int offsetForString = 0x10; // offset between string and function
static int offsetForEnd = 0x100;
int iLengthStr = strlen(name);
//Теперь нужно перейти на адрес куда мы можем нужную строку + саму функцию.
this.SetAddr((view_as<int>(g_ServerDLL.base) + g_ServerDLL.size) - offsetForEnd - offsetForString - (iLengthStr * 2)); // Address for string
Pointer pString = this.GetAddr();
// this.WriteUnicodeString(name) возвращает нам адрес, куда была записан последний байт строки и делаем маленькое смещения для написания уже самой функции.
this.SetAddr( this.WriteUnicodeString(name) + PTR(offsetForString));
Pointer pFunc = this.GetAddr();
// Реализуем функцию из псевдокода, который был выше
this.WriteByte(0x68, _, MemoryEx_AddAfterWrite); // push
this.WriteInt(pString, _, MemoryEx_AddAfterWrite); // Адрес записаной unicode строки
this.WriteWord(0x15FF, _, MemoryEx_AddAfterWrite); // call dword ptr
this.WriteInt(module, _, MemoryEx_AddAfterWrite); // Адрес GetModuleHandleW
this.WriteByte(0xC3, _, MemoryEx_AddAfterWrite); // retn
Код:
StartPrepSDKCall(SDKCall_Static);
PrepSDKCall_SetAddress(pFunc);
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
Handle h = EndPrepSDKCall();
Pointer iRes = SDKCall(h);
Это позволяет использовать все функции из данной библиотеки.
Если вы попробуйте найтм какую-то строку, но не сделаете MemoryEx::Init() то ваш плагин будет вызывать подобную ошибку
Код:
L 12/18/2019 - 19:33:28: [SM] Stack trace requested: MemoryEx wasn't be initialized
L 12/18/2019 - 19:33:28: [SM] Called from: nobots_bypass.smx
L 12/18/2019 - 19:33:28: [SM] Call stack trace:
L 12/18/2019 - 19:33:28: [SM] [0] LogStackTrace
L 12/18/2019 - 19:33:28: [SM] [1] Line 437, E:\server\bhopserver\csgo\addons\sourcemod\records\2\include\MemoryEx.inc::MemoryEx::GetModuleSize
L 12/18/2019 - 19:33:28: [SM] [2] Line 735, E:\server\bhopserver\csgo\addons\sourcemod\records\2\include\MemoryEx.inc::MemoryEx::FindString
L 12/18/2019 - 19:33:28: [SM] [3] Line 18, nobots_bypass.sp::OnPluginStart
MemoryEx::SaveBytes(bool) - Нужно ли сохранять байты, которые были изменены в ходе работы плагина.
MemoryEx::NeedSave - true - Включена необходимость сохранять байты
MemoryEx::RestoreBytes - восстанавливает все измененные байты.
Пример:
MemoryEx::NeedSave - true - Включена необходимость сохранять байты
MemoryEx::RestoreBytes - восстанавливает все измененные байты.
Пример:
Код:
#include <MemoryEx>
MemoryEx g_hMem;
public void OnPluginStart()
{
RegServerCmd("sm_nobots", Cmd_NoBots);
}
public Action Cmd_NoBots(int iArgs)
{
if(iArgs)
{
g_hMem.RestoreBytes();
}
else
{
if(g_hMem.Init())
{
g_hMem.SaveBytes(true);
Pointer pEnd = g_hMem.GetBaseAddress("server") + PTR(g_hMem.GetModuleSize("server"));
g_hMem.SetAddr(pEnd - PTR(0x200));
g_hMem.WriteString("Memory", _, MemoryEx_AddAfterWrite);
g_hMem.WriteUnicodeString("Extended", _, MemoryEx_AddAfterWrite);
g_hMem.WriteByte(0x31, _, MemoryEx_AddAfterWrite);
g_hMem.WriteWord(0x32, _, MemoryEx_AddAfterWrite);
g_hMem.WriteInt(0x33, _, MemoryEx_AddAfterWrite);
}
}
}
Pointer GetModuleHandle(const char[] library) - Возвращает Base Address указанного модуля [На основе WINAPI GetModuleHandleW]. "0" - Возвращает адрес srcds.
Pointer InitModule(const char[] library) - Инициализирует Base/End address указанного модуля
Pointer GetBaseAddress(const char[] library) - Возвращает Base Address уже из инициализированного модуля
int GetModuleSize(const char[] library) - Возвращает инициализированный размер библиотеки
Pointer GetEndModule(const char[] library) - Возвращает адресс последнего инициализированного байта библиотеки [MemoryEx::GetBaseAddress + MemoryEx::GetBaseAddres]
Пример всех этих функций:
Результат
base [0x75170000] == base1 [0x75170000] == base2 [0x75170000] size [0xE0000] end1 [0x75250000] == end2 [0x75250000] srcds [0xB10000]
Pointer InitModule(const char[] library) - Инициализирует Base/End address указанного модуля
Pointer GetBaseAddress(const char[] library) - Возвращает Base Address уже из инициализированного модуля
int GetModuleSize(const char[] library) - Возвращает инициализированный размер библиотеки
Pointer GetEndModule(const char[] library) - Возвращает адресс последнего инициализированного байта библиотеки [MemoryEx::GetBaseAddress + MemoryEx::GetBaseAddres]
Пример всех этих функций:
Код:
#include <MemoryEx>
public void OnPluginStart()
{
MemoryEx mem;
if(!mem.Init()) return;
Pointer base = mem.GetModuleHandle("kernel32.dll");
Pointer base1 = mem.InitModule("kernel32.dll");
Pointer base2 = mem.GetBaseAddress("kernel32.dll");
Pointer srcds = mem.GetModuleHandle("0");
int size = mem.GetModuleSize("kernel32.dll");
Pointer end1 = base1 + PTR(size);
Pointer end2 = mem.GetEndModule("kernel32.dll");
PrintToServer("base [0x%X] == base1 [0x%X] == base2 [0x%X] size [0x%X] end1 [0x%X] == end2 [0x%X] srcds [0x%X]", base, base1, base2, size, end1, end2, srcds);
}
base [0x75170000] == base1 [0x75170000] == base2 [0x75170000] size [0xE0000] end1 [0x75250000] == end2 [0x75250000] srcds [0xB10000]
1) Внедряем .dll через SourcePawn [GetProcAddress + вызов WINAPI LoadLibraryA]
Код:
#include <MemoryEx>
public void OnPluginStart()
{
MemoryEx mem;
mem.Init();
mem.lib.InitModule("kernel32.dll");
PrintToServer("ServerBase 0x%X size 0x%X kernel base 0x%X size 0x%X", GetServerDLLBase(), GetServerDLLSize(), mem.lib.GetBaseAddress("kernel32"), mem.lib.GetModuleSize("kernel32"));
Pointer libAddr = mem.lib.GetProcAddress("kernel32", "LoadLibraryA");
StartPrepSDKCall(SDKCall_Static);
PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer);
PrepSDKCall_SetAddress(libAddr);
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
Handle h = EndPrepSDKCall();
int baseDLL = SDKCall(h, "D:/CSS_HOOK.dll");
PrintToServer("libaddr = 0x%X Base Address DLL = 0x%X ",libAddr, baseDLL);
delete h;
}
Первая претензия была в том, что геймдату нужно изредко обновлять. Теперь мы просто ищем данное слово и "уничтожаем его"
Код:
#include <MemoryEx>
public void OnPluginStart()
{
MemoryEx mem;
mem.Init();
mem.lib.InitModule("engine");
Pointer pNoBots = mem.lib.FindString("server", "-nobots");
if(pNoBots != nullptr)
{
mem.mem.SetAddr(pNoBots + PTR(0x01));
for(int y = 0; y < 6; y++) mem.mem.WriteByte(GetRandomInt(0x61, 0x7A), y);
}
}
Код:
#include <MemoryEx>
public void OnPluginStart()
{
static int pattern[8] = {0x2D, 0x6E, 0x6F, 0x62, 0x6F, 0x74, 0x73, 0x00}; // `-nobots`;
MemoryEx mem;
if(mem.Init())
{
ModuleInfo server;
server.base = mem.GetBaseAddress("server");
server.size = mem.GetModuleSize("server");
Pointer pStr = mem.FindPattern(server.base, server.size, pattern, sizeof(pattern), 0x01); // bypass `-`
if(pStr != nullptr)
{
mem.SetAddr(pStr);
for(int y = 0; y < 6; y++) mem.WriteByte(GetRandomInt(0x61, 0x7A), y);
}
}
}
- Требования
- SourceMod 1.10+