Реверс-инжиниринг прошивки устройства на примере мигающего «носорога». часть 1

Реальная задача

И все-таки. Посмотреть список функций, вообще говоря, можно и стандартными средствами IDA. Так что мы всего лишь изобрели велосипед (хотя и познакомились с основными понятиями IDC и IDAPython). Но попробуем все-таки решить реально полезную задачу.
Как известно, IDA является основным инструментом вирусных аналитиков, используемым для статического анализа отловленной малвари. Малварь же, в свою очередь, стремится как можно сильнее усложнить процедуру исследования. Очень часто при исследовании вредоносных программ обнаруживается, что приложение не импортирует никаких функций. Или, например, заменяет имена API-вызовов хэшами, определяя адрес нужной функции путем поиска в таблице экспорта, вычисляя хэш для каждого имени и сравнивая с желаемым. Рассмотрим этот прием подробнее.

Программа из PEB (Process Environment Block) получает адрес библиотеки kernel32.dll и в ее таблице экспорта ищет по хэшу адреса LoadLibrary и GetProcAddress. С помощью этих двух вызовов можно подгружать остальные библиотеки и искать в них адреса нужных функций. Вообще функция GetProcAddress даже не нужна, так как, зная адрес библиотеки, можно самостоятельно получить адрес нужной функции из таблицы экспорта. Это популярный механизм, который нередко используется в малвари. Итак, вырисовывается вполне понятная задача — написать скрипт, который автоматизировал бы поиск названий функций и упростил таким образом жизнь реверсера.

Чтобы разговор был предметным, напишем небольшое подопытное приложение, которое будет использовать вышеперечисленные техники маскировки. Весь функционал приложения будет сводиться к выводу сообщения «Hello, world!» при помощи функции MessageBox. Для этого нам потребуется всего три типа вызовов: GetKernelAddress() для получения адреса kernel32.dll, CalcHash() для вычисления хэша по имени функции, а также GetProcAddressEx() для получения адреса функции по хэшу.

Полный исходный код тестового приложения ты найдешь на диске:

После компиляции код будет выглядеть примерно так:

Как видишь, никаких вызовов API-функций в чистом виде. Обращение к MessageBox превратилось в call (по адресу 0040104C). Причем в случае настоящей малвари были бы зашифрованы еще и строки. На практике такой простой прием может значительно увеличить время анализа приложения. Как быть? Предположим, мы восстановили алгоритм работы программы и поняли, что значения, которые кладутся в стек по адресам 0040100E и 0040102D — это хэши функций. Но как узнать, что за функции скрываются за ними? В нашем текстовом образце — всего несколько вызовов, поэтому нет большой проблемы просто запустить отладчик и посмотреть, куда приведет нас call. Но что делать, когда таких вызовов сотни? Вот тут-то и понадобятся возможности IDAPython.

Parameters

The following options are available for .exe, .dll, .obj, .lib, and .winmd files.

Option Description
/out= Creates an output file with the specified , rather than displaying the results in a graphical user interface.
/rtf Produces output in rich text format. Invalid with the /text option.
/text Displays the results to the console window, rather than in a graphical user interface or as an output file.
/html Produces output in HTML format. Valid with the /output option only.
/? Displays the command syntax and options for the tool.

The following additional options are available for .exe, .dll, and .winmd files.

Option Description
/bytes Shows actual bytes, in hexadecimal format, as instruction comments.
/caverbal Produces custom attribute blobs in verbal form. The default is binary form.
/linenum Includes references to original source lines.
/nobar Suppresses the disassembly progress indicator pop-up window.
/noca Suppresses the output of custom attributes.
/project Displays metadata the way it appears to managed code, instead of the way it appears in the native Windows Runtime. If is not a Windows metadata (.winmd) file, this option has no effect. See .NET Framework Support for Windows Store Apps and Windows Runtime.
/pubonly Disassembles only public types and members. Equivalent to /visibility:PUB.
/quoteallnames Includes all names in single quotes.
/raweh Shows exception handling clauses in raw form.
/source Shows original source lines as comments.
/tokens Shows metadata tokens of classes and members.
/visibility: Disassembles only types or members with the specified visibility. The following are valid values for :PUB — PublicPRI — PrivateFAM — FamilyASM — AssemblyFAA — Family and AssemblyFOA — Family or AssemblyPSC — Private Scope For definitions of these visibility modifiers, see MethodAttributes and TypeAttributes.

The following options are valid for .exe, .dll, and .winmd files for file or console output only.

Option Description
/all Specifies a combination of the /header, /bytes, /stats, /classlist, and /tokens options.
/classlist Includes a list of classes defined in the module.
/forward Uses forward class declaration.
/headers Includes file header information in the output.
/item: ] Disassembles the following depending upon the argument supplied: — Disassembles the specified .- Disassembles the specified of the .- Disassembles the of the with the specified signature . The format of is: [] (, , …, )Note In the .NET Framework versions 1.0 and 1.1, must be followed by a closing parenthesis: . Starting with the Net Framework 2.0 the closing parenthesis must be omitted: .
/noil Suppresses IL assembly code output.
/stats Includes statistics on the image.
/typelist Produces the full list of types, to preserve type ordering in a round trip.
/unicode Uses Unicode encoding for the output.
/utf8 Uses UTF-8 encoding for the output. ANSI is the default.

The following options are valid for .exe, .dll, .obj, .lib, and .winmd files for file or console output only.

Option Description
/metadata Shows metadata, where is:MDHEADER — Show the metadata header information and sizes.HEX — Show information in hex as well as in words.CSV — Show the record counts and heap sizes.UNREX — Show unresolved externals.SCHEMA — Show the metadata header and schema information.RAW — Show the raw metadata tables.HEAPS — Show the raw heaps.VALIDATE — Validate the consistency of the metadata. You can specify /metadata multiple times, with different values for .

The following options are valid for .lib files for file or console output only.

Option Description
/objectfile= Shows the metadata of a single object file in the specified library.

Note

All options for Ildasm.exe are case-insensitive and recognized by the first three letters. For example, /quo is equivalent to /quoteallnames. Options that specify arguments accept either a colon (:) or an equal sign (=) as the separator between the option and the argument. For example, /output: filename is equivalent to /output= filename.

IDA Pro is a debugger

In software analysis – as in real life – things are rarely simple and obvious. Hostile code usually does not cooperate with the analyst. Creators of viruses, worms and trojans often write their code in an obfuscated way, making it extremely hard to read and analyse. More powerful tools are thus required to efficiently help analysts. The debugger in IDA Pro complements the static analysis capabilities (examining the code without executing the program) of the disassembler by allowing users to single step through the code being investigated; this way, the debugger often bypasses the obfuscation and helps obtain data that the more powerful static disassembler will be able to process in depth.

IDA Pro runs on Windows, Linux and Mac OS X and can debug a large array of specific platforms (Windows 32/64-bit, Linux 32/64-bit, OS X x86/x64, iOS, Android, etc.). This can be carried out either locally or remotely. Remote debuggers are very useful to safely dissect potentially harmful programs. Some IDA debuggers can also run the application in a virtual environment makinges malware analysis even safer.

Делимся кодом

Если редактировать код вместе не нужно, а нужно поделиться исходниками, какими-нибудь конфигами, XML-ками, дампами или просто текстовыми файлами, пригодится инструмент, о котором мы недавно говорили в WWW2 — Pastie (pastie.org). Все, что требуется — это скопипастить в форму текст и указать его тип (скажем, исходник на C++ или Python). Сервис выдаст короткий линк, перейдя по которому, любой увидит исходный текст с красиво подсвеченным синтаксисом. Всего сервис поддерживает 38 различных языков программирования, файлов-конфигураций и вывода некоторых программ. Во время просмотра можно изменить тему для отображения так, чтобы было максимально похоже на привычную среду разработки. Сам проект написан на Ruby, причем правила для подсветки синтаксиса были позаимствованы у TextMate. Помимо Pastie есть еще один интересный сервис, нацеленный на обмен сниппетами. В публичном репозитории кода Snipplr хранится огромное количество исходников и сниппетов на разные темы. Обязательное требование к заметке — четкое описание и теги, поэтому в репозитории очень легко найти нужные участки кода.

ПримерыExamples

Следующая команда выводит метаданные и дизассемблированный код PE-файла в стандартный графический пользовательский интерфейс программы Ildasm.exe.The following command causes the metadata and disassembled code for the PE file to display in the Ildasm.exe default GUI.

Следующая команда дизассемблирует файл и сохраняет выходной текст ассемблера IL в файле MyFile.il.The following command disassembles the file and stores the resulting IL Assembler text in the file MyFile.il.

Следующая команда дизассемблирует файл и выводит выходной текст ассемблера IL в окно консоли.The following command disassembles the file and displays the resulting IL Assembler text to the console window.

Если файл содержит внедренные управляемые и неуправляемые ресурсы, при выполнении следующей команды будет создано четыре файла: MyApp.il, MyApp.res, Icons.resources и Message.resources.If the file contains embedded managed and unmanaged resources, the following command produces four files: MyApp.il, MyApp.res, Icons.resources, and Message.resources:

Следующая команда дизассемблирует метод класса в файле и выводит результат в окно консоли.The following command disassembles the method within the class in and displays the output to the console window.

В предыдущем примере допустимо наличие нескольких методов с именем и различными сигнатурами.In the previous example, there could be several methods named with different signatures. Следующая команда дизассемблирует метод экземпляра с типом возвращаемого значения void и типами параметров int32 и string.The following command disassembles the instance method with the return type of void and the parameter types int32 and string.

Примечание

В .NET Framework версии 1.0 и 1.1 открывающей скобке, которая следует за именем метода, должна соответствовать закрывающая скобка после сигнатуры: .In the .NET Framework versions 1.0 and 1.1, the left parenthesis that follows the method name must be balanced by a right parenthesis after the signature: . В .NET Framework 2.0 и более поздних версий закрывающая скобка должна быть опущена: .Starting with the .NET Framework 2.0 the closing parenthesis must be omitted: .

Чтобы извлечь метод (метод в Visual Basic), следует опустить ключевое слово .To retrieve a method ( method in Visual Basic), omit the keyword . Типы классов, которые не являются простыми типами (такими как и ), должны включать пространство имен и перед ними необходимо указывать ключевое слово .Class types that are not primitive types like and must include the namespace and must be preceded by the keyword . Перед внешними типами должно быть указано имя соответствующей библиотеки в квадратных скобках.External types must be preceded by the library name in square brackets. Следующая команда дизассемблирует статический метод с именем , имеющий один параметр типа AppDomain, и возвращает значение типа AppDomain.The following command disassembles a static method named that has one parameter of type AppDomain and has a return type of AppDomain.

Перед вложенным типом необходимо указывать содержащий его класс, отделенный косой чертой (/).A nested type must be preceded by its containing class, delimited by a forward slash. Например, если класс содержит вложенный класс с именем , вложенный класс указывается следующим образом: .For example, if the class contains a nested class named , the nested class is identified as follows: .

Disassemblers and emulators

A dynamic disassembler can be incorporated into the output of an emulator or hypervisor to ‘trace out’, line-by-line, the real time execution of any executed machine instructions. In this case, as well as lines containing the disassembled machine code, the register(s) and/or data change(s) (or any other changes of «state», such as condition codes) that each individual instruction causes can be shown alongside or beneath the disassembled instruction. This provides extremely powerful debugging information for ultimate problem resolution, although the size of the resultant output can sometimes be quite large, especially if active for an entire program’s execution. OLIVER provided these features from the early 1970s as part of its CICS debugging product offering and is now to be found incorporated into the XPEDITER product from Compuware.

Ассемблерные инструкции, подходящие на роль «склеивающих инструкций»

Список инструкций, подходящих на роль склеивающей инструкции, уникален для каждой конкретной маскирующей инструкции. Ниже представлен список (сгенерированный по алгоритму, представленному на нижеследующем рисунке) на примере 9-байтового NOP’а.

Формируя этот список, мы учитывали только те варианты, при которых ZZ занимает 1 байт (иначе на скрытый код мало места останется). Вот список подходящих склеивающих инструкций, для 9-байтового NOP’а.

Среди этого списка инструкций нет ни одной, которая была бы свободна от побочных действий. Каждая из них меняет либо EFLAGS, либо регистры общего назначения, либо и то и другое сразу. Этот список разделён на 4 категории, – в соответствии с тем, какое побочное действие оказывает инструкция.

В первую категорию включены инструкции, которые изменяют регистр EFLAGS, но при этом не меняют регистры общего назначения. Инструкциями из этой категории можно пользоваться, когда в цепочке скрытых инструкций нет условных переходов и каких-либо инструкций, действие которых основано на оценке информации из регистра EFLAGS. К этой категории в данном случае (для 9-байтового NOP’а) относятся только две инструкции: TEST и CMP.

Ниже представлен простой пример скрытого кода, который в качестве инструкции склеивания использует TEST. Этот пример осуществляет системный вызов exit, который для любых версий ОС Linux возвращает значение 1. Чтобы для наших нужд правильно сформировать инструкцию TEST, мы должны будем установить последнему байту первого NOP’а – значение 0xA9. Этот байт, при сцепке с первыми четырьмя байтами следующего NOP’а (66 0F 1F 84), превратится в инструкцию TEST EAX, 0x841F0F66. На следующих двух рисунках представлен соответствующий ассемблерный код (для маскирующей цепочки и скрытой цепочки). Скрытая цепочка активируется, когда управление передаётся на 4-й байт первого NOP’а.

Во вторую категорию входят инструкции, которые меняют значения регистров общего назначения или доступную память (стек, например), но при этом не изменяют регистр EFLAGS. При выполнении инструкции PUSH или любого варианта MOV, где в качестве второго операнда задано непосредственное значение, – регистр EFLAGS остаётся неизменным. Т.о. склеивающие инструкции второй категории можно помещать даже между инструкцией сравнения (TEST, например) и инструкцией, оценивающей регистр EFLAGS. Однако инструкции этой категории ограничивают возможности пользования регистром, который фигурирует в соответствующей склеивающей инструкции. Например, если в качестве склеивающей инструкции используется MOV EBP, 0x841F0F66, – то возможности пользования регистром EBP (из остальной части скрытого кода) значительно ограничиваются.

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

Немного практики

Для старта нужны базовые знания Python’а и умение подглядывать в документацию по IDAPython. Чтобы ощутить, насколько проще осуществляется обработка данных на питоне, напишем скрипт для решения ранее озвученной задачи — отобразим на экране все вызываемые функции. Воспользуемся низкоуровневыми вызовами из модуля idaapi.

ищем в цикле от начала сегмента до конца

func = get_func(seg.startEA)
while func is not None and func.startEA < seg.endEA:
funcea = func.startEA
print «Function %s at 0x%x» %
(GetFunctionName(funcea), funcea)
ref = get_first_cref_to(funcea)
while ref != BADADDR:
print » called from %s(0x%x)» %
(get_func_name(ref), ref)
ref = get_next_cref_to(funcea, ref)
func = get_next_func(funcea)

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

Дизассемблер

Помимо прочего, proview phpp — это еще и дизассемблер. Допускается выбрать опции для дизассемблирования, а также выбрать цветовую схему листинга, наиболее привычную по одной из известных программ (SoftICE, IDA, W32Dasm, Ollydbg). Но, увы, получить дизассемблированный код получится для файлов не более 100 Кб. Чтобы обойти это неприятное ограничение, рекомендую взглянуть на более современный сервис — Pym’s online disassembler (pyms86.appspot.com), построенный на базе Google App Engine.

Первый плюс в том, что можешь анализировать не только целый файл, но и отдельные HEX-дампы. Проекту по зубам 64-битные приложения, причем для анализа используются Python-библиотеки Pym (code.google.com/p/pymsasid), pefile (code.google.com/p/pefile) и networkx (networkx.lanl.gov), которые очень прогрессивно развиваются. Возможно, в будущем мы расскажем, как собрать свой особенный сервис для самостоятельного дизассемблирования.

Склеивающие инструкции

Комбинацию ZZ XX – мы будем называть склеивающей инструкцией. Склеивающая инструкция нужна, во-первых, для стыковки скрытых инструкций, которые располагаются в соседних маскирующих инструкциях и, во-вторых, для выполнения первого необходимого условия, озвученного в начале статьи: инструкции обеих цепочек всегда должны пересекаться друг с другом (поэтому склеивающая инструкция всегда находится на пересечении двух маскирующих инструкций).

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

Приведённое описание техники перекрытия проиллюстрировано на следующем рисунке. Если выполнение начинается с начальных байтов (XX), то активируется маскирующая цепочка инструкций. А если с байтов YY – активируется скрытая цепочка инструкций.

What is a Disassembler?[edit]

In essence, a disassembler is the exact opposite of an assembler. Where an assembler converts code written in an assembly language into binary machine code, a disassembler reverses the process and attempts to recreate the assembly code from the binary machine code.

Since most assembly languages have a one-to-one correspondence with underlying machine instructions, the process of disassembly is relatively straight-forward, and a basic disassembler can often be implemented simply by reading in bytes, and performing a table lookup. Of course, disassembly has its own problems and pitfalls, and they are covered later in this chapter.

Many disassemblers have the option to output assembly language instructions in Intel, AT&T, or (occasionally) HLA syntax. Examples in this book will use Intel and AT&T syntax interchangeably. We will typically not use HLA syntax for code examples, but that may change in the future.

Problems of disassembly

Writing a disassembler which produces code which, when assembled, produces exactly the original binary is possible; however, there are often differences. This poses demands on the expressivity of the assembler. For example, an x86 assembler takes an arbitrary choice between two binary codes for something as simple as . If the original code uses the other choice, the original code simply cannot be reproduced at any given point in time. However, even when a fully correct disassembly is produced, problems remain if the program requires modification. For example, the same machine language jump instruction can be generated by assembly code to jump to a specified location (for example, to execute specific code), or to jump a specified number of bytes (for example, to skip over an unwanted branch). A disassembler cannot know what is intended, and may use either syntax to generate a disassembly which reproduces the original binary. However, if a programmer wants to add instructions between the jump instruction and its destination, it is necessary to understand the program’s operation to determine whether the jump should be absolute or relative, i.e., whether its destination should remain at a fixed location, or be moved so as to skip both the original and added instructions.

Компилятор онлайн

Через пару минут я уже имел на руках скомпилированный бинарник. Помог сервис некоего турецкого программиста, который когда-то озадачился подобной проблемой и реализовал на своем сайте веб-интерфейс для доступа к компилятору. А чтобы пользоваться было еще удобнее, прикрутил компонент для редактирования кода. Подсветка синтаксиса, нумерация строк и даже автодополнения команд — почти маленькая IDE. Собирается в один исходник в один клик или по хоткею Ctrl-F7, после чего сервис отдает готовый бинарник. Если во время сборки произошли ошибки, то сообщения компилятора отобразятся в отдельной панели. Адрес для такого удачного сервиса на редкость странный:

login.php, причем сайт откроется только в Internet Explorer. Другое серьезное ограничение — отсутствие поддержки C++. А куда же сейчас без нее? Поэтому когда зашла речь о компиляции сорца С++, пришлось отыскать замену. К счастью, разработчики прогрессивного компилятора Comeau C/C++ на официальном сайте сделали фронтенд, который выполнен как раз в виде веб-приложения (www.comeaucomputing.com/tryitout).

Допускается выбор различных режимов компиляции, версии Comeau, а также любые другие параметры, которые ты мог передать сборщику через командную строку. Кстати говоря, последние релизы Comeau уже поддерживают большинство расширений C++0x (обновленный и модернизировнный стандарт С++), поддержка которого появилась сейчас в Visual Studio 2010.

«Зачем вообще привязываться к какому-то конкретному языку», — подумали ребята из ideone (ideone.com) и реализовали универсальный компилятор в 40 различных языков. Помимо С/C++ здесь поддерживаются еще и Java, C#, Pascal, Visual Basic .NET, все скриптовые языки, и даже ассемблер

Важно только уложиться в ограничения: 10 секунд на компиляцию, 5 секунд на выполнение, 256 мегабайт памяти. Помимо этого, программа не сможет обращаться в Сеть и работать с файлами

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector