разработка
19 коммент.
Но потом он повздорил с народом, разозлился и свалил в частную контору, унеся все исходники с собой. Документации нет. Копий исходников нет. Есть обрывки личной переписки и намёки в сопровождающих файлах на тип алгоритма. Вариант физического воздействия на автора тупыми тяжёлыми предметами не рассматривается.
Нужно восстановить исходники если и не до компилируемого состояния, то во всяком случае выудить оттуда алгоритмы и ключевые методы, использованные при реализации.
- Для C# или Java есть много декомпиляторов - байткод на java содержит много информации. Это помогает восстанавливать декомпилятору исходник до состояния, пригодного к повторной компиляции.
- Совершенно другая история с двоичными файлами, в которых, как правило, отладочной информации нет. Тем не менее, динамически связанные библиотеки функций, как правило, вызываются по имени. Часто, типы параметров библиотечных функций известны, и это может помочь до известных пределов.
Если вы думаете, что с помощью декомпилятора вы получите обратно красивый код на С - вы будете сильно разочарованы. В лучшем случае на выходе будет общая (и довольно грубая) структура программы, в худшем - только имена функций и немного мусора.
Так что ответ на вопрос заголовка поста: "Обхватить голову руками и закричать #$@@@@@!" :-)
Сразу замечу: скорее всего, ни один декомпилятор не выдаст вам сразу компилируемый код. Придётся потратить порядком времени и сил, чтобы это месиво превратить в код, который можно читать (желательно, не только компилятору).
Boomerang
Boomerang это C decompiler с открытыми исходами:
- поддерживаемые бинарные форматы: ELF PE-COFF Mac-OS
- платформы: Windows/Linux
- поддерживаемые архитектуры: IA32 MIPS PPC
- метод работы: поточный, есть жалкий графический интерфейс, лучше использовать CLI.
Качество кода, выдаваемого декомпилятором:
- Структурирование: очень хорошее
- Переменные: хорошее
- Типы данных: очень хорошее
Программа всё ещё в состоянии бета-версии и для больших проектов не подходит. Скачать можно здесь.
RecStudio
Интерактивный декомпилятор RecStudio для С и (отчасти) С++, закрытая разработка:
- поддерживаемые бинарные форматы: ELF PE-COFF AOUT RAW PS-X
- платформы: Windows/Linux/MacOS
- поддерживаемые архитектуры: x86 (ia32) x86_64 Mips PowerPC mc68k
- метод работы: поточный и интерактивный, есть графический интерфейс.
- Структурирование: хорошее
- Переменные: частично
- Типы данных: частично или никак
Программа работает вполне стабильно, есть сборки под Linux. Скачать можно здесь.
dcc - DOS to C decompiler
Поточный декомпилятор Dcc, только ANSI C и для exe-файлов, с открытым исходным кодом под GPL:
- поддерживаемые бинарные форматы: EXE/COM
- платформы: Windows
- поддерживаемые архитектуры: x86
- метод работы: поточный.
- Структурирование: хорошее
- Переменные: частично
- Типы данных: частично или никак
Скачать можно отсюда (на сайте университета).
Hex Rays - plugin для IDA Pro
На самом деле Hex Rays не является отдельной программой - это плагин-декомпилятор для IDA Pro. Комбинация продвинутых возможностей IDA Pro (это дизассемблер) и Hex Rays в качестве декомпилятора очень впечатляет, как и аэрокосмическая цена.
По причине закрытости продукта (нет даже демо-версии) и нереальной цены в этом разделе про Hex Rays больше ничего написано не будет.
Ходовые испытания в реальных условиях
Для начала попробуем декомпилировать что-нибудь совсем простенькое и написанное на ANSI C и с использованем библиотеки BLAS для векторых и матричных операций. Бинарный файл можно скачать здесь.
1. Простенький C-шный бинарник + BLAS
Собственно, код на C для перемножения матрицы и вектора (используется CBLAS). Исходник:
#include <stdio.h> #include <cblas.h> double m[] = { 3, 1, 3, 1, 5, 9, 2, 6, 5 }; double x[] = { -1, -1, 1 }; double y[] = { 0, 0, 0 }; int main(void) { int i, j; for (i=0; i<3; ++i) { for (j=0; j<3; ++j) printf("%5.1f", m[i*3+j]); putchar('\n'); } cblas_dgemv(CblasRowMajor, CblasNoTrans, 3, 3, 1.0, m, 3, x, 1, 0.0, y, 1); for (i=0; i<3; ++i) printf("%5.1f\n", y[i]); return 0; }
После перемножения выдаст результат на консоль.
Выхлоп Boomerang
У него много ключей и параметров, часть которых не знает даже официальная, скажем так, документация. Тем не менее, для ключа -Td (Use data-flow-based type analysis) мы имеем выхлоп в стиле дзен:
double y; double m = 3.; // address: 0x80484e4 int main(int argc, char **argv, char **envp) { void *local23; // r28 for(;;) { proc1(); } }
Скажем так, не слишком ободряюще. Ключ -Tc (Use old constraint-based type analysis) выдаёт больше информации к размышлению:
char y[24]; long long m[9] = { 0x4008000000000000LL, 0x3ff0000000000000LL, 0x4008000000000000LL, 0x3ff0000000000000LL, 0x4014000000000000LL, 0x4022000000000000LL, 0x4000000000000000LL, 0x4018000000000000LL, 0x4014000000000000LL }; // address: 0x80484e4 int main(int argc, char **argv, char **envp) { int local10; // r24 int local13; // r28 int local14; // r29 int local15; // r32 int local2; // m[r28 + 72]{60} int local3; // r28{181} int local6; // m[r28 + 72]{119} int local7; // m[r28 + 72]{149} int local8; // m[r28 + 76]{9} int local9; // m[r28 + 76]{49} *(int*)(local13 - 4) = local14; *(int*)(local13 - 12) = 0; local3 = local13 - 84; if (*(int*)(local3 + 72) > 2) { *(int*)(local3 + 52) = 1; *(int*)(local3 + 48) = 0x8049868; *(long long*)(local3 + 40) = 0.; *(int*)(local3 + 36) = 1; *(int*)(local3 + 32) = 0x8049848; *(int*)(local3 + 28) = 3; *(int*)(local3 + 24) = 0x8049800; *(long long*)(local3 + 16) = 1.; *(int*)(local3 + 12) = 3; *(int*)(local3 + 8) = 3; *(int*)(local3 + 4) = 111; *(int*)local3 = 101; cblas_dgemv(); local6 = 0; for(;;) { *(long long*)(local3 + 4) = y[0]; *(int*)local3 = 0x80486b6; proc1(); local7 = *(int*)(local3 + 72) + 1; } } local8 = 0; for(;;) { local10 = *(int*)(local3 + 72) + *(int*)(local3 + 72) + *(int*)(local3 + 72); local15 = m[local10]; *(long long*)(local3 + 4) = local15; *(int*)local3 = 0x80486b0; proc1(); local9 = *(int*)(local3 + 76) + 1; } }
Подробностей тут больше, и тут выловлен самый главный ключик - cblas_dgemv();
Выхлоп RecStudio
Намного более обилен и представляет собой следующий поток сознания:
// Generated by Rec Studio 4 - build Oct 20 2012 _init() {// addr = 0x08048398 _unknown_ __ebx; // r1 _unknown_ __ebp; // r6 _unknown_ _t2; // _t2 __esp = __esp - 4; L1(); _pop(__ebx); if( *((intOrPtr*)(_t2 + 0x1414)) != 0) { __gmon_start__(); } frame_dummy(); __do_global_ctors_aux(); _pop(__eax); return; } L080483A4() { _unknown_ _t2; // _t2 _pop(__ebx); if( *((intOrPtr*)(_t2 + 0x1414)) != 0) { __gmon_start__(); } frame_dummy(); __do_global_ctors_aux(); _pop(__eax); _pop(__ebx); __esp = __ebp; _pop(__ebp); return; } __gmon_start__() {// addr = 0x080483D8 goto __imp____gmon_start__; } putchar() {// addr = 0x080483E8 goto __imp__putchar; } __libc_start_main() {// addr = 0x080483F8 goto __imp____libc_start_main; } cblas_dgemv() {// addr = 0x08048408 goto __imp__cblas_dgemv; } printf() {// addr = 0x08048418 goto __imp__printf; } _start( signed int __eax, // r0 _unknown_ __edx // r3 ) {// addr = 0x08048430 _unknown_ __ebx; // r1 signed int _t5; // _t5 _unknown_ _t6; // _t6 _unknown_ _t10; // _t10 __edx = __edx; _t4 = __eax; _pop(__esi); __ecx = __esp; __esp = __esp & 240; _push(__eax); _push(__esp); _push(__edx); _push(__libc_csu_fini); _push(__libc_csu_init); _push(__ecx); _push(_t10); _push(main); __libc_start_main(); asm("hlt "); 0; 0; _push(0); _push(_t6); __esp = __esp - 4; if(completed.5982 != 0) { } else { _t4 = dtor_idx.5984; _t6 = ( &__DTOR_END__ - &__DTOR_LIST__ >> 2) - 1; if(_t4 >= _t6) { } else { do { _t5 = _t4 + 1; dtor_idx.5984 = _t5; *((intOrPtr*)(_t5 * 4 + &__DTOR_LIST__))(); _t4 = dtor_idx.5984; } while(_t4 < _t6); } completed.5982 = 1; } __esp = __esp + 4; _pop(__ebx); _pop(__ebp); return; } __do_global_dtors_aux( _unknown_ __esi // r5 ) {// addr = 0x08048460 _unknown_ __ebx; // r1 _unknown_ __ebp; // r6 _unknown_ _t4; // _t4 signed int _t5; // _t5 signed int _t6; // _t6 _unknown_ _t10; // _t10 if(completed.5982 == 0) { _t5 = dtor_idx.5984; _t10 = ( &__DTOR_END__ - &__DTOR_LIST__ >> 2) - 1; if(_t5 >= _t10) { L4: completed.5982 = 1; return; } do { _t6 = _t5 + 1; dtor_idx.5984 = _t6; *((intOrPtr*)(_t6 * 4 + &__DTOR_LIST__))(); _t5 = dtor_idx.5984; } while(_t5 < _t10); goto L4; } return; } frame_dummy() {// addr = 0x080484C0 _unknown_ __ebp; // r6 __eax = __JCR_LIST__; if(__JCR_LIST__ == 0) { } else { __eax = 0; if(__eax != 0) { *__esp = &__JCR_LIST__; *__eax(); return; } } return; } main( _unknown_ __fp0 // r28 ) {// addr = 0x080484E4 signed int _v8; // _cfa_fffffff8 signed int _v12; // _cfa_fffffff4 intOrPtr _v32; // _cfa_ffffffe0 char* _v36; // _cfa_ffffffdc intOrPtr _v48; // _cfa_ffffffd0 char* _v52; // _cfa_ffffffcc intOrPtr _v56; // _cfa_ffffffc8 char* _v60; // _cfa_ffffffc4 intOrPtr _v72; // _cfa_ffffffb8 intOrPtr _v76; // _cfa_ffffffb4 intOrPtr _v80; // _cfa_ffffffb0 _unknown_ __ebp; // r6 __fp0 = __fp0; __esp = __esp & 240; __esp = __esp - 80; _v12 = 0; while(_v12 <= 2) { _v8 = 0; while(_v8 <= 2) { __fp0 ?_? *((long long*)((_v12 + __edx + __edx + _v8) * 8 + &m)); asm("fstp qword [esp+0x4]"); *__esp = 134514352; printf(); _v8 = _v8 + 1; } *__esp = 10; putchar(); _v12 = _v12 + 1; } _v32 = 1; _v36 = &y; asm("fldz "); asm("fstp qword [esp+0x28]"); _v48 = 1; _v52 = &x; _v56 = 3; _v60 = &m; asm("fld1 "); asm("fstp qword [esp+0x10]"); _v72 = 3; _v76 = 3; _v80 = 111; *__esp = 101; cblas_dgemv(); _v12 = 0; while(_v12 <= 2) { __fp0 ?_? *((long long*)(_v12 * 8 + &y)); asm("fstp qword [esp+0x4]"); *__esp = "%5.1f\n"; printf(); _v12 = _v12 + 1; } return 0; } __libc_csu_fini() {// addr = 0x080485F0 _unknown_ __ebp; // r6 return; } __libc_csu_init( intOrPtr _a4, // _cfa_4 intOrPtr _a8, // _cfa_8 intOrPtr _a12 // _cfa_c ) {// addr = 0x08048600 intOrPtr _v36; // _cfa_ffffffdc intOrPtr _v40; // _cfa_ffffffd8 _unknown_ __ebx; // r1 _unknown_ __edi; // r4 signed int __esi; // r5 _unknown_ __ebp; // r6 _unknown_ _t14; // _t14 _unknown_ _t15; // _t15 signed int _t18; // _t18 __i686.get_pc_thunk.bx(); _t15 = _t14 + 4529; __esp = __esp - 28; _init(); _t18 = _t15 + -248 - _t15 + -248 >> 2; if(_t18 == 0) { } else { __esi = 0; do { _v36 = _a12; _v40 = _a8; *__esp = _a4; *((intOrPtr*)(_t15 + -248 + __esi * 4))(); __esi = __esi + 1; } while(__esi < _t18); } __esp = __esp + 28; return; } __i686.get_pc_thunk.bx() {// addr = 0x0804865A return; } __do_global_ctors_aux() {// addr = 0x08048660 intOrPtr* __ebx; // r1 _unknown_ __ebp; // r6 __eax = __CTOR_LIST__; if(__eax == 255) { } else { __ebx = &__CTOR_LIST__; asm("o16 nop "); do { __ebx = __ebx - 4; *__eax(); __eax = *__ebx; } while(__eax != 255); } return; } _fini() {// addr = 0x0804868C _unknown_ __ebx; // r1 _unknown_ __ebp; // r6 _unknown_ _t1; // _t1 __esp = __esp - 4; L1(); _pop(__ebx); __do_global_dtors_aux(__esi); _pop(__ecx); return; } L08048698() { _unknown_ _t1; // _t1 _pop(__ebx); __do_global_dtors_aux(__esi); _pop(__ecx); _pop(__ebx); __esp = __ebp; _pop(__ebp); return; } L08048698() { _unknown_ _t1; // _t1 _pop(__ebx); @rec __do_global_dtors_aux@__do_global_dtors_aux@(__esi); _pop(__ecx); _pop(__ebx); __esp = __ebp; _pop(__ebp); return; } // Statistics: // 74 Register nodes // 35 Temporaries nodes // 5 Casts // 207 Statements // 2 Labels // 1 Gotos // 17 Blocks // 469 Nodes // 10 Assembly nodes // 27 Unknown Types Total time: 0 seconds.
Структура программы (вначале) в общем несколько лучше, чем у Boomerang, и куда больше подробностей.
2. Бен, ай нид хелп: MEX-файл, написанный на C + BLAS, исходников которому нет.
Этот пример в посте приводить не стану, так как он длинный, но желающим попробовать своё декомпиляйшн-кунфу такая возможность предоставится:
- Вот оригинальный MEX-файл под Linux [скачать, 17kB]
- это выхлоп Boomerang;
- это выхлоп RecStudio.
- спасибо комментатору Gregory, вот выдача IDA Pro.
Короче, важен не столько алгоритм, сколько его составные компоненты (стратегия и подпрограммы для lower bound estimation).
Автор этих строк, поковыряв выхлоп RecStudio, нашёл для себя подсказку на строчке 12319:
qps_mq_sub( .....
и особенно на строчке 12088:
getalp( ....что позволило автору предположить, что для lower/upper bounds использутеся Liner Programming. Не слишком много, но по крайней мере понятно, в какую сторону копать.
Если у кого-то вдруг проявится желание потыкать в оный бинарник палочкой, IDA Pro и ещё чем - не стесняйтесь отписываться в комментариях.
Обновление: теперь можно сравнить выдачи Boomerang, RecStudio, и (спасибо, Григорий!) IDA Pro. В самом деле, выдача IDA Pro куда лучше того, что дают остальные, особенно boomerang. Можно выудить (до некоторой степени) структуру программы и даже сообщения об ошибках.
Ссылки
Интересующийся читатель может попробовать полистать вебстраницы автора RecStudio с полезной информацией, сходить на wiki-ресурс по обратной разработке.
Помимо познавательных диссертаций Michael James Van Emmerik (Boomerang) и Cristina Cifuentes [C. Cifuentes, Reverse Compilation Techniques], есть хорошие книжки по теме:
- "Compilers - Principles, Techniques and Tools", Aho, Sethi, Ullman, 1986 Addison-Wesley Publishing Co. ISBN 0-201-10088-6.
- "Advanced Compiler Design & Implementation", Steven Muchnick, 1997 Morgan Kaufmann Publishers, ISBN 1-55860-320-4.
- "How debuggers work - Algorithms, Data Structures, and Architecture", Jonathan Rosemberg, 1996 John Wiley and Sons, ISBN 0-471-14966-7.
Дело это интересное, но весьма утомительное, хотя может помочь при раскопках очередного legacy-software и сэкономить вам полжизни.