Изменения

ARM-декомпилятор

988 байтов добавлено, 08:56, 30 марта 2011
м
Нет описания правки
В какой-то момент я ещё нашёл страничку http://www.backerstreet.com/decompiler/creating_statements.php, и понял, что все мои мысли полностью соответствуют стандартной теории декомпиляции.
== Начальный анализ == Потом понялстало ясно, что парсить вывод IDA — IDA (листинги) — занятие тяжкое и приносящее малый профит, и в случае ARM точно лучше использовать objdump, хотя он и не является рекурсивным дизассемблером. Вообще, в случае ARM всё ну очень просто, так как любая инструкция занимает 4 байта и всегда выровнена на границу 4 байт. Поэтому натравленный на файл objdump всегда дизассемблирует его корректно, только некоторые «инструкции», которые на самом деле не инструкции, нужно будет заменить на данные.
Поэтому сначала делаем начальный анализ:
 
* Дизассемблировать код objdump’ом и разобрать вывод на адреса, инструкции, их аргументы и комментарии — как уже сказано, это гораздо легче, чем парсить бешеные листинги из IDA.
* Представить адреса, которые очевидно читаются/пишутся, данными:
* Построить граф ветвлений — литературное название также «Control Flow Graph», или «граф управляющей логики».
** Граф тоже может меняться в процессе анализа.
 
== Продолжение анализа ==
Далее начинается более сложный анализ:
* Найти все ASCII-строки в бинарнике, и подставить в обращения к ним.
== Выделение блоков == Далее нужно перейти к выделению блоков — то есть, циклов и условных операторов- тому, что обычно называют "структурированием", т.е. воссозданием программы из линейного листинга: === Циклы === У любого цикла есть точка входа, на которую можно поставить метку "continue", т.к. оператор "continue" как раз на неё и переходит, и стандартная точка выхода, на которую можно аналогично поставить метку "break". Внутри цикла переходы на точки входа и выхода для удобочитаемости нужно заменять на continue и break. Цикл:* Начинается с какого-либо узла, являющегося к тому же единственной точкой входа в этот цикл.* Должен включать в себя все пути, ведущие в любой внешний узел, и возвращающиеся в любой внутренний узел (''обобщённое требование цикла'').* Если добавили какой-то узел, и получили две точки входа, то этот узел не принадлежит циклу.* Если добавили какой-то узел и единственная точка входа изменилась, идём нафиг — цикл из изначального узла не растёт. Циклы бывают с предусловием и с постусловием. Цикл с предусловием:<graph>digraph G { 0->1->2->0; 0->3; }</graph> С постусловием:<graph>digraph G { 0->1->0; 1->3; }</graph> === Условные операторы === Условный оператор определить можно так: если что-то откуда-то ветвится и это не цикл, это условный оператор :)
* Каждый цикл:** Начинается с какого-либо узла, являющегося к тому же единственной точкой входа в этот цикл.** Должен включать в себя все пути, ведущие в любой внешний узел, и возвращающиеся в любой внутренний узел (''обобщённое требование цикла'').** Если добавили какой-то узел, и получили две точки входа, то этот узел не принадлежит циклу.** Если добавили какой-то узел и единственная точка входа изменилась, идём нафиг — цикл из изначального узла не растёт.*: Когда залезаем в цикл, переходы на начало и выход из цикла заменяем на continue и break для удобочитаемости.* Условный оператор:<br /> Если что-то откуда-то ветвится и это не цикл, это условный оператор :) <br /> Транслировать его следует так:** Ищем точку сбора всех веток, выходящих из начального узла (узел, в котором все они объединяются).** Залезаем в каждую ветку, всё обрабатываем точно так же, как до этого, и точно так же (в обработке линейных участков и циклов тоже помечаем).** Помечаем пройденные узлы как обработанные.** Когда где-то видим уже обработанный узел, заменяем переход к нему на goto. То есть, if «без дублирования» кусков веток будет «нормальным», без goto.
Пример «плохого» if’а: 0->1->3->4, 0->2->3, 0->4, 0->5->4.