IPB

Добро пожаловать, гость ( Вход | Регистрация )

 
Reply to this topicStart new topic
> Работа с отладчиком
Siberian GRemlin
Jun 14 2018, 11:14
Сообщение #1


Advanced Member
***

Группа: CTPAX-X
Сообщений: 472
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 200 раз(а)



Дайте кто-нибудь совет новичку, кратко, чтобы не читать целую книгу.

Как найти в отладчике функцию в exe игры, которая отвечает за расшифровку файла? Игра полностью читает файл в память, в котором предположительно лежит ключ и сами зашифрованные данные, либо ключ как-то генерируется из имени файла или его размера, ибо для каждого файла ключ свой. Проблема в том, что файл сначала был сжат zlib'ом, затем зашифрован, также используется выравнивание до размера кратного 16. Возможно, файлы сжимаются блоками и уже их выравнивали, а затем всё шифровали, так как чем больше размер файла тем больше разница со сжатым мной — в паке есть и зашифрованные/сжатые файлы и чистые, но использует только зашифрованные.

Судя, по основному архиву игры, шифруется обычным цикличным xor. В архиве к каждому файлу прилагался свой ключ в 20 байт.

Хотелось бы овладеть навыком находить ключи и сами алгоритмы шифрования.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Jun 14 2018, 17:15
Сообщение #2


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,188
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 286 раз(а)



Ты прям хочешь руководство в две строчки. Так не бывает.
Короче (для x86 - 32х битных приложений) - только самое основное:
1) Скачиваешь Ольку 1.10 (слева в меню "Odbg110.zip").
2) Скачиваешь CommandBar для неё: CommandBar
3) Распаковываешь Ольку, распаковываешь CommandBar и кидаешь в каталог к Ольке.
4) Запускаешь и открываешь там свой исполняемый файл (F3 или File - Open).
5) Т.к. ты брезгуешь нормальными системами, то запускаешь Ольку от имени Администратора, плюс помни, что первый вылет в ней будет не на OEP, а где-то в дебрях системы, ибо Windows Vista и выше - гоумно. Так что делай сразу F9, чтобы оно до OEP дошло.
6) Теперь жмёшь Ctrl+D или переключаешься на CommandBar снизу.
7) Пишешь там и жмёшь Enter:
bpx CreateFileA
Убедись, что там появилось "+ Breakpoint is set" или вводи CreateFileW или ещё чем там игра файлы открывает.
8) Отпускаешь программу на выполнение (F9). Если там нет антиотладочных приёмов, то она должна выпасть на вызове CreateFileA или на чём ты бряк ставил.
9) Когда выпадет - смотри окно справа-снизу. Это стек - смотри там имя открываемого файла. Жмёшь F9, пока там не будет интересующий тебя файл.
10) Теперь делаешь F8 - Step over, чтобы функция открытия выполнилась. Смотришь окно в верхнем-правом углу - это регистры, тебя интересует eax - это дескриптор только что открывшегося файла. Если там -1 (0xFFFFFFFF), значит файл не открылся - смотри в том же окне сообщение об ошибке (LastErr).
11) Теперь ты знаешь дескриптор файла. Ставишь бряк на:
bpx ReadFile
и отпускаешь на выполнение F9.
12) Выпал на чтении файла - смотри в стеке какой там дескриптор. Если твой - значит читается тот файл, которые тебя интересует, иначе - F9. Если файл твой - щёлкай правой кнопкой мышки в стеке на втором параметре сверху (адрес буфера) и выбираешь там "Follow in Dump" (третий пункт снизу). Теперь у тебя в окне слева-снизу показано начало памяти, куда будет прочитан файл.
13) Читаешь файл - F8. Теперь в окне памяти смотришь, что прочиталось именно то что нужно (можешь ещё на всякие SetFilePointer бряков понаставить, если файл не сначала читается, но больше лишних телодвижений будет). Ставишь на эту память бряк - щёлкаешь на первом байте левой клавишей, затем со Shift'ом щёлкаешь на каком-нибудь ещё, чтобы они выделились, затем правой клавишей на выделении и выбираешь Breakpoint - Memory, on access. Можешь выбрать на "on write", будет быстрее, но это только при условии, что буфер расшифровывается сам в себя, а не записывается куда-то ещё. К тому же "on access", позволит перехватить момент, когда прочитан первый байт, чтобы понять что дальше с ним делается.
14) Отпускаешь программу на выполнение (F9) и, теоретически, ты должен выпасть в месте, где память читают и расшифровывают.
15) Обрати внимание, что есть программы написанные наркоманами, так что там перед расшифровкой блок память могут таскать между буферами: buf1 - buf2 - buf3. Тогда бряк выпадет на копировании памяти из первого буфера во второй. А расшифровка, как ты понимаешь, будет третьего. Т.к. копирование выполняется в большинстве случаев стандартными средствами, то смотри регистр edi - это адрес памяти назначения. Переходим туда (правой клавишей мышки - Follow in Dump), и, после копирования, ставим там бряк на это место памяти. Этот бряк добавится к уже существующему, так что остальные можешь удалить, если не нужны, чтоб лишний раз не дёргало - Breakpoint - Remove memory breakpoint (после этого ставишь новый бряк, ибо эта функция удаляет все бряки).
Будут вопросы - задавай. Только с конкретными деталями что именно не получается.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Jun 20 2018, 18:21
Сообщение #3


Advanced Member
***

Группа: CTPAX-X
Сообщений: 472
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 200 раз(а)



На пункте 14 идёт чтение по одному байту.
CODE
REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]


Затем идёт заполнение блока памяти значением $CD.
CODE
REP STOS BYTE PTR ES:[EDI]


Я так понимаю, считывалось по байту отсюда, расшифровывалось и писалось в другое место? Я на чтении дал F9, но, полагаю, надо было F8.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Jun 20 2018, 19:51
Сообщение #4


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,188
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 286 раз(а)



Да, это пересылка, нужно было просто F8.
REP - сокращение от REPEAT (повторить).
В ECX должно быть количество раз (сколько повторить - C = Counter; ECX - регистр-счётчик).
MOVS - пересылка (BYTE/WORD/DWORD), у тебя пересылается BYTE - значит в ECX должно быть количество пересылаемых байт.
EDI - destination - адрес буфера назначения (куда пересылается).
ESI - source - адрес буфера источника (откуда).
Там ещё флаг направления задаётся командой CLD (+) или STD (-) - в первом случае значение EDI до MOVS будет указывать на начало буфера (увеличивается), а во втором на его конец (уменьшается), но, думаю, здесь всё же идёт увеличение.

Совсем забыл:
Ctrl+F2 - перезапустить программу.
Alt+F2 - закрыть и выгрузить программу, но не закрывать Ольку - это пригодится, если нужно изменить исполняемый файл, после чего достаточно нажать упомянутое выше Ctrl+F2, чтобы Олька сама перезагрузила последний открытый файл. Только замечу тут же, что Олька после любого изменения будет считать файл новым, так что все выставленные при помощи клавиши F2 бряки в коде вылетят в трубу - здесь снова спасает CommandBar, ибо набранное там, к примеру:
bp 401000
Сохранится в истории и никуда не денется, так что достаточно выбрать и снова нажать Enter. Кстати, посмотри справку к CommandBar, если интересно, там есть ещё condition break (помечаются фиолетовым цветом, а не красным) - это когда бряк должен срабатывать при определённом условии - здорово выручает, когда у тебя какой-то код вызывается из 1000 разных мест, а тебе нужно конкретное - ставишь условие, когда, например, в регистре EAX будет 0x1234 и только тогда такой бряк сработает.

Ещё пара полезных команд для CommandBar:
AT <адрес> - перейти на адрес в дизассемблере.
D <адрес> - перейти на адрес в дампе памяти; в принципе, можно всегда щёлкнуть по регистру, стеку или памяти и выбрать там "Show in Dump", но иногда, бывает, ты заранее уже знаешь какой-то статический адрес, где будет что-то лежать - можно сразу перейти туда после перезапуска программы и поставить там бряк на обращение.

И ещё насчёт команды BP и BPX. Иногда бывает такое, что функция из ядра системы (та же CreateFileA, например) импортируется, но "bpx CreateFileA" ничего не даёт и бряк не ставится. Так вот в такой ситуации можно поставить бряк на саму функцию CreateFileA в ядре системы:
1) Щёлкаешь правой клавишей на окне дизассемблера.
2) Выбираешь View -> Module 'kernel32'.
3) Теперь тебя переключили из основной программы на выбранный модуль.
4) Тут пишешь не bpx, а именно "bp CreateFileA", никаких сообщений не будет, но бряк установится.
5) Возвращаешься назад в программу (см. п.2) и запускаешь её.
6) Когда вылетит, то ты будешь стоять в начале кода функции. Чтобы узнать кто её вызвал, делай Ctrl+F9 (Execute till return), затем F8 и ты на инструкции, которая идёт после открытия файла. Или можно сразу после вылета на бряке встать на стеке на последний параметр, щёлкнуть правой клавишей мышки и выбрать "Follow in Disassembler" (Enter), чтобы сразу перейти на адрес возврата.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Jun 21 2018, 08:05
Сообщение #5


Advanced Member
***

Группа: CTPAX-X
Сообщений: 472
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 200 раз(а)



Я так понимаю, операция «CALL» просто строкой отображается и надо уже в IDA смотреть что там за функция? Как-то быстро после чтения файла попадаю в место, где передаётся размер разжатых данных, а затем начинают мелькать значения параметров из разжатого XML, то есть идёт полным ходом чтение расшифрованного и разжатого файла.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Jun 21 2018, 20:03
Сообщение #6


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,188
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 286 раз(а)



Эээ... это я опять торможу. F7 - зайти внутрь функции (Step into).
А ещё F8 на инструкции loop сразу пройдёт весь цикл, в то время как F7 будет переходить на начало при каждом шаге.
Смотри в пунктах меню Ольки (как минимум View и Debug) что есть, а то я так сразу про все важные клавиши и не вспомню.
Ещё выше писал про пропажу бряков от клавиши F2. Но про саму клавишу не написал: F2 - поставить/снять бряк. Если файл не изменялся, то при перезапуске бряки не будут пропадать, кроме случаев когда бряк стоит на памяти которая была динамически выделена или внутри .DLL.
Пробел в окне дизассемблера вызывает диалог изменения кода (все изменения вылетят в трубу, после перезапуска программы). Наверное их как-то можно сделать постоянными, но не осилил разбираться, ибо оно не так уж и часто нужно было.
А ещё Alt+F9 - Execute till user code. Это полезно, когда программа выплюнула какое-то окно, а тебе нужно после него что-то посмотреть. Если делать пошаговое выполнение, то ты, как правило, будешь крутиться в бесконечном цикле, где сообщения Windows туда-сюда гоняются. Эта комбинация клавишь позволит вывалиться в отладчик, когда окно было закрыто. Самое простое применение:
1) Ждём пока программа покажет MessageBox() с каким-либо нужным сообщением.
2) Прицепляемся в памяти к программе: File -> Attach и выбираем среди процессов нашу программу.
3) Жмём Alt+F9.
4) Жмём чего-ниубдь на диалоговом окне MessageBox(), чтобы оно закрылось.
5) Опа - мы на следующей инструкции после этого окна - смотрим откуда сюда переход был и что там нужно поменять.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Aug 9 2018, 15:43
Сообщение #7


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,188
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 286 раз(а)



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

А после можно смело переходить на вот это: x64dbg.

Пусть не смущает название - там и x32 отладчик есть. Выглядит точь-в-точь как Олька, только функций больше и их по разным местам растащили (например, задавать командную строку запускаемой программе теперь нужно в меню "File", заместо "Debug"), там даже command bar уже встроен (правда пара команд изменилась, ибо что-то я сходу не нашёл как там AT (перейти на адрес кода в дизассемблере) делается).
Так что можно даже x64 приложения дебажить - всё что изменится, это название регистров.
На примере регистра eax:
CODE
(=============================== 64 bit =======================) - rax
                             (============== 32 bit ===========) - eax
                                               (=== 16 bit ====) - ax
                                               ( 8 bit | 8 bit ) - ah | al
Такая же фигня с ebx, ecx и edx - они теперь все будут с "r" начинаться и размером в 64 бита. А, ну и всякие "push 0" положат на стек уже не 4 байта (32 бита), а 8 (64 бита). Короче, думаю, разобраться что к чему не сложно будет.

И ещё, меня тут в личке спрашивали что делать с играми, у которых оконного режима нет.
Есть три варианта:
1) Запустить игру в окне при помощи D3DWindower, DxWnd или подобных утилит.
2) Если есть дополнительный выход на видеокарте и возможность (в наличии второй монитор) - подключить второй монитор и вытащить окно отладчика туда.
3) Запусть игру под отладчиком и подцепиться к отладчику с другого компьютера (удалённо). Сразу скажу, что я такого никогда не делал, хотя знаю что можно (может, конечно, не все отладчики такое поддерживают), так что помочь не смогу если кто совета спросит - гугл в помощь.

P.S. Перетащу-ка я эту тему в подфорум "Статьи" - там ей самое место.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Oct 6 2018, 14:06
Сообщение #8


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,188
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 286 раз(а)



Несколько корректировок.

QUOTE(-=CHE@TER=- @ Jun 14 2018, 17:15) *
5) Т.к. ты брезгуешь нормальными системами, то запускаешь Ольку от имени Администратора, плюс помни, что первый вылет в ней будет не на OEP, а где-то в дебрях системы, ибо Windows Vista и выше - гоумно. Так что делай сразу F9, чтобы оно до OEP дошло.
Это можно обойти, чтобы сразу на OEP вставало, если в опциях выбрать игнорировать последнее исключение (Debugging Options - Exceptions - Add last exception, не забыть галку на Ignore also following custom exception or ranges чтобы список работал).

QUOTE(-=CHE@TER=- @ Jun 21 2018, 20:03) *
А ещё F8 на инструкции loop сразу пройдёт весь цикл, в то время как F7 будет переходить на начало при каждом шаге.
Спутал с отладчиком DOSBox. Во всяком случае в классической Ольке это не работает так.

QUOTE(-=CHE@TER=- @ Jun 21 2018, 20:03) *
Если файл не изменялся, то при перезапуске бряки не будут пропадать, кроме случаев когда бряк стоит на памяти которая была динамически выделена или внутри .DLL.
x64dbg допилили, так что там бряки внутри .DLL не пропадают - удобно. Вроде бы, даже файл на диске изменять можно. Насчёт остального не проверял.

А ещё, рассказываю, как и обещал когда-то, одно ноу-хау, о том как можно быстро найти код распаковки/расшифровки чего-либо, если отлаживать с самого начала программу очень долго и муторно.

Чтение файлов в Windows, как правило, идёт через CreateFile + ReadFile. Исключение - когда файл мапится в память (тогда придётся перехватывать ещё mapping-функции и высчитывать адрес нужного блока памяти для смены прав). Поэтому можно поступить так:
1) Делаем wrapper-оболочку-прослойку между игрой и системной kernel32.dll:
game.exe -> kernelxx.dll -> kernel32.dll
2) В kernelxx перехватываем работу с CreateFile и сравниваем с именем файла, который нам нужен. Если наш - сохраняем дескриптор открытого файла.
3) В ReadFile смотрим какой файл читается - если нужный (сравнили с дескриптором), то ставим (после того как оригинальная функция отработала!) на весь буфер бряк программно - запрещаем обращение к этой памяти:
VirtualProtect(lpBuffer, nNumberOfBytesToRead, PAGE_NOACCESS, @tmp);
4) Теперь если где-то программа обратится к этому куску памяти, то отладчик сразу остановится на этом месте, т.к. выбьет ошибку. Можно вместо этого вызвать DebugBreak(); тогда в этом месте управление передастся отладчику и можно будет дальше смотреть кто и что с этим буфером делает (это если с ним сразу работают, а не через 10 минут после загрузки, когда понадобился).
5) Обращаю внимание, что изменить в отладчике права на обращения к памяти нельзя, а т.к. код распаковки будет постоянно это делать, то будут постоянно лезть ошибки - т.е. нашли адрес кода распаковки, bp <адрес> и перекомпилили библиотеку, чтобы больше запрета на обращение к памяти не было. Тут можно попробовать, наверное, PAGE_GUARD (см. документацию - оно как раз один раз срабатывает и возвращает старые права на память после этого), вместо PAGE_NOACCESS, но я уже не помню почему от PAGE_GUARD отказался (не срабатывало, вроде бы).

Вот пример такой библиотеки (я его слегка поменял и уменьшил, чтобы было нагляднее, но не тестировал, так что возможны ошибки в работе, хотя всё и компилируется без проблем под Delphi 7):
CODE
library KERNELXX;

uses Windows;

const
MyFileName = 'filename.ext';

var
MyHandle: THandle = INVALID_HANDLE_VALUE;

function CreateFileA(
lpFileName: PAnsiChar; dwDesiredAccess, dwShareMode: DWORD;
lpSecurityAttributes: PSecurityAttributes;
dwCreationDisposition, dwFlagsAndAttributes: DWORD;
hTemplateFile: THandle
): THandle; stdcall;
var s: ansistring;
begin
result:=Windows.CreateFileA(
lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile
);
s:=lpFileName;
if (
(result <> INVALID_HANDLE_VALUE) and (Length(s) >= Length(MyFileName)) and
(Windows.lstrcmpiA(PAnsiChar(Copy(s, Length(s) - Length(MyFileName) + 1,
Length(MyFileName))), PAnsiChar(MyFileName)) = 0)
) then
begin
MyHandle:=result;
end;
end;

function ReadFile(hFile: THandle; Var Buffer; nNumberOfBytesToRead: DWORD;
var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
var
d: DWORD;
p: pointer;
begin
result:=Windows.ReadFile(
hFile, Buffer, nNumberOfBytesToRead,
lpNumberOfBytesRead, lpOverlapped
);
if (result = true) then
begin
if (hFile = MyHandle) then
begin
p:=@Buffer;
Windows.VirtualProtect(p, nNumberOfBytesToRead, PAGE_NOACCESS, @d);
// DebugBreak();
end;
end;
end;

function CloseHandle(hObject: THandle): BOOL; stdcall;
begin
result:=Windows.CloseHandle(hObject);
if (hObject = MyHandle) then MyHandle:=INVALID_HANDLE_VALUE;
end;

procedure RtlUnwind; stdcall; external 'kernel32.dll' name 'RtlUnwind';

exports
CreateFileA,
ReadFile,
CloseHandle,
SetFilePointer,
GetFileSize,
CreateDirectoryA,
GetCommandLineA,
RtlUnwind,
GetVersion;

end.

Обращаю внимание на несколько важных вещей связанных с этим кодом:
1) В exports должны быть все функции, которые экспортирует игра/программа, иначе она тупо не запустится.
2) Блок exports обязательно должен идти после перехваченных функций, иначе перехватчики не будут работать (будет вызов оригинальных функций напрямую).
3) Если нужно вызвать оригинальный код из kernel32 (или другого юнита), то это нужно делать добавив имя такого юнита перед именем функции, как у объекта класса:
Windows.CreateFileA(...)
Потому что вызов:
CreateFileA(...)
создаст бесконечную рекурсию.
4) Ну и, конечно, не забудьте заменить в исполняемом файле игры/программы все строки "KERNEL32.DLL" на "KERNELXX.DLL", чтобы использовалась эта библиотека.
5) Можно не только работу с файлами перехватывать, но и работу с памятью, сетью, реестром (advapi32.dll - я обычно это делаю, чтобы при отладки игры мне в реестр не гадили) и так далее.

Мне идея такой библиотеки пришла в голову в 2015 году (обычные wrapper-то я ещё раньше делал - в старой версии патча для Турка) и безумно выручила - я как раз по заказу писал конвертер графики для какой-то игры (уже не помню точно для какой, но на сайте конвертер есть) и не мог никак до функции конвертирования добраться - в меню загружались обычные .BMP, а мне нужен был собственный формат игры, который только в игре загружался и только в определённый момент (когда этот объект был виден на экране). Там проблема, если правильно помню, была в том, что если ставить бряк на обращение к памяти (memory on access), то Олька почему-то начинает о-о-очень медленно работать, так что было практически невозможно дойти с момента загрузки до момента, когда нужный спрайт появлялся на экране (декодировался - шло обращение к памяти).


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0 -

 



Упрощённая версия Сейчас: 20th March 2019 - 23:49