Как декомпилировать hex файл

Обновлено: 05.07.2024

Парни прощу помощи, есть прошивка HEX код рабочий , но частоты мало надо добавить или время перехода с MAX на MIN , но для этого надо прошивку привести к виду для изменения (файл-проект, арр, и тд) у меня не получается, про бывал IDA Pro Free, MPLAB, как сделать или помогите , брат залетел на Сайбере на 1800 км надо срочно накрутить

Это автор написал про свое устройство:
Вот девайс специально разработанный мною для ЭТИХ целей.

Генератор качающейся частоты с остановкой на нужной частоте
и с функцией выключения спидометра во-время движения машины.
Чип: ATtiny13
РВ0 (пин5) - кнопка.
РВ3 (пин2) - управление реле.
РВ4 (пин3)- выход генератора.
РВ1 (пин6)- тестовый выход (постоянно меандр).
Девайс сделан для скрытного монтажа в спидометр. Управляется родной кнопкой щитка.
Потенциал управления минусовой (логическая ноль, земля).

Алгоритм работы такой:
1. Нажали и держим кнопку. Через 7-8 секунд включится реле коммутации и стрелка спидометра пойдёт набирать скорость. Отпустили кн.
2. Когда спидометр наберёт нужную скорость, нажимаем кнопку и стрелка остановится на выбранном значении скорости.
3. Следующее нажатие - машина едет, но стрелка на нулю (когда надо поюзать авто но что б счётчик не считал).
4. 4-е нажатие - система возвращается в заводское состояние.

а это код (прошивка)
:0E0000000AC0FECFFDCF41C0FBCFFACFA1 C09A
:10000E00A5C0F7CFF6CF0000F894EE27EC BBE5BF06
:10001E00F8E1A4B7A77FA4BFF1BDE1BD8D E0A2E0DA
:10002E00ED938A95E9F780E4A0E6ED938A 95E9F7DA
:10003E00E4E1F0E0C8953196802DC89531 96902D6B
:10004E000097B9F0C8953196A02DC89531 96B02D70
:10005E00C8953196102CC89531966E2F7F 2FF02DA6
:10006E00E12DC89531960D920197D9F7E6 2FF72F0E
:10007E00E1CFEFE9EDBFC0E7D0E06DC00A 92AA93E1
:10008E00EA93EFB7EA9372BEE894C19B68 94A8B363
:10009E00A1F9A8BBE6B3E1705E2EE52DE4 256E2E28
:1000AE00E42DE621E17021F0C39902C068 9420F896
:1000BE00E52DE621E17029F09924E89420 F8C49802
:1000CE00C398EAE5E91518F4E1E08E2E04 C0E0E0ED
:1000DE0020FCE1E09E0E6BD06FD0E1E07F D0E021FE
:1000EE0019F06624E2E08E2E62D06AD0E2 E076D07D
:1000FE00E02119F06624E3E08E2E59D061 D0E3E0C2
:10010E006DD0E02111F066248824A89588 2011F482
:10011E00C498C398E1E0E81511F458D073 94E2E066
:10012E00E81511F453D0772CE3E0E81519 F4C39ACF
:10013E00C4987724452CE991EFBFE991A9 910990D4
:10014E001895EA93EAEAE6BFE9911895EA 93EBEB84
:10015E00E9BDE9911895E0E8E6BDE2E0E6 BDE0E034
:10016E008E2E7E2E6E2E5E2EE1E04E2EEF E3E7BB40
:10017E00E1E0E8BBEEE3E7BBE0E0EFBDE5 E0E3BFC7
:10018E00EAEFE2BFEAEAE6BFEBEBE9BDE0 E0EBBF88
:10019E00E5BFEEE0E9BFE0E8E8B9E0E0E3 B9E9E3A6
:1001AE00E1BDE9E2E1BD7894A895FECFFF CFE0E096
:1001BE00C399E1E0E6210895E1700E2EA8 2D089571
:1001CE00E421AE2FE52D0FD0EA23F6CFC3 9AE894A3
:1001DE00C49B6894A8B3A4F9A8BB0895EA 17E1E0FC
:1001EE0009F0EE270895EE23E1E009F0EE 270895D9
:00000001FF

Начинающие реверсеры, еще не познавшие все прелести чистого ассемблера, постоянно спрашивают на хакерских форумах, где бы им раздобыть
декомпилятор для Си или Паскаля. Декомпиляторов-то много, но вот результат. без дизассемблера все равно ну никак не обойтись. И вот, наконец, свершилось!
Ильфак (автор легендарного дизассемблера IDA Pro) выпустил декомпилятор нового поколения
HexRays, делающий то, что другим не под силу. Мир вздрогнул от восторга (реклама вообще обещала чудо), порождая сейсмические волны впечатлений: от оргазма до полного отвращения. Истина же, как водится, где-то посередине, и стоит обозначить эту середину, рассмотрев слабые и сильные стороны
декомпилятора, а также
области его применения.

Почему у Ильфака получилось то, что упорно не желало получаться у других? Начнем с того, что
HexRay представляет собой всего лишь плагин к IDA Pro — интерактивному дизассемблеру более чем с десятилетней историей, первая версия которой увидела свет 6 мая 1991 года. Спустя некоторое время к работе над ней подключился удивительный человек, гениальный программист и необычайно креативный кодер Юрий Харон. Вместе с Ильфаком (не без помощи других талантливых парней, конечно) они начали работать в том направлении, куда еще никто не вкладывал деньги. До этого дизассемблеры писались исключительно на пионерском энтузиазме параллельно с изучением ассемблера и довольно быстро забрасывались. Ильфак и
Юрий Харон решили рискнуть. В итоге IDA Pro стал не только основным, но и, пожалуй, единственным продуктом фирмы
DataRescue (мелкие утилиты и прочие ответвления не в счет). Стоит ли удивляться, что парням удалось решить практически все фундаментальные проблемы дизассемблирования, над которыми просто не хотели работать остальные разработчики, зная, что быстрой отдачи не будет и проект потребует десятилетий упорного труда. Самое время выяснить, что же это за проблемы такие, откуда они берутся и как решаются.

Фундаментальные проблемы декомпиляции

Компиляция — процесс однонаправленный и необратимый, в ходе которого теряется огромное количество лишней информации, совершенно ненужной процессору. Это не шутка! Исходный текст, написанный на языке высокого уровня, чрезвычайно избыточен, что с одной стороны упрощает его понимание, а с другой — страхует программиста от ошибок. Взять хотя бы информацию о типах. На низком уровне процессор оперирует базовыми типами: байт, слово, двойное слово. Строки превращаются в последовательности байтов, и, чтобы догадаться, что это строка, приходится прибегать к эвристическим алгоритмам.

Структуры и классы также «расщепляются» в ходе компиляции, и, за исключением некоторых виртуальных функций, все остальные члены класса теряют свою кастовую принадлежность, становясь глобальными процедурами. Восстановить иерархию классов в общем случае невозможно, а в принципе не сильно и нужно.

Никто из здравомыслящих людей не требует от декомпилятора получения даже приближенной копии исходного кода, но мы вправе рассчитывать на удобочитаемость листинга, а также на то, что повторная компиляция не развалит программу, а создаст вполне работоспособный модуль — тогда мы сможем вносить любые изменения в
декомпилированный текст, развивая его в нужном нам направлении. В противном случае такому
декомпилятору место на свалке, и фиксить баги декомпиляции ничуть не проще, чем переписывать подопытную программу с нуля.
Декомпиляции препятствует ряд серьезных проблем, важнейшие из которых перечислены ниже.

Что в имени твоем

Комментарии, имена функций и переменных в процессе компиляции теряются безвозвратно (исключение составляют динамические типы, имена которых в двоичном модуле хранятся как есть, а потому и тормозят, словно динозавры). Ну, комментариев в большинстве исходных текстов и так негусто, так что их отсутствие еще можно пережить, но вот без имен переменных и функций логика работы даже простейших программ становится совершенно непонятной.

К счастью, современные программы наполовину (а то и более) состоят из библиотечных и API-функций, которые распознаются классическим сигнатурным поиском. Главное — собрать обширную базу библиотек всех популярных (и непопулярных) компиляторов с учетом многообразия их версий.

IDA Pro уже давно поддерживает технологию FLIRT (Fast Library Identification and Recognition Technology), не только распознающую библиотечные функции, но также восстанавливающую их прототипы вместе с прототипами API-функций, которые, кстати говоря, могут импортироваться не только по имени, но еще и по ординалу, то есть по номеру. Чтобы превратить бессловесный номер в имя, понятное человеку, опять-таки требуется база.

Почему реконструкция прототипов так важна для декомпилятора? А потому, что она позволяет восстанавливать типы передаваемых функциями переменных, назначая им хоть и обезличенные, но все же осмысленные имена в стиле: hWnd, hModule, X, Y, nWidth, nHeight, hCursor, hMenu, hDlg. Согласись, это намного лучше, чем dword_1008030, dword_1008269, dword_1006933, что характерно для подавляющего большинства остальных
декомпиляторов.

Даже если часть переменных распознана, то это уже упрощает анализ программы и реконструкцию оставшихся переменных.

Константа или смещения

В силу архитектурных особенностей x86-процессоров константы и смещения (они же в терминах языков высокого уровня указатели) синтаксически абсолютно неразличимы, и
декомпилятору (равно как и дизассемблеру) приходится задействовать мощные эвристические алгоритмы, чтобы не попасть впросак.

А какая между этими двумя сущностями разница? Очень большая! Вот, например, в регистр EAX загружается число 106996h. Если это указатель на ячейку памяти или массив данных, то дизассемблер должен воткнуть по этому адресу метку (label) и написать MOV EAX, _offset_ lab_106996 (естественно, имя метки дано условно, и совпадение с ее численным значением абсолютно случайно — при повторной компиляции она может оказаться расположенной по совершенно другому адресу). А вот если 106996h — это константа, выражающая, например, среднюю плотность тушканчиков на один квадратный метр лесополосы или количество полигонов на морде монстра, то ставить offset ни в коем случае нельзя, поскольку это введет нас в
заблуждение при анализе, а еще, как уже говорилось, при повторной компиляции смещение может уплыть, превращая число 106996h черт знает во что. Программа либо будет работать неправильно, либо сразу рухнет (особенно, если путаница между константами и смещениями происходит не единожды, а совершается на протяжении всего листинга).

Так как же все-таки определить, кто есть кто? На первый взгляд, все достаточно просто. Если данная сущность используется как указатель (то есть через нее происходит обращение к памяти по заданному адресу: mov eax, 106996h/mov ebx,[eax]), тут и
дизассемблеру ясно, что это смещение. Но стоит лишь немного усложнить пример, скажем, передать 106996h какой-нибудь функции или начать производить с ней сложные манипуляции, то дизассемблеру потребуется высадиться на полную реконструкцию всей цепочки преобразований. Но даже тогда его решение может оказаться неверным, поскольку в языке Си индексы (то есть константы) и указатели (то есть смещения) полностью взаимозаменяемы и конструкция buf[69] не
только равносильна 69[buf], но и транслируется в идентичный машинный код, при реконструкции которого возникает очевидная проблема.

У нас есть два числа A и B, сумма которых представляет указатель (то есть существует возможность определить, что это указатель, да и то если попыхтеть), но вот что из них индекс? Увы. Формальная математика не дает ответа на этот вопрос, и дизассемблеру приходится полагаться лишь на свою интуицию да эвристику. Указатели в win32, как правило, велики, поскольку минимальный базовый адрес загрузки файла равен 100000h. Индексы же, как правило (опять! как правило!), малы и много меньше указателей, однако если у нас имеется массив, расположенный по адресу 200000h и состоящий из 3 145 728 (300000h) элементов (а почему бы, собственно, и нет?!), то тут индексы старших элементов становятся больше
базового указателя на массив, что вводит дизассемблер в заблуждение.

Код или данные

Вот так проблема! Наверное, даже самый тупой дизассемблер разберется, что ему подсунули: код или данные, тем более что в PE-файлах (основной формат исполняемых файлов под Windows) они разнесены по разным секциям. Ну, это в теории они разнесены, а на практике компиляторы очень любят пихать данные в секцию кода (особенно этим славиться продукция фирмы Borland).

С другой стороны, практически все компиляторы (особенно на Си++) сплошь и рядом используют вызовы функций типа CALL EBX. Чтобы понять, куда ведет такой вызов, необходимо установить значение регистра EBX, что требует наличия более или менее полного эмулятора процессора, отслеживающего всю историю содержимого EBX с момента его инициализации и до непосредственного вызова.

Но непосредственные вызовы - ерунда. Вот косвенные - это да! Реконструкция CALL dword ptr DS:[EBX] требует не только эмуляции процессора (то есть набора машинных команд), но и эмуляции памяти! Естественно, это на порядок усложняет дизассемблерный движок, зато нераспознанные массивы данных мгновенно превращаются в функции и, что самое главное, дизассемблер показывает, кто именно и откуда их вызывает!

Наличие эмулятора процессора и памяти позволяет также отслеживать положение регистра ESP, используемого для адресации локальных переменных и аргументов, передаваемых функциям. Если же эмулятора нет (а в IDA он есть), дизассемблер способен распознавать лишь простейшие формы адресации/передачи аргументов. Механизм, ответственный за распознание и отслеживание аргументов, носит красивое название PIT, но эта аббревиатура отнюдь не расшифровывается как «Пивоваренный [завод] Ивана Таранова», а происходит от
Parameter Identification and Tracking — идентификация и отслеживание параметров, то есть аргументов. Тех, что бывают у функций. А еще бывают функции без аргументов, но это уже другая история.

Реконструкция потока управления

Языки высокого уровня оперируют такими конструкциями, как циклы, операторы выбора, ветвления и т.д., что делает программу простой и наглядной. Собственно говоря, именно отсюда и пошло структурное программирование. А ведь когда-то, давным-давно, в Бейсике (и других языках того времени), кроме примитивного GOTO, ничего не было и программа, насчитывающая больше сотни строк, превращалась в сплошные «спагетти», лишенные всякой логики, внутренней организации и следов цивилизации.

Начиная с пятой версии в IDA Pro появился механизм графов, позволяющий автоматически реконструировать циклы, ветвления и прочие «кирпичи» языков высокого уровня. Впрочем, польза от графов была небольшой, и в реальности они только замедляли анализ, поскольку подобрать адекватный механизм визуализации и навигации Ильфаку так и не удалось… Но ведь не пропадать же труду?!

У истоков HexRays

К пятой версии IDA Pro имела в своем арсенале все необходимое для автоматической декомпиляции, причем не просто декомпиляции, а очень
качественной декомпиляции, декомпиляции принципиально нового уровня, до которого не дотягивает ни один другой существующих
декомпилятор.

Вот так и родилась идея дописать к IDA еще небольшую (на самом деле очень большую) порцию кода, переводящую китайскую ассемблерную грамоту в доступный и понятный листинг на языке Си. Закипела напряженная работа, по ходу которой выявлялись все новые и новые подводные камни, обход которых требовал времени, усилий и мозговой активности. А всякая (или практически всякая) работа упирается в деньги, тем более что у Ильфака фирма!

Включать декомпилятор в дистрибутив IDA Pro Ильфак не стал. Тому было несколько причин. Первая и главная — основной массе текущих пользователей IDA декомпилятор не сильно нужен, если они им и будут пользоваться, то лишь из чистого любопытства, запустят пару раз, плюнут и вернутся к привычному стилю жизни - анализу дизассемблерного листинга. Второе — зарабатывать на жизнь (Ильфаку) и содержать фирму как-то же надо?!

Все это привело к тому, что декомпилятор, получивший название (HexRays), был выпущен отдельным продуктом, но - внимание на экран - требующим
обязательного присутствия IDA, поскольку HexRays — всего лишь плагин. Таким образом, реверсеру, желающему упростить свою жизнь за счет автоматической декомпиляции, необходимо прибрести как саму IDA, так и
HexRays. Причем приобретать этот комплект будет совсем другая пользовательская аудитория, совсем не та, что приобретала ИДУ и почитала ее как самый лучший интерактивный дизассемблер. Интерактивный — значит, тесно взаимодействующий с пользователем (в смысле с хакером). В противовес ей, пакетные дизассемблеры стремятся к максимальной автоматизации
реверсинга, лишая пользователя возможности вмешиваться в процесс и отдавать указания.
HexRays в отличии от IDA Pro интерактивностью не обладает: она у него атрофирована еще в зародыше. Нет даже опций настройки! А там где нет интерактивности, нет и хакеров. И тут мы плавно переходим к ответу на вопрос кому нужен
HexRays.

Боевое крещение

Итак, Hex-Rays у нас на руках. Устанавливаем его туда же, где располагаются все остальные
плагины для IDA Pro, и приступаем к тестированию. В качестве объекта тестирования используется стандартный блокнот (в данном случае из комплекта поставки W2KSP0), на котором обкатываются все вирусы, все упаковщики исполняемых файлов (вместе с распаковщиками), все протекторы. словом, декомпилятор не станет исключением. А что? Блокнот - достаточно простая программа, обуславливающая тот минимум функционала, ниже которого декомпилятор из мощного программного комплекса превращается в дорогостоящую, но абсолютно бесполезную игрушку.

Загрузив notepad.exe в IDA Pro и ответив на ряд несложных вопросов мастера автоанализа, даем дизассемблеру некоторое время поработать и, когда он перейдет в режим ожидания, заходим в меню «File -> Produce File -> Create C file» или нажимаем горячие клавиши <Ctrl-F5>.

Сначала на экране появляется логотип HexRays с предложением провериться на обновления и после нажатия на OK
HexRays думает некоторое время, а когда он закончит заниматься магической деятельностью, на диске образуется файл notepad.c.

Может, мы не тот компилятор выбрали для тестирования? Вообще-то, по идее, декомпилятор должен выдавать листинг на ANSI C, поддерживаемый любым ANSI-совместимым C-компилятором, но… все мы хорошо знаем любовь Ильфака к продукции фирмы Borland. На нем написана сама IDA, на нем же вышло первое SDK… Хорошо, берем последнюю версию
Borland C++ (благо она бесплатна) и что же?! Вновь простыня ошибок и ругательств, приводящих к преждевременному прерыванию компиляции. Оказывается,
чтобы откомпилировать декомпилированный файл, над ним еще предстоит основательно поработать. Вот тебе и раз…

А вот тебе и два! Декомпилятор выдал чистый *.c-файл, проигнорировав тот факт, что notepad.exe содержит еще и секцию ресурсов, но даже если при загрузке блокнота в IDA форсировать обработку ресурсов, мы все равно ничего не получим. Ну не умеет
HexRays с ними работать и все-тут! Возможно, когда-нибудь он этому и научиться, но сейчас - нет.

Другими словами, HexRays не позволяет компилировать декомпилированные программы без дополнительной ручной работы и реально пригоден лишь для анализа. Возникает резонный вопрос: и насколько же он упрощает анализ? Чтобы не быть голословным, приведу два фрагмента кода:

if ( RegOpenKeyExA(
HKEY_CLASSES_ROOT,
"CLSID\\\\InprocServer32",
0,
0x20019u,
&hKey) )
result = 0;
>
else
cbData = 260;
if ( !RegQueryValueExA(hKey, ValueName, 0, 0, lpData, &cbData) ) v1 = 1;
RegCloseKey(hKey);
result = v1;
>
return result;

Просто? Красиво? Понятно? Элегантно? Еще как! А вот как выглядел тот же самый код в чистом дизассемблере:

.text:0100318F push ebp
.text:01003190 mov ebp, esp
.text:01003192 push ecx
.text:01003193 push ecx
.text:01003194 lea eax, [ebp+hKey]
.text:01003197 push esi
.text:01003198 xor esi, esi
.text:0100319A push eax ; phkResult
.text:0100319B push 20019h ; samDesired
.text:010031A0 push esi ; ulOptions
.text:010031A1 push offset SubKey ; lpSubKey
.text:010031A6 push 80000000h ; hKey
.text:010031AB call ds:RegOpenKeyExA
.text:010031B1 test eax, eax
.text:010031B3 jnz short loc_10031E7
.text:010031B5 lea eax, [ebp+cbData]
.text:010031B8 mov [ebp+cbData], 104h
.text:010031BF push eax ; lpcbData
.text:010031C0 push [ebp+lpData] ; lpData
.text:010031C3 push esi ; lpType
.text:010031C4 push esi ; lpReserved
.text:010031C5 push offset ValueName ; lpValueName
.text:010031CA push [ebp+hKey] ; hKey
.text:010031CD call ds:RegQueryValueExA
.text:010031D3 test eax, eax
.text:010031D5 jnz short loc_10031DA
.text:010031D7 push 1
.text:010031D9 pop esi
.text:010031DA
.text:010031DA loc_10031DA: ; CODE XREF: sub_100318F+46?j
.text:010031DA push [ebp+hKey] ; hKey
.text:010031DD call ds:RegCloseKey
.text:010031E3 mov eax, esi
.text:010031E5 jmp short loc_10031E9
.text:010031E7
.text:010031E7 loc_10031E7: ; CODE XREF: sub_100318F+24?j
.text:010031E7 xor eax, eax
.text:010031E9
.text:010031E9 loc_10031E9: ; CODE XREF: sub_100318F+56?j
.text:010031E9 pop esi
.text:010031EA leave
.text:010031EB retn 4

Согласись, что сравнение отнюдь не в пользу дизассемблера, но не спеши радоваться. Рассмотрим еще один пример:

Что делает этот код? Совершенно непонятно. То есть не то чтобы совсем непонятно, но смысл как-то ускользает. Тут требуется проанализировать, что это за переменные такие, кто еще (помимо этой функции) их модифицирует и зачем? В дизассемблере листинг выглядит схожим образом, но мощная система навигации по коду вкупе с перекрестными ссылками и подсветкой одноименных переменных/функций сокращает время анализа на несколько порядков, причем ни в одном текстовом редакторе, ни в одной среде программирования подобной системы навигации по коду нет!

А это вид в дизассемблере:

.text:01002306 loc_1002306: ; CODE XREF: sub_1002239+C4?j
.text:01002306 mov eax, dword_1008028
.text:0100230B push ebx
.text:0100230C push edi
.text:0100230D mov dword_1008BCC, eax
.text:01002312 push dword_10087D0
.text:01002318 call sub_10059A3
.text:0100231D test eax, eax
.text:0100231F jz short loc_1002328

Наш вердикт

HexRays – это, безусловно, большой шаг вперед и неплохое подспорье для начинающих, но… он не стоит тех денег, которые за него просят, к тому же (это касается начинающих), однажды потратив время на изучение ассемблера, мы обретаем возможность реверсировать что угодно и в чем угодно (на худой конец с помощью утилиты DUMPBIN.EXE, входящей в состав SDK), а обольщенные возможностями автоматической декомпиляции, мы становимся заложниками
HeyRays со всеми вытекающими отсюда последствиями.



Полную версию статьи
читай в февральском номере Хакера! Демонстрационную версию IDA ты найдешь на нашем диске. К сожалению, мы не можем выложить HexRays по правовым соображениям. На нашем диске будет видеоролик, демонстрирующий возможности HexRays.



В этой статье я собираюсь рассказать о плагине для IDA Pro, который написал прошлым летом, еще находясь на стажировке в нашей компании. В итоге, плагин был представлен на ZeroNights 2016 (Слайды), и с тех пор в нём было исправлено несколько багов и добавлены новые фичи. Хотя на GitHub я постарался описать его как можно подробнее, обычно коллеги и знакомые начинают пользоваться им только после проведения небольшого воркшопа. Кроме того, опущены некоторые детали внутренней работы, которые позволили бы лучше понять и использовать возможности плагина. Поэтому хотелось бы попытаться на примере объяснить, как с ним работать, а также рассказать о некоторых проблемах и тонкостях.

HexRaysPyTools, как можно догадаться из названия, предназначен для улучшения работы декомпилятора Hex-Rays Decompiler. Декомпилятор, создавая псевдо-С код, существенно облегчает работу ревёрсера. Основым его достоинством, выделяющим инструмент на фоне других, является возможность трансформировать код, приводя его к удобному и понятному виду, в отличие от ассемблерного кода, который, даже при самом лучшем сопровождении, требует некоторой доли внимания и сосредоточенности для понимания его работы. У Hex-Rays Decompiler, как и самой IDA Pro, есть API, позволяющий писать расширения и выходить за рамки стандартного функционала. И хотя API очень широк, и в теории позволяет удовлетворить самые изысканные потребности разработчика дополнений, у него есть несколько существенных недостатков, а именно:

  • Слабая документированность. Для того, чтобы найти подходящие функции или классы, самый эффективный способ — произвести поиск регулярными выражениями по файлам, угадывая ключевые слова
  • Произвольные названия функций и структур — часто их название не говорит ни о чём
  • Deprecated методы, для которых не предложено замены
  • Общая запутанность работы. Например, для изменения типа аргумента у функции, нужно написать 8 строк кода и задействовать 3 класса. И это не самый странный пример
  • API для языка Python не совсем совпадает с тем, что для C++. idaapi содержит новые методы, наткнуться на которые получилось чисто случайно
  • Есть неочевидные вещи: например, IDA Pro будет падать, если не отключать Garbage Collector для объектов, добавленных с помощью idaapi в абстрактное синтаксическое дерево (оно всегда строится, когда происходит декомпиляция, и в нём можно изменять объекты или вставлять свои).

Чтобы разобраться во всем этом, помогали рабочие примеры, собранные в Интернете (что-то интересное нашлось даже в китайском сегменте). Так что теперь, если кто-то захочет создать что-то своё для декомпилятора, можно обратиться еще и к исходным кодам моего плагина.

Перейдем к описанию плагина. В HexRaysPyTools можно выделить две отдельные категории — это помощь по трансформации дефолтного вывода Hex-Rays Decompiler к удобному виду и реконструкция структур и классов.

Изначально, после запуска декомпилятора клавишей F5, IDA Pro выдаёт не очень понятый код, состоящий преимущественно из стандартных типов и имён переменных. И несмотря на то, что местами пытается угадать типы, создать массивы или назвать эти переменные (которым повезло оказаться аргументами у стандартных функций), получается это не очень хорошо. В целом, задача ревёрсера — привести в адекватный вид декомпилированный код. К сожалению, есть вещи, которые невозможно сделать, не прибегая к IDA SDK. Например, отрицательные обращения к полям структур, которые всегда выглядят безобразно (порой превращаясь в массивы с отрицательными индексами), а также длинные условные вложения, тянущиеся из левого верхнего угла в правый нижний. Кроме этого, очень не хватает горячих клавиш и опций для более быстрой трансформации кода. По мере получения информации в процессе анализа программы приходится изменять сигнатуры функций, переименовывать переменные и изменять типы. Всё это требует большого количества манипуляций мышкой и копирований-вставок. Перейдем к описанию того, что предлагает плагин для решения этих проблем.

Отрицательные смещения

Очень часто встречаются при реверсинге драйверов или ядра Windows или модулей ядра Linux. Например, несколько разных структур может быть расположено в двусвязном списке с использованием структуры LIST_ENTRY. При этом у каждой структуры обращение к двусвязному списку может производиться из произвольного поля.

В результате, когда мы смотрим на то, что получается в IDA Pro, видим следующую картину:


Такой вывод будет всякий раз, когда в исходных кодах программ используются макросы CONTIAINING_RECORD (windows) и container_of (linux). Эти макросы возвращают указатель на начало структуры по её типу, адресу и названию поля. И именно их плагин позволяет вставлять в дизассемблер. Вот как выглядит пример после его применения:


Еще с отрицательными смещениями можно встретиться при множественном наследовании, но это довольно изысканный пример, он редко встречается на практике.

Для того, чтобы вставить макрос в дизассемблер, нужно, чтобы подходящая в данном контексте структура существовала в Local Types или в одной из библиотек в Types Library (их может быть несколько). Мы кликаем по вложенной структуре правой кнопкой и выбираем Select Containing Structure. Далее определяем, где искать структуру, — либо в Local Types, либо в Types Library, и плагин составляет список подходящих структур. Для этого он анализирует, как указанная переменная используется в коде и определяет минимальную и максимальную границы, в которых может находиться поле типа этой переменной. Затем, используя эти сведения, проходит по всем структурам, содержащим поле, у которых все в порядке с границей. При поиске плагин смотрит вложенные структуры и объединения на любую глубину.

В примере выше у exe-файла есть символы, поэтому список подходящих структур получился довольно большой:


Помимо этого, существует ситуация, когда плагин автоматически может вставить макрос. Дело в том, что если есть явное присвоение указателя, IDA Pro догадывается (иногда неправильно) его вставить, но не распространяет его дальше в коде.



Сильная вложенность

Пожалуй, лучше всего показать искусственный пример. Без плагина:



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

Переименования

Идея в том, чтобы называть переменную или аргумент приходилось не более одного раза, а дальше все переименования совершались горячими клавишами или двумя кликами.

Часто IDA Pro создаёт дублированные переменные. Можно было бы, используя стандартную опцию "map to another variable", избавиться от них. Но это не всегда удобно при отладке, может быть ошибочно и к тому же невозможно откатить, не пересоздавая функцию заново.

Перебросить можно имя с одной переменной на другую, при этом добавляется символ "_":



Можно переименовать аргумент у функции, заставив её взять имя переменной (при этом лишние символы подчёркивания уберутся). Либо, наоборот, переменной присвоить имя аргумента функции.

Recasts

Существует множество ситуаций, когда есть взаимодействие между двумя некоторыми сущностями с разными типами, и нам нужно перенести тип одной сущности на другую. Под сущностями понимаются локальные, глобальные переменные, аргументы, функции, поля структур (с обращением по ссылке и без) и возвращаемые значения функции. Плагин позволяет это быстро произвести. Сложно показать картинкой, рекомендую в случае, если нужно перенести тип одной сущности на другую, кликнуть правой кнопкой мыши по ней и посмотреть на опции. Наверняка там появится "Recast . " (а если не появится, то можно написать мне, и я попробую её добавить).

Прочее

Помимо этого, добавляются следующие опции:

  1. Поиск структуры по размеру и замена числа на sizeof(Structure) ). Удобно для поиска структуры подходящего размера по числу байт, указанных оператору new или функции malloc .
  2. Быстрое изменение сигнатуры функции. Кликнув правой кнопкой по её объявлению, можно добавить/удалить возвращаемое значение, удалить аргумент, сбросить соглашение о вызове к __stdcall.
  3. Переход по двойному клику у виртуальных методов.

Одной из самых сложных и энергозатратных задач ревёрс-инжиниринга является понимание работы и реконструкция структур и классов. HexRaysPyTools выступает помощником в этом процессе. В чём, собственно, проблема? Средствами по умолчанию можно только залить уже готовое объявление структуры, поэтому приходится "ползать" по коду, пытаясь насобирать сведения о полях, вручную высчитывать смещения и записывать куда-то всю информацию (например, в блокнот). Но, если у нас размеры классов исчисляются сотнями байт и, в придачу, имеют множество методов и несколько виртуальных таблиц, всё становится гораздо сложнее.

Рассмотрим на примере, как помогает в данном случае плагин. Когда-то (исключительно ради самообразования :D) мной был создан бот для онлайн-игрушки. В процессе наткнулся на защиту, шифрующую пакеты, не позволявшую модифицировать код в памяти и мешающую хукать вызов шифрующей функции (которая была жестко обфусцирована). Для того, чтобы обойти её, нужно было распарсить класс, отвечающий за обмен данными между клиентом и сервером и научиться, используя его, вызывать отправку пакетов и считывать полученные, расшифрованные пакеты за несколько вызовов от функций защиты. Тогда это было непростой задачей, но с плагином всё делается довольно просто.

Вот так выглядит метод, принимающий пакеты. this и v1 являются указателями на объект класса, gepard_1 — это функция, заменяющая recv


Если заглянуть внутрь функций sub_41AF50 и sub_41AFF0 , можно увидеть довольно много кода, обращающегося к разным полям. И даже это — лишь часть функционала, ответственного за создание и отправку пакетов, поэтому разобраться в назначении полей может быть непросто. Плагин помогает автоматически проанализировать большое количество кода, и из собранной информации составить некий каркас структуры, которая в дальнейшем анализе может быть изменена исследователем и использована для автоматического создания нового типа. Для начала надо открыть Structure Builder через Edit->Plugins->HexRaysPyTools. Это окошко будет содержать собранную информацию, предоставлять возможность для редактирования имен полей и разрешения конфликтов, а также просмотра виртуальных таблиц и сканирования виртуальных функций.

Есть 3 возможных способа собирать информацию о полях:

1) Можно кликнуть правой кнопкой по переменной и, нажав Scan Variable, запустить сканирование в пределах одной функции. При сканировании будет рассматриваться то, как происходит обращение к переменной и, если оно под падает под паттерн обращения к полю, такая информацию будет запомнена. Если другой переменной присваивается значение первой (причем их типы не обязательно должны совпадать), она так же подключается к сканированию (и отключается, если ей будет присвоено новое значение). Вот какой результат будет, если применить этот метод к переменной this в функции выше:


Хотя мы запускали сканирование для переменной this , информация о v1 также была собрана.
Жёлтым отмечены разного рода обращения к полям, и нужно выбрать, какой вариант подходит больше всего, отключив все остальные. До тех пор, пока есть конфликты, создать финальную структуру будет нельзя. Красный — это смещение, начиная с которого производится сканирование. Его можно сдвинуть с помощью кнопки Origin для того, чтобы просканировать потенциальную подструктуру. Например, можно зайти в функцию sub_41AF50 и, сдвинув указатель, собрать немного новой информации:


Если дважды кликнуть по столбцу Offset интересующего поля, можно увидеть список всех обращений к этому полю и быстро переместиться в дизассемблере к месту обращения. Поэтому есть смысл как можно больше покрывать сканированиями все места использования восстанавливаемой структуры. Больше информации о полях — проще разобраться, что зачем нужно. Сканировать каждую переменную может быть весьма утомительно, поскольку указатель на структуру может путешествовать по большому количеству функций, поэтому есть другой способ сбора информации.

2) Кликнув правой кнопкой по переменной, можно выбрать опции "Deep Scan Variable". Основной процесс сканирования будет таким же, как и у первого способа, только теперь, если указатель на структуру будет передаваться как аргумент функции, будет запущено рекурсивное сканирование данного аргумента. Warning! Здесь есть одна проблема — декомпилятор не всегда распознает правильно аргументы функции, которую он еще не декомпилировал, поэтому приходится рекурсивно заходить и декомпилировать каждую функцию, которая потенциально может содержать указатель на нашу структуру как аргумент. Этот процесс запускается автоматически, и происходит только один раз за сессию для каждой функции. Поэтому первые процессы глубокого сканирования могут занять некоторое время (порядка пары минут).

Переместившись несколько раз вверх по цепочке вызов, можно найти место, где создается структура:


Запустив здесь сканирование, получаем следующее:


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

Если зайти в функции sub_419890 , которая впервые возвращает указатель на структуру, то можно увидеть, что используется паттерн-одиночка:


Количество вызовов этой функции очень велико:


Сканировать каждую переменную было бы утомительно, поэтому есть возможность запустить сканер для всех сразу, кликнув по заголовку функции и выбрав опцию "Deep Scan Returned Variables".

Вот результат применения на примере:


Можно заметить, что нашлась информация об обращении к полям 0x8 — 0x14. Так же нашлись виртуальные таблицы — они отображены жирным шрифтом и, дважды кликнув по ним, можно увидеть список виртуальных функций (а заодно и просканировать их как по одной, так и все сразу).

Теперь можно разбираться с устройством структуры. Напомним, что, кликнув по офсету, можно увидеть все обращения к полям.

Вот, что получилось после непродолжительного анализа:


Подготовка создания структуры завершена. Теперь можно кликнуть "Finalize" и, внеся последние изменения, закончить её создание:


Далее, везде, куда "дотянулся" сканнер, переменным, которые являлись указателем на эту структуру, будет применён её новосозданный тип. Вот как преобразится функция отправки пакетов:


Помимо представленного, в Structure Builder можно создать или попытаться угадать подструктуры, выделив необходимое количество полей и нажав Pack или Recognize Shape соответственно. При поиске подходящей структуры учитываются типы полей — они должны точно совпадать, за исключением базовых типов ( char — BYTE , int — DWORD — int * ), считающихся одинаковыми.

Для уже созданных классов (структур с виртуальными таблицами), в плагине есть возможность более удобной работы с ними. По пути View-> Open Subviews-> Classes можно открыть следующее окошко:


  1. Переименовать методы, в результате чего изменения будут произведены сразу в коде и виртуальных таблицах (сделано, чтобы избежать рассинхронизации)
  2. Изменить объявление методов
  3. Быстро конвертировать первый аргумент в this
  4. Переместиться к функции в окне дизассемблера
  5. Фильтровать информацию регулярными выражениями

И еще. Хотелось бы еще раз напомнить, что при работе с классами очень удобно использовать плагин ClassInformer. Если в файле есть RTTI-информация, это поможет восстановить иерархию классов, а плагин возьмёт имена виртуальных таблиц, что поможет получить близкие к оригиналу имена классов.

Дизассемблеры и декомпиляторы исполняемых файлов


Дизассемблеры и декомпиляторы исполняемых файлов

В комментариях к статьям меня часто спрашивают где взять тот или иной инструмент, используемый в исследовании. По возможности я всегда указываю ссылки, но теперь настало время самых мощных инструментов, а именно дизассемблеров и декомпиляторов исполняемых файлов. Сразу уточню терминологию. Дизассемблирование - преобразование программы из двоичного кода к ее ассемблерному представлению. Декомпиляция - процесс воссоздания исходного кода программы.

Скриншот программы dnSpy


Скриншот программы dnSpy

Скриншот программы IDA Pro Advanced


Скриншот программы IDA Pro Advanced

IDA Pro (сокращение от Interactive DisAssembler) - один из моих основных инструментов для реверс-инжиниринга и разбора файлов. Это интерактивный дизассемблер и отладчик с поддержкой множества форматов исполняемых файлов для большого числа процессоров и операционных систем. Чтобы перечислить все его возможности потребуется целая книга. Но даже тут возможности IDA не заканчиваются. Плагин Hex-Rays для IDA Pro позволяет декомплировать ассемблерный листинг в более-менее человекопонятный псевдокод, по синтаксису похожий на C. В некоторых случаях это значительно облегчает работу. Просто так приобрести IDA Pro частным лицам практически невозможно, и дело не только в непомерной цене, а в том, что автор придерживается абсолютно неадекватной политики в плане продаж. К счастью, несколько последних версий этого замечательного дизассемблера, несмотря на все трудности, были успешно слиты в свободный доступ. Это IDA Pro Advanced 6.8, последняя доступная версия, которая работает с 32-битными системами, а также IDA Pro Advanced 7.0 и IDA Pro Advanced 7.2 для 64-битных систем. Если по каким-то причинам вы не можете использовать варез, то на офсайте есть бесплатные демо-версии с урезанным функционалом.

Скриншот программы Interactive Delphi Reconstructor


Скриншот программы Interactive Delphi Reconstructor

IDR (Interactive Delphi Reconstructor) - бесплатный декомпилятор исполняемых файлов и динамических библиотек. В отличие от IDA Pro, этот декомпилятор создан специально для разбора файлов, написанных на языке Delphi. Сейчас проект прекратил развитие, если какие изменения и вносятся, то исключительно косметические. Исходники для доработки открыты. Лично я пользуюсь стабильным комплектом Interactive Delphi Reconstructor 2.6.0.1.

Скриншот программы VB Decompiler Pro


Скриншот программы VB Decompiler Pro

Еще один специализированный декомпилятор - VB Decompiler Pro. Он работает с программами (EXE, DLL, OCX), написанными на Visual Basic. В случае, если приложение собрано в p-code, декомпилятор может разобрать его практически до исходного кода. Но даже если приложение скомпилировано в native code, в этом случае VB Decompiler анализирует и восстанавливает довольно много инструкций, чтобы насколько это возможно приблизить ассемблерный код к исходному. Это сильно упростит задачу анализа алгоритмов исследуемой программы. Честные граждане могут воспользоваться бесплатной Lite-версией с офсайта, для любителей полных версий софта есть релиз VB Decompiler Pro 10.0. Антивирусы могут ругаться на активатор, но тут вы уже сами решайте что делать.

Конечно, это далеко не полный список инструментов для дизассемблирования и декомпиляции, который есть в свободном доступе. Например, та же набирающая популярность Ghidra от АНБ может составить конкуренцию IDA Pro с Hex-Rays. Но я в этой статье перечислил лишь те программы, которыми пользуюсь сам и которые упоминаются в статьях на этом сайте.

Читайте также: