Главная » Статьи » Крэкинг

Дизассемблирование C# программ - от A до Z

Введение

Технология .NET готовится отпраздновать свой юбилей. За это время было написано множество коммерческих программ (и малвари в том числе), но как только дело доходит до того, чтобы заглянуть внутрь p-кода на предмет "отломать" пару ненужных байт, выясняется, что достойных хакерских инструментов нет, и судя по всему, не появится, поэтому приходится использовать то, что есть, хакерствуя в весьма стесненных обстоятельствах, словно шахтеры в забое!

Отказ от ответственности

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

Что нам понадобится

  • Visual Studio 2008 Express:
    Последняя версия знаменитой "студии" от MS, распространяющаяся бесплатно и включающая в себя все необходимое для полноценной работы: трансляторы Visual Basic, C#, а так же новый компилятор Cи++ способный транслировать Си++ программы в байт-код. На W2K устанавливаться категорически отказалась, на Server 2003 встала с полпинка (offline install занимает 894 Мбайт в .iso-формате): http://www.microsoft.com/express/download/;

Microsoft Visual Studio

Рисунок 1. Отсюда можно бесплатно скачать последнюю версию Microsoft Visual Studio.

  • Mono 2.0 Beta for Windows:
    Альтернативная реализация .NET от компании Novell, поддерживающая различные версии Linux и Windows, нормально встала на W2K, но потребовала MS Framework, кроме того - комплект поставки неполный (в частности, нет дизассемблера), а откомпилированные сборки жутко тормозят, зато дистрибутив занимает 71 Мбайт: http://www.go-mono.com/mono-downloads/download.html;

Реализация платформы .NET

Рисунок 2. Единственная (на данный момент) реализация платформы .NET вне Microsoft.

  • Standard ECMA-335/Common Language Infrastructure (CLI)/4th edition (June 2006):
    Полная версия европейского стандарта, описывающая архитектуру виртуальной машины, байт-код, структуру ассемблерных сборок, в общем - солидный 556-страничный документ из серии "маст хэв" (на английском языке в формате pdf): www.ecma-international.org/publications/standards/Ecma-335.htm, а вот прямая ссылка: www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf;

  • CIL Instruction Set:
    Краткий справочник опкодов с "C# Online.NET free encyclopedia" - жутко неполный, но зато вполне удобный и не содержащий воды (на английском языке): http://en.csharp-online.net/CIL_Instruction_Set;

  • Введение в MSIL:
    Цикл статей от Кенни Керр, в переводе Aquila (одного из основателей WASM) - подробное описание архитектуры виртуальной машины и концепций языка C#, рекомендуемое к прочтению всеми начинающими хакерами (на русском языке): http://www.wasm.ru/series.php?sid=22;

  • Common Intermediate Language:
    Поверхностное описание CIL-языка (общего для всех языков платформы .NET) на Wikipedia со ссылками на кучу полезных ресурсов (на английском языке): http://en.wikipedia.org/wiki/Common_Intermediate_Language;

  • Linking native C++ into C# applications:
    Любопытный пост на форуме, показывающий - как вызывать Си++ модули из .NET-программ, образуя смесь управляемого и неуправляемого кода, высаживающего инструменты для анализа .NET-программ на измену (на английском языке): blogs.msdn.com/texblog/archive/2007/04/05/linking-native-c-into-c-applications.aspx;

  • Joel Pobar's CLR weblog:
    Блог одного из ведущих разработчиков CLR с тонной полезной инфы, раскрывающей многие недокументированные особенности (на английском языке): http://blogs.msdn.com/joelpob/;

  • ilasm.exe:
    Штатный .NET-ассемблер, входящий в комплект поставки MS VS и Mono: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ilasm.exe;

  • ildasm.exe:
    Штатный (и весьма недурственный) .NET-дизассемблер, входящий в комплект поставки MS VS (в Mono ничего подобного не входит, ну или я не нашел): C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\ildasm.exe;

  • Thottam R. Sriram blog:
    Блог одного из ведущих разработчиков CLR, рассказывающий - как пользоваться штатным ассемблером и дизассемблером для .NET сборок (на английском языке): http://blogs.msdn.com/thottams/archive/2007/02/01/using-ilasm-and-ildasm.aspx;

  • IDA Pro:
    Основной хакерский дизассемблер, поддерживает .NET-сборки на весьма продвинутом уровне, хотя местами кое-где ошибается, не умеет дампить ресурсы и переваривает далеко не все нестандартные расширения. Отладка .NET-программ также не поддерживается, pdb-файлы не генерируются и еще куча других недостатков, затрудняющих взлом и вынуждающих пользоваться ildasm/ilasm: http://idapro.com;

  • Decompiler.NET 2005 v2.0.0.230 by Jungle Creatures:
    Популярный коммерческий декомпилятор для .NET-программ, но по сравнению со штатным (и бесплатным!) ildasm'ом он реально сосет, не говоря уже за IDA Pro: http://www.junglecreatures.com (официальное логово), www.wasm.ru/baixado.php?mode=tool&id=366 (хакнутая версия);

  • Mdbg.exe:
    Штатный отладчик .NET-программ, входящий в комплект поставки MS VS и работающий на уровне байт-кода через стандартный ICorDebug-интерфейс, а потому требующий наличия символьной информации. В win32-mono мыщъх не нашел ничего подобного (может, плохо искал?! как же мы без отладчика?!): C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\Mdbg.exe;

  • Dotnet IL Editor:
    Бесплатный отладчик .NET-программ, работающий на уровне байт-кода через все тот же ICorDebug-интерфейс, требующий символьной информации. По уровню функциональности сопоставим с mdbg.exe, только если mdbg.exe - консоль, но subj уже имеет графическую оболочку, работать с которой чуть-чуть удобнее: http://sourceforge.net/projects/dile;

  • Debugging IL:
    Познавательная статья одного блоггера, разъясняющая все трудности отладки .NET-программ на уровне байт-кода и предлагающая некоторые "обходные" решения для отладки чужих программ без исходных текстов (на английском языке): http://blogs.msdn.com/jmstall/archive/2004/10/03/237137.aspx;

  • ICorDebug Interface:
    Описание штатного интерфейса, встроенного в ядро платформы .NET, и предназначенного для отладки приложений на IL-уровне (на английском языке): msdn2.microsoft.com/en-us/library/ms230588.aspx и msdn2.microsoft.com/en-us/library/ms404484.aspx;

  • Code Protection:
    Бесплатный (но довольно мощный) протектор для .NET-программ от Microsoft, поддерживающий, в том числе, и среду mono (требует регистрации): http://www.microsoft.com/SLPS/Default.aspx;

  • dotNet Protector:
    Монстроузный и навороченный .NET-протектор, ужасно тормозит и хочет денег: http://dotnetprotector.pvlog.com/Downloads.aspx;

  • How to unpack dotNet Protector:
    Статья, посвященная распаковке dotNet Protector'а (на английском языке): http://www.exetools.com/forum/showthread.php?t=7968;

  • YAP .Net:
    Еще один протектор для .NET-программ, на этот раз бесплатный, но сильно сырой: forum.vingrad.ru/forum/topic-186025.html и sllh.net.ru/download/yapnet/yapnet.php;

Пишем свой первый crackme

Глубоководное погружение в байт-код .NET-программ начинается! Пристегиваем ремни, проверяя - все ли на месте и... запускаем Microsoft Visual Studio или... FAR + Colorer. Работать в IDE, конечно, удобнее (особенно начинающим, когда среда автоматически отображает список методов для каждого класса - не надо постоянно лазить в справочники), однако это порочный путь, абстрагирующий нас от машины и мы - хакеры старого поколения - предпочитаем консольные текстовые редакторы с подсветкой синтаксиса или... даже без таковой.

Текст нашего первого crackme, написанного на C#, (который мы будем ломать всеми силами) в простейшем случае выглядит так (см. листинг 1). Это консольная программа, вручную набранная в текстовом редакторе. IDE пихает сюда много лишнего, затрудняющего понимание, однако на скомпилированном коде это никак не отражается.

using System;       // использовать классы основной системной библиотеки

class nezumi        // имя класса - произвольно и может быть любым
{
        static void Main()
        {
                string s;                // объявляем переменную s типа строка
                
                // запрашиваем у пользователя пароль
                System.Console.Write("enter password:");
                s = System.Console.ReadLine();
                
                if (s == "nezumi")       // сравниваем введенный пароль с эталонным
                        System.Console.WriteLine("hello, master!");
                else
                        System.Console.WriteLine("fuck off, hacker!");
        }
}

Листинг 1. Исходный текст программы n2k_crackme_01h.cs.

В среде IDE сборка .NET-программ осуществляется клавишей <F6>, а из командой строки (при этом csc.exe должен находится в путях, чего не происходит при установке по умолчанию):

$csc.exe n2k_crackme_01h.cs

Листинг 2. Компиляция C#-программы из командной строки.

В Mono вместо csc.exe используется файл mcs/mcs.bat, но независимо от способа сборки, мы получаем n2k_crackme_01h.exe, готовый к непосредственному запуску, после которого нас спросят пароль и если мы введем его неверно - пошлют на хрен.

.NET-программы на Си++

Компилятор Cи++, входящий в состав Microsoft Visual Studio 2008, умеет транслировать программы не только в машинный, но и в байт-код, позволяя нам использовать все прелести .NET платформы из привычных плюсов (трансляция "чистых" Си программ в байт-код все еще не поддерживается).

#include <stdio.h>
#include <string.h>

using namespace System;        // использовать классы основной системной библиотеки

void main()
{
        char buf[0x666];
        printf("enter password:"); gets(buf);
        if (strcmp(buf, "nezumi"))
                printf("fuck off, hacker!\n");
        else
                printf("hello, master!\n");
}

Листинг 3. Пример простейшей Cи++ программы, написанной с учетом специфики .NET.

Компиляция осуществляется путем указания ключа /CLR в командной строке компилятора CL.EXE (рядом с которым можно указать ключ /Ox для форсирования максимальной оптимизации):

$cl.exe /clr hello-clr.cpp

Листинг 4. Трансляция Си++ программы в .NET сборку из командной строки.

Полученный файл hello-clr.exe представляет собой смесь управляемого байт-кода с большим количеством вызовов неуправляемого машинного кода из различных библиотек (плюс тянет за собой RTL, написанную на байт-коде), что позволяет сочетать достаточно высокую скорость выполнения с управляемостью и безопасностью, однако "чистые" C# сборки все-таки выигрывают как в размерах, так и в скорости. Чуть позже мы убедимся в этом самостоятельно, а пока же поверим мыщъх'у на слово.

Первые эксперименты

Загружаем подопытный n2k_crackme_01h.exe в HIEW, дважды давим на <ENTER> для перевода редактора в дизассемблерный режим, жмем <F5> и попадаем в точку входа, где красуется команда jmp _CorExeMain ; mscoree.dll (см. рис. 3). Это и есть весь машинный код, который только есть (простите за каламбур).

.NET сборка

Рисунок 3. Так выглядит "честная" .NET сборка в hex-редакторе.

Дальнейшее расследование показывает, что jmp находится в самом конце секции .text, за которой располагаются секции ресурсов и перемещаемых элементов, а выше - байт-код виртуальной машины, просматривая который в hex-mode, мы обнаружим все текстовые строки (и пароль в том числе!) записанные в формате Unicode, причем перед строкой находится байт, определяющий длину строки. Узнав оригинальный пароль, мы, конечно, без труда смогли бы "взломать" crackme, однако редкая программа хранит пароли открытым текстом, да и неинтересно это.

Лучше загрузим в HIEW другой исполняемый файл, написанный на Си++ - hello-clr.exe. Он также вызывает CorExeMain (см. рис. 4), но в отличие от "чистой" .NET сборки, написанной на C#, здесь присутствует большое количество "переходников" к машинному коду - функциям strcmp, gets, printf, напрямую вызываемых из библиотеки MSVCR90.DLL, за что отвечает механизм P/Invoke, позволяющий создавать "гибридные" программы, часть из которых транслируется в "живой" машинный код, а часть - в интерпретируемый байт-код, что серьезно затрудняет взлом, однако никакой P/Invoke нас не остановит! Но это будет потом, а сейчас мы покурим и загрузим .NET-сборку в нормальный дизассемблер.

Внешний вид .NET-сборки

Рисунок 4. Внешний вид .NET-сборки, полученной путем трансляции Си++ программы.

Техника дизассемблирования

Загружаем n2k_crackme_01h.exe в IDA Pro и видим (см. рис. 5), что ничего ужасного в CIL-коде нет. Напоминает байт-код виртуальной Java-машины. IDA Pro не только создает перекрестные ссылки, но даже показывает опкоды и расставляет комментарии к командам, чтобы не было нужды каждый раз заглядывать в справочник (ECMA-335/Partition III/CIL Instruction Set).

Консольная версия IDA

Рисунок 5. Консольная версия IDA Pro дизассемблирует .NET сборку.

Впрочем, чтобы заставить дизассемблер быть более дружелюбным к хакеру, необходимо выполнить следующие действия: в меню "Options" выбрать пункт "Text representation", там указать количество байт для отображения опкода ("Number of opcode bytes") - шести хватит вполне, а в разделе "Line prefixes" сбросить все галочки, кроме "Function offsets", после чего вновь возвратиться в меню "Options", зайти в "Comments" и взвести "Display auto comments" для автоматического отображения комментариев ко всем инструкциям (впрочем, при наличии некоторого опыта работы с CIL-кодом этого можно и не делать).

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

Графическая версия IDA

Рисунок 6. Графическая версия IDA Pro дизассемблирует .NET сборку.

А теперь посмотрим, на что способен штатный дизассемблер от Microsoft, по умолчанию расположенный в каталоге C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ и зовущийся ilasm.exe. Загружаем в него n2k_crackme_01h.exe и... хм, в общем-то, довольно неплохая картина получилась (см. рис. 7), а навигация по классам выполнена даже лучше, чем в IDA Pro, причем намного лучше (примечание: чтобы ildasm.exe отображал опкоды инструкций, необходимо в меню View взвести галочку "Show bytes")

Штатный дизассемблер

Рисунок 7. Штатный дизассемблер ildasm.exe за работой.

Теперь самое время исследовать дизассемблерный текст нашей программы (см. листинг 5). Ну, тут все ясно без травы и даже без комментариев (особенно тем, кто знаком с виртуальными машинами со стековой организацией). Если опустить детали, то получается следующее: программа вызывает System.Console::Write("enter password"), после чего считывает строку в переменную V_0 и вызывает функцию System.String::Equality(V_0, "nezumi") для провеки строк на соответствие и в случае их несовпадения на экран выводится строка "fuck off, hacker!", управление на которую передается машинной командой brtrue.s IL_0031 (с опкодом 2Dh 0Dh).

.method private hidebysig static void Main(string[] args) cil managed
// SIG: 00 01 01 1D 0E
{
.entrypoint
// Method begins at RVA 0x2050
// Code size 61 (0x3d)
.maxstack 2
.locals init (string V_0, bool V_1)
IL_00: /* 00 | */ nop
IL_01: /* 72 | (70)000001 */ ldstr "enter password:"
IL_06: /* 28 | (0A)000003 */ call void [mscorlib]System.Console::Write(string)
IL_0b: /* 00 | */ nop
IL_0c: /* 28 | (0A)000004 */ call string [mscorlib]System.Console::ReadLine()
IL_11: /* 0A | */ stloc.0
IL_12: /* 06 | */ ldloc.0
IL_13: /* 72 | (70)000021 */ ldstr "nezumi"
IL_18: /* 28 | (0A)000005 */ call bool [mscorlib]System.String::Equality(str,str)
IL_1d: /* 16 | */ ldc.i4.0
IL_1e: /* FE01| */ ceq
IL_20: /* 0B | */ stloc.1
IL_21: /* 07 | */ ldloc.1
IL_22: /* 2D | 0D */ brtrue.s IL_0031
IL_24: /* 72 | (70)00002F */ ldstr "hello, master!"
IL_29: /* 28 | (0A)000006 */ call [mscorlib]System.Console::WriteLine(str)
IL_2e: /* 00 | */ nop
IL_2f: /* 2B | 0B */ br.s IL_003c
IL_31: /* 72 | (70)00004D */ ldstr "fuck off, hacker!"
IL_36: /* 28 | (0A)000006 */ call [mscorlib]System.Console::WriteLine(str)
IL_3b: /* 00 | */ nop
IL_3c: /* 2A | */ ret
} // end of method nezumi::Main

Листинг 5. Результат работы штатного дизассемблера ildasm.exe.

Логично - чтобы заставить программу воспринимать все пароли как правильные, двухбайтовый условный переход brtrue.s IL_0031 необходимо заменить на пару однобайтовых команд nop (опкод - 00h, а вовсе не 90h как на x86). Или же... заменить brtrue.s IL_0031 на brFALSE.s IL_0031, тогда любой неправильный пароль будет восприниматься как правильный и, соответственно, наоборот. Открыв ECMA-335, мы узнаем, что инструкция brfalse.s имеет опкод 2Ch - и это все, что нам необходимо знать для взлома программы.

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

А теперь загрузим в ildasm.exe бинарную сборку, выданную Си++ компилятором с ключом /CLR, и посмотрим, чем она отличается от "нормальной" .NET-сборки. Если забыть о том, что Си++ сборка тащит за собой весьма тяжеловесный RTL (в котором для нас нет ровным счетом ничего интересного) и сосредоточиться исключительно на функции main, то можно обнаружить, что все не так уж и страшно (см. листинг 6).

Длинные имена методов класса (сокращенные для экономии бумаги), конечно, на первых порах вызывают шевеление волос на голове, но потом к ним быстро привыкаешь, автоматически "вычленяя" привычные "позывные" типа printf, gets, etc, однако структура кода далека от совершенства и на его анализ уходит намного больше времени, что, кстати говоря, представляет собой не такой уж "тупой" защитный прием от начинающих хакеров. Просто компилируем свои Си++ программы с ключом /CLR и хрен кто их взломает.

.method assembly static int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)
main() cil managed
{
.vtentry 1 : 1
// Code size 59 (0x3b)
.maxstack 2
.locals (valuetype '<CppImplementationDetails>'.$ArrayType$$$BY0GGG@D V_0)
IL_00: ldsflda $ArrayType$
IL_05: call modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)printf(*)
IL_0a: pop
IL_0b: ldloca.s V_0
IL_0d: call int8 gets( * )
IL_12: pop
IL_13: ldloca.s V_0
IL_15: ldsflda $ArrayType$$
IL_1a: call modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) strcmp(,)
IL_1f: brfalse.s IL_002e
IL_21: ldsflda $ArrayType$
IL_26: call modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)printf(*)
IL_2b: pop
IL_2c: br.s IL_0039
IL_2e: ldsflda $ArrayType$
IL_33: call modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)printf(*)
IL_38: pop
IL_39: ldc.i4.0
IL_3a: ret
} // end of method 'Global Functions'::main

Листинг 6. Дизассемблирование .NET-сборки, полученной путем трансляции Си++ программы.

Техника патча

Так, где там наш HIEW?! Готов ко взлому или... еще не готов? Как нам определить местоположение байта, который мы собрались захачить? Ведь виртуальные адреса в контексте CIL-кода вообще неуместны!

Воспользуемся дедовским способом и поищем последовательность байт (сигнатуру), обитающую в окрестностях целевой команды. В данном случае это может быть 2Dh 0Dh 72h 2F 00h 00h 70h 28h (об обратном порядке байт не забываем, да? ildasm автоматически "нормализует" аргументы команд, IDA Pro - нет, показывая их такими, какие они есть - наименее значимый байт располагается по младшему адресу).

Короче, вбиваем заданную последовательность в поиск и убедившись в том, что данное вхождение - единственное, переводим HIEW в режим записи по <F3>, заменяем 2Dh на 2Ch (см. рис. 8), сохраняем изменения в файле по <F9> и выходим.

Поиск сигнатуры

Рисунок 8. Поиск сигнатуры в HIEW'е и bit-hack (исправление "неправильного" байта на "правильный").

Запускаем хакнутый файл и... о чудо!!! Он работает!!! Теперь любой, наугад взятый пароль - например, "123456" воспринимается как правильный (см. рис. 9). Конечно, если программа снабжена цифровым сертификатом подлинности или использует механизм контроля целостности собственного кода, этот номер уже не пройдет, но... ведь надо же с чего-то начинать ломать!

Хакнутая программа

Рисунок 9. Хакнутая программа любой пароль воспринимает как правильный.

Техника отладки

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

И вот тут выясняется довольно любопытная вещь. На уровне исходных текстов Microsoft Visual Studio справляется с отладкой на ура, но готовые бинарные сборки, увы, не поддерживает и для работы с ними необходимо использовать ICorDebug-интерфейс, встроенный в ядро платформы .NET и реализующий базовые отладочные возможности (установка точек останова, пошаговое исполнение и т.д.), предоставляя их в виде набора API-функций. Все .NET-отладчики, которые только видел мыщъх, являются достаточно тонкими обертками вокруг ICorDebug Interface, наследуя его худшие черты, а именно - невозможность отлаживать программы без символьной (отладочной) информации, автоматически удаляемой из всех Release-проектов. Выходит, что мы можем отлаживать только свои собственные программы?! Нехорошо!!!

Расследование показало, что штатному .NET отладчику (зовущемуся mdbg.exe, где "m" - сокращение от managed, т.е. управляемый код) для нормальной работы вполне достаточно pdb-файла, вот только как этот файл получить? IDA Pro может подготовить map-файл, но готовых конвертеров map2pdb в Сети что-то не наблюдается, а писать самому - лениво и непродуктивно.

К счастью, существует весьма простой и элегантный путь. Дизассемблируем бинарную сборку штатной утилитой ildasm.exe, после чего ассемблируем ее заново штатным же транслятором ilasm.exe, не забыв указать "волшебный" ключик /pdb для генерации отладочной информации. Поскольку ildasm.exe поддерживает ресурсы и корректно их дампит, то предложенный способ работает в подавляющем большинстве случаев, что мы сейчас и продемонстрируем.

Запускаем ildasm.exe с настройками по умолчанию, загружаем в него n2k_crackme_01h.exe (есно, оригинальный, а не хакнутый), в меню File находим пункт Dump (или нажимаем <CTRL-D>), в появившемся окне "Dump options" (см. рис. 10) оставляем все галочки в состоянии по умолчанию. Главное, чтобы была взведена галочка "Dump IL Code", после чего нажимаем <OK> и вводим имя файла для дампа - например, "cracked".

Дамп двоичной .NET-сборки

Рисунок 10. Дамп двоичной .NET-сборки в ассемблерный файл штатным дизассемблером ildasm.exe.

По окончании дизассемблирования на диске образуются два файла - cracked.il с ассемблерным текстом программы и cracked.res - с ресурсами. Cracked.il представляет собой обыкновенный текстовой файл, который можно править в любом текстовом редакторе, при необходимости заменяя "brtrue.s IL_0031" на "brfalse.s IL_0031", но сейчас нас в первую очередь интересует не патч, а отладка.

Берем штатный ассемблер и собираем файл следующим образом:

$ilasm.exe cracked.il /pdb

Листинг 7. Ассемблирование сдампленного файла штатным ассемблером.

На диске образуются файлы cracked.exe и cracked.pdb, готовые к загрузке в отладчик (что примечательно - отладочная информация непосредственно в сам исполняемый файл не записывается, что очень и очень хорошо, иначе нам пришлось бы потом убирать ее оттуда или мириться с увеличением размера поломанного exe, что вряд ли входит в наши планы).

Ок, набираем в командной строке "$mdbg.exe cracked.exe" и... оказываемся в консольном окне отладчика, автоматически останавливающегося на первой команде функции Main, передавая нам бразды правления. А что такого крутого и хорошего мы можем сделать?!

Начнем с просмотра окрестностей, за что отвечает команда "show" или ее более короткий алиас "sh", результат работы которого выглядит так (см. листинг 8):

run cracked.exe # запущена бинарная сборка cracked.exe
STOP: Breakpoint Hit # точка останова в функции main
43: IL_0000: nop # следующая выполняемая команда
[p#:0, t#:0] mdbg> sh # просим отладчик показать окрестности кода по "sh"
40 .maxstack 2
41 .locals init (string V_0, bool V_1)
43:* IL_0000: nop # команда выполняемая следующей
44 IL_0001: ldstr "enter password:"
45 IL_0006: call [mscorlib]System.Console::Write(string)
[p#:0, t#:0] mdbg>

Листинг 8. Результат работы команды "sh", показывающей IL-код.

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

Просматривая ассемблерный файл cracked.il, находим команду "IL_0020: stloc.1", стягивающую со стека результат сравнения двух строк, возвращенный функцией System.String::op_Equality, за которой следует команда "IL_0021: ldloc.1", загружающая полученное значение в локальную переменную V_1, в зависимости от содержимого которой команда "IL_0022:  brtrue.s IL_0031" прыгает на метку IL_0031 (неверный пароль) или... не прыгает. Все ясно! Нам нужно установить точку останова на команде "IL_0020: stloc.1", расположенной в 55-й строке файла cracked.il, ну а дальше мы уже сориентируемся (см. листинг 9).

$D:\KPNC\C#>mdbg cracked.exe # грузим бинарную сборку в отладчик
MDbg (Managed debugger) v3.5.21022.8 (RTM.021022-0800) started.
Copyright (C) Microsoft Corporation. All rights reserved.

run cracked.exe # отладчик запускает бинарную сборку
STOP: Breakpoint Hit # отладчик останавливается на main
43: IL_0000: nop # окрестности функции main
[p#:0, t#:0] mdbg> b 55 # брякаемся на строку 55
Breakpoint #1 bound (line 55 in cracked.il) # отладчик говорит, что с бряком все ОК
[p#:0, t#:0] mdbg> g # продолжаем выполнение программы
enter password:password # вводим в качестве пароля "password"
STOP: Breakpoint 1 Hit # !!! срабатывает наша точка останова
55: IL_0020: stloc.1 # мы брякнулись на команде stloc.1
[p#:0, t#:0] mdbg> p # просим отладчик распечатать переменные
V_0="password" # V_0 хранит введенный пароль
V_1=False # V_1 содержит значение False
args=array [0] # аргументы программы нам не интересны
[p#:0, t#:0] mdbg> n # выполняем следующую команду
56: IL_0021: ldloc.1 # следующая команда загружает V_1
[p#:0, t#:0] mdbg> p # печатаем содержимое переменных еще раз
V_0="password" # V_0 не изменилась
V_1=True # V_1 ИЗМЕНИЛАСЬ (вот где собака!!!)
args=array [0] # -//-
[p#:0, t#:0] mdbg> set V_1=0 # просим отладчик записать в V_1 число 0
V_1=False # теперь V_1 вновь содержит False
[p#:0, t#:0] mdbg> n # выполняем следующую команду
57: IL_0022: brtrue.s IL_0031 # а следующей команда у нас - ветвление!
[p#:0, t#:0] mdbg> n # ну, и куда это ветвление нас заведет?
59: IL_0024: ldstr "hello, master!" # после изменения V_1 мы на верном пути!
[p#:0, t#:0] mdbg> g # продолжаем выполнение программы
hello, master! # программа пишет "hello, master!"
STOP: Process Exited # процесс завершается
mdbg> q # выходим из отладчика

Листинг 9. Сеанс работы с отладчиком mdbg.exe (команды, вводимые хакером, выделены полужирным шрифтом).

Ниже, для наглядности тот же самый сеанс работы с отладчиком продемонстрирован в графическом виде (см. рис. 11):

Сеанс работы с отладчиком

Рисунок 11. Сеанс работы с отладчиком mdbg.exe "как он есть".

Если кому-то религия запрещает использовать консоль, что ж - к его услугам Dotnet IL Editor - бесплатный IL-отладчик с GUI-интерфейсом (см. рис. 12), однако mdbg.exe мыщъх'у как-то больше по душе, да к тому же под него расширения всякие можно писать.

Dotnet IL Editor

Рисунок 12. Dotnet IL Editor - IL-отладчик с GUI-интерфейсом.

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

Заключение

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

А пока же имеет смысл потренироваться на простых несильно защищенных коммерческих программах (которые можно найти в Сети), малвари (взятой оттуда же) и crackme, залежи которых находятся на сайте www.crackmes.de (см. рис. 13) и где даже есть специальный раздел, посвященный исключительно платформе .NET.

Мыщъх надеется, что данная статья обеспечит хороший старт, ну а остальное - дело времени, техники и бесчисленных экспериментов!

Коллекция .NET crackmes

Рисунок 13. Коллекция .NET crackmes на одноименном сайте.

Основные команды отладчика mdbg.exe

  • ?, help:
    вывод встроенной справки, "help команда" - подробная справка по команде;

  • a[ttach]:
    подключение к активному .NET-процессу;

  • b[reak]:
    установка точки останова или отображение уже существующих;

  • ca[tch]:
    просмотр событий(events), вызывающих останов отладчика;

  • conf[ig]:
    просмотр опций отладки/конфигурирования отладчика;

  • del[ete]:
    удаление точки останова;

  • de[tach]:
    отключение от отлаживаемого .NET-процесса;

  • g[o]:
    продолжение работы программы;

  • n[ext]:
    Step Over;

  • o[ut]:
    Steps Out;

  • s[tep]:
    Step Into;

  • p[rint]:
    вывод содержимого локальных переменных;

  • q[uit]:
    выход из программы;

  • r[un]:
    запуск программы под отладчиком;

  • set:
    изменение значение переменных;

  • setip:
    установка текущей выполняемой команды;

  • sh[ow]:
    показ окрестностей выполняемого кода;

Терминологическое болото

  • .NET:
    Древняя как мир идея двухстадийной компиляции: сначала программа (написанная хоть на Java, хоть на Visual Basic, хоть на Cи++, хоть на C#, хоть на F#) транслируется в промежуточный байт-код, который окончательно транслируется в "родной" двоичный код на конкретной целевой машине или же исполняется в режиме интерпретации;

  • CLR:
    Common Language Runtime ("общая среда выполнения языков") - компонент Microsoft .NET Framework, включающий себя виртуальную машину и необходимые библиотеки;

  • CIL:
    Common Language Infrastructure ("спецификация общеязыковой инфраструктуры"), определяющая архитектуру исполнительной системы, базовые классы, синтаксис и мнемонику байт-кода;

  • MSIL:
    Microsoft Intermediate Language ("промежуточный язык от Microsoft") - байт-код виртуальной .NET машины в реализации от Microsoft;

  • IL:
    Intermediate Language ("промежуточный язык") - байт-код виртуальной .NET машины, стандартизованный в рамках ECMA-335.
Категория: Крэкинг | Добавил: -=Hellsing=- (06.01.2010) | Автор: Крис Касперски
Просмотров: 19412 | Рейтинг: 2.5/2
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]