Ida дизассемблер как пользоваться

Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нём некорректно.
Необходимо обновить браузер или попробовать использовать другой.

Введение в реверсинг с нуля используя > Дата публикации 12 фев 2017 | Редактировалось 2 дек 2018

Приветствуя тебя, Гость.
Если тебе не очень понравится данный перевод, хочу сказать, что дальше всё становится лучше. Уже сейчас ты можешь открыть последние главы, например №24 или №25 и увидеть, что всё читается по другому. Как только курс будет переведён и закончен, я вернусь к первым главам и переделаю их.

ВВЕДЕНИЕ В РЕВЕРСИНГ С НУЛЯ ИСПОЛЬЗУЯ IDA PRO

Идея этих серий учебных пособий является обновить наш оригинальный курс по реверсингу, но используя IDA PRO. Будем обучаться использовать ее с нуля и работать будем с последней версией Windows. В моем случае, я использую Windows 10 Anniversary Update x64 со всеми патчами, вплоть до 29 октября 2016.

ПОЧЕМУ ИМЕННО IDA PRO

Потому что, пока OllyDBG просто 32-х битный отладчик режима пользователя, а IDA PRO целая программа для реверсинга, которая может быть использована в 32/64 системах, как и отладчик и как дизассемблер. Она позволяет делать статический реверс, который не может быть выполнен в OllyDBG и которая обучает как использовать ее, не смотря на сложный процесс обучения, она позволяет работать в Windows, Linux или Mac как внутри системы, так и удаленно в следующих операционных системах.

Для того, чтобы иметь представление о поддерживаемых процессорах, здесь есть список.

Как мы можем видеть, обучение как использовать IDA позволяет нам улучшить рабочее окружение, мы сфокусируемся на 32/64 разрядной Windows в пользовательском режиме и иногда в режиме ядра. Это позволит нам легче адаптироваться к любому использованию .

Здесь мы увидим большинство вещей, которые мы видели в Введение в отладку с нуля с использованием OllyDBG, но сейчас будет IDA. Попытаемся идти дальше с самого начала.

В этом курсе будет всё: статический и динамический реверс, крякме, будем учиться исследовать эксплоиты и распаковку. Я постараюсь писать о какой-нибудь важной детали с нуля.

Нам нужна IDA PRO. Проблема в том, что это коммерческая программа и мы должны платить за нее и она того стоит. Мы не можем и не будем распространять её, но вы можете поискать утекшие в сеть версии через Гугл по такому запросу: IDA PRO 6.8 + HEXRAYS. Эта версия с которой мы будем работать. Последняя же версия — IDA 6.95.160808 ( 08 Августа 2016).

Когда мы скачали, можем увидеть zip файлы и такой установщик

Пароль на установку хранится в файле install_pass.txt.

Также будет установлен Python 2.7.6. Он используется, чтобы избежать проблем используя другие версии IDA. Если вы установили Питон самостоятельно отдельно, он должен быть той же версии, что использует IDA.

После установки, загрузим крэкме Cruehead, он идет вместе с этим туториалом.

Так как это 32-х битный исполняемый файл, мы загрузим его в IDA для 32-х битных
файлов напрямую.

Если мы запустим крэкме вне IDA, мы увидим через Диспетчер Задач, что это 32-х битный процесс. Если мы хотим знать 32-х битный это или 64-х битный файл, без запуска, мы можем использовать 16-тиричный редактор. Например такой.

Самый простой способ — это открыть файл в хекс-редакторе, чтобы узнать какой он.


То, что мы видим — это Snipping Tools. И это 64-х битное приложение. Мы видим, что после слова PE есть следующие значения -> PE..d†

Наш крэкми — 32-х битный, после слова PE видим -> PE..L


Поэтому мы уже знаем какой мы файл имеем, чтобы загрузить его с использованием IDA 32. Когда IDA покажет окно QUICK START, мы выберем NEW, чтобы открыть новый файл, найдем наш крэкми и выберем его.

Сейчас не будем трогать настройки, потому что IDA правильно определила версию файла и мы можем нажать OK.

Если дальше нажмем YES на PROXIMITY VIEW, отобразиться дерево просмотра программы.

Чтобы перейти к графическому режиму или неграфическому — режиму инструкций нужно нажать пробел.

Также в OPTIONS — DEBUGGING OPTIONS — LINE PREFIXES мы можем добавить адреса в переднюю часть при графическом представлении.

Когда вы открываете исполняемый файл, первое, что открывается, это окно дизассемблера, которое называется LOADER, оно не запускает программу, а только ее анализирует для реверсинга и создает файл idb или базу данных (database).

Чтобы отладить программу мы должны выбрать среди всех доступных отладчиков включенных в IDA и запустить его в Режиме отладки, который мы разберем позже

Мы видим, что многие опции программы представлены в виде вкладок, в меню VIEW — OPEN SUBVIEW можно выбрать те вкладки, которые вам нравятся или нужны, чтобы не были открыты все.

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

То же происходит и в конце, там мы находим начало дизасма, в этом случае инструкция соответствующая 401000 — PUSH 0

В IDA есть возможность настроить интерфейс по умолчанию отдельно для LOADER и для DEBUGGER.

Как только вы привыкнете, например, в LOADER, наиболее часто используемые окна и вкладки можно сохранить, перейдя в WINDOWS-SAVE DESKTOP и отметив «по умолчанию», то же самое можно сделать для режима дебаггера и изменить настройку по умолчанию.


В любой из вкладок IDA, где есть такие списки, как FUNCTIONS, STRINGS, NAMES можно с помощью CTRL+F открыть поисковую систему, которая фильтрует информацию, в соответствии с тем, что мы набираем.

В VIEW-OPEN SUBVIEW-STRINGS точно также будут показываться строки, содержащие "try".

Через VIEW-OPEN SUBVIEW-DISASSEMBLY можно открыть второе дизасм окно, показывающее и выполняющее другие функции.

В OPEN SUBVIEW в LOADER есть hex представление или через вкладку HEX DUMP.

В OPEN SUBVIEW я могу показать импортируемые функции или вкладка IMPORTS.

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

Также имеются вкладки STRUCTURES, EXPORTS, NAMES, SEGMENTS и другие, которые я объясню позже, когда мы их будем использовать

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

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

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

До встречи во второй части.
Ricardo Narvaja

Перевод на английский: IvinsonCLS
Перевод на русский: Яша_Добрый_Хакер.
27.11.2016

Содержание статьи

С легкой руки Денниса Ритчи повелось начинать освоение нового языка программирования с создания простейшей программы «Hello, World!». Не будем нарушать эту традицию и оценим возможности IDA Pro следующим примером.

Чтобы все было как в статье, компилируй примеры с помощью Visual C++ 2017 вызовом cl.exe first.cpp /EHcs . Флаг /EHcs нужен, чтобы подавить возражение компилятора и вместе с тем включить семантику уничтожения объектов.

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

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

Чтобы открыть текстовое представление, надо из контекстного меню выбрать пункт Text view.

Какую версию IDA Pro выбрать?

Последней версией IDA Pro на момент написания статьи была 7.3. Ее цена может быть великовата для покупки в исследовательских целях. Как известно, Ильфак Гильфанов очень строго относится к утечкам и появлению продуктов своей компании в интернете и не допускает подобного.

Однако на сайте компании Hex-Rays в публичный доступ выложена бесплатная версия дизассемблера с функциональными ограничениями. Например, она не получает обновления после достижения майлстоуна целой версии, то есть сейчас для свободной загрузки доступна версия 7.0. Также она поддерживает только архитектуры x86 и x64.

Тем не менее этого вполне достаточно для наших целей. Потому что нам не придется разбираться в коде для процессоров ARM, Motorola, Sparc, MIPS или Zilog. Еще одно ограничение накладывается на использование в коммерческих целях, но и тут наша совесть чиста.

Закончив автоматический анализ файла first.exe , IDA переместит курсор к строке .text:0040628B — точке входа в программу. Не забудь из графического режима отображения листинга переключиться в текстовый. Также обрати внимание на строчку .text:00406290 start endp ; sp-analysis failed , выделенную красным цветом в конце функции start . Так IDA отмечает последнюю строку функции в случае, если у нее в конце return и значение указателя стека на выходе из функции отличается от такового на входе.

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

  • argc — количество аргументов командной строки;
  • argv — массив указателей на строки аргументов;
  • _environ — массив указателей на строки переменных окружения.

Заполняется структура OSVERSIONINFOEX , которая среди прочего включает:

  • dwBuildNumber — билд;
  • dwMajorVersion — старшую версию операционной системы;
  • dwMinorVersion — младшую версию операционной системы;
  • _winver — полную версию операционной системы;
  • wServicePackMajor — старшую версию пакета обновления;
  • wServicePackMinor — младшую версию пакета обновления.

Далее Start инициализирует кучу (heap) и вызывает функцию main, а после возвращения управления завершает процесс с помощью функции Exit . Для получения значений структуры OSVERSIONINFOEX используется функция GetVersionEx .

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

CRtO.demo.c

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

OS Version: 6.2
Build: 156
Arguments count: 1
Argument 01: CRt0.demo.exe
Environment variables count: 99
.
Variable 20: FrameworkVersion=v4.0.30319
Variable 19: FrameworkDIR32=C:WINDOWSMicrosoft.NETFramework
Variable 18: FrameworkDir=C:WINDOWSMicrosoft.NETFramework
Variable 17: Framework40Version=v4.0
Variable 16: ExtensionSdkDir=C:Program Files (x86)Microsoft SDKsWindows Kits10ExtensionSDKs
Variable 15: DriverData=C:WindowsSystem32DriversDriverData

Очевидно, нет никакой необходимости анализировать стандартный стартовый код приложения, и первая задача исследователя — найти место передачи управления на функцию main . К сожалению, чтобы гарантированно решить эту задачу, потребуется полный анализ содержимого функции Start . У исследователей есть много хитростей, которые позволяют не делать этого, но все они базируются на особенностях реализации конкретных компиляторов и не могут считаться универсальными.

Например, Visual C++ всегда, независимо от прототипа функции main , передает ей три аргумента: указатель на массив указателей переменных окружения, указатель на массив указателей аргументов командной строки и количество аргументов командной строки, а все остальные функции стартового кода принимают меньшее количество аргументов.

Рекомендую ознакомиться с исходниками стартовых функций популярных компиляторов. Для Visual C++ 14 в соответствии с архитектурой они находятся в подпапках каталога %Program Files (x86)Microsoft Visual Studio 14.0VCcrtsrc . Их изучение упростит анализ дизассемблерного листинга.

Ниже в качестве иллюстрации приводится фрагмент стартового кода программы first.exe , полученный в результате работы W32Dasm.

//******************** Program Entry Point ********************
:0040628B E802070000 call 00406992
:00406290 E974FEFFFF jmp 00406109

* Referenced by a CALL at Addresses:
|:0040380C , :004038B2 , :0040392E , :00403977 , :00403A8E
|:00404094 , :004040FA , :00404262 , :00404BF4 , :00405937
|:004059AE
|

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004062B6(U)
|
:00406295 8B4DF4 mov ecx, dword ptr [ebp-0C]
:00406298 64890D00000000 mov dword ptr fs:[00000000], ecx
:0040629F 59 pop ecx
:004062A0 5F pop edi
:004062A1 5F pop edi
:004062A2 5E pop esi
:004062A3 5B pop ebx
:004062A4 8BE5 mov esp, ebp
:004062A6 5D pop ebp
:004062A7 51 push ecx
:004062A8 F2 repnz
:004062A9 C3 ret
.

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

Поэтому способности дизассемблера тесно связаны с его версией и полнотой комплекта поставки — далеко не все версии IDA Pro в состоянии работать с программами, сгенерированными современными компиляторами.

.text:0040628B start proc near
.text:0040628B
.text:0040628B ; FUNCTION CHUNK AT .text:00406109 SIZE 00000127 BYTES
.text:0040628B ; FUNCTION CHUNK AT .text:00406265 SIZE 00000026 BYTES
.text:0040628B
.text:0040628B call sub_406992
.text:00406290 jmp loc_406109
.text:00406290 start endp ; sp-analysis failed
.text:00406290
.text:00406295 ; [00000015 BYTES: COLLAPSED FUNCTION __EH_epilog3. PRESS CTRL-NUMPAD+ TO EXPAND]
.text:004062AA ; [00000011 BYTES: COLLAPSED FUNCTION __EH_epilog3_GS. PRESS CTRL-NUMPAD+ TO EXPAND]
.text:004062BB ; [00000034 BYTES: COLLAPSED FUNCTION __EH_prolog3. PRESS CTRL-NUMPAD+ TO EXPAND]
.text:004062EF ; [00000037 BYTES: COLLAPSED FUNCTION __EH_prolog3_GS. PRESS CTRL-NUMPAD+ TO EXPAND]
.text:00406326 ; [00000037 BYTES: COLLAPSED FUNCTION __EH_prolog3_catch. PRESS CTRL-NUMPAD+ TO EXPAND]
.text:0040635D
.text:0040635D ; =============== S U B R O U T I N E ===============
.text:0040635D
.text:0040635D ; Attributes: thunk
.text:0040635D
.text:0040635D sub_40635D proc near ; CODE XREF: sub_4042FD+19↑p
.text:0040635D jmp sub_406745
.text:0040635D sub_40635D endp
.text:0040635D
.text:00406362
.

Перечень поддерживаемых компиляторов можно найти в файле %IDA%/SIG/list . В нем есть старинные Microsoft C и Quick C, Visual C++ с первой по восьмую версию и Visual.Net. А вот Visual C++ 14 из Visual Studio 2017 здесь нет. Однако, взглянув в окно IDA, мы видим, что дизассемблер сумел определить многие (но не все) функции.

Заглянем в окно вывода, находящееся внизу. Там, немного прокрутив вывод, мы обнаружим строчку Using FLIRT signature: SEH for vc7-14 , говорящую о том, что используемая версия IDA все же понимает компиляторы Visual C++ от 7 до 14.

Давай разбираться в получившемся листинге. Первое и в данном случае единственное, что нам надо найти, — это функция main . В начале стартового кода после выполнения процедуры sub_406992 программа совершает прыжок на метку loc_406109 :

.text:0040628B start proc near
.text:0040628B call sub_406992
.text:00406290 jmp loc_406109

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

В данном случае, как мы видим по комментарию, IDA отправила нас в начало стартового куска кода. Немного прокрутим листинг вниз, обращая внимание на плавные переходы по меткам.

В итоге доходим до вызова функции: call sub_4010D0 . Похоже, это и есть функция main , поскольку здесь дизассемблер смог распознать строковую переменную и дал ей осмысленное имя aHelloSailor , а в комментарии, расположенном справа, для наглядности привел оригинальное содержимое Hello, Sailor!
. Смещение этой строки компилятор закинул на вершину стека, а затем ниже через строчку, по всей видимости, происходит вызов функции вывода на экран:

.text:004010D3 push offset aHelloSailor ; "Hello, Sailor!
"
.text:004010D8 push offset unk_42DE30
.text:004010DD call sub_401170

Чтобы убедиться в этом, зайдем в последнюю процедуру. Разобраться в ней без пива не представляется возможным, разве что огромное количество параметров намекает нам на схожесть с функцией printf .

Если поместить курсор в границы имени aHelloSailor и нажать Enter, IDA автоматически перейдет к строке, в которой выполняется определение переменной:

.rdata:0041E1A0 aHelloSailor db ‘Hello, Sailor!’,0Ah,0 ; DATA XREF: sub_4010D0+3↑o

Выражение DATA XREF: sub_4010D0+3↑o называется перекрестной ссылкой и свидетельствует о том, что в третьей строке процедуры sub_4010D0 произошло обращение к текущему адресу по его смещению ( o от слова offset), а стрелка, направленная вверх, указывает на относительное расположение источника перекрестной ссылки.

Если навести курсор на выражение sub_4010D0+3↑o и нажать Enter, то IDA Pro перейдет к следующей строке:

.text:004010D3 push offset aHelloSailor ; "Hello, Sailor!
"

Нажатие Esc отменяет предыдущее перемещение, возвращая курсор в исходную позицию.

К слову, дизассемблер W32Dasm даже не смог распознать строковую переменную.

Что не так с IDA?

Положа руку на сердце — я был слегка разочарован, ибо ожидал, что IDA распознает больше библиотечных процедур. Поэтому я решил натравить «Иду» на такую же программу, но сгенерированную более ранней версией компилятора. Подопытным кроликом выступил Visual C++ 8.0 (VS 2005).

Сравним результаты работы компиляторов. Тот же исходник, компиляция из командной строки (папка first05 ). Загрузим итоговый экзешник в «Иду». Листинг приводится в сокращенном виде для экономии пространства.

Мало того что стартовый код меньше по объему, так еще было автоматически определено большее количество библиотечных функций, среди которых: GetVersionExA, GetProcessHeap, HeapFree и ряд других. Среди них достаточно просто найти вызов main и перейти на саму функцию.

Тем не менее VC++ 8.0 — ушедшая эпоха, и в пример я ее привел только для ознакомления.

На этом анализ приложения first.cpp можно считать завершенным. Для полноты картины остается переименовать функцию sub_4010D0 в main . Для этого подводи курсор к строке .text:004010D0 (началу функции) и жми N. В появившемся диалоге можешь ввести main . Результат должен выглядеть так:

.text:004010D0 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:004010D0 main proc near ; CODE XREF: start-8D↓p
.text:004010D0
.text:004010D0 argc = dword ptr 8
.text:004010D0 argv = dword ptr 0Ch
.text:004010D0 envp = dword ptr 10h
.text:004010D0
.text:004010D0 push ebp
.text:004010D1 mov ebp, esp
.text:004010D3 push offset aHelloSailor ; "Hello, Sailor!
"
.text:004010D8 push offset unk_42DE30
.text:004010DD call sub_401170
.text:004010E2 add esp, 8
.text:004010E5 xor eax, eax
.text:004010E7 pop ebp
.text:004010E8 retn
.text:004010E8 main endp

Обрати внимание: IDA в комментарии подставила прототип функции, а ниже параметры по умолчанию.

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.

Крис Касперски

Известный российский хакер. Легенда ][, ex-редактор ВЗЛОМа. Также известен под псевдонимами мыщъх, nezumi (яп. 鼠, мышь), n2k, elraton, souriz, tikus, muss, farah, jardon, KPNC.

Юрий Язев

Широко известен под псевдонимом yurembo. Программист, разработчик видеоигр, независимый исследователь. Старый автор журнала «Хакер».

Вначале было слово. Двойное

Открыв файл кейгена в Ida, видим список функций.

Проанализировав этот список, мы видим несколько стандартных функций (WinMain, start, DialogFunc) и кучу вспомогательных-системных. Все это стандартные функции, составляющие каркас.
Пользовательские функции, которые представляют реализацию задач программы, а не ее обертку из API-шных и системных вызовов, дизассемблер не распознает и называет попросту sub_цифры. Учитывая, что такая функция здесь всего одна — она и должна привлечь наше внимание как, скорее всего, содержащая интересующий нас алгоритм или его часть.

Давайте запустим кейген. Он просит ввести две 4-значных строки. Предположим, в функцию расчета ключа отправляются сразу восемь символов. Анализируем код функции sub_401100. Ответ на гипотезу содержится в первых двух строках:

var_4= dword ptr -4
arg_0= dword ptr 8

Вторая строка недвусмысленно намекает нам на получение аргумента функции по смещению 8. Однако размер аргумента — двойное слово, равное 4 байтам, а не 8. Значит, вероятнее всего за один проход функция обрабатывает одну строку из четырех символов, а вызывается она два раза.
Вопрос, который наверняка может возникнуть: почему для получения аргумента функции резервируется смещение в 8 байт, а указывает на 4, ведь аргумент всего один? Как мы помним, стек растет вниз; при добавлении в стек значения стековый указатель уменьшается на соответствующее количество байт. Следовательно, после добавления в стек аргумента функции и до начала ее работы в стек добавляется что-то еще. Это, очевидно, адрес возврата, добавляемый в стек после вызова системной функции call.

Найдем места в программе, где встречаются вызовы функции sub401100. Таковых оказывается действительно два: по адресу DialogFunc+97 и DialogFunc+113. Интересующие нас инструкции начинаются здесь:

Сначала подряд вызываются две функции SendDlgItemMessageA. Эта функция берет хэндл элемента и посылает ему системное сообщение Msg. В нашем случае Msg в обоих случаях равен 0Dh, что является шестнадцатиричным эквивалентом константы WM_GETTEXT. Здесь извлекаются значения двух текстовых полей, в которые пользователь ввел «две 4-символьных строки». Буква А в названии функции указывает, что используется формат ASCII — по одному байту на символ.
Первая строка записывается по смещению lParam, вторая, что очевидно — по смещению var_1C.
Итак, после выполнения функций SendDlgItemMessageA текущее состояние регистров сохраняется в стеке с помощью команды pusha, затем в регистры ecx, edx и eax записывается по одному байту одной из строк. В результате каждый из регистров принимает вид: 000000##. Затем:

  1. Команда SHL сдвигает битовое содержимое регистра eax на 1 байт или, другими словами, умножает арифметическое содержимое на 100 в шестнадцатиричной системе или на 256 в десятичной. В результате еах принимает вид 0000##00 (например, 00001200).
  2. Выполняется операция OR между полученным значением eax и регистром ecx в виде 000000## (пусть это будет 00000034). В результате еах будет выглядеть так: 00001234.
  3. В «освободившийся» есх записывается последний, четвертый байт строки.
  4. Содержимое еах снова сдвигается на байт, освобождая место в младшем байте для следующей команды OR. Теперь еах выглядит так: 00123400.
  5. Инструкция OR выполняется, на этот раз между еах и edx, который содержит, допустим, 00000056. Теперь еах — 00123456.
  6. Повторяются два шага SHL eax,8 и OR, в результате чего новое содержимое ecx (00000078) добавляется в «конец» еах. В итоге, еах хранит значение 12345678.

Затем это значение сохраняется в «переменной» — в области памяти по смещению arg_4. Состояние регистров (их прежние значения), ранее сохраненное в стеке, вытаскивается из стека и раздается регистрам. Затем в регистр еах снова записывается значение по смещению arg_4 и это значение выталкивается из регистра в стек. После этого следует вызов функции sub_401100.

В чем смысл этих операций? Выяснить очень просто даже на практике, без теории. Поставим в отладчике брейкпойнт, например, на инструкции push eax (перед самым вызовом подфункции) и запустим программу на выполнение. Кейген запустится, попросит ввести строки. Введя qwer и tyui и остановившись на брейкпойнте, смотрим значение еах: 72657771. Декодируем в текст: rewq. То есть физический смысл этих операций — инверсия строки.

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

В самом начале здесь ничего интересного — состояния регистров заботливо сохраняются в стеке. А вот первая команда, которая нам интересна — следующая за инструкцией PUSHA. Она записывает в есх аргумент функции, хранящийся по смещению arg_0. Потом это значение перекидывается в еах. И обрезается наполовину: как мы помним, в нашем примере в sub_401100 передается 72657771; логический сдвиг влево на 10h (16 в десятичной) превращает значение регистра в 77710000.
После этого значение регистра инвертируется инструкцией NOT. Это значит, что в двоичном представлении регистра все нули превращаются в единицы, а единицы — в нули. Регистр после выполнения этой инструкции содержит 888ЕFFFF.
Инструкция ADD добавляет (прибавляет, плюсует, и т.д.) получившееся значение к исходному значению аргумента, которое все еще содержится в регистре есх (теперь понятно, зачем было записывать его сначала в есх, а затем в еах?). Результат сохраняется в есх. Проверим, как будет выглядеть есх после выполнения этой операции: FAF47770.
Этот результат копируется из есх в еах, после чего к содержимому еах применяется инструкция SHR. Эта операция противоположна SHL — если последняя сдвигает разряды влево, то первая сдвигает их вправо. Подобно тому, как операция логического сдвига влево эквивалентна умножению на степени двойки, операция логического сдвига вправо эквивалентна такому же делению. Посмотрим, какое значение окажется результатом этой операции: 7D7A3BB.
Теперь совершим еще одно насилие над содержимым еах и есх: инструкция XOR — сложение по модулю 2 или «исключающее ИЛИ». Суть этой операции, грубо говоря, в том, что в результат ее равен единице (истине) только, если операнды ее раЗнозначные. Например, в случае 0 xor 1 результатом будет истина, или единица. В случае 0 xor 0 или 1 xor 1 — результатом будет ложь, или ноль. В нашем случае в результате выполнения этой инструкции применительно к регистрам еах (7D7A3BB) и есх (FAF47770) в регистр еах запишется значение FD23D4CB.

Следующая команда LEA ecx, [eax+eax*8] элегантно и непринужденно умножает еах на 9 и записывает результат в есх. Затем это значение копируется в edx и сдвигается вправо на 13 разрядов: получаем 73213 в еdx и E6427B23 в есх. Затем — снова ксорим есх и edx, записывая в есх E6454930. Копируем это в еах, сдвигаем влево на 9 разрядов: 8А926000, затем инвертируем это, получая 756D9FFF. Прибавляем это значение к регистру есх — имеем 5BB2E92F. Копируем это в еах, сдвигаем вправо аж на 17 разрядов — 2DD9 — и ксорим с есх. Получаем в итоге 5BB2C4F6. Затем… затем… что там у нас? Что, все.
Итак, мы сохраняем это значение в область памяти по смещению var_4, загружаем из стека состояния регистров, снова берем из памяти итоговое значение и окончательно забираем из стека оставшиеся там состояния регистров, сохраненные в начале. Выходим из функции. Ура. впрочем, радоваться еще рано, пока что на выходе из первого вызова функции мы имеем максимум — четыре полупечатных символа, а ведь у нас еще целая необработанная строка есть, да и эту еще к божескому виду привести надо.

Перейдем на более высокий уровень анализа — от дизассемблера к декомпилятору. Представим всю функцию DialogFunc, в которой содержатся вызовы sub_401100, в виде С-подобного псевдокода. Собственно говоря, это дизассемблер называет его «псевдокодом», на деле это практически и есть код на С, только страшненький. Глядим:

Это уже легче читать, чем ассемблерный листинг. Однако не во всех случаях можно положиться на декомпилятор: нужно быть готовым часами следить за нитью ассемблерной логики, за состояниями регистров и стека в отладчике… а потом давать письменные объяснения сотрудникам ФСБ или ФБР. Под вечер у меня особенно смешные шутки.
Как я уже сказал, читать это легче, но до совершенства еще далеко. Давайте проанализируем код и дадим переменным более удобочитаемые названия. Ключевым переменным дадим понятные и логичные названия, а счетчикам и временным — попроще.

Эпилог

Level complete. Cледующая (и заключительная) цель — это написание своего кейгена по этому алгоритму. Писать я, по привычке, буду на языке скриптов командной оболочки Linux bash. test $ <#reg1>-gt && reg1=`echo "$"` — это обрезка строки, содержащей эмулированное значение регистра, до 8 младших символов. При выполнении операций туда добавлялись лишние старшие разряды. Все остальное — кропотливая эмуляция ассемблерного листинга. Я же указал вверху хаб «Ненормальное программирование», да.

bash-реализация пресловутой sub_401100:

Основная функция кейгена:

Оцените статью
Ремонт оргтехники
Добавить комментарий