То была DLL-ка с секцией кода ~100 килобайт, она брала на вход многоканальный сигнал и выдавала другой многоканальный сигнал. Там много всего было связано с обработкой сигналов. Внутри было очень много FPU-кода. Написано по-олдскульному, так, как писали в то время, когда передача параметров через аргументы ф-ций была дорогой, и потому использовалось много глобальных переменных и массивов, почти всё хранилось в них, а ф-ции, напротив, имели сравнительно мало аргументов, если вообще. Функции большие, их было около ста.
Тесты были, много.
Проблема была в том, что функции слишком большие и Hex-Rays неизменно выдавал немного неверный код. Нужно было очень внимательно всё чистить вручную. В процессе работы, я нашел в нем каких-то баг: https://github.com/DennisYurichev/RE-for-beginners/blob/master/other/hexrays_RU.tex.
Все 100 ф-ций декомпилировать сразу нельзя -- где-то будут ошибки, тесты не пройдут, и где вы будете искать эти ошибки? Приходится переписывать по чуть-чуть.
В DLL-ке есть некая корневая ф-ция, скажем, ProcessMain(). Я переписываю её на Си при помощи Hex-Rays, она запускается из обычного .exe-процесса. Все ф-ции из DLL-ки, которые вызываются далее, у меня вызывались через указатели на ф-ции. DLL-ка загружена, и пока они все там.
ASLR отключил, и DLL-ка каждый раз грузится по одному и тому же адресу, потому и адреса всех ф-ций одни и те же. Важно, что и адреса глобальных массивов тоже одни и те же.
Затем переписываю ф-ции, вызывающиеся непосредственно из ProcessMain(), затем еще ниже, итд. Таким образом, ф-ции я постепенно перетаскивал из DLL в свою .exe. Каждый раз тестируя.
Много раз бывало и так -- ф-ция слишком большая, например, несколько килобайт x86-кода, и после декомпиляции в Си, там что-то косячит, неизвестно где. Из IDA я экспортировал её листинг в текст на ассемблере и компилировал при помощи обычного ассемблера (ML в MSVC). Она компилируется в .obj-файл и прикомпилируется к главной .exe, и пока всё ОК. Затем я делил эту ф-цию на более мелкие, здорово пригодился (когда бы еще?) опыт написания программ на чистом ассемблере в середине 90-х (руки до сих пор помнят). Если всё работает, более мелкие ф-ции постепенно переписывал на Си при помощи Hex-Rays, в то время как "головная" ф-ция более высокого уровня всё еще на ассемблере.
Интересно, что было много глобальных массивов, но границы между ними были сильно размыты. Но я вижу что есть какой-то большой кусок в секции .data, где лежит всё подряд. Дошел до стадии, когда на Си переписано уже всё, а все обращения к массивам происходят по адресам внутри секции .data в подгружаемой DLL-ке, впрочем, там почти не было констант. Затем, чтобы совсем отказаться от DLL-ки, я сделал большой глобальный "кусок" уже у себя на Си, и вся работа с массивами шла через мой "кусок", при том, что все массивы всё еще не были отделены друг от друга.
Вот реальный фрагмент оттуда, как было в начале. Значение -- это адрес в .data-секции в DLL-ке:
int *a_val511=0x1002B588; int *a_val483=0x1002B590; int *a_val481=0x1002B5B8; int *a_val515=0x1002B6E4; ...
И все обращения происходят через указатели.
Потом я сделал "кусок":
char lump[0x1000000]; /* 0x1002B588 */int *a_val511=(int*)&lump[0x2B588]; /* 0x1002B590 */int *a_val483=(int*)&lump[0x2B590]; /* 0x1002B5B8 */int *a_val481=(int*)&lump[0x2B5B8]; /* 0x1002B6E4 */int *a_val515=(int*)&lump[0x2B6E4]; ...
DLL-ку теперь можно было наконец-то отцепить и разбираться с границами массивов. Этот процесс я хотел немного автоматизировать и использовал для этого Pin. Я написал утилиту, которая показывала, по каким адресам в глобальном "куске" были обращения из каждого адреса. Точнее, в каких пределах? Так стало проще видеть границы массивов.
"На войне все средства хороши", так что я доходил и до того, что использовал Mathematica и Z3 для сокращения слишком длинных выражений (Hex-Rays не всё может оптимизировать): https://github.com/DennisYurichev/SAT_SMT_by_example/blob/master/proofs/simplify_EN.tex
Очень хорошим тестом было пересобрать всё под Linux при помощи GCC и заставить работать -- как всегда, это было нелегко. Плюс, чтобы работало корректно и под x86 и под x64.