- Автор ресурса
- #1
Первое что хотелось бы сказать, что философия данного inc состоит в том, чтобы все манипуляции воспроизводились без создания и нужды дополнительных файлов на сервере.
Данный INC находится в разработке и из-за этого - WINDOWS ONLY Он был выложен для показа возможностей библиотеке, а так же возможной обратной связи.
Так же статья будет дописываться :)
А благодаря этим людям, этот inc был дописан хотя бы до такого состояния:
@Kailo - за помощь в обучении ASM/Отладчиков/Работе с IDA
@komashchenko - За помощь, которая помогла мне разобраться с SDKCall
Данное "оружие" было создано для удобной работы с другими .dll, ведь в функциях SourcePawn можно работать только с 3-мя => server.dll/engine.dll/matchmaking_ds
Что же из этого вышло? - Давайте рассмотрим его структуру =>
Самая интересная функция как по мне -> Pointer GetModuleHandle(const char[] name) которая вызывает и возвращает результат WINAPI GetModuleHandleW через SourcePawn :)
Описании некоторых функций:
Примеры:
1) Внедряем .dll через SourcePawn [GetProcAddress + вызов WINAPI LoadLibraryA]
На ранней стадии создании данного inc он был раскритикован @Kruzya в теме с моим плагином => Bypass -nobots и я действительно был с ним согласен, по этому теперь давайте рассмотрим использование данного inc именно на данном плагине.
Первая претензия была в том, что геймдату нужно изредко обновлять. Теперь мы просто ищем данное слово и "уничтожаем его"
Но если посмотрим на претензию до конца, то видно, что если снять сжатие, было видно строки, что упрощает поимку плагина, что же делать? - Попробуем другой способ - через MemoryEx::FindPattern
А благодаря этим людям, этот inc был дописан хотя бы до такого состояния:
@Kailo - за помощь в обучении ASM/Отладчиков/Работе с IDA
@komashchenko - За помощь, которая помогла мне разобраться с SDKCall
Данное "оружие" было создано для удобной работы с другими .dll, ведь в функциях SourcePawn можно работать только с 3-мя => server.dll/engine.dll/matchmaking_ds
Что же из этого вышло? - Давайте рассмотрим его структуру =>
C-подобный:
#define Pointer Address
#define nullptr Address_Null
#define PTR(%0) view_as<Pointer>(%0)
OS GetServerOS() // Вы можете использовать этот stock вне MemoryEx
int HexToDec(const char[] hex)
int IsHexSymbol(int ch)
enum struct MemoryEx
{
Pointer pAddrBase;
OS os;
bool bSaveBytes;
bool bInit;
ModifByte lastModif;
ArrayList hModifList;
StringMap hModules;
// --------------functions--------------
bool Init();
OS GetOS();
void GetWindowsVersion(int& iMajorVer, int& iMinorVer);
void SetAddr(any address);
Pointer GetAddr();
void Add(any iOffset);
void SaveBytes(bool save);
bool NeedSave();
void RestoreBytes();
void ChangeSettings(bool bSave = true);
Pointer InitModule(const char[] sName);
Pointer GetBaseAddress(const char[] sName);
int GetModuleSize(const char[] sName);
Pointer GetEndModule(const char[] sName);
Pointer GetModuleHandle(const char[] name);
Pointer GetProcAddress(const char[] sLibrary, const char[] sName);
Pointer FindPattern(Pointer base, any size, const int[] pattern, int iLength, int iOffset = 0);
int ReadByte(int iOffset = 0);
void WriteByte(any iByte, int iOffset = 0, int flags = MemoryEx_NoNeedAdd);
int ReadWord(int iOffset = 0);
void WriteWord(any iWord, int iOffset = 0, int flags = MemoryEx_NoNeedAdd);
int ReadInt (int iOffset = 0);
void WriteInt(any iNumber, int iOffset = 0, int flags = MemoryEx_NoNeedAdd);
void WriteData(const int[] data, int iSize, int flags = MemoryEx_NoNeedAdd);
int ReadString(char[] sString, int iMaxLength);
void WriteString(const char[] sString, bool bNull = true, int flags = MemoryEx_NoNeedAdd);
void WriteUnicodeString(const char[] sString, bool bNull = true, int flags = MemoryEx_NoNeedAdd);
Pointer FindString(const char[] sModule, const char[] sString);
Pointer FindUnicodeString(const char[] sModule, const char[] sString);
Pointer FindValue(const char[] sModule, any iValue, int iNextByte = 0x2A );
}
Самая интересная функция как по мне -> 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, так же можно заметить, что последним аргументом в данной функции вспомогательный байт 0xFF, что же это?
Если проанализировать где используется данная строка, всегда после нее идет call ds:GetModuleHandleW т.е первый байт = 0xFF.
И так, узнали где используется kernel32.dll - теперь делаем смещение + 0x6 и тем самым загружаем адрес GetModuleHandleW. И так, как же это выглядит в библиотеке? =>
Дальнейшие действия такие:
Чтобы было более понятно, в итоге это привело к этому:
Далее просто вызываем ее через SDKCall и получаем Base Address любой интересующей нас dll
Как можно заметить - первым аргументом она принимает название библиотеки, но для использования GetModuleHandleW - нужна unicode строка? - По этому и была создана функция MemoryEx::WriteUnicodeString.
В свободной памяти мы генерируем данную строку, делаем небольшой отступ [0x10] и мы должны создать функцию, которая вызывает ее, псевдокод на ASM =>
C-подобный:
push [string address]
call dword ptr [GetModuleHandleW]
retn
Что же inc делает?
GetModuleHandleW в server.dll используется в двух случаях и во всех - использует kernel32. По этому мы ищем адрес данной строки, и можно использовать MemoryEx::FindString, но т.к это unicode - используем MemoryEx::FindUnicodeString. После того, как мы нашли адрес данной строки - ищем где используется данная строка через MemoryEx::FindValue, так же можно заметить, что последним аргументом в данной функции вспомогательный байт 0xFF, что же это?
C-подобный:
.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
Если проанализировать где используется данная строка, всегда после нее идет call ds:GetModuleHandleW т.е первый байт = 0xFF.
И так, узнали где используется kernel32.dll - теперь делаем смещение + 0x6 и тем самым загружаем адрес GetModuleHandleW. И так, как же это выглядит в библиотеке? =>
C-подобный:
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));
Дальнейшие действия такие:
C-подобный:
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
Чтобы было более понятно, в итоге это привело к этому:
Далее просто вызываем ее через SDKCall и получаем Base Address любой интересующей нас dll
C-подобный:
StartPrepSDKCall(SDKCall_Static);
PrepSDKCall_SetAddress(pFunc);
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
Handle h = EndPrepSDKCall();
Pointer iRes = SDKCall(h);
Описании некоторых функций:
Данную функцию вы должны вызывать при инициализации MemoryEx, в ней определяется OS сервера, base address + size server.dll/.so.
Это позволяет использовать все функции из данной библиотеки.
Если вы попробуйте найтм какую-то строку, но не сделаете MemoryEx::Init() то ваш плагин будет вызывать подобную ошибку
Это позволяет использовать все функции из данной библиотеки.
Если вы попробуйте найтм какую-то строку, но не сделаете MemoryEx::Init() то ваш плагин будет вызывать подобную ошибку
C-подобный:
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 - восстанавливает все измененные байты.
Пример:
C-подобный:
#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]
Пример всех этих функций:
Результат
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]
Пример всех этих функций:
C-подобный:
#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]
C-подобный:
#include <MemoryEx>
public void OnPluginStart()
{
MemoryEx mem;
if(!mem.Init()) return;
mem.InitModule("kernel32.dll");
Pointer libAddr = mem.GetProcAddress("kernel32.dll", "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");
delete h;
PrintToServer("libaddr = 0x%X Base Address DLL = 0x%X ",libAddr, baseDLL);
}
На ранней стадии создании данного inc он был раскритикован @Kruzya в теме с моим плагином => Bypass -nobots и я действительно был с ним согласен, по этому теперь давайте рассмотрим использование данного inc именно на данном плагине.
Первая претензия была в том, что геймдату нужно изредко обновлять. Теперь мы просто ищем данное слово и "уничтожаем его"
C-подобный:
#include <MemoryEx>
public void OnPluginStart()
{
MemoryEx mem;
if(mem.Init())
{
Pointer pStr = mem.FindString("server", "-nobots");
if(pStr != nullptr)
{
mem.SetAddr(pStr + PTR(0x01)); // bypass `-`
for(int y = 0; y < 6; y++) mem.WriteByte(GetRandomInt(0x61, 0x7A), y);
}
}
}
C-подобный:
#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);
}
}
}
Последнее редактирование: