Можно ли дизассемблировать windows

Обновлено: 04.07.2024

Я думаю, большинство людей, кто застал времена DOS и ранних Windows 9x, играли или хотя-бы слышали о такой замечательной игре, как Supaplex. Лично для меня — это игра-легенда. Я до сих пор с трепетом вспоминаю долгие зимние вечера, проведённые в попытке пройти какой-нибудь сложный уровень на стареньком 286-м.

Так сложилось, что я программист. И не просто программист, а программист компьютерных игр. Так что, где-то в 2008 году я занялся написанием движка-«убийцы крузиса» (или что там было круто на тот момент, я уже не помню). Примерно через год меня настигло прозрение, что моих человеко-часов на проект уровня Unreal Engine не хватит. И я решил сделать легкую, «казуальную», версию движка и написать на ней пару простеньких игрушек.

Мой выбор пал на клон Supaplex. Правда, на тот момент уже существовало несколько клонов под Windows и другие платформы, поэтому просто писать все с нуля было неинтересно. Зато в мою голову заползла крамольная мысль: а что, если дизассемблировать оригинальный Supaplex и сделать игру с логикой, «идентичной натуральной». Такая задача казалась крайне заманчивой, и я взялся за её осуществление. Я заказал 3D-модельки у одного хорошего знакомого фрилансера и, пока он был занят моделлингом, я приступил к изучению пациента. Сразу предупреждаю, что я уже не помню многих деталей и могу что-то забыть или приврать, всё-таки это было довольно давно.

Дизассемблирование

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

Началось всё с того, что исполняемый файл то ли запакован, то ли зашифрован. После безуспешного поиска чего-нибудь, что сможет его распаковать (хотя я не особо старательно искал), было принято решение писать свой распаковщик. Наверное, велосипедостроение у меня в крови. Я уже не помню досконально, как он работал, но идея заключалась в эмуляции инструкций CPU до точки, когда программа успешно запустилась, после чего содержимое памяти сохранялось на диск в виде EXE-файла. Этот замечательный декомпрессор можно найти в материалах к статье.

Для дизассемблирования я использовал IDA. Насколько я помню, мне приходилось пересоздавать базу данных несколько раз, поэтому вместо неё я использовал idc-скрипт, проставляющий имена символам. Его вы тоже сможете найти на гитхабе. Если вам было интересно покопаться в кишках Supaplex, но было сложно разобраться в мешанине ассемблерных инструкций, этот скрипт очень сильно поможет (даже если у вас нет IDA, можно использовать указанные там смещения в любом другом дизассемблере).

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

Результатом всего этого стал почти чистый C++ код игровой логики, с небольшим вкраплением goto и переменных со странными именами типа byte_403C3. Его вы тоже найдете в материалах к статье.

Однажды, в 2009 году, вышла Windows 7. В то время я сидел на Висте, которая притормаживала на моем стареньком компьютере, и я решил пересесть на семерку сразу после ее выхода.

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


Сразу же полез в настройки, чтобы отключить это безобразие, и с удивлением обнаружил, что группировка не отключается. Наиболее близкий к желаемому вариант, Never combine, кнопки все же группирует.


Я думал, что как и мне, многим это не понравится, и был уверен, что спустя неделю-две в интернете всплывет решение этой проблемы. Но я так ничего путного и не нашел, и понял, что придется действовать самому.

Приступаем

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

Для того, чтобы отучить панель задач от группировки, нам понадобится OllyDbg — бесплатный проприетарный 32-битный отладчик уровня ассемблера для операционных систем Windows, предназначенный для анализа и модификации откомпилированных исполняемых файлов и библиотек, работающих в режиме пользователя (ring-3) © Wikipedia.

Повторить процесс можно на ОС Windows 7 или Windows 8. К сожалению, на данный момент имеется только 32-битная версия OllyDbg, так что если у вас 64-битная ОС, повторить следующие действия у вас не получится (в таком случае можно использовать виртуальную машину).

Инсталляция OllyDbg

OllyDbg — портативная программа, и в инсталляции не нуждается. Создайте папку с правами записи, и перепишите туда файлы с этого архива.

Настройка Microsoft Symbol Server

Microsoft Symbol Server — Майкрософтовский сервер отладочных символов, благодаря которым мы, кроме чистого ассемблера, будем видеть еще и названия функций и переменных. Это очень помогает при анализе кода.

    Скачайте этот архив, и поместите его содержание в папку программы OllyDbg.

  • symsrv.dll — DLL файл Майкрософта, предназначен для работы с их сервером отладочных символов. Подписан Майкрософтом.
  • symsrv.yes — пустой файл. Обозначает, что вы согласны с условиями предоставленных услуг.
  • symsrv.ini — исключает все файлы кроме тех, которые начинаются на exp — для нашего explorer.exe. Иначе, если будут качаться все символы, придется очень долго ждать, да и не нужно оно нам для данной демонстрации.



Присоединение процесса explorer

Как известно, панель задач — часть процесса Windows Explorer. Запускаем OllyDbg, и выбираем File -> Attach. . Затем, выбираем explorer и жмем на Attach.




Attach, с английского — присоединять — то есть мы присоединяем наш отладчик к проводнику для его отладки.

Затем ждем, пока все модули загрузятся. Это может занять несколько минут, и в это время проводник не будет реагировать. После завершения загрузки в строке состояния справа будет написано Paused на желтом фоне — то есть процесс приостановлен. Жмем F9 чтобы возобновить работу процесса.

Внимание, исключения: если процесс приостанавливается, и в строке состояния появляется надпись Exception xxxxxxxx (как на скриншоте ниже), нажмите Shift+F9 чтобы передать исключение по назначению.


Обзор функций проводника

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

Сначала, откроем модуль процесса explorer в окошке CPU отладчика. Для этого откроем окошко модулей (буква E на голубом фоне), нажмем правой кнопкой на Explorer, и выберем View code in CPU.



Затем, нажмем правой кнопкой на код, и выберем Search for -> Names. Перед нами появится список функций, присутствующих в проводнике. Для более удобной работой со списком, отсортируем его по имени, скопируем и вставим в текст в любимый текстовой редактор.




Итак, нам нужно отключить группировку. Логично начать поиск со слова group. В глаза сразу бросаются два класса: CTaskBtnGroup и CTaskGroup. Вместе у этих двух классов 131 функций — не очень много для беглого просмотра названий.

Мне сложно сказать, можно ли найти нужную нам функцию, всего лишь изучая названия, так как я уже неплохо с многими из них знаком. В любом случае, нужная нам функция — CTaskGroup::DoesWindowMatch, название которой переводится как совпадает ли окошко, и делает она именно это — отвечает, подходит ли окошко к определенной группе.


Просмотр выбранной функции

Мы нашли функцию с интересным названием, теперь давайте посмотрим, чего она из себя представляет. Вернемся в OllyDbg, нажмем на Ctrl+G и перейдем на адрес функции (тот восьмизначный номер в начале строки, в нашем случае 00973629).

P.S. Советую включить подсветку Jumps and calls, благодаря которой четко видны вызовы функций и условные/безусловные прыжки.



Итак, вот наша функция:

Столбцы, слева на право: адрес, байты, команды (ассемблер), комментарии.
Голубым цветом помечены функции, желтым — переходы. На них мы и сосредоточимся.

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

Дальше мы видим три перехода. Первый перепрыгивает через второго и третьего. Второй и третий переходят куда-то подальше — давайте посмотрим, куда…

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


Для этого, пропишем безусловный прыжок JMP, как на скриншоте сверху.

Второй
Как и в первом разе, сразу видим WinAPI функцию для сравнения, на этот раз текста — CompareStringOrdinal. Опять же, после экспериментирования становится ясно, что и это сравнение связано с группами. На этот раз сравниваются так называемые Application ID — идентификатор аппликации, по которым панель задач группирует кнопки.


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

Пробуем

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

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


Так как мы изменили код только в памяти запущенного процесса, наша модификация продержится только до завершения процесса.

Бонус — декомпилированная версия функции CTaskGroup::DoesWindowMatch

Несмотря на то, что оригинальный язык данного кода — C++, ниже превидена самописная декомпиляция на чистом C.

Готовое решение

Программа 7+ Taskbar Tweaker, автором которой являюсь я, умеет отключать группировку как глобально, так и выборочно по Application ID. Кроме этого, имеются в наличие еще несколько интересных настроек панели задач.


Заключение

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

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

Дизассемблеры и декомпиляторы исполняемых файлов


Дизассемблеры и декомпиляторы исполняемых файлов

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

Скриншот программы dnSpy


Скриншот программы dnSpy

Скриншот программы IDA Pro Advanced


Скриншот программы IDA Pro Advanced

IDA Pro (сокращение от Interactive DisAssembler) - один из моих основных инструментов для реверс-инжиниринга и разбора файлов. Это интерактивный дизассемблер и отладчик с поддержкой множества форматов исполняемых файлов для большого числа процессоров и операционных систем. Чтобы перечислить все его возможности потребуется целая книга. Но даже тут возможности IDA не заканчиваются. Плагин Hex-Rays для IDA Pro позволяет декомплировать ассемблерный листинг в более-менее человекопонятный псевдокод, по синтаксису похожий на C. В некоторых случаях это значительно облегчает работу. Просто так приобрести IDA Pro частным лицам практически невозможно, и дело не только в непомерной цене, а в том, что автор придерживается абсолютно неадекватной политики в плане продаж. К счастью, несколько последних версий этого замечательного дизассемблера, несмотря на все трудности, были успешно слиты в свободный доступ. Это IDA Pro Advanced 6.8, последняя доступная версия, которая работает с 32-битными системами, а также IDA Pro Advanced 7.0 и IDA Pro Advanced 7.2 для 64-битных систем. Если по каким-то причинам вы не можете использовать варез, то на офсайте есть бесплатные демо-версии с урезанным функционалом.

Скриншот программы Interactive Delphi Reconstructor


Скриншот программы Interactive Delphi Reconstructor

IDR (Interactive Delphi Reconstructor) - бесплатный декомпилятор исполняемых файлов и динамических библиотек. В отличие от IDA Pro, этот декомпилятор создан специально для разбора файлов, написанных на языке Delphi. Сейчас проект прекратил развитие, если какие изменения и вносятся, то исключительно косметические. Исходники для доработки открыты. Лично я пользуюсь стабильным комплектом Interactive Delphi Reconstructor 2.6.0.1.

Скриншот программы VB Decompiler Pro


Скриншот программы VB Decompiler Pro

Еще один специализированный декомпилятор - VB Decompiler Pro. Он работает с программами (EXE, DLL, OCX), написанными на Visual Basic. В случае, если приложение собрано в p-code, декомпилятор может разобрать его практически до исходного кода. Но даже если приложение скомпилировано в native code, в этом случае VB Decompiler анализирует и восстанавливает довольно много инструкций, чтобы насколько это возможно приблизить ассемблерный код к исходному. Это сильно упростит задачу анализа алгоритмов исследуемой программы. Честные граждане могут воспользоваться бесплатной Lite-версией с офсайта, для любителей полных версий софта есть релиз VB Decompiler Pro 10.0. Антивирусы могут ругаться на активатор, но тут вы уже сами решайте что делать.

Конечно, это далеко не полный список инструментов для дизассемблирования и декомпиляции, который есть в свободном доступе. Например, та же набирающая популярность Ghidra от АНБ может составить конкуренцию IDA Pro с Hex-Rays. Но я в этой статье перечислил лишь те программы, которыми пользуюсь сам и которые упоминаются в статьях на этом сайте.

После изучения основ реверс-инжиниринга NET-приложений, настало время более подробно рассмотреть язык MSIL. В этой статье не ставится цель научиться вас программировать на этом языке.

image

Автор: Суфиан Тахири (Soufiane Tahiri)

После изучения основ реверс-инжиниринга NET-приложений, настало время более подробно рассмотреть язык MSIL. В этой статье не ставится цель научиться вас программировать на этом языке. Я постараюсь разъяснить новые нюансы по использованию и управлению IL-кодом посредством новых инструментов. Также я представлю вам технику «циклической разработки» (которая применяется не только при разработке NET-приложений) и расскажу о преимуществах, которые может дать эта техника реверс-инженеру.

В этой статье мы ознакомимся более подробно с IL-кодом и двумя взаимосвязанными инструментами, которые помогут нам удалить первую защиту у Crack Me, специально сделанного для этой цели. Как только вы научитесь работать с этими инструментами и узнаете основы циклической разработки, в следующей статье я расскажу о продвинутых методах циклической разработки для удаления второй защиты Crack Me. Так что сделайте глубокий вдох, и мы начинаем!

Понятие «Циклической разработки»

Первоначально IL-ассемблер и дизассемблер были созданы как внутренние инструменты, используемые при разработке среды CLR. Когда обе утилиты стали в достаточной мере синхронизированными, на базе ILAsm начали появляться (наряду с библиотекой классов NET Frameworks) сторонние компиляторы, ориентированные на платформу NET.

Замечание: Управляемые NET-приложения называются «сборками», а управляемые исполняемые файлы NET называются «модулями».

В этой статье мы будем работать только с двумя инструментами, официально представленными компанией Microsoft в рамках Windows SDK: IL-ассемблер (ILASM) и IL-дизассемблер (ILDASM, который мы использовали в прошлой статье). В принципе, мы можем исследовать любую NET-сборку / модуль при помощи этих двух утилит.

Прежде чем приступить к анализу Crack ME (о нем я еще не рассказывал), я подробнее остановлюсь на некоторых аспектах платформы NET и начну со среды Common Language Runtime.

Среда CLR является промежуточным звеном между NET-сборками и операционной системой, в которой запускаются сборки; на данный момент (я надеюсь) вы знаете, что каждая NET-сборка «транслируется» в низкоуровневый промежуточный язык (Common Intermediate Language – CIL или Microsoft Intermediate Language – MSIL). Несмотря на разработку на высокоуровневом языке, сборка независима от целевой платформы. Такой вид «абстракции» позволяет вести разработку на разных языках.

Common Intermediate Language опирается на набор спецификаций, гарантирующих совместное сосуществование разных языков; этот набор спецификаций известен как Common Language Specifications – CLS, как это определено в спецификации общеязыковой структуры (Common Language Infrastructure), стандарте Ecma International, и международной организации по стандартизации (International Organization for Standardization – ISO; ссылку для загрузки Раздела I можно найти в разделе «Ссылки»).

NET-сборки и модули, спроектированные для запуска в среде Common Language Runtime (CLR), состоят в основном из метаданных и управляемого кода.

Управляемый код состоит из набора инструкций, которые являются «сердцем» сборки / модуля и представляет собой набор функций и методов закодированный в абстрактной и стандартизированной форме известной как MSIL (или CIL), что позволяет опознать управляемый исходный код, который запускается исключительно в среде CLR.

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

Таким образом, можно сказать, что модуль состоит из двух компонентов: метаданных и IL-кода; среда CLR подразделяется на две подсистемы: «загрузчик» и JIT-компилятор.

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

Рисунок ниже описывает механизм создания и запуска управляемого модуля:


Наш третий объект исследования – управляемый модуль «CrackMe3-InfoSecInstitute-dotNET-Reversing.exe» (ссылка для загрузки представлена в разделе «Ссылки) выглядит так:



Наша задача – удалить всплывающее окно и подобрать серийный номер. Далее мы рассмотрим методы циклической разработки для обхода обеих защит этого Crack Me.

Шаг 1: Дизассемблирование

Первым делом дизассемблируем наш Crack Me, используя ILDASM и посмотрим встроенный IL-код управляемого модуля. Сосредоточим внимание на двух узлах:


Управляемый модуль содержит одну форму Form1 и один класс GetSerial. Рассмотрим первый узел (с формой):


Дважды щелкните по методу Form_Load, чтобы увидеть IL-код:

.method private instance void Form1_Load(object sender, class [mscorlib]System.EventArgs e) cil managed
<
// Code size 19 (0×13)
.maxstack 8
IL_0000: ldstr “I’m a nag screen, remove me.”
IL_0005: ldc.i4.s 16
IL_0007: ldstr “Nagging you!”
IL_000c: call valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxResult [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::MsgBox(object, valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxStyle, object)
IL_0011: pop
IL_0012: ret
> // end of method Form1::Form1_Load

Для повышения читабельности кода все служебные слова ILAsm (читай инструкции) выделены жирным шрифтом. Рассмотрим детально механизм работы этого участка кода и наши дальнейшие действия.

Понимание механизма работы метода Form_Load():

.method private instance void Form1_Load(…) cil managed элемент метаданных Method Definition.

Служебные слова private и instance определяют флаги элемента Method Definition. Ключевое слово public означает, что метод Frm1_Load() доступен всем членам, для которых видим «материнский» класс (тот, который содержит этот метод). Служебное слово instance говорит нам о том, что метод связан с объектом, а не классом.

Служебное слово void в явной форме определяет тип возвращаемого значения (по умолчанию) текущего метода. Void означает, что метод ничего не возвращает.

Служебные слова cil и managed означают, что тело метода представлено IL-кодом и определяют флаги реализации Method Definition.

.maxstack 8 – директива, определяющая максимальное число элементов, которое может находиться в стеке вычислений во время выполнения метода.

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

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

Ldstr «I’m a nag screen, remove me». Создает объект типа строка из передаваемой строковой константы и помещает ссылку на этот объект в стек вычислений. Подобные константы хранятся в метаданных. Эта строковая константа общеязыковой среды выполнения или строковая константа метаданных всегда хранится в формате Unicode (UTF-16).

call valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxResult [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::MsgBox(object, valuetype [Microsoft.VisualBasic]Microsoft.VisualBasic.MsgBoxStyle, object); valuetype используется перед объектом, который мы хотим создать. Указывается полное имя класса, включая имя библиотеки, так происходит вызов (не виртуального?) метода. Ключевое слово valuetype обязательно для экземпляров общего типа, поскольку они представлены в метаданных как TypeSpecs.

Инструкция pop удаляет из стека строку «Nagging you!».

После выполнения инструкции ret происходит возврат из текущего метода, и в стек вычислений помещается значение определенного типа. В нашем случае метод возвращает значение типа void, что означает отсутствие какого-либо значения в стеке вычисления во время возврата из метода.

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

Технически нам нужно на основе нашей сборки сгенерировать файл .il (выгрузив его из ILDasm), произвести необходимые манипуляции и собрать его заново. Однако перед этим нам нужно выяснить версию сборки, чтобы не было проблем с повторной компиляцией (используя ILASM).

Информацию о версии можно узнать, если посмотреть содержимое манифеста сборки:


Директива .ver указывает номер версии:


Рисунок 1. Версия сборки

Теперь выгрузим сборку. Выполните команду File->Dump->Ok (или Ctrl+D) и выберите директорию, куда сохранять файлы. В результате получится нечто подобное:


Файл с расширением .il содержит все IL-инструкции управляемого модуля. Откройте его в текстовом редакторе и найдите метод Form1_Load:


Теперь мы можем пойти несколькими путями. Например, удалить все инструкции внутри метода или удалить весь метод, поставив в начало инструкцию ret. Я предпочитаю удалить содержимое так, чтобы метод Form1_Load() не выполнял вообще никаких операций.

Удалите строки с 1192 до 1201 и сохраните изменения:


После всех манипуляций нам нужно реассемблировать измененный .il файл. Мы будем использовать утилиту ILAsm, которая поставляется вместе с Visual Studio и Windows SDK. С помощью ILAsm мы сможем собрать исполняемый файл из файла инструкций на языке Microsoft Intermediate Language.

Шаг 2: Реассемблирование

Переходим в командную строку Microsoft Windows Command (CMD) или Visual Studio Command Prompt (в любом случае результат будет один и тот же).

Выполните команду Start->Run->CMD.

Filename = полный путь к .il файлу

Параметр –res необязательный и используется для сохранения ресурсов из первоначальной сборки (например, иконок)


Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
Writing PE file
Operation completed successfully

И в новой версии уже не будет всплывающего окна:


Замечание: вы также можете изменить текст, появляющийся во всплывающем окне посредством изменения строки, которая загружается в стек вычисления, или поменять стиль всплывающего окна:

Читайте также: