![]() |
Добро пожаловать, гость ( Вход | Регистрация )
![]() ![]() |
![]() |
Siberian GRemlin |
![]()
Сообщение
#1
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
Дайте кто-нибудь совет новичку, кратко, чтобы не читать целую книгу.
Как найти в отладчике функцию в exe игры, которая отвечает за расшифровку файла? Игра полностью читает файл в память, в котором предположительно лежит ключ и сами зашифрованные данные, либо ключ как-то генерируется из имени файла или его размера, ибо для каждого файла ключ свой. Проблема в том, что файл сначала был сжат zlib'ом, затем зашифрован, также используется выравнивание до размера кратного 16. Возможно, файлы сжимаются блоками и уже их выравнивали, а затем всё шифровали, так как чем больше размер файла тем больше разница со сжатым мной — в паке есть и зашифрованные/сжатые файлы и чистые, но использует только зашифрованные. Судя, по основному архиву игры, шифруется обычным цикличным xor. В архиве к каждому файлу прилагался свой ключ в 20 байт. Хотелось бы овладеть навыком находить ключи и сами алгоритмы шифрования. |
-=CHE@TER=- |
![]()
Сообщение
#2
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Ты прям хочешь руководство в две строчки. Так не бывает.
Короче (для 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 (после этого ставишь новый бряк, ибо эта функция удаляет все бряки). Будут вопросы - задавай. Только с конкретными деталями что именно не получается. Спасибо сказали:
|
Siberian GRemlin |
![]()
Сообщение
#3
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
На пункте 14 идёт чтение по одному байту.
CODE REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] Затем идёт заполнение блока памяти значением $CD. CODE REP STOS BYTE PTR ES:[EDI] Я так понимаю, считывалось по байту отсюда, расшифровывалось и писалось в другое место? Я на чтении дал F9, но, полагаю, надо было F8. |
-=CHE@TER=- |
![]()
Сообщение
#4
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Да, это пересылка, нужно было просто 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), чтобы сразу перейти на адрес возврата. Спасибо сказали:
|
Siberian GRemlin |
![]()
Сообщение
#5
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 537 Регистрация: 4-February 08 Пользователь №: 2 Спасибо сказали: 221 раз(а) ![]() |
Я так понимаю, операция «CALL» просто строкой отображается и надо уже в IDA смотреть что там за функция? Как-то быстро после чтения файла попадаю в место, где передаётся размер разжатых данных, а затем начинают мелькать значения параметров из разжатого XML, то есть идёт полным ходом чтение расшифрованного и разжатого файла.
|
-=CHE@TER=- |
![]()
Сообщение
#6
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Эээ... это я опять торможу. 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) Опа - мы на следующей инструкции после этого окна - смотрим откуда сюда переход был и что там нужно поменять. Спасибо сказали:
|
-=CHE@TER=- |
![]()
Сообщение
#7
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Ещё добавлю - Олька в оригинале устарела (но для начала и обучения азам - самое-то, ибо ещё не сильно навороченная).
А после можно смело переходить на вот это: x64dbg. Пусть не смущает название - там и x32 отладчик есть. Выглядит точь-в-точь как Олька, только функций больше и их по разным местам растащили (например, задавать командную строку запускаемой программе теперь нужно в меню "File", заместо "Debug"), там даже command bar уже встроен (правда пара команд изменилась, ибо что-то я сходу не нашёл как там AT (перейти на адрес кода в дизассемблере) делается). Так что можно даже x64 приложения дебажить - всё что изменится, это название регистров. На примере регистра eax: CODE (=============================== 64 bit =======================) - rax Такая же фигня с ebx, ecx и edx - они теперь все будут с "r" начинаться и размером в 64 бита. А, ну и всякие "push 0" положат на стек уже не 4 байта (32 бита), а 8 (64 бита). Короче, думаю, разобраться что к чему не сложно будет.(============== 32 bit ===========) - eax (=== 16 bit ====) - ax ( 8 bit | 8 bit ) - ah | al И ещё, меня тут в личке спрашивали что делать с играми, у которых оконного режима нет. Есть три варианта: 1) Запустить игру в окне при помощи D3DWindower, DxWnd или подобных утилит. 2) Если есть дополнительный выход на видеокарте и возможность (в наличии второй монитор) - подключить второй монитор и вытащить окно отладчика туда. 3) Запусть игру под отладчиком и подцепиться к отладчику с другого компьютера (удалённо). Сразу скажу, что я такого никогда не делал, хотя знаю что можно (может, конечно, не все отладчики такое поддерживают), так что помочь не смогу если кто совета спросит - гугл в помощь. P.S. Перетащу-ка я эту тему в подфорум "Статьи" - там ей самое место. Спасибо сказали:
|
-=CHE@TER=- |
![]()
Сообщение
#8
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Несколько корректировок.
5) Т.к. ты брезгуешь нормальными системами, то запускаешь Ольку от имени Администратора, плюс помни, что первый вылет в ней будет не на OEP, а где-то в дебрях системы, ибо Windows Vista и выше - гоумно. Так что делай сразу F9, чтобы оно до OEP дошло. Это можно обойти, чтобы сразу на OEP вставало, если в опциях выбрать игнорировать последнее исключение (Debugging Options - Exceptions - Add last exception, не забыть галку на Ignore also following custom exception or ranges чтобы список работал).А ещё F8 на инструкции loop сразу пройдёт весь цикл, в то время как F7 будет переходить на начало при каждом шаге. Спутал с отладчиком DOSBox. Во всяком случае в классической Ольке это не работает так.Если файл не изменялся, то при перезапуске бряки не будут пропадать, кроме случаев когда бряк стоит на памяти которая была динамически выделена или внутри .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), то Олька почему-то начинает о-о-очень медленно работать, так что было практически невозможно дойти с момента загрузки до момента, когда нужный спрайт появлялся на экране (декодировался - шло обращение к памяти). Спасибо сказали:
|
LexSafonov |
![]()
Сообщение
#9
|
Member ![]() ![]() Группа: Authorized Сообщений: 21 Регистрация: 29-October 20 Пользователь №: 18,034 Спасибо сказали: 1 раз(а) ![]() |
Внесу свою лепту вопросов.
Существуют сейчас в природе нормальные инструменты для отладки Dos приложений?(Ну помимо отладчика самого досбокса) Ida Pro + DosBox у меня чёт вообще нивкакую не пашет. Более того, не пашет даже сам досбокс, он запускается, даже открывает игру, но не может включить процесс отладки. Всё просто зависает намертво. Более того, скачал я версию Ida Pro 6.1, бинарники к этой версии. Делаю по мануалу, всё положил в нужные директории. Запускаю досбок, монтирую диски, пишу "debug TRILOGY.exe", процесс зависает. Ладно, иду тем временем в Ida Pro, там открываю сам exe, но в списках дебаггера упрямо не вижу вариант DosBox debugger. Только "борщ" и GDB. Версию иды 6.4 я вообще найти не смог. Хотел попробовать через "борщ" отладить, как многие советуют, но тоже нифига не понял, игру не открывает, жалуется на "некорректные" файлы, которые создаёт сама IDA. Я так понял тот же "борщ" надо компилить под определённую версию иды? Если вариантов вообще нет и остаётся только встроенный отладчик коробки, то есть ли в природе нормальные мануалы по отладке в этой среде? Листать миллион строк кода(и непонятно какого) тоже ведь не вариант. |
-=CHE@TER=- |
![]()
Сообщение
#10
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Существуют сейчас в природе нормальные инструменты для отладки Dos приложений? (Ну помимо отладчика самого досбокса) Если и есть, то мне они неизвестны.Я так понял тот же "борщ" надо компилить под определённую версию иды? Никогда с этим не работал, так что, увы, не могу что-то посоветовать.Если вариантов вообще нет и остаётся только встроенный отладчик коробки, то есть ли в природе нормальные мануалы по отладке в этой среде? Листать миллион строк кода(и непонятно какого) тоже ведь не вариант. Ну, листать код в любом случае придётся, даже при отладке, но меньше.Набор того что понадобится при работе с DOSBox Debugger и, вообще, отладке DOS-приложений: 1) Собственно, сам отладчик: DOSBox Debugger. На текущий момент там последняя версия 0.74-3 - её и рекомендую. 2) Примочка к нему DOSBox Debugger Helper, которая будет очень круто экономить время позволяя сохранять набранные команды и прочее. С 0.74-3 версия из первого сообщения не работает, но в последнем сообщение (самом низу) есть данные, которые необходимо добавить в "DOSBoxDH.ini" чтоб заработало. 3) Справка по DOS под название TechHelp! Есть отдельный сайт techhelpmanual.com, где она представлена, но как-то криво сконвертированна. Есть скомпилированная нормальная версия в здесь: TECH Help! 6 by Flambeaux Software in .CHM. Справочник содержит ошибки, так что осторожно. Более или менее безопасно там можно функции DOS смотреть: TECH Topics - DOS Functions QuickRef. Ассемблер там точно смотреть не стоит из-за ошибок - лучше здесь искать нужную команду: x86 Instruction Set Reference. 4) Небольшая, но очень годная документация от разработчиков SCUMMVM о том как работать с IDA и DOSBox: Using the DosBox Debugger - там как раз рассказывается как искать смещения. И, по традиции, добавлю несколько советов от себя. Если нужно узнать что делается в каком-то коде, то я поступаю так: - создаю копию (чтобы потом откатиться можно было) исполняемого файла игры под другим именем; - в оригинальном файле перехожу на смещение (IDA его показывает) которое мне нужно и два байта перед интересующим меня кодом (или в начале функции) заменяю на байты EB FE - именно в этом порядке. Это короткий jump на самого себя (т.е. на ту позицию, где эти байты написаны); - теперь запускаю игру под отладчиком DOSBox и жду пока указанный код выполнится - т.е. игра зависнет (ВАЖНО: если игра перехватывает прерывание таймера, то музыка, например, может без проблем продолжать играть, главное чтобы всё остальное не двигалось); - теперь в окне DOSBox где игра нажимаю Alt+Break, что останавливает выполнение кода; - переходив во второе окно, где отладчик и восстанавливаем там код назад такой командой (32-х битного режима как в Alien Trilogy): SM CS:EIP 85 C0 Или такой (для 16-ти битного режима): SM CS:IP 85 C0 Вместо 85 и C0 должны быть те байты, которые были на том месте перезаписанных нами EB FE. Далее уже можно отлаживать код по шагам. - если предполагается что программу придётся перезапускать несколько раз, то записываем сегмент и смещение по которому оказался зацикленный код, затем восстанавливаем оригинальный исполняемый файл из копии, перезапускаем DOSBox Debugger и просто ставим бряк уже на известный нам адрес: BP 0168:12345678 Где вместо сегмента 0168 и смещения 12345678 вписываем тот, которые у нас был, где начинались байты EB FE. Тогда нам не придётся восстанавливать код командой SM каждый раз при перезапуске программы. К сожалению, в DOSBox Debugger нет возможности поставить бряк на обращение к памяти. Поэтому тут приходится выкручиваться через бряки на изменение памяти. Делается это так (только для 1 байта): BPM X:Y Где X - сегмент, а Y - смещение. Обращаю внимание, что отладчик по умолчанию считает что байт на который поставили бряк равен нулю. Так что если он отличен от нуля, то первую остановку отладчик сделает когда это видит, а не когда байт поменялся. Дальше начинаются сложные размышления на тему того, а как бы так нам подобраться к нужному коду? Например, когда Siberian GRemlin просил помочь с указателями на текст во вступительном ролике первой Кирандии, то я долго ломал голову как бы на них выйти. Потом понял что это можно сделать через изменение видеопамяти. В режиме VGA 13h 320x200, если не используются bank'и, то экран напрямую отображается в память по адресу A000:0000 (начало). Дальше делаем скриншот, когда на экране выводится текст субтитров, затем считаем координаты какого-нибудь пикселя из строки текста. Например, пусть это будет пиксель X:100, Y:45. Тогда нам нужно поставить такой бряк: BPM A000:38A4 Потому что (45 * 320) + 100 = 14500 или 38A4 в шестнадцатеричном. Всё, теперь перезапускаем программу и ждём пока этот байт изменится в нужном месте (на экране могут быть промежуточные спецэффекты, пока выполнение не дойдёт до нужного места - игнорируем такие остановки). Когда отладчик остановится на нужном месте - это будет функция вывода текста на экран. Ищем там указатель на текст, затем поднимаемся выше и смотрим кто её вызвал, ну и там дальше очевидно. Это штука не очень поможет в случае Alien Trilogy, потому что там bank'и используются и будет слишком много ложных срабатываний, чтобы этот подход можно было применить, даже с 256-ти цветовым исполняемым файлом. Ещё одна проблема будет если игра использует прокрутку (как заставка в Кирандии). Тогда нужный пиксель может оказаться совсем не там, где ожидается. Можно так поступить (опять же, если игра не использует bank'и): MEMDUMPBIN A000:0000 FA00 320 * 200 = 64000 или FA00 в шестнадцатеричном (в случае с прокрутной может взять размер больше). Теперь находим появившийся в каталоге DOSBox файл с дампом MEMDUMP.BIN и открываем его чем-нибудь, что может интерпретировать файлы как картинки, с параметрами 320x200 256 цветов и ищем где и как там расположен нужный нам пиксель шрифта, затем пересчитываем его по формуле ((Y * 320) + X) и ставим бряк на этот байт в памяти. Это - основное. Может быть потом что-нибудь ещё допишу, если вспомню. |
![]() ![]() |
Упрощённая версия | Сейчас: 6th September 2024 - 21:58 |