REC Studio 4 - Examples

Содержание

Домашняя Страница
Загрузки
Скриншоты
Примеры
Как Использовать
Проектирование

Последний раз обновлено: 1 Марта, 2011

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

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

Rec Studio использует набор алгоритмов, который может выполнять операцию вывода разной степени точности. Вот пример основного цикла Windows приложения Hello World (Привет Мир):

L00401000(_unknown_ r7, struct HINSTANCE__* _a4)

{

    intOrPtr _v16;

    char _v68;

    intOrPtr _v76;

    struct tagMSG _v100;

    _unknown_ r1;

    _unknown_ r4;

    _unknown_ r5;

    _unknown_ r6;

    _unknown_ _t10;

    _unknown_ _t11;

    _unknown_ _t13;

    struct HACCEL__* _t14;

    int _t15;

    int _t18;

    int _t20;

    _unknown_ _t21;

    _unknown_ _t22;

    _unknown_ _t23;

    _unknown_ _t24;

    _unknown_ _t29;

    _unknown_ _t30;

    _unknown_ _t31;

    struct HINSTANCE__* _t32;

    struct HACCEL__* _t33;

    _unknown_ _t34;

    _unknown_ _t35;

 

    r7 = r7;

    _t32 = _a4;

    _push(_t29);

    LoadStringA(_t32, 103,  &M004054F4, 100);

    LoadStringA(_t32, 109,  &M00405490, 100);

    _push(_t32);

    L004010C0(r7);

    _t13 = L00401150(r7, _t32, _v16);

    r7 = r7 + 4;

    if(_t13 != 0) {

        _t14 = LoadAcceleratorsA(_t32, 109);

        _push(0);

        _push(0);

        _push(0);

        _push( &_v68);

        _t33 = _t14;

        _t15 = GetMessageA();

        if(_t15 != 0) {

            _push(_t23);

            _push(_t34);

            do {

                _t18 = TranslateAcceleratorA(_v100.time, _t33,  &(_v100.time));

                if(_t18 == 0) {

                    TranslateMessage( &(_v100.message));

                    DispatchMessageA( &(_v100.hwnd));

                }

                _t20 = GetMessageA( &(_v100.message), 0, 0, 0);

            } while(_t20 != 0);

            _pop(r6);

            _pop(r1);

        }

        _pop(r4);

        return _v76;

    } else {

        _pop(r4);

        return _t13;

    }

}

Эта программа не имеет никакой доступной кодированной информации, так как она была составлена в режиме Выпуска. Тем не менее, Rec Studio могла определить основной цикл и передаваемые аргументы большинства функций.

Результат включает в себя множество локальных переменных, явно недостающих в первоначальном коде. Эти переменные - "объекты временного характера", которые не могут быть безопасно убраны при образовании конечного кода, и являются остаточными в неполном алгоритме Статического Единственного Присваивания (Single Static Assignment), используемом для "uncolor"-записей. Чем крупнее и более оптимизированной есть процедура декомпилирования, тем более вероятно, что будут образованы эти временные переменные (иногда в количестве многих дюжин). В других случаях декомпилятор сможет устранить большинство, если не все, эти переменные.

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

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

        _push(0);

        _push(0);

        _push(0);

        _push( &_v68);

        _t33 = _t14;

        _t15 = GetMessageA();

Это потому, что присваивание "_t33 = _t14" находится между вызовом и понуканием к работе параметров, и декомпилятор пока не может определить, имеет ли присваивание какие-нибудь побочные результаты на аргументы по “побуждающим” указаниям (например, изменилось ли местоположение, которое было вызвано ранее). В таких случаях декомпилятор сохранит наименее правильный, но наиболее очевидный код, так чтобы пользователь понимал, что происходит.

В некоторых случаях декомпилятор не может безопасно объединить предписания в высокоуровневых конструкциях, таких как for() и while(), но он оставляет самую простую форму потока управления if-командами перехода, как в следующем фрагменте:

L3:

    _t6 =  *((signed char*)(_t9 + 1));

    _t9 = _t9 + 1;

    if(_t6 == 34 || _t6 == 0) {

        goto L7;

    }

L5:

    _t7 = _t6 & 255;

    _push(_t7);

    L004020E8(r7);

    _t14 = _t7;

    _pop(r2);

    if(_t14 != 0) {

        _t9 = _t9 + 1;

    }

    goto L3;

L7:

    if( *_t9 != 34) {

        goto L11;

    } else {

        goto L8;

    }

L8:

В этом случае L5 – это основная часть (тело) цикла while(), но из-за 2 присваиваний в начале L3 декомпилятор не может разложить if() в массиве L3 на предписание while(). Качество генерируемого потока управления сильно меняется в зависимости от сложности и степени оптимизации компилируемого кода. И все равно Rec Studio может неоднократно восстанавливать сложные последовательности потоков управления, включая switch()/операторы выбора.