C#-diz

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

крис касперски ака мыщъх, no-email

победоносное шествие .NET-платформы по миру все продолжается и продолжается, а вменяемых руководств по взлому как-то не наблюдается… сегодня мы расскажем как дизассемблировать Visual Basic/C# сборки (включая Cи++ программы откомпилированные в байт-код), как их патчить в hiew'е, вести отладку на уровне байт-кода, словном, заправим мозговые баки свежей порцией авиационного керосина, выведем двигатели на взлетный режим и дадим хороший старт, написав свой собственный crackme и тут же взломав его всеми доступными средствами

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

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

  1. Visual Studio 2008 Express:
    1. последняя версия знаменитой «студии» от MS, распространяющаяся бесплатно и включающая в себя все необходимое для полноценной работы: трансляторы Visual Basic, C#, а так же новый компилятор Cи++ способный транслировать Си++ программы в байт-код. на W2K устанавливаться категорически отказалась, на Server 2003 встала с полпинка (off-line install занимает 894 Мбайт в .iso-формате): http://www.microsoft.com/express/download/__; Рисунок 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__;

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

  1. Standard ECMA-335/Common Language Infrastructure (CLI)/4th edition (June 2006):
    1. полня версия европейского стандарта, описывающая архитектуру виртуальной машины, байт-код, структуру ассемблерных сборок, в общем, солидный 556-страничный документ из серии «маст хэв» (на английском языке в формате pdf): www.ecma-international.org/publications/standards/Ecma-335.htm__ а вот прямая ссылка: www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf__;
  2. CIL Instruction Set:
    1. краткий справочник оп-кодов с «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__;
  3. Common Intermediate Language:
    1. поверхностное описание 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____/__;
  4. ilasm.exe:
    1. штатный .NET-ассемблер, входящий в комплект поставки MS VS и Mono: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ilasm.exe;
  5. ildasm.exe:
    1. штатный (и весьма недурственный) .NET-дизассемблер, входящий в комплект поставки MS VS (в Mono ничего подобного не входит, ну или я не нашел): C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\ildasm.exe;
  6. Thottam R. Sriram blog:
    1. блог одного из ведущих разработчиков 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__;
  7. Decompiler.NET 2005 v2.0.0.230 by Jungle Creatures:
    1. популярный коммерческий декомпилятор для .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__;
  8. Debugging IL:
    1. познавательная статья одного блоггера, разъясняющая все трудности отладки .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__;
  9. dotNet Protector:
    1. монстроузный и навороченный .NET-протектор, ужасно тормозит и хочет денег: http://dotnetprotector.pvlog.com/Downloads.aspx__; - how to unpack dotNet Protector: - статья, посвященная распаковке dotNet Protector'а (на английском языке): http://www.exetools.com/forum/showthread.php?t=7968__;
  10. YAP .Net:
    1. еще один протектор для .NET-программ, на этот раз бесплатный, но сильно сырой: forum.vingrad.ru/forum/topic-186025.html и sllh.net.ru/download/yapnet/yapnet.php;

Глубоководное погружение в байт-код .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 you, 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). Это и есть _весь_ машинный код, который только есть (простите за каламбур). Рисунок 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-сборку в нормальный дизассемблер. Рисунок 4 внешний вид .NET-сборки, полученной путем трансляции Си++ программы ===== техника дизассемблирование ===== Загружаем n2k_crackme_01h.exe в IDA Pro и видим (см. рис. 5), что ничего ужасного в CIL-коде нет. Напоминает байт-код виртуальной Java-машины. IDA Pro не только создает перекрестные ссылки, но даже показывает опкоды и расставляет комментарии к командам, чтобы не было нужны каждый раз заглядывать в справочник (ECMA-335/Partition III/CIL Instruction Set). Рисунок 5 консольная версия IDA Pro дизассемблирует .NET сборку Впрочем, чтобы заставить дизассемблер быть более дружелюбным к хакеру, необходимо выполнить следующие действия: в меню «Options» выбрать пункт «Text representation», там указать количество байт для отображения опкода («Number of opcode bytes») — шести хватит вполне, а в разделе «Line prefixes»сбросить все галочки, кроме «Function offsets», после чего вновь возвратиться в меню «Options», зайти в «Comments»и взвести «Display auto comments»для автоматического отображения комментариев ко всем инструкциям (впрочем, при наличии некоторого опыта работы с CIL-кодом это можно и не делать). Поклонники графической версии IDA Pro могут задействовать графы (см. рис. 6), упрощающие (на самом деле — усложняющие) понимание структуры программы, но тут уж как говориться, на вкус и цвет все фломастеры разные. Лично мыщъх никогда не пользовался графами и другим не советует. Рисунок 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 atRVA 0x2050 Code size61 (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 you, 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 int32modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) main() cil managed { .vtentry 1 :1 Code size59 (0x3b) .maxstack2 .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» Рисунок 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# команда выполняемая следующей 44IL_0001:ldstr«enter password:» 45IL_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 мыщъх'у как-то больше по душе, да к тому же под него расширения всякие можно писать. Рисунок 12 Dotnet IL Editor – IL-отладчик с GUI-интерфейсом Впрочем, выбор отладчика непринципиален. Важна сама суть — техника исследования .NET-программ, которую мы только что и продемонстрировали. ===== заключение ===== Разумеется, в рамках одной-единственной статьи просто невозможно охватить все аспекты взлома .NET-программ. В частности, совершенно нетронутой осталась тема упаковщиков бинарных сборок и протекторов, распаковывать которые приходится руками, но это по любому тема отдельного большого разговора. А пока же имеет смысл потренироваться на простых несильно защищенных коммерческих программах (которые можно найти в Сети), малвари (взятой оттуда же) и crackme, залежи которых находятся на сайте www.crackmes.de (см. рис. 13) и где даже есть специальный раздел, посвященный исключительно платформе .NET. Мыщъх надеется, что данная статья обеспечит хороший старт, ну а остальное — дело времени, техники и бесчисленных экспериментов! Рисунок 13 коллекция .NET crackmes на одноименном сайте ===== »> врезка основные команды отладчика mdbg.exe ===== - ?, help: - вывод встроенной справки, «help команда» — подробная справка по команде; - a[ttach]: - подключение к активному .NET-процессу; - b[reak]: - установка точки останова или отображение уже существующих; - ca[tch]: - просмотр событий (evens), вызывающих останов отладчика; - 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: - древняя как мир идея 2х стадийной компиляции: сначала программа (написанная хоть на 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;