Декомпилятор С++

Тема в разделе "WASM.RESEARCH", создана пользователем Vam, 16 июл 2008.

  1. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    andruha123
    Пока нет - аппетит приходит во время еды.
    Пока рано, надо хоть одну стадию довести до логического завершения, а далее... видно будет. Но на вторую задачу я и сейчас готов предоставить часть исходников.
     
  2. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Vam
    Неслабая задачка. :derisive:
    Нельзя ли для этого использовать сам компилятор?
     
  3. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    green
    Какая есть..., но база для размещения type info у меня готова, осталось только её заполнить - а это упрощает задачу.
    Имеется в виду VS? Может для первой части задачи и можно, только я не знаю как заставить его скомпилировать полностью библиотеку, он ведь использует выборочный алгоритм. Но в реализации второй части он точно не поможет.
     
  4. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Vam
    Я думаю, стоит порыть тему precompiled headers. В pch-файле должна находиться вся информация о типах из инклудов.
    Иначе задача по сложности сравнима с написанием C++ frontend.

    Наверное, лучше использовать для этого gcc, т.к. микрософтовский PCH абсолютно недокументирован.
     
  5. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    green
    и
    В общем случае это конечно может решить первую часть задачи, при условии известности формата pch файла MS или наличия соответствующей type info после gcc. Но, хотелось бы большего:
    Во-первых: extern "C" обгрызает типы до безобразия, кроме простого имени от них ничего не остается, а я хочу полную информацию о типе, которая есть в h файле.
    Во-вторых: В type info нужно добавить ещё один тип данных, которого нет у MS, это define и добавить не в простом виде, а в виде enumа, так как он сгруппирован в h файле, например, Window Message (WM_...)
    Не совсем так, задача намного проще, написать парсер ключевых слов, пробежаться по h файлу с анализом ключевых блоков и заполнением информационных структур (эта часть есть), затем записать их в базу данных (файл). Повторить на всю директорию(и). Причем за один проход можно решить обе части задачи.
     
  6. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Vam
    extern "C" не позволяет определить типы аргументов ф-ции по её имени.
    А в debug info информация о параметрах сохраняется независимо от extern "C".

    Гм...
    Во-первых нужен полноценный С препроцессор. Ладно, можно взять готовый.
    Далее, нужно учитывать области видимости, наследование. без это нельзя, как минимум, корректно разрулить typedef-ы и enum-ы.
    А как быть с шаблонами? Сделать инстанциирование для всех вариантов параметров невозможно, придётся сохранять в базе не конкретные типы, как в реальной debug info, а шаблонные.
    И потом в декомпиляторе решать задачу поиска не типа, а шаблона типа, что должно быть на порядок сложнее, как я понимаю. Даже если известно имя инстанциированного типа (например, из частичной дебаг инфы или имени ф-ции), то определить этот тип, т.е. выполнить инстанциирование шаблона - в общем случае задача очень нетривиальная.
     
  7. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    green
    Конечно, так оно и есть, сам разбирал debug info...
    Согласен. В дальнейшем по нему можно сделать разные слепки type info для различных ключей компиляции.
    До обсуждения этого вопроса предлагаю для однозначности понимания определить что же нам необходимо от type info.
    По большому счету MS debug info (далее ДИ) состоит из двух видов информации:
    1. symbol info (далее СИ) - это то, что реально существует в коде программы (тела функций, их блоков, статические данные и т.д.). Каждый тип СИ описывается своим образом и имеет один общий атрибут - адрес размещения в памяти.
    2. type info (далее ТИ) - это абстрактное понятие и в коде программы отсутствует. Каждый тип ТИ описывается своим образом и имеет один общий атрибут - идентификатор типа.

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

    2. Частичная ДИ (минимальная) состоит только из СИ, причем сильно урезанных, имеются практически всего два атрибута - имя и адрес размещения в памяти. В этом случае нам нужны базы данных ТИ по библиотекам. Понятно, что в программе будет и user type info, отсутствующая в библиотеках, но это вопрос отдельный, декомпилятор будет её восстанавливать по определенным правилам. Но теперь встает вопрос, как восстановить связь между СИ и ТИ. Для этой цели подходит только имя СИ, причем это имя должно быть полным (манглед) или уникальным, иначе задача неразрешима. Декомпилятор разбирая конкретный СИ по его имени (деманглед) находит соответствующий ТИ и делает между ними связку.

    3. ДИ отсутствует. В этом случае декомпилятор строит СИ блоки с одним атрибутом - адрес в памяти, так же здесь нам нужны базы данных ТИ по библиотекам. Вопрос восстановления связи между СИ и ТИ здесь может быть решен через IDA с механизмом FLIRT и сигнатурами тел библиотечных функций, т.е. полное имя СИ, которое в варианте 2 мы брали из ДИ, здесь будем брать из IDA. Далее осуществляется связка аналогично варианту 2.

    Понятно, что могут встречаться и другие варианты когда программа слинкована из модулей с разным уровнем ДИ, но это будет просто набор вариантов 1 - 3.

    Теперь вернемся к "нашим баранам":
    Про какую область видимости здесь идет речь? Если по коду, то это бесполезно, смотрим, как выполнена связка между СИ и ТИ в этом случае - через имя типа, а оно одно на всю программу.
    Наследование - да, учитывать можно и нужно, но это свойство только ТИ, и оно будет отражено при разборе библиотечной ТИ в соответствующих атрибутах.
    Согласен, в MS ДИ в ТИ шаблонов как таковых вообще нет, так как в СИ реализации производится инстанциация под конкретный тип параметров шаблона. Здесь в библиотечной ТИ достаточно будет иметь один тип с общими параметрами шаблона и уникальным именем, т.к. С++ не позволяет иметь под одним именем разное кол-во шаблонных параметров. Связка конкретной инстанциации СИ шаблона с ТИ шаблона будет выполнена по имени. На завершающей стадии декомпилятор просто (а может и не очень) объединит инстанциации СИ шаблона в одно тело.

    Конечно у меня есть ещё нерешенные вопросы по MS ДИ, но это тема другого поста...
     
  8. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    Так я разбираю все блоки MS debug info (ДИ), даже те, которые и не нужны, но в дальнейшем могут понадобится, то имеется пара нерешенных вопросов. Надеюсь на вашу помощь.

    1. В ДИ в type info (ТИ) имеется блок модификатор типа - LF_MODIFIER, с двумя полями:
    - m_nAttribute - тип модификатора: const, volatile или unaligned
    - m_Type - идентификатор типа, к которому применим этот модификатор.
    Применить модификатор к указанному типу (например, int) нельзя, т.к. тип один на всю программу. Можно создать копию типа и связать её с модификатором, но тогда возникает другой вопрос, как связать такой тип с символом из symbol info (СИ)? Может быть это недоделка MS?

    2. В ДИ внутри СИ встречается символ типа S_XXXXXXXXX_32 с кодом идентификатора 0x1012 (ХХХ - это моё, имени этого символа не нашел) и размером в 32 байта.
    Что это может быть такое?

    По остальным блокам ДИ, которые конечно встретились мне в различных файлах, пока вопросов больше нет.
     
  9. Simaticov

    Simaticov New Member

    Публикаций:
    0
    Регистрация:
    5 сен 2008
    Сообщения:
    1
    Vam - тебе надо определиться в тех целях, ради чего ты делаешь декомпилятор :
    Ты хочешь получить удобочитаемый и удобопонимаемый Си-код для анализа работы программы?
    Ты хочешь получить абы какой Си-код для внесения в него изменений и последующей компиляции ?

    В любом случае есть смысл типизировать имена переменных из просто VAR в String_Var198, Bool_Var1E4, и т.п.
    и добавлять объявление используемых переменных в начало
    Код (Text):
    1. int main( int iArgc, char **ppArgv )
    2. {  
    3.     struct  _stat buf;  
    4.     struct  _utimbuf utim;
    5.     BYTE    ReadData[10];
    6.     int     result;
    7.     DWORD   ByteRead;
    8.     bool    bAdjust = false;
    9. ....
    В принципе любой ассемблерный код можно переписать на Си/Паскале, с чем многие успешно справляются вручную при выпуске программ делающих тоже, что и дизассемблированные исходники.
    Тема декомпиляции/дешифрации интересная и захватывающая - в любом случае полезна для понимания и приобретения навыков программирования.

    На счёт оптимизированных кусков кода оптимизаторами.
    Интел например маниакально рекомендует избавляться от условных JMP конструкций и превращать программу в линейную безжумповую, чтобы не опустошать неоптимизированный конвеер процессора.
    http://narod.ru/disk/2440987000/Intel_64_32_Architectures_Optimization_Reference_Manual_248966_2007.pdf.html
    Поэтому иногда могут встречаться абсолютно нелогичные силиконовые конструкции
    Example 3-1. Assembly Code with an Unpredictable Branch
    Код (Text):
    1. cmp a, b ; Condition
    2. jbe L30 ; Conditional branch
    3. mov ebx const1 ; ebx holds X
    4. jmp L31 ; Unconditional branch
    5. L30:
    6. mov ebx, const2
    7. L31:
    в итоге превращается в
    Example 3-2. Code Optimization to Eliminate Branches
    Код (Text):
    1. xor ebx, ebx ; Clear ebx (X in the C code)
    2. cmp A, B
    3. setge bl ; When ebx = 0 or 1
    4. ; OR the complement condition
    5. sub ebx, 1 ; ebx=11...11 or 00...00
    6. and ebx, CONST3; CONST3 = CONST1-CONST2
    7. add ebx, CONST2; ebx=CONST1 or CONST2
    Иногда при создании декомпилятора может помочь статистический метод накопления вариантов ассемблерных шаблонов типичных языковых конструкций.
    То есть пишем простую программу, дизассемблируем её и находим шаблоны.
    Мне подобный метод помогал - правда в отношении другой системы программирования.

    Рекомендую сосредоточиться на придании понятливости декомпилированному тексту.
     
  10. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    Simaticov
    Цели мне понятны - получить рабочий Си-код для анализа работы программы, максимально приближенный к оригинальным исходникам + иметь возможность вносить в него изменения и делать последующую компиляцию.
    Это всё делается, второй пример (пост №43) это показывает, в первом же примере листинг промежуточный и в нем отсутствуют типы локальных переменных. Все же эти var_ промежуточные, созданные декомпилятором в debug mode, при следующих проходах от них ничего не останется (см. второй пример).
    и
    Знаю и делаю так, чтобы после обработки декомпилятором они выглядели так, как в первоначальном исходнике.
    Спасибо. Вот ещё один пример почти готового листинга функции:
    Код (Text):
    1. int XD3D_AppIDDEnumCallback(_GUID* pGUID, signed char* lpDriverDescription, signed char* lpDriverName, void* lpContext)
    2. {
    3.     IDirectDraw7* pDD = 0;
    4.     if(!DirectDrawCreateEx(pGUID, (LPVOID*)&pDD, &IID_IDirectDraw7, 0))
    5.         if(pGUID)
    6.             dxDeviceGUIDflag = 1;
    7.             dxDeviceGUID = *pGUID;
    8.         else
    9.             dxDeviceGUIDflag = 0;
    10.         pDD->GetDeviceIdentifier(&dddi, 0);
    11.         int curDriver = 0;
    12.         IDirect3D7* pD3D;
    13.         if(!pDD->QueryInterface(IID_IDirect3D7, (void**)&pD3D))
    14.             pD3D->EnumDevices(XD3D_EnumDeviceCallback, &curDriver);
    15.             dxDeviceModeNum[dxDeviceNumber] = 0;
    16.             pDD->EnumDisplayModes(0, 0, 0, XD3D_EnumModesCallback);
    17.             if(dxDeviceModeNum[dxDeviceNumber] > 0)
    18.                 dxDeviceNumber += 1;
    19.         if(pD3D)
    20.             pD3D->Release();
    21.             pD3D = 0;
    22.     if(pDD)
    23.         pDD->Release();
    24.     return dxDeviceNum - 1 != dxSelectDevice;
    25. }
     
  11. _basmp_

    _basmp_ New Member

    Публикаций:
    0
    Регистрация:
    10 июл 2005
    Сообщения:
    2.939
    Vam
    Прошу прощения, плохо слежу за этой веткой.
    Это что текст на выходе вашего декомпилера?! Где скачать его можно? Уже хочу его помучить.
     
  12. toto

    toto New Member

    Публикаций:
    0
    Регистрация:
    15 июн 2008
    Сообщения:
    36
    присоединяюсь к _basmp_
    Vam отличный листинг)
     
  13. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    _basmp_
    Принимается :), но если бы следили лучше, то не возникли бы эти вопросы.
    Да, при условии присутствия полной дебаг инфы.
    Ответ в посте №55.
     
  14. Dump

    Dump New Member

    Публикаций:
    0
    Регистрация:
    23 сен 2008
    Сообщения:
    7
    от последнего кода в ауте, как успехи? :)
     
  15. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    Ранее писал
    вот сейчас и повышаю этот процент, а заодно привожу созданные исходники к более читабельному (и как можно ближе к оригиналу) виду. Процесс этот не быстрый, в исследуемых экзешниках несколько тысяч функций и надо каждую после декомпиляции сравнить с исходной вручную.
     
  16. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    Прошу ответить на вопрос:
    Почему после вызова одной и той же функции с постоянным кол-вом аргументов из стека изымается разное кол-во байт, а в функцию через стек всегда передается требуемое кол-во аргументов?
    Примеры функции с двумя аргументами и возвращаемым float значением:
    Код (Text):
    1. .text:005DD7D5                 mov     ecx, [ebp+arg_4]
    2. .text:005DD7D8                 push    ecx             ; 2ой аргумент 4 байта
    3. .text:005DD7D9                 push    2
    4. .text:005DD7DB                 mov     ecx, [ebp+var_4]
    5. .text:005DD7DE                 add     ecx, 10h
    6. .text:005DD7E1                 call    sub_4016E5
    7. .text:005DD7E6                 mov     edx, [eax]
    8. .text:005DD7E8                 push    edx
    9. .text:005DD7E9                 push    1
    10. .text:005DD7EB                 mov     ecx, [ebp+var_4]
    11. .text:005DD7EE                 add     ecx, 10h
    12. .text:005DD7F1                 call    sub_4016E5
    13. .text:005DD7F6                 mov     eax, [eax]
    14. .text:005DD7F8                 push    eax
    15. .text:005DD7F9                 push    0
    16. .text:005DD7FB                 mov     ecx, [ebp+var_4]
    17. .text:005DD7FE                 add     ecx, 10h
    18. .text:005DD801                 call    sub_4016E5
    19. .text:005DD806                 mov     ecx, [eax]
    20. .text:005DD808                 push    ecx
    21. .text:005DD809                 lea     ecx, [ebp+var_1C]
    22. .text:005DD80C                 call    sub_4057CC
    23. .text:005DD811                 push    eax             ; 1ый аргумент 4 байта
    24. .text:005DD812                 call    j_operator_     ; вызываемая функция
    25. .text:005DD817                 add     esp, 4          ; изымается 4 байта
    26. .text:005DD81A                 fstp    [esp+7Ch+var_7C]
    и
    Код (Text):
    1. .text:005D34CE                 mov     ecx, [ebp+arg_4]
    2. .text:005D34D1                 add     ecx, 18h
    3. .text:005D34D4                 push    ecx             ; 2ой аргумент 4 байта
    4. .text:005D34D5                 mov     edx, [ebp+arg_4]
    5. .text:005D34D8                 add     edx, 24h
    6. .text:005D34DB                 push    edx
    7. .text:005D34DC                 mov     eax, [ebp+arg_0]
    8. .text:005D34DF                 add     eax, 24h
    9. .text:005D34E2                 push    eax
    10. .text:005D34E3                 lea     ecx, [ebp+var_CC]
    11. .text:005D34E9                 push    ecx
    12. .text:005D34EA                 call    sub_4085CB
    13. .text:005D34EF                 add     esp, 0Ch
    14. .text:005D34F2                 push    eax             ; 1ый аргумент 4 байта
    15. .text:005D34F3                 call    j_operator_     ; вызываемая функция
    16. .text:005D34F8                 add     esp, 8          ; изымается 8 байт
    17. .text:005D34FB                 fcomp   ds:__real@4@00000000000000000000
    или
    Код (Text):
    1. .text:005D3645                 mov     eax, [ebp+arg_4]
    2. .text:005D3648                 add     eax, 18h
    3. .text:005D364B                 push    eax             ; 2ой аргумент 4 байта
    4. .text:005D364C                 lea     ecx, [ebp+var_20]
    5. .text:005D364F                 push    ecx             ; 1ый аргумент 4 байта
    6. .text:005D3650                 call    j_operator_     ; вызываемая функция
    7. .text:005D3655                 add     esp, 8          ; изымается 8 байт
    8. .text:005D3658                 push    ecx
    9. .text:005D3659                 fstp    dword ptr [esp+254h+var_258+4]
     
  17. Velheart

    Velheart New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2008
    Сообщения:
    526
    Судя по
    Код (Text):
    1.  add     esp, 4          ; изымается 4 байта
    2. .text:005DD81A                 fstp    [esp+7Ch+var_7C] ==[esp]
    и

    Код (Text):
    1. add     esp, 8          ; изымается 8 байт
    2. .text:005D3658                 push    ecx --- выделяем память
    3. .text:005D3659                 fstp    dword ptr [esp+254h+var_258+4] == [esp]
    можно предположить, что в первом случае просто соптимизировано выделение в стеке места под результат.
     
  18. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    Velheart
    Проанализировав все вызовы этой функции, пришел к выводу, что это действительно так, хотя компиляция выполнялась без оптимизации и Microsoft на этот счет ничего не говорит.
    Вывод: Если функция имеет соглашение вызова __ccall и возвращаемое значение float (4 байта) или double (8 байт) и оно используется только как временное (для передачи последним аргументом в следующую за вызываемой функцию), то кол-во изымаемых из стека байт = кол-ву выделенных под аргументы байт - размер возвращаемого значения. Спасибо за подсказку.
     
  19. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Vam
    На самом деле в release по умолчанию происходит оптимизация. Есть такая штука как доступ к локальным переменным через esp и управление переменными (выделение, уничтожение внутри бока C++).

    P.S Затеял ты конечно интересную и сложную вещь, если получится можно тебе будет памятник ставить.
     
  20. Vam

    Vam New Member

    Публикаций:
    0
    Регистрация:
    16 июл 2008
    Сообщения:
    149
    Позволяет ли SDK для IDA создать свой обработчик исключений в плагине?