Показаны сообщения с ярлыком Инъекция кода. Показать все сообщения
Показаны сообщения с ярлыком Инъекция кода. Показать все сообщения

20150303

ESP. Часть 2. Выводим найденное в консольку.

А вот и видео!


В нем я вслух и почти построчно разбираю 100 строк кода на Си, которые делают следующее:

0. Находят окно игры
1. Получают хэндл процесса с правами чтения из памяти
2. Читают количество игроков на карте
3. Бегут в цикле, читая адрес каждой структуры игрока
4. Выводят в консоль его здоровье и координаты

В следующем уроке я расскажу, как именно мы будем выводить данные в окно игры (напомню, что это будет НЕ через DirectX-хук - мы легких путей не ищем!) и что для этого понадобится (не бойтесь, одна-единственная, но очень важная переменная в памяти игры. Будет еще немного СЕ), а через урок - покажу, какой код это делает.

20140119

D3D: Трагически наступившее "скоро".

Всем привет!

У меня нашлось время и я решил ещё немного поковыряться с D3D и прочими неприличными словами. (:

Остановились мы на том, что написали перехватчик методов D3D, ответственных за рисование, и смогли нарисовать на экране чужого приложения квадратик. Попутно мы написали свой собственный инжектор DLL-ок.

Это всё, конечно, круто, но не очень сильно впечатляет и далековато от наших амбиций. Итак, встречаем:


Вот эта самая функция нам и понадобится. Я, честно сказать, фигово разбираюсь в 3д-графике и рисовании в целом, так что гуру этой области прошу меня сильно не пинать - объяснять буду, как понял сам.

Как мы помним, читали или где-нибудь слышали, нельзя просто так взять и нарисовать оружие, машину или нечто такое. Видеокарточки туповаты и рисовать умеют только примитивы - точки, линии, треугольники. Захотели квадрат - сложили два треугольника вместе. Эти штуки ещё иногда зовут полигонами, ага. Дык вот эта самая функция используется для отрисовки примитивов, если очень кратко. Нас там будет интересовать параметр BaseVertexIndex, в суть которого я тоже вник крайне смутно и пытаться объяснить это кому-то пока что не стану - давайте просто звать эту штуку индексом. Ещё пара крутых штук ждёт нас чуть дальше, когда я код приведу. Так как у меня возникли некоторые трудности с перехватом этой функции, то перехватывать мы её будем не вручную, а при помощи библиотечки detours от microsoft (все необходимые файлы идут в комплекте с исходниками), так что код слегка видоизменился. Вот, например:



Тут сразу бросаются в глаза последние три строчки - это, собственно, и есть перехват. Делает библиотека всё то же самое - мы скармливаем ей адрес найденной фунции, которую нужно перехватить, в её начало дописывается переход (JMP 00112233) на перехваченную функцию, а перехваченная выставляет всё, как было, отрабатывает и возвращает хук на место. Едем дальше:



Это - перехваченная DrawIndexedPrimitive, при помощи которой игра будет пытаться рисовать всё то, что мы с вами видим на экране. Я не буду вдаваться глубоко в подробности, нам это нужно один раз, так что приведу примерную суть:

1. Каждый вызов у нас есть некие данные (Stride и BaseVertexIndex) , которые нам надо узнать и запомнить для использования уже в чите. Они характеризуют тот или иной объект на экране.
2. Каждый раз, когда игра пытается что-нибудь нарисовать, мы заносим структуру этих данных в массив.
3. В логгере есть кнопки, позволяющие управлять процессом - менять значения Stride и BaseVertexIndex, выбранные на данный момент будут подсвечиваться зелёным цветом.
4. Задача - натыкать такое значение, чтобы нужный нам объект(-ты) подсветился(-лись) зелёным. Запомнить цифры.

Скомпилируется всё это дело в DLL, которую надо будет заинжектить в процесс игры (я не парюсь и продолжаю делать это через Cheat Engine). Поддерживаются любые игры, которые рисуют при помощи D3D9.

ВНИМАНИЕ тем, кто запускает игры из Steam (как я, например): В стиме в свойствах игры надо отрубить steam overlay ui, так как он работает точно так же - иначе получим вылет.

ВНИМАНИЕ тем, кто до сих пор не использует MS Visual Studio 2013 (как я, например): Если вам лень ставить 13-ю студию, то просто слейте все исходники и создайте новый проект (DLL). Ему понадобится в свойствах указать путь до include и lib для MS DirectX SDK (полгига которого можно спокойно скачать в гугле), если заругается на отсутствие detours.lib - она лежт в папке проекта.

Результат:


А вот и [ссылка] на все исходники. На днях будем учить этих инопланетных парней просвечивать сквозь стены. (:

20121124

D3D: Пишем DLL-инжектор, но уже на C++

[Ссылочка] на урок. Разбираем код DLL-инжектора на C++, который я сваял из тестового D3D-приложения.

А [вот] ссылочка на весь проект, выложенная на [GitHub]. Комментарии на английском. Проект будет пилиться вместе с записью уроков, так что можно его считать тестовой площадкой. Для компиляции понадобится MSVS, я использую 2012 Express.

Сейчас буду записывать следующий урок, для затравки - читаем [это] и [это].

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 - работает при помощи инъекции кода.
-Поддержка (какая-никакая) шрифтов.

В следующем видео будем свой инжектор для этого дела писать. :)

20120715

Продолжаем бороться с DMA.

Или встречайте [новый видеоурок]!

Демонстрирую небольшую фишку, когда надо "заморозить" значение, а указатель искать лень или не получается. Ещё, кстати, код отлаживаем в OllyDbg - вдруг кому-то кроме меня интересен этот замечательный отладчик.

Ну и небольшую фишку по ассемблерным скриптам в СЕ тоже покажу.

20111225

Ломаем игры по заявкам читателей.

На этот раз ломаем закись азота (N2O) в игре Need For Speed: Carbon. Попутно узнаём ещё одну новую инструкцию по работе с дробными (float) числами. :)

[Ссылка] на урок.

20111222

Третья часть про трейнеры!

На этот раз - видео. Вот [ссылка].
Заодно выкладываю [архив] с исходным кодом и скомпилированным трейнером.

Нагуглите и прочитайте обо всём, что не стало понятным из урока (в частности - про новые WinAPI-функции), спросите об оставшеимся тут, в комментариях на youtube или на [форуме], медитируйте, делайте зарядку и чистите зубы 2 раза в день. :)

Согласен, урок получился довольно скомканным - но у меня очень мало времени, простите. Работа, да и праздники, как-никак.

PS: Очень полезно будет загрузить скомпилированный трейнер в Olly (File -> Open) и посмотреть на его работу пошагово. Многое запросто может встать на свои места. Попробуйте, не бойтесь экспериментировать! Это же так весело и интересно. :)

20111216

ВНЕЗАПНО, видеоурок!

По стратегиям, который я всё-таки записал, самым невероятным образом. Расскажу - не поверят!. Итак, опираясь на недоперевод статьи из предыдущих постов - переходим к наглядной практике:

[ссылка на урок]

Уже после записи урока хотел бы добавить пару слов - не обязательно лезть в дебри отладки и кода, когда можно чуток включить голову, ну правда. :)

Если сложно и не ломается - да забейте вы на этот отладчик, попейте чаю, возьмите листочик с карандашом и порисуйте лучше. Ну, в смысле, как на ваш взгляд всё это должно работать. Тогда может и идея какая придёт в светлую голову, так-то!

И в очередной раз можно убедиться в исключительной полезности гугла и петабайт справочных материалов, по сети раскиданных. Встретили команду, но не знаете нафига она? Гугл! Забыли как правильно пишется какое-то слово? Гугл! Хотите научиться ломать игры? Г!.. Ан-нет, - keng. :)


20110929

Вторая часть седьмого видеоурока - делаем спидхак для Crimsonland!

С разбегу - [ссылка]!

Во второй части я покажу, как делается взлом скорости ("быстро бегать") для замечательной аркады Crimsonland, попутно объясню значение пары новых ассемблерных команд, характерных только для данного типа взлома. А именно:

fld [eax] - загружает значение типа float (с плавающей точкой) в [математический сопроцессор] (он же FPU) для последующих операций над ним.
fstp [eax] - соответственно, выгружает результат обратно в регистр eax.

Как именно вся эта фигня работает нам знать не настолько важно. Нам будут встречаться конструкции вида:

fld [eax+1234]
fadd [edi] (то же, что и add)
fsub [esx] (то же, что и sub)
fstp [eax+1234]


Основная мысль заключается в том, что главный процессор не умеет работать с дробными числами (float), так что для этого существует процессор поменьше - сопроцессор, который умеет только это. Командой fld (float load) мы заставляем главный процессор скормить нужное нам число сопроцессору, дальше в нём происходят какие-то действия над числом (fadd, fsub и т.п.), после чего по команде fstp число грузится обратно в главный процессор. Очень похоже на code-injection, правда? :)

Наша задача - или вклиниться в действия над значением после его загрузки в сопроцессор (пример - на видео), или посмотреть, что происходит чуть раньше - скажем, какая разница в координатах прибавляется\убавляется, часто можно обнаружить некоторый множитель. Типа, идём шагом - координаты умножаются на единичку, бежим бегом - умножаются на пять (бежим впятеро быстрее, чем идём шагом). Механизм действий схож для большинства игр, т.к. практически все хранят координаты в типе float, но ньюансы встречаются - нет универсального решения.

Теорию про сопроцессор и сопутствующие ему команды я выложу тут.



20110916

Шестой видеоурок, так сказать! :D

А если серьёзно - просто видеопояснение к [предыдущей записи] о Code-Injection. Ломаем таймер матча в Mortal Combat 4, а ещё - узнаём две новых команды ассемблера (совсем простеньких):

inc \ dec [регистр] - англ. increment (увеличение) \ decrement (уменьшение) - увеличивает или уменьшает содержимое [регистра] на единичку. Пример:

mov edi, 5 //кладём в edi пятёрочку
dec edi      //делаем магию!!

В итоге в edi будет лежать четыре. :)


В следующих двух видеоуроках будем учиться высоко (очень высоко!) прыгать и не менее быстро бегать. Оставайтесь на линии, Ваш звонок очень важен для нас! :D

[ссылка на урок]


Code-Injection: Подробности

В [пятом уроке] (и в четвёртом тоже ^^) мы рассматривали такую технику, как Code-Injection, или, если буквально, "инъекция кода". Давайте остановимся на этой штуке ещё немного - больно уж часто она используется. Больше скажу - одна из самых распостранённых техник взлома. :)

Итак. Представим, что имеется у нас вот такой гипотетический код игры:

Адрес   |  Команда      |    Комментарий
---------------------------------------------------------------------------------
0000000: mov eax, 3     //помещаем в регистр eax число "3"
0000001: sub eax, 1      //вычитаем из еах единичку
0000002: cmp eax, 0    //сравниваем еах с нулём
0000003: je "exit"         //если еах = 0, то выходим из игры
0000004: add eax, 2    //иначе прибавляем к еах двойку
0000005:
0000006:
0000007:
0000008:
0000009:
---------------------------------------------------------------------------------

Скажем, нашли мы нужный адрес в Cheat Engine, включили отладчик дабы посмотреть - а с чего это у нас заканчиваются жизни (которые, как вы поняли, в еах находятся), отладчик нам выплёвывает адрес 0х00000001.

Если мы хотим, чтобы жизни просто не убавлялись, мы заменяем команду "sub eax, 1" nop'ами - командами, которые ничего не делают. Почему я написал во множественном числе? Всё просто - одна команда "nop" занимает один байт места, а вот "sub eax, 1" места занимает чуток побольше. Точные значения нас сейчас не волнуют (их можно посмотреть в отладчике), скажем, что это четыре байта. Поэтому вместо:

sub eax, 1

Мы должны будем написать четыре команды "nop", по одной на каждый байт:

nop
nop
nop
nop

Если не соблюдать это правило (количество байт кода всегда неизменно) - получите синий экран и придётся делать заново.

Идём дальше. В чём суть техники Code-Injection? Мы берём какую-то команду (в данном примере - ту, что вычитает у нас жизни) и заменяем её на команду jmp - прыжок или переход. Там выполняем какой-то код, затем возвращаемся обратно - в код игры. Смотрите:

Адрес   |  Команда      |    Комментарий
---------------------------------------------------------------------------------

0000000: mov eax, 3             //помещаем в регистр eax число "3"
0000001: jmp [0000006]      //переходим ("прыгаем") на адрес 0x0000006
0000002: cmp eax, 0            //сравниваем еах с нулём
0000003: je "exit"                 //если еах = 0, то выходим из игры
0000004: add eax, 2            //иначе прибавляем к еах двойку
0000005:
0000006: add eax, 10 //добавляем к еах десять
0000007: jmp [0000002] //прыгаем обратно - на адрес 0x0000002
0000008:
0000009:
---------------------------------------------------------------------------------
В результате этих действий мы заставили игру выполнить наш код (находящийся под адресам 0х0000006-0х0000007) а затем вернуться обратно и продолжить свой. Что нам это даёт? На самом деле - что угодно, всё зависит от фантазии и навыков программирования. Можно добавить жизней, можно патронов, можно всех врагов убить, можно любимой бабушке привет  передать - в общем, чего душа пожелает, а дальше игра послушно вернётся к своим делам. :)

Почему делать нужно именно так? Потому что про количество байт и длину кода я писал не просто так - мы не можем уместить тысячу байт кода в размеры одной трёх-четырёхбайтовой команды - поэтому засовываем код в отдельный участок памяти, обращаемся к нему, а затем продолжаем исполнение игрового кода. Прелесть в том, что код всегда будет находиться в одном и том же месте - адреса меняться не будут. Такая вот загогулина. Использовать этот не слишком хитрый приём мы будем ещё очень много раз, так что понимание успеет придти. Если же нет - пишите, задавайте вопросы. :)