20121126
20121124
D3D: Пишем DLL-инжектор, но уже на C++
[Ссылочка] на урок. Разбираем код DLL-инжектора на C++, который я сваял из тестового D3D-приложения.
А [вот] ссылочка на весь проект, выложенная на [GitHub]. Комментарии на английском. Проект будет пилиться вместе с записью уроков, так что можно его считать тестовой площадкой. Для компиляции понадобится MSVS, я использую 2012 Express.
Сейчас буду записывать следующий урок, для затравки - читаем [это] и [это].
А [вот] ссылочка на весь проект, выложенная на [GitHub]. Комментарии на английском. Проект будет пилиться вместе с записью уроков, так что можно его считать тестовой площадкой. Для компиляции понадобится MSVS, я использую 2012 Express.
Сейчас буду записывать следующий урок, для затравки - читаем [это] и [это].
20121103
D3D: Пишем DLL-инжектор
[Ссылочка] на урок.
[Ссылочка] на компилятор (версия demo для win x86).
А вот полный код:
OpenLibrary(0, "kernel32.dll")
OpenLibrary(1, "user32.dll")
#PROCESS_ALL_ACCESS = $1F0FFF
#MEM_COMMIT = $1000
#MEM_RESERVE = $2000
#PAGE_READWRITE = $4
Text$ = "test.dll"
dllName = @Text$
dllNameSize = 8
loadLibAddr = GetFunction(0, "LoadLibraryA")
Debug(loadLibAddr)
hProcess = RunProgram("d:\keng_loader\d3d9_lesson0_keng", "", GetCurrentDirectory(), #PB_Program_Open)
pID = ProgramID(hProcess)
pHandle = CallFunction(0, "OpenProcess", #PROCESS_ALL_ACCESS, 0, pID)
allocAddr = CallFunction(0, "VirtualAllocEx", pHandle, 0, dllNameSize, #MEM_COMMIT+#MEM_RESERVE, #PAGE_READWRITE)
Debug(allocAddr)
CallFunction(0, "WriteProcessMemory", pHandle, allocAddr, dllName, dllNameSize, 0)
CallFunction(0, "CreateRemoteThread", pHandle, 0, 0, loadLibAddr, allocAddr, 0, 0)
CallFunction(0, "CloseHandle", pHandle)
CloseLibrary(0)
CloseLibrary(1)
Почему на бейсике? Потому что моя задача - объяснить алгоритм работы, а реализация на конкретном языке - дело вкуса. Кому-то ассемблер нравится, кому-то Си, кому-то - C# или Delphi. Не переживайте, в видео всё разжёвано.
[Ссылочка] на компилятор (версия demo для win x86).
А вот полный код:
OpenLibrary(0, "kernel32.dll")
OpenLibrary(1, "user32.dll")
#PROCESS_ALL_ACCESS = $1F0FFF
#MEM_COMMIT = $1000
#MEM_RESERVE = $2000
#PAGE_READWRITE = $4
Text$ = "test.dll"
dllName = @Text$
dllNameSize = 8
loadLibAddr = GetFunction(0, "LoadLibraryA")
Debug(loadLibAddr)
hProcess = RunProgram("d:\keng_loader\d3d9_lesson0_keng", "", GetCurrentDirectory(), #PB_Program_Open)
pID = ProgramID(hProcess)
pHandle = CallFunction(0, "OpenProcess", #PROCESS_ALL_ACCESS, 0, pID)
allocAddr = CallFunction(0, "VirtualAllocEx", pHandle, 0, dllNameSize, #MEM_COMMIT+#MEM_RESERVE, #PAGE_READWRITE)
Debug(allocAddr)
CallFunction(0, "WriteProcessMemory", pHandle, allocAddr, dllName, dllNameSize, 0)
CallFunction(0, "CreateRemoteThread", pHandle, 0, 0, loadLibAddr, allocAddr, 0, 0)
CallFunction(0, "CloseHandle", pHandle)
CloseLibrary(0)
CloseLibrary(1)
Почему на бейсике? Потому что моя задача - объяснить алгоритм работы, а реализация на конкретном языке - дело вкуса. Кому-то ассемблер нравится, кому-то Си, кому-то - C# или Delphi. Не переживайте, в видео всё разжёвано.
Монетизация
...если это можно так назвать. :)
Подумал тут - а может, делать трейнеры на заказ? Скажем, символичные 10 центов за опцию. 10 опций - доллар, 12 опций - доллар 20 центов. Ну и бесплатные обновления, если в следующих версиях игр что-то перестанет работать.
Обсуждения и другие варианты - в комментарии.
Подумал тут - а может, делать трейнеры на заказ? Скажем, символичные 10 центов за опцию. 10 опций - доллар, 12 опций - доллар 20 центов. Ну и бесплатные обновления, если в следующих версиях игр что-то перестанет работать.
Обсуждения и другие варианты - в комментарии.
20121031
На картинке - наглядная демонстрация, как выглядит Virtual Method Table на более низком уровне. Первая строчка - закомментированный вызов той же самой функции, но при помощи макроса, следующие три - уже без макроса.
Помещаем содержимое pd3d в eax, помещаем содержимое этого адреса опять в eax, обращаемся к нужной функции используя полученный адрес. Помните, что я в видео говорил про двойной указатель? Вот, как бы, он и есть.
Если по-человечески, то выглядеть это будет как-то вот так:
pd3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
А вот чтобы выковырять адрес функции из pd3d, нам (или сишному компилятору) нужно сделать такой вот финт ушами, как на картинке выше.
PS: Если вы вдруг читаете заголовочные файлы D3D перед сном, то наверняка заметили, что в 99% функций первым аргументом идёт или D3DObject или D3DDevice. Смекаете, а? Можно перехватить любую функцию и получить доступ к актуальному на данный момент объекту\устройству.
Помещаем содержимое pd3d в eax, помещаем содержимое этого адреса опять в eax, обращаемся к нужной функции используя полученный адрес. Помните, что я в видео говорил про двойной указатель? Вот, как бы, он и есть.
Если по-человечески, то выглядеть это будет как-то вот так:
pd3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
А вот чтобы выковырять адрес функции из pd3d, нам (или сишному компилятору) нужно сделать такой вот финт ушами, как на картинке выше.
PS: Если вы вдруг читаете заголовочные файлы D3D перед сном, то наверняка заметили, что в 99% функций первым аргументом идёт или D3DObject или D3DDevice. Смекаете, а? Можно перехватить любую функцию и получить доступ к актуальному на данный момент объекту\устройству.
Я тут начитался всяких крутых форумов и вот, что получилось в процессе экспериментирования:
Это, товарищи, обыкновенный Prince of Persia: Sands of Time, только вот ему было сказано не заполнять полигоны текстурами, а оставлять только сетку. Wireframe mode, типа.
PS: Я не ожидал, что wallhack и прочие подобные читы делаются настолько просто, пусть и в довольно сыром виде. Вот и будет тема следующих уроков, когда загрузчик для меню допишем. :)
Это, товарищи, обыкновенный Prince of Persia: Sands of Time, только вот ему было сказано не заполнять полигоны текстурами, а оставлять только сетку. Wireframe mode, типа.
PS: Я не ожидал, что wallhack и прочие подобные читы делаются настолько просто, пусть и в довольно сыром виде. Вот и будет тема следующих уроков, когда загрузчик для меню допишем. :)
20121030
D3D: Новый алгоритм и работа со шрифтами.
Да-да, видеоурок с подробными объяснениями, но достаточно плохимм звуком. Выложу тут ссылку (видео будет доступно как на youtube, так и на форуме gamehacklab.ru), как только оно обработается.
[Ссылка] на урок.
Собственно, полный исходный код из видео:
#include
#include
#pragma comment(lib,"d3dx9.lib")
typedef IDirect3D9* (__stdcall *DIRECT3DCREATE9)(unsigned int);
typedef long (__stdcall *PRESENT9)(IDirect3DDevice9* self, const RECT*, const RECT*, HWND, void*);
PRESENT9 g_D3D9_Present = 0;
BYTE g_codeFragment_p9[5] = {0, 0, 0, 0, 0};
BYTE g_jmp_p9[5] = {0, 0, 0, 0, 0};
DWORD g_savedProtection_p9 = 0;
DWORD present9 = 0;
bool indicator = 0;
D3DRECT rec = {10, 10, 120, 30};
ID3DXFont *m_font = 0;
RECT fontRect = {10, 15, 120, 120};
D3DCOLOR bkgColor = 0;
D3DCOLOR fontColor = 0;
void DrawIndicator(void* self)
{
IDirect3DDevice9* dev = (IDirect3DDevice9*)self;
dev->BeginScene();
D3DXCreateFont(dev, 12, 0, FW_BOLD, 0, 0, 1, 0, 0, 0 | FF_DONTCARE, TEXT("Arial"), &m_font);
if(indicator)
{
bkgColor = D3DCOLOR_XRGB(0, 0, 255);
fontColor = D3DCOLOR_XRGB(0, 255, 255);
}
else
{
bkgColor = D3DCOLOR_XRGB(255, 0, 0);
fontColor = D3DCOLOR_XRGB(255, 0, 0);
}
dev->Clear(1, &rec, D3DCLEAR_TARGET, bkgColor, 1.0f, 0);
m_font->DrawText(0, "keng.gamehacklab.ru", -1, &fontRect, 0, fontColor);
dev->EndScene();
}
void GetDevice9Methods()
{
HWND hWnd = CreateWindowA("STATIC","dummy", 0, 0, 0, 0, 0, 0, 0, 0, 0);
HMODULE hD3D9 = LoadLibrary("d3d9");
DIRECT3DCREATE9 Direct3DCreate9 = (DIRECT3DCREATE9)GetProcAddress(hD3D9, "Direct3DCreate9");
IDirect3D9* d3d = Direct3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE d3ddm;
d3d->GetAdapterDisplayMode(0, &d3ddm);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
IDirect3DDevice9* d3dDevice = 0;
d3d->CreateDevice(0, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3dDevice);
DWORD* vtablePtr = (DWORD*)(*((DWORD*)d3dDevice));
present9 = vtablePtr[17] - (DWORD)hD3D9;
d3dDevice->Release();
d3d->Release();
FreeLibrary(hD3D9);
CloseHandle(hWnd);
}
long __stdcall HookedPresent9(IDirect3DDevice9* self, const RECT* src, const RECT* dest, HWND hWnd, void* unused)
{
BYTE* codeDest = (BYTE*)g_D3D9_Present;
codeDest[0] = g_codeFragment_p9[0];
*((DWORD*)(codeDest + 1)) = *((DWORD*)(g_codeFragment_p9 + 1));
DrawIndicator(self);
DWORD res = g_D3D9_Present(self, src, dest, hWnd, unused);
codeDest[0] = g_jmp_p9[0];
*((DWORD*)(codeDest + 1)) = *((DWORD*)(g_jmp_p9 + 1));
return res;
}
void HookDevice9Methods()
{
HMODULE hD3D9 = GetModuleHandle("d3d9.dll");
g_D3D9_Present = (PRESENT9)((DWORD)hD3D9 + present9);
g_jmp_p9[0] = 0xE9;
DWORD addr = (DWORD)HookedPresent9 - (DWORD)g_D3D9_Present - 5;
memcpy(g_jmp_p9 + 1, &addr, sizeof(DWORD));
memcpy(g_codeFragment_p9, g_D3D9_Present, 5);
VirtualProtect(g_D3D9_Present, 8, PAGE_EXECUTE_READWRITE, &g_savedProtection_p9);
memcpy(g_D3D9_Present, g_jmp_p9, 5);
}
DWORD __stdcall TF(void* lpParam)
{
GetDevice9Methods();
HookDevice9Methods();
return 0;
}
DWORD __stdcall KeyboardHook(void* lpParam)
{
while(1)
{
if(GetAsyncKeyState(VK_F1))
{
indicator = !indicator;
Beep(500,200);
}
Sleep(100);
}
return 0;
}
int __stdcall DllMain(HINSTANCE hInst, DWORD ul_reason_for_call, void* lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
CreateThread(0, 0, &TF, 0, 0, 0);
CreateThread(0, 0, &KeyboardHook, 0, 0, 0);
}
return 1;
}
Изменения с предыдущей версии:
-Новый алгоритм перехвата, похож на MS Detours - работает при помощи инъекции кода.
-Поддержка (какая-никакая) шрифтов.
В следующем видео будем свой инжектор для этого дела писать. :)
[Ссылка] на урок.
Собственно, полный исходный код из видео:
#include
#include
#pragma comment(lib,"d3dx9.lib")
typedef IDirect3D9* (__stdcall *DIRECT3DCREATE9)(unsigned int);
typedef long (__stdcall *PRESENT9)(IDirect3DDevice9* self, const RECT*, const RECT*, HWND, void*);
PRESENT9 g_D3D9_Present = 0;
BYTE g_codeFragment_p9[5] = {0, 0, 0, 0, 0};
BYTE g_jmp_p9[5] = {0, 0, 0, 0, 0};
DWORD g_savedProtection_p9 = 0;
DWORD present9 = 0;
bool indicator = 0;
D3DRECT rec = {10, 10, 120, 30};
ID3DXFont *m_font = 0;
RECT fontRect = {10, 15, 120, 120};
D3DCOLOR bkgColor = 0;
D3DCOLOR fontColor = 0;
void DrawIndicator(void* self)
{
IDirect3DDevice9* dev = (IDirect3DDevice9*)self;
dev->BeginScene();
D3DXCreateFont(dev, 12, 0, FW_BOLD, 0, 0, 1, 0, 0, 0 | FF_DONTCARE, TEXT("Arial"), &m_font);
if(indicator)
{
bkgColor = D3DCOLOR_XRGB(0, 0, 255);
fontColor = D3DCOLOR_XRGB(0, 255, 255);
}
else
{
bkgColor = D3DCOLOR_XRGB(255, 0, 0);
fontColor = D3DCOLOR_XRGB(255, 0, 0);
}
dev->Clear(1, &rec, D3DCLEAR_TARGET, bkgColor, 1.0f, 0);
m_font->DrawText(0, "keng.gamehacklab.ru", -1, &fontRect, 0, fontColor);
dev->EndScene();
}
void GetDevice9Methods()
{
HWND hWnd = CreateWindowA("STATIC","dummy", 0, 0, 0, 0, 0, 0, 0, 0, 0);
HMODULE hD3D9 = LoadLibrary("d3d9");
DIRECT3DCREATE9 Direct3DCreate9 = (DIRECT3DCREATE9)GetProcAddress(hD3D9, "Direct3DCreate9");
IDirect3D9* d3d = Direct3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE d3ddm;
d3d->GetAdapterDisplayMode(0, &d3ddm);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
IDirect3DDevice9* d3dDevice = 0;
d3d->CreateDevice(0, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3dDevice);
DWORD* vtablePtr = (DWORD*)(*((DWORD*)d3dDevice));
present9 = vtablePtr[17] - (DWORD)hD3D9;
d3dDevice->Release();
d3d->Release();
FreeLibrary(hD3D9);
CloseHandle(hWnd);
}
long __stdcall HookedPresent9(IDirect3DDevice9* self, const RECT* src, const RECT* dest, HWND hWnd, void* unused)
{
BYTE* codeDest = (BYTE*)g_D3D9_Present;
codeDest[0] = g_codeFragment_p9[0];
*((DWORD*)(codeDest + 1)) = *((DWORD*)(g_codeFragment_p9 + 1));
DrawIndicator(self);
DWORD res = g_D3D9_Present(self, src, dest, hWnd, unused);
codeDest[0] = g_jmp_p9[0];
*((DWORD*)(codeDest + 1)) = *((DWORD*)(g_jmp_p9 + 1));
return res;
}
void HookDevice9Methods()
{
HMODULE hD3D9 = GetModuleHandle("d3d9.dll");
g_D3D9_Present = (PRESENT9)((DWORD)hD3D9 + present9);
g_jmp_p9[0] = 0xE9;
DWORD addr = (DWORD)HookedPresent9 - (DWORD)g_D3D9_Present - 5;
memcpy(g_jmp_p9 + 1, &addr, sizeof(DWORD));
memcpy(g_codeFragment_p9, g_D3D9_Present, 5);
VirtualProtect(g_D3D9_Present, 8, PAGE_EXECUTE_READWRITE, &g_savedProtection_p9);
memcpy(g_D3D9_Present, g_jmp_p9, 5);
}
DWORD __stdcall TF(void* lpParam)
{
GetDevice9Methods();
HookDevice9Methods();
return 0;
}
DWORD __stdcall KeyboardHook(void* lpParam)
{
while(1)
{
if(GetAsyncKeyState(VK_F1))
{
indicator = !indicator;
Beep(500,200);
}
Sleep(100);
}
return 0;
}
int __stdcall DllMain(HINSTANCE hInst, DWORD ul_reason_for_call, void* lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
CreateThread(0, 0, &TF, 0, 0, 0);
CreateThread(0, 0, &KeyboardHook, 0, 0, 0);
}
return 1;
}
Изменения с предыдущей версии:
-Новый алгоритм перехвата, похож на MS Detours - работает при помощи инъекции кода.
-Поддержка (какая-никакая) шрифтов.
В следующем видео будем свой инжектор для этого дела писать. :)
20121029
D3D: Новости
Засел я тут за нашу менюшечку, переписал алгоритм, так что он теперь немного другой и я всё-таки запишу по нему видеоурок, в котором всё постараюсь разжевать. Почему? Потому что теперь это ни что иное, как инъекция кода. :)
Сейчас курочу шрифты и загрузчик, ждите.
Сейчас курочу шрифты и загрузчик, ждите.
Маленькая поправка к коду D3D-хука
Я тут нашёл свои старые записи и выяснилось, что вот это место:
while(1)
{
Sleep(100);
HookVTableFunction((PDWORD*)npDevice, (PBYTE)hkReset, 16);
HookVTableFunction((PDWORD*)npDevice, (PBYTE)hkPresent, 17);
}
В функции, которую крутит поток TF, выполняет очень тупой костыль - каждые 100 миллисекунд переустанавливает хук, потому что как сама винда, так и игра очень этого дела не любят и во многих играх у меня в изначальной версии (без цикла) хук довольно быстро умирал сам по себе. С чем это поведение связано - не знаю, но разберусь.
Я это к тому, что если вдруг вы прочитали код и полностью в него вникли, а не просто ктрлц+ктрлв. :D
Пока что работаю над шрифтами, они и будут темой следующего видео.
while(1)
{
Sleep(100);
HookVTableFunction((PDWORD*)npDevice, (PBYTE)hkReset, 16);
HookVTableFunction((PDWORD*)npDevice, (PBYTE)hkPresent, 17);
}
В функции, которую крутит поток TF, выполняет очень тупой костыль - каждые 100 миллисекунд переустанавливает хук, потому что как сама винда, так и игра очень этого дела не любят и во многих играх у меня в изначальной версии (без цикла) хук довольно быстро умирал сам по себе. С чем это поведение связано - не знаю, но разберусь.
Я это к тому, что если вдруг вы прочитали код и полностью в него вникли, а не просто ктрлц+ктрлв. :D
Пока что работаю над шрифтами, они и будут темой следующего видео.
Подписаться на:
Сообщения (Atom)