Хочу расковырять игру 1994 года под MS-DOS. (Alien Legacy, интересна внутренняя логика — какие триггеры у ключевых событий, какие сюжетные линии авторы не доделали и отключили и т.п.) Экзешник с внедрённым DOS/4GW, собран Watcom C++. Имею IDA 7 Freeware и базовые познания в ассемблере. С чего начать? Есть какие-то руководства помимо документации по IDA и книг Касперского? Или лучше взять другой инструмент? Конкретные вопросы: 1. IDA не может определить точку входа. Как её искать? — Ответ: Фриварная версия не имеет нужного лоадера "loaders/lx.*". Можно изучить структуру MZ- и LE- заголовков и написать скрипт на питоне, который будет задавать нужные параметры. (Примеры скриптов найти легче, чем полное описание формата LE.) Можно найти платную версию. 2. Имеет ли смысл вырезать из файла DOS/4GW и дизассемблировать без него? — Ответ: Некоторые версии лоадера нормально с ним справляются, некоторые — нет. Возможно, это связано с небольшими отличиями файла от де-факто стандарта. (Часто смещение LE-заголовка указано по смещению 3Ch, но не в данном файле. Если заменять DOS4GW на стабы от DOS32A при помощи ST, она пишет смещение в 3Ch.) 3. Текстовые строки выровнены на 4 байта. Но между ними встречается мусор ASCII. Как объяснить дизассемблеру, что адреса начал стрингов должны быть кратны 4? Дополнение: иногда начинает стринг не оттуда, даже если есть несколько ссылок на правильное начало. 4. В функциях часто возникают выражения вида [esp+28h+var_18] Как придать им более понятный вид? В смысле заменить "28h+var_18" на имя переменной, вроде "arg_C". — Ответ: Ничего заменять не надо, это именно данные в var_18. Я перепутал выражения вида [esp+28h+var_18] и [ebp+var_18]. 5. Какой конструкции C++ или C может соответствовать эта функция, заполняющая массив указателями на функции? Код (Text): cseg01:0004DDF2 mov edx, eax cseg01:0004DDF4 mov eax, [eax] cseg01:0004DDF6 mov dword ptr [eax], offset sub_4DEA0 cseg01:0004DDFC mov eax, [edx] cseg01:0004DDFE mov dword ptr [eax+4], offset sub_4DEC0 cseg01:0004DE05 mov eax, [edx] ... и т.д. до [eax+38h] 6. Для чего нужна такая таблица? Код (Text): int 0 retn int 1 retn ... int 0FFh retn Для вызова прерываний MS-DOS из защищённого режима? 7. В 2 функциях, которые обращаются к этой таблице, есть фрагменты вида: Код (Text): cseg01:00076C3C lea esi, [esi+esi*2] cseg01:00076C3F db 2Eh cseg01:00076C3F lea eax, intTable[esi] Какова роль "db 2Eh"? — Ответ: Префикс, заменяющий сегмент на CS вместо DS. Во что этот байт должен был бы декодироваться? Какие настройки в IDA изменить, чтобы декодировался правильно? — Ответ: Похоже, иначе IDA не умеет. 8. db 0CDh, 20h превращается в вызов VxD вместо досовского int 20h. Какие настройки IDA поменять? — Ответ: В "Processor Options"=> "IBM PC specific analyzer options" отключить "Interpret int 20 as VxDcall". 9. Что произойдёт при вызове int 21h, если AX=0FF00h и DX = 0078h ? Как поведёт себя версия MS-DOS, где такой функции нет? — Ответ: Это вызов DOS/4G installation check. 10. Подгружается экзешник, в котором за MZ-заголовком идёт 16 байт указателей, а собственно код начинается с 10h. Правильно ли я понимаю, что в нём инструкции вида call word ptr [bp+si+0Ch] — вызов функции, на которую указывает 2-байтное число по смещению 0Ch после MZ-заголовка? 11. Как объяснить IDA, что вызываемая таким образом функция сохраняет стек? Нередко при включённом "Stack pointer" показывает подобное: Код (ASM): fn1+55 034 call word ptr [bp+si+0Ch] fn1+58 000 movzx ax, ax P.S. В смысле, как это сделать автоматически для всех вызовов? (Вопросы по другому файлу: 16 бит, Турбо Паскаль, игра Reunion) 12. Можно ли как-то присвоить имена переменным в "куче", к которым обращаются вот так: Код (ASM): mov di, [bp+block2] shl di, 2 push word ptr [di+7E2Eh] mov di, [bp+block2] shl di, 1 mov al, [di+7EB6h] Можно ли хотя бы присвоить имена этим константам: 7EB6h, 7E2Eh и т.д.? 13. Как заставить IDA заменить последовательность push cs + call near на call far? 14. Пытаюсь патчить, менять местами call-ы, либо вообще удалить 1-й, на его место поставить 2-й. Но просто менять адреса недостаточно. Умеет ли IDA редактировать relocation table? Или как лучше это делать? 15. При ручном анализе кода и создании функций библиотечная функция то опознаётся, то нет. От чего это зависит?
Hiew возьми. Он и перекрёсные ссылки найдёт, и EP, и править позволяет. Если отлаживать надумаешь, то не всякий дос-отладчик имеет доступ к памяти 1М+ Как вариант, можешь попробовать под-эмулем 'Bochs'. У него имеется встроенный отладчик линуха GDB, который 32/64-битными регистрами видит всю память.
Бесплатная версия это умеет? До отладчиков мне далеко, но спрошу: а Dosbox так умеет? И кстати, умеет ли GDB выводить команды ассемблера в интеловской нотации? Попробовал, автоматически не нашлась. Искать LE 0x0000 и парсить заголовок LE вручную? Опыт показывает, что при замене dos4gw на dos4g эта игра начинает падать на ровном месте существенно чаще. То есть что-то в ней завязано на его размер. Поэтому я и пытался с неизменённым бинарником. За который браться? Он входит в пакет OpenWatcom? Или как он называется, если отдельная программа? Или он только в закрытой версии под ДОС был?
168 100 байт вместе с MZ-заголовком. IDA я скармливал голый LE без MZ-заголовка. Чем это лучше сделать? Стаб я вырезал утилитами от DOS32A (когда выше писал "dos4g", имел в виду именно его) — они так могут сделать? Какие-то binw/wd.exe, binnt/wd.exe, binl/wd и rdos/wd.exe в дистрибутиве лежат.
Судя по тому, что отдельный файл экстендера отсутствует, эти 164 килобайта обеспечивают всю необходимую функциональность. И я возвращаюсь к вопросу: как узнать адреса, которые подставлять?
Куда выкладывать? Аттачем к посту? Вот он. До иды доберусь только вечером. Пока спрашиваю, что делать. Вслепую уже наэкспериментировался.
dos32a утилита выдает при распаковке выдает LE файл, который ида (6.8 точно) может загружать, находит main, start функции. Можете другим путем идти - загрузить в бинарном режиме, открыть описание формата, вручную размечать.
Фриварная 7.0 не умеет, похоже. Ковыряю вручную. Расписал поля MZ- и LE- заголовков. Куда дальше? Где точки входа в start() и main()? По адресу "Initial EIP"? Откуда его отсчитывать: от начала, или от "LE"? Если от начала — попадает в середину какой-то короткой функции. Если от "LE" — получается такая функция: Код (Text): seg000:0008D43C add eax, [eax] seg000:0008D43E mov ebx, [eax] seg000:0008D440 call dword ptr [ebx+50h] seg000:0008D443 mov eax, [edx] seg000:0008D445 lgs ebx, [eax+40h] seg000:0008D449 mov esi, [eax+46h] seg000:0008D44C mov ebx, gs:[ebx] seg000:0008D44F mov ecx, edx seg000:0008D451 cmp ebx, esi seg000:0008D453 jnb short loc_8D458 seg000:0008D455 add [eax+4Ah], esi seg000:0008D458 seg000:0008D458 loc_8D458: ; CODE XREF: seg000:0008D453↑j seg000:0008D458 mov eax, [ecx] seg000:0008D45A mov [eax+46h], ebx seg000:0008D45D mov eax, [ecx] seg000:0008D45F mov ebp, [eax+4Ah] seg000:0008D462 mov eax, [edx] seg000:0008D464 add ebx, ebp seg000:0008D466 mov [eax+52h], ebx seg000:0008D469 mov eax, [edx] seg000:0008D46B mov [eax+5Ah], ebx seg000:0008D46E seg000:0008D46E loc_8D46E: ; CODE XREF: seg000:0008D437↑j seg000:0008D46E pop ebp seg000:0008D46F pop esi seg000:0008D470 pop edx seg000:0008D471 pop ecx seg000:0008D472 pop ebx seg000:0008D473 retn Выглядит правдоподобно? Или просто совпадение? И что с ней делать дальше? Как понять, куда пойдёт call dword ptr [ebx+50h] ?
В опенсорсной версии 1.9.0 тоже есть. Но игра под ним стабильно падает при инициализации. Никакого сложного шифрования там нет, вроде, так что обойдусь без него, надеюсь.
Очередной вопрос. В функциях часто возникают выражения вида [esp+28h+var_18] Как придать им более понятный вид? В смысле заменить "28h+var_18" на имя переменной.
Здесь var_18 уже есть имя переменной. Вы можете только переименовать её. Редактировать определение переменных на стеке в функции нужно в соотв. вьюхе стека (ctrl+k, кажется) функции. Там же можно изменить тип переменной. Вообще, откройте для себя уже 'The Ida pro book' .
Ответ не на тот вопрос, который я подразумевал Когда IDA пишет [esp+28h+var_18], она имеет в виду именно данные в var_18, а не в 28h байтах от var_18. При первом прочтении The Ida pro book я этот момент упустил.
Наткнулся на такую функцию: Код (Text): cseg01:0004DDF0 fillSomeTable proc near ; CODE XREF: sub_4DDD0+14p cseg01:0004DDF0 push ebx cseg01:0004DDF1 push edx cseg01:0004DDF2 mov edx, eax cseg01:0004DDF4 mov eax, [eax] cseg01:0004DDF6 mov dword ptr [eax], offset sub_4DEA0 cseg01:0004DDFC mov eax, [edx] cseg01:0004DDFE mov dword ptr [eax+4], offset sub_4DEC0 cseg01:0004DE05 mov eax, [edx] ... cseg01:0004DE6A mov dword ptr [eax+34h], offset sub_4E740 cseg01:0004DE71 mov eax, [edx] cseg01:0004DE73 mov dword ptr [eax+38h], offset sub_4E7B0 cseg01:0004DE7A xor eax, eax cseg01:0004DE7C lea eax, [eax+0] cseg01:0004DE80 cseg01:0004DE80 loc_4DE80: ; CODE XREF: fillSomeTable+A7j cseg01:0004DE80 mov ebx, [edx] cseg01:0004DE82 mov byte ptr [ebx+eax+3Ch], 0 cseg01:0004DE87 inc eax cseg01:0004DE88 mov ebx, [edx] cseg01:0004DE8A mov byte ptr [ebx+eax+0BBh], 0 cseg01:0004DE92 cmp eax, 80h cseg01:0004DE97 jb short loc_4DE80 cseg01:0004DE99 pop edx cseg01:0004DE9A pop ebx cseg01:0004DE9B retn cseg01:0004DE9B fillSomeTable endp Понял, что идёт заполнение массива указателями на функции. Какой конструкции C++ или C это соответствует? Те функции в этой таблице, с которыми я пока разобрался, открывают и загружают экзешники с побочными сюжетными линиями.
1. Наткнулся на такую последовательность: Код (Text): db 0CDh, 0, 0C3h db 0CDh, 1, 0C3h ... db 0CDh, 0FFh, 0C3h Она декодируется в: Код (Text): int 0 retn int 1 retn ... int 0FFh retn Для чего она нужна? Для вызова прерываний MS-DOS из защищённого режима? 2. В 2 функциях, которые обращаются к этой таблице, есть фрагмент: db 8Dh, 34h, 76h, 2Eh, 8Dh, 86h, 0E2h, 6Ch, 7, 0 Код (Text): cseg01:00076C3C lea esi, [esi+esi*2] cseg01:00076C3F db 2Eh cseg01:00076C3F lea eax, intTable[esi] либо то же но с "eax, [eax+eax*2]" в 1-ой строке Какова роль "db 2Eh"? Во что этот байт должен был бы декодироваться? Какие настройки в IDA изменить, чтобы декодировался правильно? 3. db 0CDh, 20h превращается в вызов VxD вместо досовского int 20h. Какие настройки IDA поменять?
То есть db 2Eh переопределяет сегмент для intTable? Спасибо. Действительно, большинство данных — в отдельном сегменте, но intTable в одном сегменте с кодом. Но в статье написано, что с версии 4.17 IDA научился работать с одиночными префиксами. А тут почему-то оставил db 2Eh.
Помогло отключить "Interpret int 20 as VxDcall" в диалоге "IBM PC specific analyzer options" (доступен по кнопке "Processor Options" при загрузке экзешника или позже через "Options => General... => Analysis => Processor specific analysis options").
Где можно узнать, что даёт функция MS-DOS FF? Имеется фрагмент: Код (Text): mov dx, 78h mov ax, 0FF00h int 21h То есть вызывается int 21h с AH=FFh. В документации такой функции нет. Что она может делать? Если эта функция присутствовала только в отдельных OEM версиях, как ведут себя остальные DOS-ы?
tkzv, Р.Браун, Дж.Кайл "Справочник по прерываниям IBM PC" том 1, изд "Мир" 1994, глава 9 Прерывание 21h функция 0FFh, назначение ― упрощение доступа к некоторым часто используемым функциям MS-DOS из защищенного режима, работают под расширителем DOS GO32 Д.Дж.Делори