Добро пожаловать, гость ( Вход | Регистрация )
| Siberian GRemlin |
Jun 14 2018, 11:14
Сообщение
#1
|
|
Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 538 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 223 раз(а) |
Дайте кто-нибудь совет новичку, кратко, чтобы не читать целую книгу.
Как найти в отладчике функцию в exe игры, которая отвечает за расшифровку файла? Игра полностью читает файл в память, в котором предположительно лежит ключ и сами зашифрованные данные, либо ключ как-то генерируется из имени файла или его размера, ибо для каждого файла ключ свой. Проблема в том, что файл сначала был сжат zlib'ом, затем зашифрован, также используется выравнивание до размера кратного 16. Возможно, файлы сжимаются блоками и уже их выравнивали, а затем всё шифровали, так как чем больше размер файла тем больше разница со сжатым мной — в паке есть и зашифрованные/сжатые файлы и чистые, но использует только зашифрованные. Судя, по основному архиву игры, шифруется обычным цикличным xor. В архиве к каждому файлу прилагался свой ключ в 20 байт. Хотелось бы овладеть навыком находить ключи и сами алгоритмы шифрования. |
![]() ![]() |
| -=CHE@TER=- |
Mar 13 2026, 23:01
Сообщение
#2
|
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,428 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 327 раз(а) |
Добавлю ещё кое-чего. Сначала код, потому объясню как им пользоваться (код из моей программы перевода The Neverhood):
CODE static void MemWrite(void *p, void *b, DWORD l) { В общем, я в сообщении выше давал код wrapper/proxy/обёртки .DLL, но бывают ситуации, когда нужно не просто экспортируемые функции библиотеки на свои заменить, но и что-то ещё перехватить.DWORD dx; if (p && b && l && VirtualProtect(p, l, PAGE_READWRITE, &dx)) { CopyMemory(p, b, l); VirtualProtect(p, l, dx, &dx); FlushInstructionCache(GetCurrentProcess(), p, l); } } #define HOOK_NONE 0x00 #define HOOK_CALL 0xE8 #define HOOK_JUMP 0xE9 static void HookAddr(void *hd, void *hs, DWORD ht) { BYTE *d, *s; d = hd; s = hs; // need to add jump / call instruction if (ht != HOOK_NONE) { MemWrite(d, &ht, 1); // recalc addr s -= (((SIZE_T) d) + 5); d++; // skip call / jump instruction } MemWrite(d, &s, 4); } Например, есть функция куда кривые/косые/неверные входные аргументы попадают. Проверить и поправить их перед тем как отдать функции места нет или нужно внутри функции ещё что-то сделать. Тогда приходится в DLL-обёртке перехватывать ещё и такие функции. Стратегий перехвата, как и с прерываниями в DOS когда-то, всего три: 1. Функция-перехватчик вызывается вместо оригинальной (полная замена). 2. Функция-перехватчик вызывается после оригинала (сначала вызываем оригинальную функцию, потом смотрим что делать с результатом). 3. Функция-перехватчик вызывается до оригинала (сначала подготавливаем входящие параметры и/или среду необходимым образом, затем уже вызываем оригинальную функцию). Предположим, есть у нас такой код в дизассемблере (точка в начале означает виртуальный адрес): CODE .40100E: E8 6D 00 00 00 call .401080 Помним, про то что у длинного JUMP, как и у CALL, после байта операции идёт не адрес вызова, а смещение относительно конца текущей команды - насколько перейти. В данном случае:0x40100E (текущий адрес) + 5 (размер текущей инструкции) + 0x0000006D (смещение) из чего получаем: 0x40100E + 5 + 0x0000006D = 0x401080 (поэтому там такой адрес и стоит напротив CALL). В общем, нашли такую функцию, которую хотим перехватить, и её адрес. Теперь как для каждой из трёх стратегий нам нужно будет поступить: В случае замены функции всё просто: CODE HookAddr((BYTE *) 0x40100E, (void *) hook_MyFunc, HOOK_CALL); Где hook_MyFunc() - это функция прототип которой полностью соответствует прототипу перехваченной функции! Т.е. тип вызова (cdecl/stdcall/thiscall/и т.п.), количество аргументов и их типы. Иначе либо стек разрушите, либо получите что попало вместо результата работы.В случае вызова функции до/после из оригинальной - точно также хучите вызов, но в коде hook_MyFunc() либо вызываете оригинал функции в начале, либо в конце, перед выходом и возвращением результата. Обратите внимание, что для вызова оригинальной функции должен быть объявлен прототип. Вот пример перехвата функции SetLastError() из WinAPI: CODE // hooked function prototype typedef void (WINAPI *LPSETLASTERROR)(DWORD dwErrCode); // hook for replaced function void WINAPI hook_SetLastError(DWORD dwErrCode) { LPSETLASTERROR orig_SetLastError; // for example overwrite access denied error if (dwErrCode == ERROR_ACCESS_DENIED) { dwErrCode = ERROR_SUCCESS; } // call original routine orig_SetLastError = (LPSETLASTERROR) 0x401080; orig_SetLastError(dwErrCode); } // somewhere in the code hook original function call HookAddr((BYTE *) 0x40100E, (void *) hook_SetLastError, HOOK_CALL); Далее. Иогда функция, которую нужно заменить, вызывается в куче разных мест и заменять через HOOK_CALL каждое такое место - удовольствие ниже среднего. В таком случае хучим уже адрес начала функции через HOOK_JUMP: CODE HookAddr((BYTE *) 0x401080, (void *) hook_SetLastError, HOOK_JUMP); Обратите внимание, что вместо 0x40100E, там теперь стоит адрес 0x401080 и вместо HOOK_CALL стоит HOOK_JUMP. Иными словами мы в начало кода функции вкорячиваем безусловный переход на нашу функцию. Хочу отметить, что при таком способе вызов оригинальной функции будет невозможен, потому что мы только что уничтожили её начало! Поэтому этот способ подходит только при полной замене функции, которая много вызывается в разных местах. Наконец, HOOK_NONE используется когда нужно в массив адресов функций (какая-то структура) просто вписать адрес нужной нам функции-перехватчика без каких-либо изменений. Последнее, что хочу заметить, так это то что функции вполне себе нормально хучить в DLL-обёртке из DllMain() при fdwReason == DLL_PROCESS_ATTACH. Однако хучить функции и вызывать код - две большие разницы! Помните, что в DllMain() можно делать очень небольшой набор вещей, иначе получите ту же проблему, что и в старых 3DMark (ссылка). Если нужно вызвать код, который что-либо инициализирует, то перехватываете какую-нибудь функцию в WinMain() или сам вызова WinMain() из CRTStartup() и оттуда уже вызываете код всяких библиотек, создаёте окна и всё такое прочее. Если есть какие-то ресурсы, которые нужно непременно корректно освободить перед закрытием программы, то перехватываете какую-либо функцию, которая вызывается при закрытии приложения, и там уже делаете необходимые действия не забывая до или после вызвать перехваченный оригинал. |
Siberian GRemlin Работа с отладчиком Jun 14 2018, 11:14
-=CHE@TER=- Ты прям хочешь руководство в две строчки. Так не б... Jun 14 2018, 17:15
-=CHE@TER=- Несколько корректировок.
5) Т.к. ты брезгуешь нор... Oct 6 2018, 14:06
Siberian GRemlin На пункте 14 идёт чтение по одному байту.
REP MOVS... Jun 20 2018, 18:21
-=CHE@TER=- Да, это пересылка, нужно было просто F8.
REP - сок... Jun 20 2018, 19:51
Siberian GRemlin Я так понимаю, операция «CALL» просто строкой отоб... Jun 21 2018, 08:05
-=CHE@TER=- Эээ... это я опять торможу. F7 - зайти внутрь функ... Jun 21 2018, 20:03
-=CHE@TER=- Ещё добавлю - Олька в оригинале устарела (но для н... Aug 9 2018, 15:43
LexSafonov Внесу свою лепту вопросов.
Существуют сейчас в пр... Oct 30 2020, 08:10
-=CHE@TER=- Существуют сейчас в природе нормальные инструменты... Oct 30 2020, 14:28
-=CHE@TER=- По DOSBox Debugger, пока не забыл.
Если кто-то буд... Nov 16 2025, 11:45![]() ![]() |
| Упрощённая версия | Сейчас: 1st May 2026 - 13:05 |