Войти
ФлеймФорумПрограммирование

Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++) (4 стр)

Advanced: Тема повышенной сложности или важная.

Страницы: 13 4 5 612 Следующая »
#45
(Правка: 18:46) 18:42, 18 июля 2021

Удалось подключить пару функций на C++ в код игры.

Первая функция без параметров - её подключить легко.

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

Функция на асме (фрагмент) - установка палитры, фейд(-in/-out):

Clipboard02 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

Её сишный вариант, сгенерённый Hex-rays:

Clipboard02 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

Из картинки ясно, что параметр функции лежит в стеке [esp+C]. также видно, что это - указатель на палитру (тройки значений R,G,B).

Опытным путём (через вывод значений содержимого) установил, что необходимо отнять 12 для корректировки смещения.

Подменённые вызовы функции:

Clipboard02 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

Последние 4 свитча - и есть вызов палитры из мест, где прерывание INT1 (icebp):

Clipboard02 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

Как раз 1 из вариантов - оригинальная процедура игры была по адресу CSEG1 ..... +0x15DEB
Остальные байты забиты NOP-ами.
Исходная инструкция вызова была 5 байт.

Все регистры заранее сохранены в системном обработчике исключения - о нём я рассказывал здесь:
https://gamedev.ru/flame/forum/?id=262348&page=3&m=5419998#m32

Игра работает и не артефачит.

На первый раз процесс муторный.  Много времени уходит на уточнение корректировок  фрейма стека и прочего. Хорошо, что это надо 1 раз сделать!

Посоветуйте удобный патчер файла, а то через ИДУ байты набирать неудобно.  Что-то типа HIEW, только 32-битный - чтобы дизасмила и патчила EXE моментально.

И как заставку в ДОС-боксе отключить?  Много отнимает времени при каждом запуске, когда кидаешь новые файлы...


#46
(Правка: 22:19) 22:03, 18 июля 2021

Gradius
> патчер файла

Древняя технология crk/xck файлов не прокатит?

http://old-dos.ru//index.php?page=files&mode=files&do=list&cat=350

В современных условиях, наверное проще использовать http://xdelta.org/  вопрос в том, как этот delta патч наваять, не имея на руках исходного и патченного файлов.

#47
11:28, 19 июля 2021

Просто оставлю это здесь
obiwan | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

#48
(Правка: 2:51) 2:28, 20 июля 2021

gudleifr
> Просто оставлю это здесь

Реверс танчиков - занятное дело ))

---

Продолжаю войну с Wacom и IDA.

Нашёл решение, как заставить компилятор относительно обращаться к переменным и процедурам, которые за пределами исходника. Это позволило избавиться от указателей на внешнюю память и сохранить относительность вызовов.

Нужно создать огромный ROM - образ данных и кода в памяти, и точно по нужным адресам вписать в него все метки (процедур и данных).
Что-то типа-такого:

  .686p
  .model flat

  public _aNotEnoughMemor

  extern MyMain_ : proc

  .code

  org 0012E000H  ;с этого адреса сегмент данных в памяти (игра)
  include dseg4.asm ; дизассемблированный сегмент из IDA

  org 0015E000H  ;по этому адресу - код (игра)
  include cseg1.asm ; дизассемблированный сегмент кода из IDA, превращённый в месево байтов


  org 00194000H  ;с этого адреса второй сегмент данных в памяти (игра)
  include dseg3.asm ; дизассемблированный второй сегмент из IDA

  org 0045E000H ;с этого адреса мы выполняем инжектор

START:

  jmp  MyMain_  ; прыжок на Сишный main()

  mov  esi,offset aNotEnoughMemor  ; это был тест вывода строки из сегмента данных игры
  mov  edi,0xB8000
  mov  ah,15

  Draw1:
  mov  al,[esi]
  cmp  al,0
  jz  Draw2  

  mov  [edi],ax
  inc  esi
  add  edi,2
  jmp  Draw1

  Draw2:
  jmp  Draw2

  end START

Получается на выходе ROM на 4 Мегабайта с раздутыми пустотами.
Именно при таком раскладе получается в коде инжектора относительно адресовать с правильными смещениями вызовы функций и обращение к данным игры.

А теперь - последнее:  берём и обрезаем из ROM всё, что ниже адреса инжектора:  0045E000H

Получается маленький бинарный обрубок инжектора, который может работать с относительной адресацией данных игры!

Команды постройки инжектора на Watcom.

1) батник:

wasm ROM.asm
wpp386 injector.cpp -i="C:\WATCOM10/h" -w0 -e3 -zq -otexan -s -zl -ez -6r -bt=dos -fo=.obj -mf -xd -fx
wlink.exe @wlink.ld
pause

2) скрипт линкера:

name ROM.bin
 output raw
    offset=0x000000
 file ROM.obj
 file injector.obj
 order
    clname CODE offset=0x000000
    clname DATA offset=0x000000
option verbose
option map

Только так!  Ни одна софтина млять, не позволит относительно адресовываться к чужим процедурам-данным за пределами сорцов! Поэтому пришлось имитировать огромный образ памяти в пределах 4 МБ, а потом срезать всё что ниже инжектора.

Вот это:

include dseg4.asm ; дизассемблированный сегмент из IDA
include cseg.asm ; дизассемблированный кодовый сегмент из IDA, превращённый в месево байтов
include dseg3.asm ; дизассемблированный второй сегмент из IDA

берётся из ИДы и подготавливается:

1) align(xxx) заменяется на цепочку байтов - потому что долбаный Watcom ругается на то что выравнивание в сегменте больше чем выравнивание самого сегмента.  Если же включить большее выравнивание сегмента, то бинарник инжектора пишется в начало, а не в конец - я так и не понял в чем прикол...

2) убираются ссылки на внешние байты

3) убираются нахрен все макросы - превращаются в байты

4) с сегментом кода:

a) "d" - избавляемся от align()
b) "u" - разбираем код на байты
с) запускаем скрипт создания имён (был получен парсером по отладочной инфе игры), так как на этапе b - имена стираются

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

Вот так всё через задницу. ))

Проверка расположения сегментов:

dseg3:
1 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

Инжектор:
2 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

#49
10:25, 20 июля 2021

Gradius
> Вот так всё через задницу. ))
Знания именно в этой теме у меня не очень хорошие, но читать, конечно, твои мини-отчеты прям интересно))

#50
10:52, 20 июля 2021

Gradius
> Продолжаю войну с Wacom и IDA.
А, с другими Си компиляторами, типа DJGPP, TCC, Pelles C, ..., по новому придётся бодаться?

#51
(Правка: 10:57) 10:53, 20 июля 2021

Vlad2001_MFS
> Знания именно в этой теме у меня не очень хорошие, но читать, конечно, твои
> мини-отчеты прям интересно))

Спасибо! :)

Сейчас играюсь с флагом трассировки (TF - восьмой бит регистра EFLAGS). Одна проблема - прерывания таймера и клавиатуры херят INT1 и сбрасывают флаг трассировки.  Если запретить аппаратные прерывания, то трассировка работает нормально: идёт вывод дебаг-инфы.

Идея была такая - в обработчике трассировочного прерывания определять адрес и опкод следующей инструкции  - если это интересующая процедура - то изменяем опкод на вызов своей.  Это бы дало возможность не курочить бинарник игры и не патчить его.

Но аппаратные прерывания всё подосрали.  Есть ли способ решить проблему без запрета аппаратных прерываний?

Игра использует аппаратные прерывания клавиатуры и звуковой карты.

Думал это косяк ДОС-бокса, скачал последнюю версию - всё тоже самое.


Флаг TF взвожу так:

void EnableTF(void)
{
 _asm
 {
  pushfd
  pop   eax
  or    eax,0x00000100
  push  eax
  popfd
 }
}

KPG
> А, с другими Си компиляторами, типа DJGPP, TCC, Pelles C, ..., по новому
> придётся бодаться?

Не знаю.  Из всех, с которыми работал - Watcom и DJGPP.  С DJGPP немного сложнее - там линейный адрес не равен физическому, что заставляет ещё конвертить адреса.

#52
(Правка: 12:18) 11:04, 20 июля 2021

Gradius
> Думал это косяк ДОС-бокса, скачал последнюю версию - всё тоже самое.
А, под железным DOS тоже самое? (Исходники, кстати, Dos 6.22 можно посмотреть и Dos же тоже есть разных версий), В DosBox идентифицируется Dos 5.0 версии.
Попробовал сравнить разные форки DosBox (DosBox-ECE, DosBox-Pure, DosBox-Daum, DosBox-wqvga, DosBox-staging, DosBox-X, DosBox-X-remc2, FastDosBox, vDos, vDosPlus) так в них, похоже, "кто в лес, кто по дрова" :)
DosBox 74-3 под MinGW собирается у меня без проблем (./configure , make), но заметил, что они эмулируют 486-DX 37МГц (по тестам системы 9.7 ХТ) запускал без конфигурационного файла.

P.S. Библиотека системного программиста братьев Фроловых

https://frolov-lib.ru/

#53
(Правка: 11:32) 11:31, 20 июля 2021

KPG
> А, под железным DOS тоже самое?
Для экспериментов с Watcom-фрагментами удобнее всего чистый Win95. Он использует тот же расширитель памяти (DPMI). Был даже целый класс игр, рассчитанных на запуск в DOS из-под Win-95 (например "графика" DOS-FOBOS и PASCAL-проект из "баек о движках"). (Строго по Фроловым).

#54
(Правка: 13:20) 13:19, 20 июля 2021

KPG
> А, под железным DOS тоже самое?

К сожалению реального ДОСа и реального железа у меня теперь нету.  Продал свой дюрон 800 МГц за 2000р - надо было  себе оставить...

gudleifr
> Для экспериментов с Watcom-фрагментами удобнее всего чистый Win95. Он
> использует тот же расширитель памяти (DPMI). Был даже целый класс игр,
> рассчитанных на запуск в DOS из-под Win-95 (например "графика" DOS-FOBOS и
> PASCAL-проект из "баек о движках"). (Строго по Фроловым).

:)  Я в начале  2000-х работал в 98-й винде, давился 16-битным борланд паскалем, потому что о 32-битном ваткоме ничего не знал. 

Позже я узнал про TMT Pascal и Watcom C.  Оба работают в защищённом DPMI режиме и под ДОС.

Но это уже произошло после, как научился сам переводить процессор в защищённый и нереальный режимы.  В то время меня бесило, что под виндой это не идёт, потому что говно-V86 режим.

Gradius
> Есть ли способ решить проблему без запрета аппаратных прерываний?

Проблема оказалась в DOS4GW.  Заменил его на PMODEW - трассировка работает, ничего не останавливается.
Пришлось курить как отсоединять стаб от DOS4GW и присоединять к игре стаб от PMODEW.
Утилита: pmwbind.exe

Хорошо, что игра идёт со звуком с этим PMODEW.  С DOS32/a  звука нет в игре.

Кстати, я получил ответ на свой вопрос - кто грузит сегменты и куда: ответ - досэкстендер!
С PMODEW адреса сегментов уже другие!

Новая проблема - запускаю игру с трассировкой.  Трассировка какое-то время идёт, затем заламывается и всё повисает.

DOSBox кидает эксепшн: illegal descriptor type 0 for int 1.

Какая-та сука в игре скидывает дескриптор или какой-то самодеятельностью занимается.  Причем адрес возврата всегда разный. Надо курить.

#55
(Правка: 14:40) 13:40, 20 июля 2021

Gradius
> К сожалению реального ДОСа и реального железа у меня теперь нету. Продал свой
> дюрон 800 МГц за 2000р - надо было себе оставить...
Можно и на современное железо, с разной степенью удачности, установить Win98 (Win98IF)
у меня получилось на бук с i5 Haswell вкорячить Win98IF (правда графика 640х480 - 256 и то после замены напрямую Vga.sys в системе от проекта VBEM 9x :)
Забавно запускать DOS, Win софт на таком железе, (тесты железа, демки, какой то софт ...)
Скорость в каких то тестах CPU, Mem просто запредельная в сравнении с каким то Win98 аутентичным железом.

P.S. На avito, кстати, почти задаром продают много устаревшего железа в сборе на выбор.

Интересно, а есть какие то сервисы, кроме yutube, где ещё кроме почты ещё хотят номер телефона для регистрации на сервисе для публикации видео и вставки в сообщение по подобию всяких хостиного изображений? (webarxive? понимаются ли видео с него для встраивания в собщения на местном форуме)

#56
(Правка: 14:14) 14:13, 20 июля 2021

KPG
> Можно и на современное железо, с разной степенью удачности, установить Win98
> (Win98IF)

Эмулятор удобен тем, что если ДОС-прога зависла - прибил. И снова открыл.  Время - доли секунды.
На реальном железе придётся ждать загрузки..

Gradius
> Новая проблема - запускаю игру с трассировкой. Трассировка какое-то время
> идёт, затем заламывается и всё повисает.
>
> DOSBox кидает эксепшн: illegal descriptor type 0 for int 1.

Всё-же разобрался с третьим экстендером DOS32/a, прибиндил его.  Звук пошёл.  И трассировка тоже идёт и не падает.

Дело либо в ДОС-экстендере (не выдерживают они постоянной дрочки трассировкой), либо в эмуляции.


Игра очень тормозит на трассировке!

Оно и понятно почему - после каждой инструкции(за некоторым исключением) вызывается INT1.  Даже если он пустой -всеравно тормозит :)

Надо переходить на бряки (INT3) или всё-же вручную патчить :)

Распределение сегментов и тут тоже другое )))

#57
(Правка: 16:42) 16:40, 20 июля 2021

Gradius
> Надо переходить на бряки

Ещё один камень в адрес DOSBox - он не поддерживает аппаратную отладку в защищённом режиме.  Регистры dr0..dr7 - ничего не делают.  Хотел бряк поставить и вызвать прерывание - ничего не работает.

В исходниках DOSBOx'а сделал поиск по подстрокам dr0... dr7 - нет вообще упоминания этих регистров.
В DOSEmu та же херня. 

Какой идиот пишет эмуляторы, столько времени прошло - уж можно было добавить возможность нормальной 32-битной отладки!

В общем не годятся эти побрякушки для серьёзных дел - придётся отказаться от идеи трассировки и авто-замены  опкодов на лету.  Ручками придётся править или трассировать всё с тормозами.

Про аппаратную отладку: https://vk.com/@haccking1-skrytyi-potencial-registrov-otladki-dr0-dr7

#58
17:23, 20 июля 2021

Gradius
> В общем не годятся эти побрякушки для серьёзных дел - придётся отказаться от
> идеи трассировки и авто-замены опкодов на лету. Ручками придётся править или
> трассировать всё с тормозами.
В QEMU, вроде с этим лучше.

#59
7:12, 21 июля 2021

Тут ещё возникла проблема организационного характера.
Мне нужно смешивать ассемблер, Си и С++ в одном проекте.

При дизассемблировании имена функций и переменных даются в человеческом виде - без подчёркиваний и обрамлений.

Но при стыковке с С/C++ возникает ряд проблем.  Проиллюстрирую:

1) Переменная объявлена в ассемблере просто как метка на данные:

SomeVariable  dd ?

При упоминании этой переменной в С++,  он хочет чтобы переменная была с нижним подчёркиванием:

_SomeVariable  dd ?

Тогдa:

extern "C" dword SomeVariable;

Прокатывает.  Но если всем ассемблерным переменным сделать нижний подчёрк, то начинает ругаться ассемблер.

Выкрутился макросами:

_SomeVariable dd
SomeVariable equ _SomeVariable

При таком подходе мы заставляем ассемблер работать с синонимом переменной без нижнего подчёрка, а для С++ объявлен вариант с нижним подчёркиванием.  Оба (ассемблер и С++) на самом деле работают с нижним подчёркиванием в этом случае.

2) Функции.  Тут я не знаю что делать.  Всё намного хуже.

В дизассемблерном листинге IDA функции:

load_all_tmaps
setup_host
FindColor

В декомпилированном Hex-rays эти же функции с учётом конвенций вызовов:

unsigned int __usercall load_all_tmaps(__int16 a1, __int16 a2);
int __fastcall setup_host(int a1, int a2);
char __cdecl FindColor(unsigned __int8 *a1, unsigned __int8 a2, unsigned __int8 a3, unsigned __int8 a4);

__usercall , __fastcall,  __cdecl - важно сохранить, так как они диктуют работу с параметрами и возвращаемым значением и как работать со стеком (кто очищает).

С++ ругается и требует такие имена:

Undefined Symbols:

load_all_tmaps_  
@setup_host@8 
_FindColor           

Это просто долбануться можно )))  В зависимости от конвенции - обрамление функции своё.

Можно ли как-нибудь разрулить эту ситуацию?

Отключить декорирование в С++ или заставить ассемблер генерить имя в зависимости от указанной конвенции ?

Страницы: 13 4 5 612 Следующая »
ФлеймФорумПрограммирование