Руководство по ida pro на русском

0x00 start

; {EN} entry point, do nothing, just run _main {EN}

Статья для начинающих “воинов тьмы”, тех, кто хочет погрузиться в темную сторону силы: реверс-инжиниринг. На нашем “операционном столе” будет небольшой кустарный сервер, который работает по протоколу TCP/IP. Для анализа протокола обмена данными нам поможет стандарт де-факто в области реверса — IDA Pro.

Статей по реверс-инжинирингу и по IDA Pro уже написано немало (хотя и не столько, как по PHP), но поскольку процесс реверса — исследовательский, то мысли, как с «другого боку» подойти к задаче обратной разработки, полезны новичкам. По крайней мере, как автор, я руководствовался тем, чтобы изложить основные практики и техники, о которых говорю в первые дни всем стажерам и на первых парах курса по реверс-инжинирингу в университете.

Чего не будет в статье?

Поиска уязвимостей и разработки эксплоитов для Google Chrome или Apple iPhone… Поэтому если вы исследователь со стажем и с большим количеством CVE на счету, маловероятно, что вы найдете для себя что-то новое.


«Once you start down the dark path, forever will it dominate your destiny».

Подопытный: crackme — не запускайте бинари на основной системе, лучше это делать на виртуалке!

Системные требования: виртуальная машина с установленной Windows 7/0xA

0x01 mov eax, ds:__stack_chk_guard

; {EN} Disclaimer {EN}

Практически вся статья написана относительно ассемблера и, соответственно, процессора x86, поэтому работа с памятью, условными переходами (флагами), инструкциями и т.п. будет описываться в контексте именно этого процессорного ядра. На момент чтения статьи считаем, что других процессорных архитектур не существует.

0x10 Что нам нужно, чтобы понять о чём пойдет речь?

; {EN} Setups prerequisites for the consequent execution {EN}

Чтобы статья была интересна и понятна, предполагается, что читатель знает:

  • язык программирования C или C++;
  • основы языка ассемблера x86;
  • о существовании форматов исполняемых файлов (PE, ELF);
  • о кадре стека функции, поверхностно — достаточно;
  • как работать с сокетами;
  • математический анализ;
  • теорию ядерной физики.

Итак первое, что нам понадобится — сама IDA Pro от фирмы Hex-Rays (на русском произносится, как “ида про” или просто “ида”, для тех, кто любит соблюдать правила английского звучания “ай да про”, но звучит в русскоговорящей среде немного необычно). Вы можете скачать бесплатную версию для того, чтобы попробовать освоить реверс-инжиниринг, однако у неё есть ряд ограничений, среди которых: нельзя сохранять результат (в терминах IDA — базу данных) и подходит только для x86.

Второе, что нам понадобится — подопытный. Подойдет любой исполняемый файл (он же бинарник, он же файл с расширением *.exe для Windows), но рекомендуется взять тот, что прикреплен к статье. Если вы возьмёте свой бинарник, то лучше, если это будет скомпилированный для x86 файл (для первого раза не стоит брать собранные под x64 — в них ассемблер сложнее).

Третье — конечно же, хотя бы начальные знания языка ассемблера x86 (assembler language).

Чтобы осознанно копаться в программе и реверсить её, очень желательно знание языка ассемблера x86: инструкций (mov, lea, push/pop, call, jmp, арифметических и условных переходов), регистров и принципов работы процессора. Если их совсем нет, то настоятельно рекомендуется в начале изучить, например:

  • подробно, на русском: Ассемблер. Уроки 2011 (не обращайте внимание, что дизайн из 90-х);
  • кратко по ассемблеру, на английском: Guide to x86 Assembly;
  • средне, что на самом деле видит процессор, на английском: A Guide To x86 Assembly.

Может показаться, что инструкций огромное количество. На самом деле достаточно понять порядка 10 штук, остальные мало чем отличаются. Смотреть референс по инструкциям можно здесь или же в самой документации на процессорное ядро (предпочтительней).

Примечание автора. Хочу отметить, что когда сам только начинал заниматься этим, ассемблер выглядел, как сейчас продолжает выглядеть regexp (регулярные выражения) — вроде все буквы знаешь, а в слова не складываются. Однако постепенно начал понимать, что делают инструкции и что происходит в процессоре.

Четвертое — 30 минут времени (хотя, может быть, 30 часов) и желание научиться.

0x20 Минутка философии или что такое IDA Pro и почему?

; {EN} Whatta hell? {EN}

Реверс-инжиниринг (или обратное проектирование) — “это процесс извлечения знаний из того, что когда-либо было сделано человеком… Фактически, реверс-инженер — исследователь (научный работник), с той лишь разницей, что разбирается в том, что получено не естественным образом, а кем-то создано” — Reversing: Secrets of Reverse Engineering, Eldad Eilam.

Что же в первую очередь нужно исследователю? Блокнот и ручка, чтобы вести и систематизировать полученные знания. Так, а при чем тут IDA? IDA — аббревиатура, которая расшифровывается как “интерактивный дизассемблер“. Ключевым и революционным в свое время было именно “интерактивный”. Это означает, что в результате работы вы получаете не просто длиннющий ассемблерный листинг, а что-то, где вы можете оставить свои заметки, то есть как будто это действительного листинг, но в котором можно сделать “заметочки на полях”, и они меняются по ходу всего листинга. Можно еще сравнить с функцией рефакторинга в современных программерских IDE — переименовали функцию в одном месте, она переименовалась везде, переменную — аналогично и т.д. Ваша задача как исследователя — из огромного массива сложно анализируемой информации оставить только важную и придать ей форму хорошо понятную для человека. Именно интерактивность IDA Pro и позволяет делать это очень эффективно, и на сегодняшний день никто её в этом не превосходит.

Стоит все же заметить, что с учетом постоянно возрастающей закрытой кодовой базы и напечатанного мартышками кода в интернете, программное обеспечение реверсить вручную и интерактивно становится все сложнее и сложнее. Из-за этого сейчас активно развиваются средства анализа кода, нацеленные на автоматизированную обработку — radare2 (что бы ни говорили адепты r2, интерактивный интерфейс Cutter не настолько интерактивен, как в IDA Pro). Более того, IDA Pro также очень хорошо автоматизирована с помощью встроенного в неё интерпретатора Python и API к нему.

И все-таки, как бы крут не был автоматизированный анализ, он не автоматический, то есть заменить на все 100% исследователя не может и рано или поздно исследователю нужно вступать в бой. Поэтому, когда все приготовления наконец-то закончены, приступаем к изучению IDA Pro! Начнём постепенно разбирать наш подопытный образец.

0x30 Смотрим на IDA Pro и познаем основы её интерфейса

; {EN}
; This function is too complex.
; Perhaps it interacts with user.
; But currently I am not sure about it.
; A lot of calls to Qt-framework.
; {EN}

0x31 Загрузка бинарника в IDA Pro

Открываем IDA Pro. Перед нами после всех стартовых окошек (которые можно сразу же закрыть), появляется начальное окно программы. Все, что нужно сделать, — перетащить в него исследуемый бинарник. После этого в появившемся окне нажать кнопку “ОК”.


Главное окно IDA Pro при загрузке бинарника

В нашем случае (если вы грузили тот самый exe-файл в IDA, который приложен к статье) никаких настроек производить не нужно, IDA Pro сама распознает формат этого файла (PE, portable execute), а вот если туда кинуть прошивку или дамп из микросхемы памяти от, например, роутера, всё будет намного сложнее.

Какого-то единого стандарта хранения прошивки, можно сказать, нет. Каждый производитель сам для себя выбирает формат файла обновления, что даёт некоторую защиту от реверс-инжиниринга, так как, чтобы докопаться до кода, приходится сначала разобрать этот формат.

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

Что же касается дампа памяти, то на самой микросхеме может уже находится файловая система с учетом “скрамблинга” (перемешивания блоков на флеш-памяти с целью уменьшения износа). Собрать обратно эти блоки часто является не самой тривиальной задачей. Чтобы восстановить схему скрамблинга, необходимо реверсить сам скрамблер.

Скрамблер в студию

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


Так выглядит граф потока выполнения скрамблера

0x32 Что нам IDA Pro показала?

После загрузки бинарника IDA Pro проводит его предварительный автоматический анализ: определяет функции, глобальные переменные, строки — всё, что можно автоматически вытащить из анализируемого файла. Анализ выполняется процессорным модулем IDA Pro (не путать с самим процессором). Фактически это плагин для IDA Pro (при желании можно написать свой на Python или C++). В нашем случае IDA использует так называемый Meta PC — вариант x86/x64, учитывающий большинство твиков, которые были добавлены в архитектуру от Intel и AMD. Автоматический анализ выполняется опять же на основе того, что есть полное описание формата файла, который мы загрузили в IDA, в нашем случае тот самый Portable Executable. Данный формат чётко указывает какие секции есть в файле, в какой из секций лежит код, где его точка входа, а в какой секции данные (константы или глобальные переменные). Как обычно, с прошивками такой финт не проходит и необходимо вручную размечать входной файл (или же писать loader-плагин для IDA, который сможет «рассовать» куски бинарника прошивки как нужно, после чего процессорный модуль IDA сможет приступить к анализу).

Автоматический анализ под капотом крайне сложная штука, но для каждой инструкции он состоит в следующем:

  • декодирование инструкции из бинарного представления во внутреннее (analyze);
  • связывание инструкции с учётом специфики её выполнения (emulate — не путать с эмуляцией, как, например, в QEMU);
  • преобразование инструкции и аргументов в мнемонику.

Поверх этого с учётом информации и “связей” между инструкциями IDA выбирает путь для анализа следующего адреса, выполняя условный обход “дерева” инструкций от начальной точки анализа. За счёт того, что в стандартных форматах десктопных программ точка входа всегда известна (иначе загрузчик ОС не смог бы создать процесс из этого файла), IDA также может использовать эту информацию.

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

Пару слов о том, как IDA Pro хранит результат реверса. После или во время автоматического анализа “проект” можно сохранить (если, конечно, у вас полная версия). IDA хранит результат анализа в виде специальной базы данных со своей структурой (нет, это не модная MongoDB) на жёстком диске. Она представляет собой один файл с расширением .idb (или .i64).

После того как автоматический анализ завершён, вы увидите окно, примерно такое, как на рисунке ниже. Основные элементы, на которые стоит обратить внимание начинающему исследователю, подписаны на самом скриншоте. Завершение автоматического анализа можно определить по надписе AU: Idle в левом нижнем углу IDA Pro.

UX/UI IDA Pro

GUI IDA Pro написан на Qt, поэтому, если вы когда-нибудь работали с приложениями, написанными на Qt, тут действуют те же самые правила:

  • тотальный drag&drop окошек;
  • все открывается в разных вкладках, которые можно переставлять как вам угодно. Иногда их можно случайно закрывать и потом долго искать меню, где открывается эта вкладка.


Граф потока выполнения функции main

Давайте рассмотрим основные окна, которые первоначально отображает нам IDA Pro.

  • Граф потока выполнения — основной вид дизассемблера (можно сказать, представление в виде блок-схемы алгоритма функции). В нем отображаются инструкции процессора, полученные после дизассемблирования из бинарного вида. В этом графе показывается только одна функция. Каждая функция может быть разделена на блоки по инструкциям условного или безусловного перехода (пошли направо — один блок, налево — другой блок). Кроме этого вида есть и другой, где результат дизассемблирования отображается в виде сплошного листинга.

Proximity View

Есть и еще один вид, который называется Proximity View. Он отображает взаимосвязь между функциями и глобальными переменными.

Примечание автора. На мой взгляд, представление в виде графа более удобно, так как дает представление о структуре функции.

  • Список функций — окно, в котором выводятся функции, которые нашлись в бинарнике. Это те самые функции, которые были написаны программистом при создании программы (не учитывая оптимизацию). Если разработчик при компиляции не убрал отладочную информацию (в gcc это опция -s) или вообще собрал программу с опцией -g, тогда мы увидим все имена функций точно в таком же виде, как и программист. Иначе IDA Pro отобразит их в виде sub_<виртуальный_адрес _функции>;
  • Список строк (Strings) — не показан, но можно построить, нажав на SHIFT+F12. Одна из самых важных менюшек IDA Pro — в ней показаны все строки, которые есть в программе. Используя строки можно в некоторых случаях, найти всю интересующую нас функциональность.

0x33 Трогаем “лапой” IDA Pro

Наконец-то открыли бинарник и увидели, как это выглядит изнутри. Что же с ним делать и с чего начать реверс? С чего начать трогание нашего основного инструмента в реверсе, IDA Pro? Первое — виртуозное владение пианино горячими клавишами. Без знания горячих клавиш жизнь реверс-инженера скучна, так как сочетания клавиш сильно помогают её разнообразить и очень поднять эффективность и скорость реверса.

Когда только учишься работать в IDA Pro лучше постоянно держать перед глазами табличку (чит-шит, cheatsheet) с горячими клавишами. Ниже приведен пример моего чит-шита. Есть и официальный чит-шит от самой фирмы разработчика IDA Pro — Hexrays. Официальный чит-шит можно найти здесь, в нем больше сочетаний кнопок, но, на мой взгляд, на первое время будет достаточно того, что приведено на чит-шите ниже.

Где CTRL+S и CTRL+Z?

Внимательный читатель может заметить, что в этой таблице нет двух важных сочетаний клавиш. Первое — сохранение базы данных результатов реверса (CTRL+W, но в бесплатной версии сохранения нет), второе — undo. Так вот, забудьте про undo — его нет. Настоящие реверс-инженеры слишком суровы, чтобы использовать всем привычный CTRL+Z. Если серьезно, то функцию undo завезли только в версии 7.3 (бесплатная — 7.0), а всё потому что выполнить undo — не очень тривиальная задача. Дело в том, что какое-то изменение в базе данных IDA, внесенное пользователем, может привести к последующим множественным лавинным изменениям. Например, создание функции (make code) ведет к рекурсивному созданию всех вызываемых функций.

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

Пройдемся и заодно опробуем различные кнопки в IDA Pro.

0x33a Навигация по графу и листингу

Навигация — одна из самых простых и понятных задач, однако, чтобы не теряться в IDA Pro, следует потренироваться в следующем:

  • перемещения по графу функции с помощью мыши;
  • перейти на различные функции с помощью двойного нажатия мыши, вернуться обратно с помощью ESC и снова вперед с помощью CTRL+ENTER (нет в чит-шите — для продвинутых);
  • переключения между графом и листингом — SPACE, если вдруг у вас включился другой вид (листинга);
  • переход по перекрестным ссылкам: поставить указатель мыши на любое имя (функции или переменной) и нажать X. После этого вы увидите окно с другими инструкциями, которые ссылаются на это имя (или же указатель в памяти программы);
  • прямой переход на имя или адрес (g): в открывшемся окне написать любое из существующих имен или же адрес (можно без 0x) из текущей база данных IDA Pro — вы перейдете на ту часть графа (листинга), где определено это имя.

0x33b Именование и заметки на полях

По изменению имен (рефакторингу) следует попробовать и отработать следующие действия:

  • Переименование имен (для этого необходимо нажать мышью на имя и после этого N): функций, переменных (локальных и глобальных), регистров и меток. Постарайтесь разобраться, что можно переименовать, а что нет.
  • Простановка комментариев к инструкциям. В целом все просто: выбрали строку, к которой хотим поставить комментарий, и нажали ;.

0x33c Представление данных (data representation)

Любую часть анализируемого бинарника можно представить в виде различных вариантов:

  • байт (byte);
  • слово (word);
  • двойное слово (dword);
  • указатель (offset);
  • неопределенное (undefined);
  • дизассемблированный код (code).

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

0x33d Отличие кода от функции

Отдельно можно отметить и представление кода:

  • Код без функции (в таком случае IDA Pro не сможет построить граф);
  • Функция (чтобы из кода создать функцию, необходимо нажать P).
    Для большей части кода во время автоматического анализа IDA Pro сама распознает, где необходимо создать функции. Каким образом? За счет того, что если во время дизассемблирования встречается инструкция вызова (call), то анализатор однозначно может утверждать, что адрес, находящийся у этой инструкции в аргументе, — адрес начала функции.

Раз здесь спойлер, значит не все так просто

В реальности за счет использования различных техник защиты от реверса или просто из-за того, что мы грузим в IDA Pro прошивку и не знаем, где в ней начало кода, IDA Pro может начать дизассемблирование не с того адреса. Это приведет к тому, что будет получена инструкция call, хотя ее там и нет, а адрес «псевдоинструкции» call также не является началом никакой функции. Кроме того, какие-то данные, необходимые для работы программы, могут быть декодированы, как инструкции вызова — результат аналогичный.

0x33e Представление аргументов инструкций

Кроме представления каждого байта в виде различных вариантов указанных выше, можно по разному представить и аргументы большинства инструкций. Например, в инструкции записи числа в регистр mov eax, 0xFFFFFFFF второй аргумент может быть представлен, как в текущей записи, так и mov eax, -1. Для того, чтобы сменить вариант представления аргумента инструкции, необходимо нажать на него правой кнопкой мыши, и IDA Pro покажет возможные варианты представления.

0x33f Вспомогательные окна IDA Pro

В первую очередь стоит обратить внимание на Strings, Names, Functions, Hex Dump. Все эти окна можно открыть перейдя из строки меню View->Open Subviews.

0x40 Всего так много и как с этим работать?

; {EN}
; I think I found actual protocol parsing in function sub_401D3C
;
; But this function just chases bytes from corner to corner
; before actual parsing... we need to go deeper
; {EN}

0x41 Что же мы будем делать?

Теперь, когда мы знаем на какие кнопки надо нажимать и приблизительно представляем как «под капотом» работает IDA Pro, давайте попробуем вернуться к нашей задаче и найти ту часть бинарника, которая отвечает за разбор протокола обмена данными подопытного сервера.

Если внимательно посмотреть на вкладку с перечнем функций, можно увидеть, что IDA нашла в нём всего лишь 76 функций, то есть это очень маленькая программа. Реальные программы и, тем более прошивки, могут состоять из сотни тысяч функций. При этом никогда не ставится задача «втупую» восстановить исходный код программы на 100% (по крайней мере в моей практике никогда такого не было). Среди прочего от реверса бывает нужно:

  • Найти в протоколе ошибки. Для этого в программе необходимо искать места, связанные с получением данных извне;
  • Разобрать некий протокол взаимодействия, чтобы, например, создать свой API;
  • Пофиксить баг в чужой неподдерживаемой программной библиотеке (серьёзно, такое приходилось делать пару раз);
  • Что-то ещё…

Таким образом, главная задача, которая стоит перед исследователем, — найти некоторые интересующие места программы. Иначе говоря, реверсить всё целиком зачастую не имеет смысла, да и процесс этот слишком трудоёмкий. Не стоит забывать, что обратный анализ кода занимает времени больше, чем его прямая разработка.

0x42 Иголка в стоге байт

; {RU}
; Ищем функцию получения данных извне;
; Ищем на неё ссылки;
; Если не нашли, пытаемся искать по логам (повторяем пп.1-3);
; {RU}

Как же найти иголку в стоге сена? На самом деле, подходы практически такие же, как когда знакомишься с новым API или OpenSource-проектом, а вся документация в нем сделана в «doxygen»: пытаемся искать функции с вменяемыми именами или идём от API операционной системы.

Что не так с doxygen?

Наличие документации в doxygen — это отлично. Подразумевается, что это единственная документация, причем во время разработки программисты не всегда удосуживались написать комментарии к функции. То есть, все, что есть, — это HTML представление кода с именами функций и параметров (крайний случай).

Поскольку исходно сказано, что это TCP/IP-сервер, логично предположить, что данные будут “приезжать” в обработчик через recv, хотя в реальности могут быть использованы и другие функции. Например, recvfrom, или API более низкого уровня — самой ОСи (для Linux — read).

Заметка: кто хочет вспомнить/познакомиться с работой с сокетами в Си, тот читает Socket programming in c using TCP/IP.

Как сделать это в IDA Pro? Сначала нам нужен перечень всех имен (строк и имен функций, вкомпилированных в программу и импортируемых из библиотек). Для этого служит сочетание клавиш SHIFT+F4. После нажатия откроется вкладка с именами (Names).

На вкладке с именами можно воспользоваться поиском, точнее фильтром (в широком смысле, это более сложная функция с возможностью фильтрации строчек с помощью регекс выражения). Для того, чтобы вызвать поиск, необходимо, находясь во вкладке с именами, нажать сочетание клавиш CTRL+F. После этого внизу вкладки откроется строка ввода (как показано на рисунке). В эту строку необходимо написать часть слова, которое мы хотим найти (в нашем случае это будет recv), список сократится, и в нем останутся только те строки, в которых встречается заданное ключевое слово (на рисунке не показано).

Во второй колонке выводится адрес соответствующего имени. Для перехода на этот адрес в листинге следует дважды кликнуть по нему (или ENTER).

Заметка: переход назад в листинге выполняется по горячей клавише ESC.


Окно имен данного бинарника

И вот мы попадаем обратно в листинг. Теперь уже по адресу функции recv (вспоминаем, что recv в данном случае — библиотечная функция, и её код находится в динамической библиотеке).

Следующим шагом необходимо найти те места в программе, в которых происходит вызов функции recv. Для этого во время анализа IDA Pro создает перекрёстные ссылки между инструкцией вызова функции (или другим обращением к функции) и самой функцией. Чтобы посмотреть места, где используется функция, необходимо навести мышку на адрес (или имя), к которому мы хотим найти перекрёстные ссылки, и нажать кнопку X. Вслед за этим откроется окно, как на рисунке ниже. В окне будут перечислены все найденные перекрестные ссылки. Причем в колонке type используется следующая нотация:

  • p[rocedure] — перекрёстная ссылка “по вызову”, то есть адрес (имя) используется в инструкции call;
  • r[ead] — перекрёстная ссылка на чтение; в этом месте программы происходит чтение из данного адреса (имени);
  • w[rite] — перекрёстная ссылка на запись; в этом месте программы происходит запись в данный адрес (имя).

В нашем случае ссылок всего две:

  1. Чтение адреса функции recv в регистр (тип r),
  2. Непосредственный вызов recv (тип p).
    Можно заметить, что реально прямой перекрёстной ссылки на recv в инструкции вызова нет. Листинг вызова выглядит следующим образом:

push    0
push    1000h   ; len
push    ebx     ; buf
push    edi     ; s
call    esi     ; recv   <----  Вызов recv здесь

Как видно из кода ассемблера, инструкция call выполняет переход по адресу из регистра esi. Во время автоматического анализа IDA отслеживает, какое значение было занесено в регистр esi, и делает вывод, что при выполнении call в регистре esi всегда будет адрес recv. Именно поэтому IDA создает перекрёстную ссылку на recv в этом адресе.


Перекрестные ссылки на recv

Выбираем из списка ссылку с типом p, и IDA перекидывает нас в граф (или листинг), где происходит вызов функции recv. Выше на экране — листинг IDA. Мы можем увидеть функцию, вызывающую recv: sub_401D3C.

Просто? Да. В данном случае. В реальности же может оказаться, что прямых ссылок нет, а вместо recv вызываются другие функции, или же данные сохраняются в какой-то буфер в структуре, и потом обрабатываются неизвестно где (но анализ всего этого — отдельная статья).

0x43 Делаем “заметки на полях”: sub_401D3C

; {EN} x_vserv_protocol {EN}

Что такое x_?

В нашей команде принято использовать префикс x_ для именования функций (от слова eXecutable), чтобы отличить поименованные вручную функции от автоматически поименнованных IDA Pro.
ax_ — префикс поименнованых функций скриптами (IDAPython);
v_ — префикс глобальных переменных (от слова Variable);
av_ — аналогично, но поименнованных скриптом (IDAPython).

Функция sub_401D3C в отличие от recv является частью данной программы. Поэтому можно исследовать, что происходит в этой части «подопытного».

Заметка: вообще исследование программы часто делится на два основных метода: статический и динамический анализ. Статика подразумевает, что весь анализ выполняется только на основе кода (без запуска программы), динамика — с учётом информации получаемой в дебаге.

В нашем случае проще было бы запустить подопытного в дебаге (англ. «debug» — отладка) и уже после этого начать изучать, что с ним происходит. Но, во-первых, чтобы поучиться мы проведем исследование чисто статикой: где-и-что делается в обработке сразу после получения данных из сокета. Во-вторых, прежде чем что-то запускать даже на виртуалке, я предпочитаю понять, чем это может закончиться

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


Место вызова функции recv

Начнем делать “заметки на полях”. Если окинуть взором функцию sub_401D3C, в которой мы очутились, можно увидеть в ней вызовы двух функций с неизвестными именами: sub_401CF0 и sub_401BFD. Кроме этого, мы видим и вызов функции puts — стандартная библиотечная функция из libc. Она выводит строку в стандартный поток вывода (stdout). Раз функция что-то печатает на экран, значит, должны быть и строки, из которых можно получить какую-то информацию!

Заметка: в предыдущем разделе мы нашли интересующую нас функцию по библиотечной функции recv. Однако “золотой жилой” являются строки. Просто пробежавшись взглядом по строкам в окне Strings (SHIFT+F12) или поискав в нём различные ключевые слова, можно извлечь очень много дополнительной информации о работе программы или же найти места, в которых происходит что-то интересное для нас как для реверс-инженеров. Никогда не пренебрегайте возможностью посмотреть на строки, которые остались в программе.

Даже не особо разбираясь в ассемблере, можно легко понять, что выводит конкретно здесь puts. В блоке по адресу 0x00401D64 (чтобы перейти в этот блок, нужно нажать кнопку g и вставить в окно указанный адрес) будет выведена строка “Received failed», в блоке по адресу 0x00401D7A — «Client disconnected», а в блоке 0x00401D9C — “VSERV protocol error…”. На основании этих строк можно сделать вывод, что данный сервер имеет внутреннее название VSERV (далее при именовании функций будем использовать такой идентификатор). Кроме этого нужно поименовать метки блоков по адресам:

  • 0x00401D76 как RECV_SUCCESS;
  • 0x00401D8C как CLIENT_NOT_DISCONNECTED.

Заметка: надо стараться именовать всё во время реверс-инжиниринга. Если вы натыкаетесь на функцию и у вас есть хотя бы малейшее предположение, что делает эта функция, — переименовывайте её. В будущем, когда натыкаетесь на эту же функцию, но при других обстоятельствах, вы будете помнить, что уже имели дело с ней, и она где-то была важна для вас. Также можете провести анализ её использования по нескольким случаям применения. Дальше по тексту уже не будет приводиться фраза: “надо переименовать”, предполагается, что это рефлекс.

Далее видно, что адрес 0x00401D54 — начало цикла, в котором сервер постоянно “крутится” и получает данные от клиента. Этот адрес можно назвать “RECV_LOOP”. Цикл в IDA Pro легко найти с помощью графового представления: стрелка перехода от нижнего блока (окончание цикла) к верхнему (начало цикла) выделяется жирным.

Хорошим вариантом для имени функции по адресу 0x00401D3C, в которой мы находимся, является, например, x_vserv_protocol. Видно, что в ней происходит приём данных от клиента, после чего вызываются две функции — в них будет либо полный разбор протокола, либо же предразбор (преобразование потока данных из TCP в “сообщения”). Из кода, который есть в функции x_vserv_protocol, невозможно сделать полноценный вывод, что же происходит внутри функций sub_401CF0 и sub_401BFD, поэтому давайте зайдем поочередно в каждую из них и попробуем понять их функциональное назначение (вернуться назад можно кнопкой ESC).


Не забываем переименовывать метки и имена функций

0x44 Делаем “заметки на полях”: sub_401CF0

; {EN} x_vserv_parse_header {EN}

Начнем, пожалуй, с sub_401CF0, так как она идёт первая по ходу выполнения. Чтобы перейти в функцию, необходимо дважды нажать на неё мышью, в результате чего мы оказываемся в очень маленькой функции sub_401CF0. Граф её потока выполнения приведён на рисунке ниже. Судя только по общему виду графа (не вдаваясь в подробности ассемблера), сразу можно сделать вывод, что эта функция:

  • На вход получает только один аргумент (причем, скорее всего, с помощью функции recv данные из TCP-сокета);
  • Не имеет циклов и содержит одно ветвление (if-else);
  • Вызывает две библиотечные функции memcmp и atoi;
  • В одной из веток возвращается 0xFFFFFFFF (-1) в качестве результата;
  • Проверяет сигнатуру в пришедших данных.


_Реверс функции sub_401CF0 она же x_vserv_parse_header_

Разберёмся по порядку, откуда что взялось.

0x44a Один аргумент и его назначение в функции sub_401CF0

Аргументы IDA пытается распознать сама (для этого она, точнее её конкретный процессорный модуль, использует знание о calling convention (соглашение о вызовах) и другие методы эвристики, но может ошибаться). Если аргумент передается через стек, а не через регистр (для x86 при соглашении о вызовах cdecl, которое используется чаще всего, это именно так), то такие смещения в стеке IDA Pro сама именует с префиксом arg_.

Заметка: передача аргументов и возврат значения из функции при компиляции целиком и полностью определяются соглашением о вызове функции (calling convention). В соглашении много нюансов, и самих вариантов соглашений довольно много (какой из них используется, определяется в том числе и компилятором). Основное, что нам сейчас нужно знать, — x86-аргументы передаются через стек (с помощью инструкции push), а возвращаемое значение через регистр eax (то, что на Си пишется после return).

Назначение аргумента. Почему на скриншоте агрумент уже назван packet_buffer? Разобраться с этим можно, взглянув на предыдущую функцию, а точнее на то, что ей передается в качестве аргумента. Для разъяснений ниже приведён еще один скриншот из функции x_vserv_protocol. Аргументом в функцию приходит значение из регистра ebx. Если нажать на ebx мышкой, IDA подсветит все его использования, за счёт чего можно легко найти предыдущее применение этого значения. Оно же передается в функцию recv (да, в ту самую) вторым аргументом (вспоминаем, что согласно соглашению о вызовах в стек аргументы в коде заносятся в «обратном порядке»).


_Аргументы функции x_vserv_parse_header_

Следующим шагом (для тех, кто забыл определение функции recv) необходимо заглянуть в документацию на recv. Из неё станет понятно, что второй аргумент — адрес буфера, в который recv запишет принятые данные. Думаю, теперь очевидно, что единственный аргумент функции sub_401CF0 и есть адрес буфера с принятыми из TCP-сокета данными.

0x44b Функция sub_401CF0 не имеет циклов и содержит одно ветвление

Вспоминаем, как в IDA Pro быстро понять, есть ли в функции циклы или нет (стрелка от нижнего блока к верхнему). Аналогично смотрим на граф функции sub_401CF0 и делаем вывод, что в sub_401CF0 циклов нет.

Наверное, уже все догадались, что две выходящие стрелочки из блока в графе IDA Pro означают, что данный блок программы является частью if-else в исходном коде. Цвет стрелки означает следующее:

  • Красный — путь выполнения программы, если переход не выполняется;
  • Зеленый — если выполняется.

Выполнение перехода, в свою очередь, зависит от предыдущей инструкции. В анализируемой функции sub_401CF0 такое ветвление только одно, и чуть позже мы разберёмся, что же проверялось в исходном коде.

Заметка: обычно конструкция if-else после компиляции превращается в две инструкции (как минимум):

  1. Сравнение значений (эта инструкция выставит флаги процессора);
  2. Условный переход на основании выставленных флагов.

0x44c Функция sub_401CF0 вызывает две библиотечные memcmp и atoi

Если мы не помним определение и функциональное назначение библиотечных функций, их необходимо загуглить, так как эта информация позволит нам понять, что делает анализируемый код. Иногда названия API-функций могут быть совсем не очевидны и даже если считаете, что хорошо знаете ту или иную функцию, но возникла тень сомнения, лучше сразу посмотреть документацию, в данном случае на memcmp.

Примечание автора: иногда “fear … leads to suffering”, давным-давно, в одной забытой Галактике, я реверсил устройство и, находясь в режиме отладки при загрузке Линукса, наткнулся на вызов функции reboot. В тот момент, когда выполнение повернуло в ветку с этой функцией, я остановил отладку и начал разбираться, в чём проблема. На все разборки ушёл практически весь рабочий день. Под конец заглянув в документацию на reboot, я прочитал следующее: «or enables/disables the reboot keystroke». Победив «страх» и нажав на F8, находясь на функции reboot, я понял, что устройство не перезагрузилось, а продолжило выполняться… RTFM!

Довольно легко можно найти, что memcmp сравнивает два массива в памяти и на вход принимает указатели этих массивов и количество байт, которое необходимо «подвергнуть» сравнению. Вроде всё просто и понятно, а вот с возвращаемым значением не всё так очевидно, и новички в Си часто делают ошибку. Предполагают, что 0 — строки неравны, а 1 — строки равны. В реальности в случае равенства строк функция вернет 0, а если строки неравны, то либо > 0, либо < 0.

Вторая API-функция atoi преобразовывает число, записанное в ascii-строке, в integer. Соответственно, на вход приходит указатель на строку, а на выходе — целочисленное значение.

0x44d Собираем всё вместе и отвечаем на два оставшихся вопроса

Какие выводы можно сделать из анализа использования этих двух API-функций в исследуемой функции?

Во-первых, memcmp проверяет сигнатуру протокола (уникальную последовательность байт, чтобы «удостовериться», что пакет реально относится к заданному протоколу). Этот вывод можно сделать на основе того, что в функцию memcmp передается напрямую буфер с принятыми данными (постарайтесь отследить это сами), константная строка «VMES» и значение 4 (очевидно длина VMES). После этого, если сигнатура не нашлась, программа может повернуть в ветку, где в регистр eax заносится значение 0xFFFFFFFF (-1), или в ветку с atoi.

В данной случае используется функция memcmp, а не strcmp, хотя и сравниваются две строки, из-за того, что необходимо указать максимальную длину сигнатуры, 4 байта. Функция strcmp будет сравнивать до тех пор пока не встретит нуль-терминатор. Хотя у сигнатуры «VMES» нуль-терминатор идет последним, пятым символом, в пришедшем пакете — нуль-терминатор может быть где угодно. Из-за этого, даже если в пакете в начале будет эта сигнатура, strcmp определит эти строки как различающиеся.

Во-вторых, atoi, скорее всего, получает длину тела-сообщения (хотя напрямую это не следует из анализа только этой части кода). Взгляните внимательно и вы увидите, что atoi берёт из полученных данных кусок буфера — четыре байта следом за VMES (это можно понять, если разобрать ассемблер в блоке по адресу 0x00401D19) — и преобразует его в число. Результат преобразования atoi передается в eax. Таким образом, в eax на выходе из функции оказывается либо значение, полученное из принятых данных, либо -1. Также вспомним, что согласно соглашению о вызовах для x86 результат возврата функции находится в регистре eax, функция проверяет наличие сигнатуры в первых четырех байтах, если этих байтов в буфере нет — возвращает -1, иначе преобразует следующие четыре байта в число и возвращает его из функции. Что может быть лучше, чем описать код на естественном языке? Правильно, написать сам код:

char tmp[5] = { 0 };
if (memcmp(&buf[0], "VMES", 4) != 0)
    return -1;
*(int*) tmp = *(int*)(&buf[4]);
return atoi(tmp);

По началу код может показаться странным. Могут возникнуть такие вопросы, как: откуда буфер на 5 байт? Зачем он вообще здесь? Почему просто нельзя передать в atoi(buf + 4)? Начнем разбираться с последнего вопроса и для этого нам понадобится документация на atoi, а точнее на документация на strtol (если открыть доку на atoi, то она ссылается на strtol с указанием системы счисления 10). В ней сказано, что конвертация происходит, пока не будет встречен символ, который не подходит для данной системы счисления. То есть, для 10-ой системы это любой символ не из диапазона от 0 до 9. В ходе реверса в таких случаях, можно предположить, что автор программы хотел защититься от того, что в сервер могли отправить специально подобранный пакет, где это значение будет указано каким-угодно большим. Однако при этом (как увидим дальше) допустил другие ошибки. По итогу: копирование в отдельный буфер с нуль-терминатором позволит избежать проблемы неправильной конвертации ascii-строки.

Размер буфера в 5 байт можно определить, если заглянуть в стек программы (о котором подробнее чуть позже в статье): в списке переменных функции сразу за buffer_length идет переменная var_D (поэтому в стеке они распологаются друг за другом). В нее заносится 0 в самом начале функции, и больше эта переменная никак не изменяется. Поэтому var_D и есть нуль-терминатор.

На основании анализа предлагается дать функции гордое название x_vserv_parse_header (на скриншотах уже была переименована).

0x45 Делаем “заметки на полях”: sub_401BFD

; {EN} x_vserv_parse_body {EN}

0x45a Немного о стеке и его кадре

Итак, мы дошли до последней неразобранной функции, чтобы целиком охватить «архитектуру» той части программы, которая отвечает за обработку протокола. Как и на предыдущем этапе реверса, начать следует с её аргументов. Обратимся к блоку функции x_vserv_protocol, который мы ранее поименовали как PARSE_BODY (для этого, как обычно, можно нажать g, вставить туда название блока и нажать ENTER). Перед вызовом самой функции видны две инструкции push, которые, очевидно, передают аргументы в нужную нам функцию sub_401BFD (на скринах она уже переименована в x_vserv_protocol_body). С этой целью взглянем на рисунок, приведенный ниже.


_Что за body_buffer?_

Первым аргументом передается адрес (так как используется инструкция lea) некой переменной (на скриншоте названа body_buffer). Вторым аргументом — то, что было получено из функции x_vserv_protocol_header (так как регистр eax передается в инструкцию push без изменений). Если со вторым аргументом все очевидно — число после atoi-преобразования, то с первым давайте разберёмся.

Чтобы понять, что такое body_buffer, следует обратить внимание на пару моментов:

  • Каким образом получен указатель на буфер, который передается в recv;
  • Структура стека функции x_vserv_protocol.

Указатель на буфер, передаваемый в recv, формируется довольно очевидно. Он передается вторым аргументом в recv и, следовательно, адрес буфера находится в регистре ebx перед вызовом функции recv (см. блок RECV_LOOP). Если нажать на ebx и отследить, какое значение заносится в него перед этим, то видно, что туда перекладывают регистр esp. Регистр esp является крайне важным (хотя все регистры важны) тем, что он всегда указывает на вершину стека и, кроме этого, неразрывно связан с push/pop. Конкретно в этом случае в esp хранится начало стекового буфера, что в исходном коде выглядело как:

char buffer[0x1000];

Заметка: почему так? Чтобы ответить, нужно разобраться с тем, что такое кадр стека, и как разложены локальные переменные, а также аргументы в стеке. Подробно тому, как располагаются данные в стеке лучше почитать по одной из ссылок в начале статьи, так как тема кадра стека сама по себе заслуживает отдельной статьи. Также можно почитать статью на Wikipedia.

Рассмотрим структуру стека (кадра или фрейма) функции x_vserv_protocol. Для отображения кадра стека в IDA Pro необходимо два раза нажать мышкой на одну из переменных, расположенных в стеке (на скриншотах — «рыжие» имена в самом начале функции). После этого вы увидите картинку, похожую на рисунок ниже.


Стековый кадр функции

Как было написано выше, указатель для приема данных через recv соответствует самому началу кадру стека (так как esp используется без смещения). В связи с этим можно поименовать (как обычно — кнопочкой N) верхушку как vmes_sign (в первых четырех байтах ожидается сигнатура «VMES»).

Следующие четыре байта — это байты, которые передаются в atoi в функции x_vserv_parse_header. Вывод о группе в четыре байта, можно сделать из первой инструкции левого блока функции x_vserv_parse_header (адрес 0x00401D19). Инструкция mov перекладывает именно четыре байта из [ebx+4] в регистр eax для последующего преобразования в atoi. Поскольку мы решили, что это длина тела пакета, поименуем их как vmes_body_len.

Теперь становится понятно, что после восьми описанных байт идут оставшиеся данные из TCP-пакета. Если вы разрабатывали клиент-серверное приложение, то очевидно, что эти оставшиеся данные — тело пакета, и его парсинг (разбор), скорее всего, будет в функции, вызываемой следом за x_vserv_parse_header. Собственно, эта функция на скриншотах практически сразу и была названа как x_vserv_parse_body.

0x45b Разбираем функцию разбора тела пакета

Вернёмся обратно в саму функцию (кнопка ESC) и соберём всё вместе. Первый аргумент для функции sub_401BFD (x_vserv_protocol_body — уже можно переименовать) — тело пакета, данные, пришедшие из TCP-сокета с помощью recv, за исключением первых восьми (судя по всему, первые восемь — заголовок пакета). Второй аргумент – данные, находящиеся по смещению +4 от начала пакета (предположительно, длина тела пакета) и «пропущенные» через atoi, чтобы получить из них число.

Заметка: если кто-то со знанием Stack BOF (он же Stack Buffer Overflow, оно же переполнение буфера в стеке) решил почитать статью, он наверняка уже учуял запах крови этого самого переполнения буфера в стеке. Из пользовательских данных берётся значение, которое преобразовывается в число. Если дальше нет валидации этих данных, жди беды переполнения.

Настала пора заглянуть в саму функцию x_vserv_protocol_body, граф которой показан на рисунке ниже.


_Граф функции x_vserv_protocol_body_

После долгих вечеров и дней реверс-инжиниринга граф функции в стиле «лесенки» практически сразу говорит о том, что в исходном коде была цепочка из if-else-if-else-if-else (возможно, также и switch). И действительно, если внимательно посмотреть на функцию, то хорошо видно, что в каждом из блоков берутся первые байты тела пакета и поочередно сравниваются с “HEXDUMP”, “TALK2ME”, “B64DECO”, “DISCONN”, “STOP!!!”. Если ничего из этого не нашлось, то в консоль выводится строка «Unknown command». Таким образом, понятно, что перечисленные выше строки — команды протокола. При обнаружении одной из них выполнение переходит на соответствующую функцию. Их можно поименовать следующим образом: x_vserv_hexdump, x_vserv_talk2me, x_vserv_b64deco, x_vserv_disconn, x_vserv_stop. Это и есть обработчики команд протокола.

0x45c Пощупаем некоторые обработчики команд vserv

Интересно отметить, что число, которое передано вторым аргументом, нигде не используется в этой функции напрямую, а только передается дальше в обработчики команд протокола. Кроме того, нет и валидации количества принятых данных, то есть программа считает, что ей обязательно пришёл пакет как минимум из 15 байт (хотя это может быть вовсе не так). Чтобы всё-таки убедиться, что второй аргумент, он же len, реально является числом, применяемым как размер данных, обратимся к одному из обработчиков x_vserv_hexdump. Интересующий нас кусок функции приведён на рисунке ниже.


_Второй аргумент x_vserv_hexdump_

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

0x45d Закругляемся на сегодня

0x50 Is this the end?

; {EN} x_vserv_parse_body {EN}

Статья направлена на то, чтобы изложить максимально подробно базовую технику реверс-инжиниринга, которая была бы понятна новичку и он смог бы попробовать изучить другие бинарники. Конечно, мы не коснулись разбора алгоритмов, а фактически только посмотрели, как понять структуру программы и куда вообще лезть после того, как открыл её в IDA Pro.

Если вдруг эта статья окажется не 9-й жизнью котика реверс-инженера, он расскажет об анализе обработчиков протокола VSERV в IDA Pro, поможет написать для него клиент и вместе с читателем поищет уязвимость RCE (она там есть и лежит на поверхности) в этом сервере.

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

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

Потому что пока 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 файлы и такой установщик

idapronw_hexarmw_hexx64w_hexx86w_150413_cb5d8b3937caf856aaae75 0455d2b4ae

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

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

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

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

То, что мы видим — это 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 IDA покажет импортируемые функции.

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

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

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

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

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

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

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

Введение в реверсинг с нуля используя IDA PRO. Часть 1

Дата публикации 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 как внутри системы, так и удаленно в следующих операционных системах.

1.png

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

2.png

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

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

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

НАЧАЛО

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

3.png

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

idapronw_hexarmw_hexx64w_hexx86w_150413_cb5d8b3937caf856aaae75 0455d2b4ae

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

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

4.png

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

5.png

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

6.png

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

Загрузите и установите английскую версию

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

7.png

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

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

8.png

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

9.png

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

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

10.png

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

10.png

11.png

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

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

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

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

12.png

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

13.png

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

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

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

14.png

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

15.png

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

16.png

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

17.png

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

18.png

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

19.png

20.png

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

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

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

21.png

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

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

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

Источник:
http://ricardonarvaja.info/WEB/INTRODUCCION AL REVERSING CON IDA PRO DESDE CERO/EN INGLES/

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


yashechka

yashechka
Ростовский фанат Нарвахи

Регистрация:
2 янв 2012
Публикаций:
90

Комментарии

      1. кофейник 22 окт 2019

        13 фев 2017 Вы упомянули Корелан. Это Ваш друг?
        За работу огромное спасибо.

      2. tkzv 14 мар 2019

        Под какую ОС прилагаемые crackme? Пока HOLA_REVERSER.exe, TEST_REVERSER.exe и VEViewer.exe отказались работать под Windows XP даже после установки рантаймов от VC++ 2017. Нужны какие-то ещё рантаймы? Или более новая ОС?

      3. Antora 2 авг 2018

        «Также в OPTIONS — DEBUGGING OPTIONS — LINE PREFIXES мы можем добавить адреса в переднюю часть при графическом представлении.» — не, не тут это живет в версии 6.8 точно, а тут: OPTIONS — GENERAL — Disassembly

      4. yashechka 14 май 2018

        Я очень рад, что Вы начали читать. Спасибо за то, что Вы есть.

      5. __sheva740 14 май 2018

        Начал изучение курса.
        Нравится все!
        Перед yashechka — снимаю шляпу.
        Рад буду помочь — чем смогу.
        )))

      6. yashechka 12 май 2018

        Спасибо. Странно, что только сейчас заметили. Есть такое дело )

      7. inzu 12 май 2018

        yashechka, вы на втором скриншоте, где список процессоров должен быть, показываете копию первого, с ОСями

      8. yashechka 27 янв 2018

        В финальном релизе, все ошибки будут учтены.

      9. texaciri 27 янв 2018

        @yashechka, поставил 6.8 которая рекомендуется.
        Вот сделал гифку, там всё видно, _yadi.sk/i/h6elGxBb3RqbCK

      10. yashechka 27 янв 2018

        Может это из-за разницы в версиях?

      11. texaciri 27 янв 2018

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

      12. Basil_pig 27 ноя 2017

        Прошу прощения за глупый вопрос, но файл Crackme.exe я не нашел :dntknw: . Какой экзешник открыть я конечно же нашел, но … наглядности примера нету.
        Может антивирус его убил?

      13. yashechka 15 фев 2017

        Пусть народ голосует, сохранены скриншоты из авторской статьи.

      14. Страница 1 из 2


    WASM

    Initial page

    Бэкап перевода Яши с wasm.in цикла статей «Введение в реверсинг с нуля, используя IDA PRO» от Рикардо Нарвахи.

    Gitbook версия

    Сборка

    Необходимо установить pandoc https://pandoc.org/.

    В папке с файлами *.md необходимо выполнить команду:

    Epub

    pandoc -f markdown -t epub -o ida.epub README.md chast-01.md chast-02.md chast-03.md chast-04.md chast-05.md chast-06.md chast-07.md chast-08.md chast-09.md chast-10.md chast-11.md chast-12.md chast-13.md chast-14.md chast-15.md chast-16.md chast-17.md chast-18.md chast-19.md chast-20.md chast-21.md chast-22.md chast-23.md chast-24.md chast-25.md chast-26.md chast-27.md chast-28.md chast-29.md chast-30.md chast-31.md chast-32.md chast-33.md chast-34.md chast-35.md chast-36.md chast-37.md chast-38.md chast-39.md chast-40.md chast-41.md chast-42.md chast-43.md chast-44.md chast-45.md chast-46.md chast-47.md chast-48.md chast-49.md chast-50.md chast-51.md chast-52.md chast-53.md chast-54.md chast-55.md chast-56.md chast-57.md chast-58.md chast-59.md chast-60.md chast-61.md chast-62.md chast-63.md chast-64.md chast-65.md chast-66.md chast-67.md

    FB2

    pandoc -f markdown -t fb2 -o ida.fb2 README.md chast-01.md chast-02.md chast-03.md chast-04.md chast-05.md chast-06.md chast-07.md chast-08.md chast-09.md chast-10.md chast-11.md chast-12.md chast-13.md chast-14.md chast-15.md chast-16.md chast-17.md chast-18.md chast-19.md chast-20.md chast-21.md chast-22.md chast-23.md chast-24.md chast-25.md chast-26.md chast-27.md chast-28.md chast-29.md chast-30.md chast-31.md chast-32.md chast-33.md chast-34.md chast-35.md chast-36.md chast-37.md chast-38.md chast-39.md chast-40.md chast-41.md chast-42.md chast-43.md chast-44.md chast-45.md chast-46.md chast-47.md chast-48.md chast-49.md chast-50.md chast-51.md chast-52.md chast-53.md chast-54.md chast-55.md chast-56.md chast-57.md chast-58.md chast-59.md chast-60.md chast-61.md chast-62.md chast-63.md chast-64.md chast-65.md chast-66.md chast-67.md

    TODO:

    1. Поправить глупые смысловые ошибки.
    2. Выполнить разбиение на блоки, перенести иллюстрации на гит.
    3. Разбить части на подглавы, дополнить описания частей, поправить оформление, примеры кода.
    4. Переписать в виде обычных таблиц картинки с таблицами, и прочие скриншоты из книг.
    5. Перенести бинарные файлы на которых показываются примеры(а надо ли?).
    6. Не достает 66 часть 2 (ждать перевод Яши / либо перевести).
    7. Состряпать инструкцию по конвертации *.md to *.tex и сборке PDF/EPUB/FB2.
    8. Исправить скриншот .gitbook/assets/12/47.png, для сравнения смотри на .gitbook/assets/12/45.png.
    9. В главах 26, 64 выделить включевые слова по аналогии с прочими главами.
    10. Требуется глобальная вычитка на предмет приведения всех слов и терминов к единообразному стилю / именованию, все числовые значения. строки кода оформить соответствующим тегом (‘*стркоа*’), вместо выделения жирным.
    11. Требуется переформулировать предложени с формулировками «Мы» и сократить повествовательную форму. Все должно быть в повелевающем наклонении.

    Сайт оригинального автора: ricardonarvaja.info.

    (Копия на github)

    Введение в реверсинг с нуля, используя IDA PRO — на английском (PDF)

    Введение в реверсинг с нуля, используя IDA PRO — на испанском (Word)

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

    Содержание

    1. Анализ исполняемого файла в IDA Pro
    2. Какую версию IDA Pro выбрать?
    3. Что не так с IDA?
    4. Анализ зашифрованных программ в IDA
    5. Данные
    6. Расшифровка
    7. Создание сегмента
    8. Создание клона
    9. Итоги
    10. Дизассемблер и отладчик в связке
    11. Способ 1
    12. Способ 2
    13. Заключение

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

    #include <iostream>

    void main()

    {

      std::cout << «Hello, Sailor!\n»;

    }

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

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

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

    Результат работы IDA Pro

    Результат работы IDA Pro

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

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

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

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

    РЕКОМЕНДУЕМ:
    Как убить защитный драйвер в Windows

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

    Закончив автоматический анализ файла
    first.exe, IDA переместит курсор к строке
    .text:0040628B — точке входа в программу. Не забудьте из графического режима отображения листинга переключиться в текстовый. Также обратите внимание на строчку
    .text:00406290 start endp ; spanalysis 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

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    #include <stdio.h>

    #include <stdlib.h>

    #include <Windows.h>

    void main()

    {

      OSVERSIONINFOEX osvi;

      ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));

      osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

      GetVersionEx((OSVERSIONINFO*)&osvi);

      int a;

      printf(«>OS Version:\t\t\t%d.%d\n\

      >Build:\t\t\t%d\n\

      >Arguments count:\t%d\n», \

        osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwOSVersionInfoSize, __argc);

      for (a = 0; a < __argc; a++)

        printf(«>\t Argument %02d:\t\t%s\n», a + 1, __argv[a]);

      a = !a 1;

      while (_environ[++a]);

        printf(«>Environment variables count:%d\n», a);

      while (a)

        printf(«>\tVariable %d:\t\t%s\n», a, _environ[a]);

    }

    Прототип функции
    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:\WINDOWS\Microsoft.NET\Framework\

    Variable 18: FrameworkDir=C:\WINDOWS\Microsoft.NET\Framework\

    Variable 17: Framework40Version=v4.0

    Variable 16: ExtensionSdkDir=C:\Program Files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs

    Variable 15: DriverData=C:\Windows\System32\Drivers\DriverData

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

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

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

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

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    //******************** 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 [ebp0C]

    :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 в состоянии работать с программами, сгенерированными современными компиляторами.

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    .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 ; spanalysis failed

    .text:00406290

    .text:00406295 ; [00000015 BYTES: COLLAPSED FUNCTION __EH_epilog3. PRESS CTRLNUMPAD+ TO EXPAND]

    .text:004062AA ; [00000011 BYTES: COLLAPSED FUNCTION __EH_epilog3_GS. PRESS CTRLNUMPAD+ TO EXPAND]

    .text:004062BB ; [00000034 BYTES: COLLAPSED FUNCTION __EH_prolog3. PRESS CTRLNUMPAD+ TO EXPAND]

    .text:004062EF ; [00000037 BYTES: COLLAPSED FUNCTION __EH_prolog3_GS. PRESS CTRLNUMPAD+ TO EXPAND]

    .text:00406326 ; [00000037 BYTES: COLLAPSED FUNCTION __EH_prolog3_catch. PRESS CTRLNUMPAD+ 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+19p

    .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 vc714, говорящую о том, что используемая версия IDA все же понимает компиляторы Visual C++ от 7 до 14.

    Текстовое отображение результата работы IDA

    Текстовое отображение результата работы IDA

    Попрубем разобраться в получившемся листинге. Первое и в данном случае единственное, что нам надо найти, — это функция
    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!\n. Смещение этой строки компилятор закинул на вершину стека, а затем ниже через строчку, по всей видимости, происходит вызов функции вывода на экран:

    .text:004010D3 push offset aHelloSailor ; «Hello, Sailor!\n»

    .text:004010D8 push offset unk_42DE30

    .text:004010DD call sub_401170

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

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

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

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

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

    .text:004010D3 push offset aHelloSailor ; «Hello, Sailor!\n»

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

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

    Что не так с IDA?

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

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

    Текстовое отображение результата работы IDA

    Текстовое отображение результата работы IDA

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

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

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

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

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    .text:004010D0 ; int __cdecl main(int argc, const char **argv, const char **envp)

    .text:004010D0 main proc near ; CODE XREF: start8Dp

    .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!\n»

    .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 в комментарии подставила прототип функции, а ниже параметры по умолчанию.

    Анализ зашифрованных программ в IDA

    Другое важное преимущество IDA — возможность дизассемблировать зашифрованные программы. В примере
    Crypt00.com используется статическое шифрование, которое часто встречается в «конвертных» защитах. Между тем этот файл не запустится в Windows 10, поскольку *.com для работы требует 16-разрядную исполняемую среду.

    Я думаю, это не повод отказаться от анализа столь интересного примера, тем более что существуют мощные средства виртуализации, поэтому поставить 32-битную Windows XP, которая выполняет 16-битные проги, — не проблема. К тому же анализ файлов .com значительно проще, чем .exe, так как первые сильно короче вторых.

    Мы уже видели непроходимые заросли библиотечного кода, вставленного компилятором в минимальной exe-программе, тогда как в com все по минимуму, в чем мы скоро убедимся. Отмечу также, что последней версией IDA Pro, работающей в 32-разрядных средах, была 6.8.

    Sourcer в деле

    Sourcer в деле

    Рассматриваемый прием шифрования полностью «ослепляет» большинство дизассемблеров. Например, результат обработки файла
    Crypt00.com при помощи Sourcer выглядит так:

    crypt00 proc far

    3A6A:0100 start:

    3A6A:0100 ъBE 010D mov si,10Dh ; (3A6A:010D=0C3h)

    3A6A:0103 loc_1: ; xref 3A6A:010B

    3A6A:0103 80 34 77 xor byte ptr [si],77h ; ‘w’

    3A6A:0106 46 inc si

    3A6A:0107 81 FE 0124 cmp si,124h

    3A6A:010B 76 F6 jbe loc_1 ; Jump if below or =

    3A6A:010D C3 retn

    3A6A:010E 7E CD 62 76 BA 56 db 7Eh,0CDh, 62h, 76h,0BAh, 56h

    3A6A:0114 B4 3F 12 1B 1B 18 db 0B4h, 3Fh, 12h, 1Bh, 1Bh, 18h

    3A6A:011A 5B 57 20 18 05 13 db 5Bh, 57h, 20h, 18h, 05h, 13h

    3A6A:0120 56 7A 7D 53 db 56h, 7Ah, 7Dh, 53h

    crypt00 endp

    Самостоятельно Sourcer не сумел дизассемблировать половину кода, оставив ее в виде дампа. С другой стороны, как-то помочь ему мы не можем. Напротив, IDA изначально проектировалась как дружественная к пользователю интерактивная среда. В отличие от Sourcer-подобных дизассемблеров, IDA не делает никаких молчаливых предположений и при возникновении проблем обращается за помощью к человеку. Результат анализа «Идой» файла
    Crypt00.com выглядит так:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    seg000:0100 public start

    seg000:0100 start proc near

    seg000:0100 mov si, 10Dh

    seg000:0103

    seg000:0103 loc_10103: ; CODE XREF: start+Bj

    seg000:0103 xor byte ptr [si], 77h

    seg000:0106 inc si

    seg000:0107 cmp si, 124h

    seg000:010B jbe short loc_10103

    seg000:010D retn

    seg000:010D start endp

    seg000:010D

    seg000:010D ; ——————————————

    seg000:010E db 7Eh, 0CDh, 62h, 76h, 0BAh, 56h, 0B4h, 3Fh, 12h, 2 dup(1Bh)

    seg000:010E db 18h, 5Bh, 57h, 20h, 18h, 5, 13h, 56h, 7Ah, 7Dh, 53h

    seg000:010E seg000 ends

    seg000:010E

    seg000:010E

    seg000:010E end start

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

    Данные

    Что представляет собой число
    10Dh в строке
    0x100 — константу или смещение? Очевидно, в регистр SI заносится смещение, потому что впоследствии операнд по этому смещению в памяти интерпретируется как байт и над ним выполняется операция XOR.

    Чтобы преобразовать константу в смещение, установите текстовый курсор на
    10Dh и нажмите O. Дизассемблируемый текст станет выглядеть так:

    seg000:0100 mov si, offset locret_1010D

    ...

    seg000:010D locret_1010D: ; DATA XREF: start+Bo

    seg000:010D retn

    seg000:010D start endp

    seg000:010D

    seg000:010D ; ——————————————

    seg000:010E db 7Eh, 0CDh, 62h, 76h, 0BAh, 56h, 0B4h, 3Fh, 12h, 2 dup(1Bh)

    seg000:010E db 18h, 5Bh, 57h, 20h, 18h, 5, 13h, 56h, 7Ah, 7Dh, 53h

    seg000:010E seg000 ends

    IDA Pro автоматически создала новое имя
    locret_1010D, которое ссылается на зашифрованный блок кода. Попробуем преобразовать его в данные. Для этого надо поставить курсор на строку 010D и дважды нажать D, чтобы утвердительно ответить на вопрос во всплывающем диалоге. Листинг примет следующий вид:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    ...

    seg000:010D word_1010D dw 7EC3h ; DATA XREF: starto

    seg000:010F db 0CDh ; =

    seg000:0110 db 62h ; b

    seg000:0111 db 76h ; v

    seg000:0112 db 0BAh ; ¦

    seg000:0113 db 56h ; V

    seg000:0114 db 0B4h ; +

    seg000:0115 db 3Fh ; ?

    seg000:0116 db 12h

    seg000:0117 db 1Bh

    seg000:0118 db 1Bh

    seg000:0119 db 18h

    seg000:011A db 5Bh ; [

    seg000:011B db 57h ; W

    seg000:011C db 20h

    seg000:011D db 18h

    seg000:011E db 5

    seg000:011F db 13h

    seg000:0120 db 56h ; V

    seg000:0121 db 7Ah ; z

    seg000:0122 db 7Dh ; }

    seg000:0123 db 53h ; S

    seg000:0123 seg000 ends

    Но на что именно указывает
    word_1010D? Понять это позволит изучение следующего кода:

    seg000:0100 start proc near

    seg000:0100 mov si, offset word_1010D

    seg000:0103

    seg000:0103 loc_10103: ; CODE XREF: start+Bj

    seg000:0103 xor byte ptr [si], 77h

    seg000:0106 inc si

    seg000:0107 cmp si, 124h

    seg000:010B jbe short loc_10103

    seg000:010B start endp

    После того как в регистр SI попадает смещение, начинается цикл, который представляет собой простейший расшифровщик: значение в регистре SI указывает на символ, команда XOR с помощью числа
    0x77 расшифровывает один байт (один ASCII-символ). Напомню, в ассемблере запись шестнадцатеричных чисел вида 77h. После этого инкрементируется значение регистра SI (указатель переводится на следующий символ) и получившееся значение сравнивается с числом
    0x124, которое равно общему количеству символов для расшифровки.

    РЕКОМЕНДУЕМ:
    Отладка MIPS с помощью GDB

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

    Давайте расширим сегмент подопытной программы. Жмем Shift-F7 (View → Open subviews → Segments), откроется вкладка Program Segmentation. В контекстном меню единственного существующего сегмента
    seg000 выбираем пункт Edit Segments (Ctrl-E). В диалоге в поле ввода End address введите значение побольше, например
    0x10125. Подтвердите свое намерение в появившемся диалоге.

    Изменение атрибутов сегмента

    Изменение атрибутов сегмента

    Можете полюбоваться на увеличившийся сегмент. Вернемся к изучению кода. Если в результате сравнения значение в регистре SI меньше общего количества байтов или равно ему, выполняется переход на метку
    loc_10103 и блок кода повторяется для расшифровки следующего байта. Отсюда можно заключить, что
    word_1010D указывает на начало последовательности байтов для расшифровки. Подведя к ней курсор, жмем N и можем дать ей осмысленное имя, например
    BeginCrypt. А константу
    124h можем сначала преобразовать в смещение (Ctrl-O), а затем переименовать, например в
    EndCrypt.

    Расшифровка

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

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

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

    Слева каждой строки указывается имя сегмента и его смещение, например
    seg000:0103. Но нам нужно другое значение. Установив текстовый курсор на нужную строку, смотрите на нижнюю часть текущей вкладки (полагаю, у вас это IDA View-A).

    Фактический номер строки

    Фактический номер строки

    При перемещении курсора соответствующее смещение тоже меняется (на рисунке выше оно обведено рамочкой). С его помощью можно обратиться к любой ячейке сегмента. Для чтения и модификации ячеек предусмотрены функции
    Byte и
    PatchByte соответственно. Их вызов может выглядеть, например, так:
    a=Byte(0x01010D) читает ячейку, расположенную по смещению
    0x01010D;
    PatchByte(0x01010D,0x27) присваивает значение
    0x27 ячейке памяти, расположенной по смещению
    0x01010D. Как следует из названия функций, они манипулируют ячейками размером в один байт.

    Знания языка C и этих двух функций вполне достаточно для написания скрипта-расшифровщика.

    Реализация IDA-С не полностью придерживается стандарта, в частности IDA не позволяет разработчику задавать тип переменной и определяет его автоматически по ее первому использованию, а объявление осуществляется ключевым словом
    auto. Например,
    auto MyVar, s0 объявляет две переменные —
    MyVar и
    s0.

    Для создания скрипта необходимо нажать Shift-F2 или выбрать в меню File пункт Script Command. В результате откроется окно Execute script. Большую его часть занимают список скриптов и поле ввода для редактирования выбранного скрипта.

    Дополнительно внизу окна находятся ниспадающий список для выбора используемого языка (IDC или Python), ниспадающий список для указания размера табуляции и четыре кнопки: Run (выполнить скрипт), Export (экспортировать скрипт в файл), Import (загрузить скрипт из файла) и Save (сохранить скрипт в базу данных проекта).

    После первого открытия окна в списке скриптов по умолчанию выбран скрипт Default snippet. В качестве его тела введем такой код:

    auto a, x;

    for (a = 0x01010D; a <= 0x010123; a++) {

      x = Byte(a);

      x = (x ^ 0x77);

      PatchByte(a, x);

      Message(x);

    }

    Встроенный редактор скриптов

    Встроенный редактор скриптов

    Как было показано выше, алгоритм расшифровщика сводится к последовательному преобразованию каждой ячейки зашифрованного фрагмента операцией
    XOR 0x77:

    seg000:0103 xor byte ptr [si], 77h

    Сам же зашифрованный фрагмент начинается с адреса
    0x01010D и продолжается вплоть до
    0x010123.

    В конце командой
    Message отправляем модифицированный символ в область вывода IDA.

    Чтобы запустить скрипт на выполнение, достаточно нажать кнопку Run. Если при вводе скрипта не было опечаток, листинг примет следующий вид:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    ...

    seg000:010D BeginCrypt dw 9B4h ; DATA XREF: starto

    seg000:010F db 0BAh ; ¦

    seg000:0110 db 15h

    seg000:0111 db 1

    seg000:0112 db 0CDh ; =

    seg000:0113 db 21h ; !

    seg000:0114 db 0C3h ; +

    seg000:0115 db 48h ; H

    seg000:0116 db 65h ; e

    seg000:0117 db 6Ch ; l

    seg000:0118 db 6Ch ; l

    seg000:0119 db 6Fh ; o

    seg000:011A db 2Ch ; ,

    seg000:011B db 20h

    seg000:011C db 57h ; W

    seg000:011D db 6Fh ; o

    seg000:011E db 72h ; r

    seg000:011F db 64h ; d

    seg000:0120 db 21h ; !

    seg000:0121 db 0Dh

    seg000:0122 db 0Ah

    seg000:0123 db 24h ; $

    seg000:0124 EndCrypt db ? ; DATA XREF: start+7o

    seg000:0124 seg000 ends

    seg000:0124

    seg000:0124

    seg000:0124 end start

    А в окне вывода появится надпись

    Возможные ошибки: несоблюдение регистра символов (IDA к этому чувствительна), синтаксические ошибки, неверно заданные адреса границ модифицируемого блока. В случае ошибки необходимо подвести курсор к строке
    seg000:010D, нажать клавишу U (для удаления результатов предыдущего дизассемблирования зашифрованного фрагмента) и затем C (для повторного дизассемблирования расшифрованного кода).

    Символы перед фразой «Hello, World!» не выглядят читаемыми; скорее всего, это не ASCII, а исполняемый код. Поставим курсор на строку
    seg000:010D, жмем C («Преобразовать в инструкцию»). В результате листинг будет выглядеть так:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    ...

    seg000:010D BeginCrypt: ; DATA XREF: starto

    seg000:010D mov ah, 9

    seg000:010F mov dx, 115h

    seg000:0112 int 21h ; DOS PRINT STRING

    seg000:0112 ; DS:DX > string terminated by «$»

    seg000:0114 retn

    seg000:0114 ; —————————————

    seg000:0115 db 48h ; H

    seg000:0116 db 65h ; e

    seg000:0117 db 6Ch ; l

    seg000:0118 db 6Ch ; l

    seg000:0119 db 6Fh ; o

    seg000:011A db 2Ch ; ,

    seg000:011B db 20h

    seg000:011C db 57h ; W

    seg000:011D db 6Fh ; o

    seg000:011E db 72h ; r

    seg000:011F db 64h ; d

    seg000:0120 db 21h ; !

    seg000:0121 db 0Dh

    seg000:0122 db 0Ah

    seg000:0123 db 24h ; $

    seg000:0124 EndCrypt db ? ; DATA XREF: start+7o

    seg000:0124 seg000 ends

    seg000:0124

    seg000:0124

    seg000:0124 end start

    Цепочку символов, расположенную начиная с адреса
    seg000:0115, можно преобразовать в удобочитаемый вид, если навести на нее курсор и нажать A. Еще можно преобразовать константу
    115h в строке 010F в смещение. Теперь экран дизассемблера будет выглядеть так:

    ...

    seg000:010D BeginCrypt: ; DATA XREF: starto

    seg000:010D mov ah, 9

    seg000:010F mov dx, offset aHelloWord ; «Hello, Word!\r\n$»

    seg000:0112 int 21h ; DOS PRINT STRING

    seg000:0112 ; DS:DX > string terminated by «$»

    seg000:0114 retn

    seg000:0114 ; —————————————

    seg000:0115 aHelloWord db ‘Hello, Word!’,0Dh,0Ah,‘$’ ; DATA XREF: seg000:010Fo

    seg000:0124 EndCrypt db ? ; DATA XREF: start+7o

    seg000:0124 seg000 ends

    seg000:0124

    seg000:0124

    seg000:0124 end start

    Команда
    MOV AH, 9 в строке
    seg000:010D подготавливает регистр AH перед вызовом прерывания
    0x21. Она выбирает функцию вывода строки на экран, а ее смещение следующей командой заносится в регистр DX. Иными словами, для успешного ассемблирования листинга необходимо заменить константу
    0x115 соответствующим смещением.

    Но ведь выводимая строка на этапе ассемблирования (до перемещения кода) расположена совсем в другом месте! Одно из возможных решений этой проблемы — создать новый сегмент и затем скопировать в него расшифрованный код. Это будет аналогом перемещения кода работающей программы.

    Создание сегмента

    Для создания нового сегмента надо открыть вкладку Segments (Shift-F7) и нажать Insert. Появится вот такое окно.

    Использование IDA Pro Создание нового сегмента

    Создание нового сегмента

    Базовый адрес сегмента может быть любым, если при этом не перекрываются сегменты
    seg000 и
    MySeg; начальный адрес сегмента задается так, чтобы смещение первого байта было равно
    0x100; размер нового сегмента сделаем таким же, как
    seg000. Не забудьте выбрать тип создаваемого сегмента: 16-битный сегмент.

    Далее будем двигаться поэтапно. Сначала скопируем команды для вывода символов в консоль. Начнем брать байты со смещения
    10D сегмента
    seg000, а вставлять — с самого начала сегмента
    MySeg. Это можно сделать скриптом следующего содержания:

    auto a, x;

    for (a = 0x0; a < 0x8; a++) {

      x = Byte(0x1010D+a);

      PatchByte(0x20100+a,x);

    }

    Для его ввода необходимо вновь нажать комбинацию клавиш Shift-F2. Создать еще один скрипт можно нажатием Insert. После выполнения экран дизассемблера будет выглядеть так (показано только начало сегмента
    MySeg):

    MySeg:0100 ; Segment type: Regular

    MySeg:0100 MySeg segment byte public » use16

    MySeg:0100 assume cs:MySeg

    MySeg:0100 ;org 100h

    MySeg:0100 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing

    MySeg:0100 db 0B4h ; +

    MySeg:0101 db 9

    MySeg:0102 db 0BAh ; ¦

    MySeg:0103 db 15h

    MySeg:0104 db 1

    MySeg:0105 db 0CDh ; =

    MySeg:0106 db 21h ; !

    MySeg:0107 db 0C3h ; +

    MySeg:0108 db ? ;

    Надо преобразовать данные в инструкции: поставить курсор на строку
    MySeg:0100 и нажать C. Листинг примет ожидаемый вид:

    MySeg:0100 mov ah, 9

    MySeg:0102 mov dx, 115h

    MySeg:0105 int 21h ; DOS PRINT STRING

    MySeg:0105 ; DS:DX > string terminated by «$»

    MySeg:0107 retn

    Чтобы программа-клон вела себя немного не так, как ее родитель, добавим ожидание ввода символа. Для этого надо поставить курсор на команду
    retn и выбрать Edit → Patch program → Assemble…

    Введите
    XOR AX, AX, нажмите Enter. Затем
    INT 16h, снова Enter. Последняя инструкция — RET, Enter и Esc для закрытия диалога.

    Использование IDA Pro Замена инструкции

    Замена инструкции

    Теперь с помощью следующего скрипта скопируем байты, составляющие текст «Hello, World!»:

    auto a, x, i;

    i = 0;

    for (a = 0x0115; a < 0x124; a++) {

      x = Byte(0x10000+a);

      PatchByte(0x2010C+i,x);

      i++;

    }

    Поставив курсор на строку
    MySeg:010C, нажимаем A и преобразуем цепочку символов в удобочитаемый вид. В строке
    MySeg:0102 надо изменить константу
    115h на фактическое значение, по которому расположена фраза для вывода:
    MySeg:010C. Для этого ставьте курсор на указанную строку и открывайте диалог Assemble Instruction (Edit → Patch program → Assemble…) и введите
    MOV DX, 10Ch.

    РЕКОМЕНДУЕМ:
    Режим гаммирования в блочном алгоритме шифрования

    Теперь надо преобразовать константу
    10Ch в смещение, а последовательность символов, расположенную по нему, обратить к светскому виду. Как это делать, вы уже знаете. Напоследок рекомендую произвести косметическую чистку — уменьшить размер сегмента до необходимого. Чтобы удалить адреса, оставшиеся при уменьшении размеров сегмента за его концом, поставьте флажок Disable Address в окне свойств сегмента.

    Казалось бы, можно уже начать ассемблирование, однако не стоит спешить! У нас в программе есть три смещения, которые являются указателями. В дизассемблированном листинге они выглядят прекрасно:

    1. mov     si, offset BeginCrypt в сегменте
      seg000;
    2. cmp     si, offset EndCrypt в сегменте
      seg000;
    3. mov     dx, offset aHelloWord_0 в сегменте
      MySeg.

    Между тем после ассемблирования символьные имена будут заменены числовыми константами. Будут ли в таком случае в результирующей программе эти смещения указывать на те же вещи, что в исходной? Давайте еще раз проанализируем наш листинг. Первое смещение,
    BeginCrypt, указывает на строку
    seg000:010D. По сути, весь предыдущий код будет скопирован, поэтому ее значение менять не нужно. Второе смещение,
    EndCrypt, указывающее на конец сегмента, должно увеличиться на четыре байта, так как мы добавили две инструкции:

    seg000:0114 xor ax, ax

    seg000:0116 int 16h

    Чтобы подсчитать их размер, достаточно из следующего за ними смещения вычесть их начальное: 118h – 114h = 4h байта. В результате
    EndCrypt должно указывать на 124h + 4h = 128h. Установите курсор на строку
    seg000:0107, вызовите окно ассемблера и замените инструкцию в ней на
    cmp si, 128h.

    Третье смещение,
    aHelloWord_0, в исходной программе равно
    10Ch. Подумаем, как должен измениться адрес. Если
    aHelloWord_0 находится в сегменте
    MySeg, то нам просто нужно прибавить к имеющемуся смещению размер расшифровщика в сегменте
    seg000. Его можно посчитать как разность начального смещения зашифрованного блока и начального адреса: 0x010D – 0x0100 = 0xD байт.

    В итоге смещение
    aHelloWord_0 должно указывать на 0x10C + 0xD = 0x119. Изменим код: установив курсор на строку
    MySeg:0102, с помощью встроенного ассемблера модифицируем ее содержимое на
    mov dx, 119h.

    В результате полный листинг выглядит следующим образом:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    seg000:0100 ; Segment type: Pure code

    seg000:0100 seg000 segment byte public ‘CODE’ use16

    seg000:0100 assume cs:seg000

    seg000:0100 org 100h

    seg000:0100 assume es:nothing, ss:nothing, ds:seg000, fs:nothing, gs:nothing

    seg000:0100

    seg000:0100 ; =========== S U B R O U T I N E ================

    seg000:0100

    seg000:0100

    seg000:0100 public start

    seg000:0100 start proc near

    seg000:0100 mov si, offset BeginCrypt

    seg000:0103

    seg000:0103 loc_10103: ; CODE XREF: start+Bj

    seg000:0103 xor byte ptr [si], 77h

    seg000:0106 inc si

    seg000:0107 cmp si, 128h

    seg000:010B jbe short loc_10103

    seg000:010B start endp

    seg000:010B

    seg000:010D

    seg000:010D BeginCrypt: ; DATA XREF: starto

    seg000:010D mov ah, 9

    seg000:010F mov dx, offset aHelloWord ; «Hello, Word!\r\n$»

    seg000:0112 int 21h ; DOS PRINT STRING

    seg000:0112 ; DS:DX > string terminated by «$»

    seg000:0114 retn

    seg000:0114 ; ————————————————

    seg000:0115 aHelloWord db ‘Hello, Word!’,0Dh,0Ah,‘$’ ; DATA XREF: seg000:010Fo

    seg000:0124 EndCrypt db ?

    seg000:0124 seg000 ends

    seg000:0124

    MySeg:0100 ; ————————————————

    MySeg:0100

    ; ===========================================================

    MySeg:0100

    MySeg:0100 ; Segment type: Regular

    MySeg:0100 MySeg segment byte public » use16

    MySeg:0100 assume cs:MySeg

    MySeg:0100 ;org 100h

    MySeg:0100 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing

    MySeg:0100 mov ah, 9

    MySeg:0102 mov dx, 119h

    MySeg:0105 int 21h ; DOS PRINT STRING

    MySeg:0105 ; DS:DX > string terminated by «$»

    MySeg:0107 xor ax, ax

    MySeg:0109 int 16h ; KEYBOARD READ CHAR FROM BUFFER, WAIT IF EMPTY

    MySeg:0109 ; Return: AH = scan code, AL = character

    MySeg:010B retn

    MySeg:010B ; ————————————————

    MySeg:010C aHelloWord_0 db ‘Hello, Word!’,0Dh,0Ah,‘$’

    MySeg:011B db ? ;

    MySeg:011B MySeg ends

    MySeg:011B

    MySeg:011B

    MySeg:011B end start

    Создание клона

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

    Для решения этой задачи напишем скрипт, автоматически выполняющий копирование и шифрование необходимых частей программы:

    auto a, f, x;

    // Открывается файл crypt01.com для записи в двоичном режиме

    f = fopen(«crypt01.com», «wb»);

    // Копируется расшифровщик

    for (a = 0x10100; a < 0x1010D; a++) {

      x = Byte(a);

      fputc(x, f);

    }

    // Копируется и на лету шифруется весь сегмент MySeg

    for (a = SegStart(0x20100); a != SegEnd(0x20100); a++) {

      x = Byte(a);

      x = (x ^ 0x77);

      fputc(x, f);

    }

    // Файл закрывается

    fclose(f);

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

    Итоги

    Выполнение скрипта приведет к созданию файла
    crypt01.com, запустив который можно убедиться в его работоспособности — он выводит строку на экран и, дождавшись нажатия любой клавиши, завершает работу.

    Использование IDA Клонированное приложение

    Клонированное приложение

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

    Дизассемблер и отладчик в связке

    Дизассемблер, включенный в отладчик, обычно слишком примитивен и не может похвастаться богатыми возможностями. Во всяком случае, дизассемблер, встроенный в WinDbg, недалеко ушел от DUMPBIN, с недостатками которого мы уже сталкивались. Насколько же понятнее становится код, если его загрузить в IDA!

    Чем же тогда ценен отладчик? Дело в том, что дизассемблер из-за своей статичности имеет ряд ограничений. Во-первых, исследователю приходится выполнять программу на «эмуляторе» процессора, «зашитом» в его собственной голове, следовательно, необходимо знать и назначение всех команд процессора, и всех структур операционной системы (включая недокументированные).

    Во-вторых, начать анализ с произвольного места программы не так-то просто — требуется знать содержимое регистров и ячеек памяти на текущий момент, а как их узнать? С регистрами и локальными переменными еще не так плохо — достаточно покрутить экран дизассемблера вверх и посмотреть, какие значения им присваиваются. Но этот фокус не пройдет с глобальными переменными, модифицировать которые может кто угодно и когда угодно. Вот бы установить точку останова… но какая же в дизассемблере может быть точка останова?

    РЕКОМЕНДУЕМ:
    Распаковка исполняемых файлов на примере банковского трояна GootKit

    В-третьих, дизассемблирование вынуждает реконструировать алгоритм каждой функции, в то время как отладка позволяет рассматривать ее как «черный ящик» со входом и выходом. Допустим, имеется у нас функция, которая расшифровывает основной модуль программы. В дизассемблере нам придется сначала разобраться в алгоритме шифрования (что может оказаться совсем не просто), затем переложить эту функцию на язык IDA-С, отладить ее, запустить расшифровщик… В отладчике же можно поручить выполнение этой функции процессору, не вникая в то, как она работает, и, дождавшись ее завершения, продолжить анализ уже расшифрованного модуля программы.

    Можно перечислять бесконечно, но и без того ясно, что отладчик отнюдь не конкурент дизассемблеру, а, напротив, партнер. Опытные хакеры всегда используют эти инструменты в паре. Алгоритм реконструируется в дизассемблере, а все непонятные моменты оперативно уточняются прогоном программы под отладчиком.

    При этом возникает естественное желание видеть в отладчике все те символьные имена, которые были внесены в дизассемблерный листинг. И IDA Pro предоставляет два способа это сделать! Рассмотрим оба.

    Способ 1

    Вернемся в Windows 10 и загрузим в IDA Pro файл
    first.exe (или ранее созданный «Идой» проект). Выберем в меню File подменю Produce file, а в нем — пункт Create MAP file. На экране появится окно с запросом имени файла (введем, например,
    first.map), а затем откроется модальный диалог, уточняющий, какие имена стоит включать в map-файл. Нажмем Enter, чтобы оставить все галочки в состоянии по умолчанию.

    Мгновение спустя на диске образуется файл
    first.map, содержащий всю необходимую отладочную информацию в map-формате Borland. Отладчик WinDbg не поддерживает такой формат, поэтому перед его использованием файл необходимо конвертировать в формат DBG — отладочный формат Microsoft.

    Конвертировать можно с помощью утилиты map2dbg, свободно распространяемой вместе с исходными кодами. Запускать ее нужно из командной строки. В один каталог с ней кладем map-файл и соответствующий .exe. Затем в нашем случае выполняем команду
    map2dbg first.exe.

    В результате утилита выведет число преобразованных символов, а в текущей папке будет создан новый файл с расширением dbg. Теперь можно загрузить
    first.exe в WinDbg. При этом, если
    first.dbg находится в том же каталоге, файл подхватится автоматически и будет скопирован в системную папку
    C:\ProgramData\dbg\sym\first.dbg\ для дальнейшего исследования экзешника.

    Сейчас в WinDbg надо выполнить команду
    .reload /f. Она заставит отладчик перезагрузить информацию из модулей. Затем можете выполнить
    lm, чтобы увидеть список загруженных модулей. Модуль first будет отмечен как codeview symbols, иначе было бы deferred:

    00400000 0041d000 first C (codeview symbols) C:\ProgramData\Dbg\sym\first.dbg\5D5D59DE1d000\first.dbg

    Исполнение команды
    x first!* выведет все символы в файле
    first.exe (показана только малая часть списка):

    ...

    004028c4 first!std::_String_const_iterator<char,std::char_traits,std::allocator >::_String_const_iterator<char,std::char_traits,std::allocator > =

    00402913 first!std::basic_streambuf<char,std::char_traits >::_Xsgetn_s =

    0040298e first!std::basic_streambuf<char,std::char_traits >::xsputn =

    00402a57 first!std::basic_filebuf<char,std::char_traits >::_Init =

    00402a9e first!std::basic_string<char,std::char_traits,std::allocator >::basic_string<char,std::char_traits,std::allocator > =

    00402adb first!std::_Fgetc =

    00402af6 first!std::_Fputc =

    00402b12 first!std::_Ungetc =

    00402b30 first!std::basic_filebuf<char,std::char_traits >::sync =

    00402b5b first!std::basic_filebuf<char,std::char_traits >::pbackfail =

    00402bc5 first!std::basic_filebuf<char,std::char_traits >::underflow =

    00402c29 first!std::basic_filebuf<char,std::char_traits >::setbuf =

    00402c70 first!std::basic_string<char,std::char_traits,std::allocator >::begin =

    00402c90 first!std::_Locinfo::~_Locinfo =

    ...

    Посмотрим содержимое системной переменной:

    0:000> da first!aSouthAfrica

    00415500 «south-africa»

    Способ 2

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

    У бесплатной версии IDA Pro есть и другие недостатки: некорректное создание выходных файлов (в том числе map-файла), некорректное построение диаграммы и прочие пока не затронутые нами вещи.

    Теперь можно подключить WinDbg к IDA. Для этого надо открыть конфигурационный файл
    ida.cfg, находящийся в каталоге
    С:\Program Files\IDA 7.0\cfg\. Проматываем его содержимое до такой строки:

    //DBGTOOLS = «C:\\Program Files\\Debugging Tools for Windows (x86)\\»;

    Ниже или вместо нее вставить путь к инструментам отладки WinDbg. В моем случае:

    DBGTOOLS = «C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\»;

    Отсюда вытекает третья мудрость: в случае с IDA 7.0 и новее, даже если приложение дизассемблируется и отлаживается в 32-битной разновидности IDA, путь надо указывать к 64-битной WinDbg. Иначе при запуске отладки вас ждет сообщение об ошибке.

    Следующим шагом надо запустить IDA, выбрать WinDbg из ниспадающего списка на панели задач и, нажав F9, запустить отладку. Только отладка приложения
    first.exe закончится, не успев начаться. Нужно сделать так, чтобы на точке входа программа замерла. Для этого надо вызвать диалоговое окно Debugger setup (пункт Debugger options в меню Debugger).

    IDA Pro как пользоваться Параметры отладчика

    Параметры отладчика

    Нашу задачу призвана решить область Events. Здесь можно выбрать, на каких событиях мы хотим подвешивать программу. Поставим третий флажок Suspend on process entry point, в результате чего выполнение проги приостановится на стартовом коде.

    IDA Pro как пользоваться. Выполнение программы было приостановлено на точке входа

    Выполнение программы было приостановлено на точке входа

    Знакомые места? Еще бы! Обратите внимание: программа оперирует регистрами процессорной архитектуры x86-64: RCX, RDX, RCI и так далее. Ядро Windows 10 экспортирует 1595 символов, учитывая все установленные обновления операционной системы на моем компьютере.

    Это можно проверить, дважды щелкнув на модуле
    kernel32.dll в окне Modules во время отладки IDA Pro. Откроется дополнительная вкладка Module: KERNEL32.DLL. Ее можно отцепить и перетащить в любое место. На нижней части рамки окна отображается общее количество символов, экспортируемых данным модулем.

    Подключение WinDbg к IDA позволяет «Иде» использовать символьную информацию модулей с публичного сервера Microsoft. Для этого можно создать переменную окружения или определить директорию непосредственно из IDA без ее перезапуска. Пойдем второй, более короткой дорогой, а создать переменную окружения вы можете на досуге. В командную строку IDA (в нижней части окна рядом с кнопкой WINDBG) введи:

    .sympath srv*c:\debugSymbols*http://msdl.microsoft.com/download/symbols

    После этого перезагрузите символы командой
    .reload /f. Число экспортируемых модулем
    kernel32.dll символов стало 5568.

    Теперь символьные имена не только отображаются на экране, что упрощает понимание кода, — на любое из них можно быстро и с комфортом установить точку останова (скажем,
    bp GetProcAddress), и отладчик поймет, что от него хотят! Нет больше нужды держать в памяти эти трудно запоминаемые шестнадцатеричные адреса!

    РЕКОМЕНДУЕМ:
    Взлом приложений для Андроид с помощью отладчика

    Заключение

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

    Понравилась статья? Поделить с друзьями:
  • Иммобилайзер включен смотри инструкцию форд транзит что делать
  • Мелатонин инструкция по применению цена аналоги дешевле
  • Укладка линолеума на осб плиту своими руками пошаговая инструкция
  • Фенилэфрин мазь от геморроя инструкция по применению взрослым
  • Дюфастон инструкция по применению при планировании беременности как принимать