Анализ современного программного обеспечения
Современное программное обеспечение сочетает в себе как Native, так и Managed программный код. Из вышеуказанного видно, что удобный анализ на основе декомпилированного кода не всегда возможен, на основе дизассемблированных в код СIL листингов так же затруднителен. Можно заметить, что в общем случае, анализ высокоуровневого программного кода гораздо удобнее анализа CIL инструкций и Native ассемблера. Однако, было так же показано, что не во всех случаях высокоуровневый код является удобным для понимания.
Управляемые сборки хранят в своем составе множество метаинформации. Такая метаинформация используется различными декомпиляторами для восстановления структуры кода. Генерация управляемых средой CLR объектов происходит во время выполнения кода. К таким объектам относятся как структуры данных различных типов, так и различные функциональные объекты.
Распространенные расширения SOS и SOSEX для отладчика WinDBG, как показали исследования, имеют ряд недостатков. Задача анализа смешанного (Native и Managed) кода значительно усложняется в случае отсутствия доступа к файлам символов.
В [12] изложено введение в функционирование программного модуля создания Rut-Time объектов в платформе .NET Framework. Генерируемый JIT-компилятором код так же является динамически создаваемым объектом и располагается в куче. The just-in-time (JIT) compiler generates x86 instructions and stores them on the JIT Code Heap [12]. На рис. 5 изображен снимок распределения памяти, полученный утилитой VMMap пакета SysinternalsSuite [13]
Рис. 5. Снимок распределения памяти управляемого процесса
Из рис. 5 мы видим, что управляемая куча (Managed Heap) имеет установленные флаги, разрешающие выполнение (Execute), запись (Write) и чтение (Read) процессом. Отметим, что переход на 64-х битную архитектуру частично обосновывался и необходимостью в защите страниц памяти с применением NX-бита (XD-бита в терминологии Intel), запрещающего выполнение кода на страницах с данными [14].
Важно отметить, что генерируемый в режиме Run-Time код и данные не фиксированы по определенным участкам памяти и могут быть перемещены исполняющей средой в процессе работы. С другой стороны, CIL код, можно обнаружить с помощью отладчика отступив от базового адреса загрузки исполняемого модуля в память смещение секции кода и смещение функции. Это проиллюстрировано на рис. 6.
Рис 6. IL код функции main в Ida Pro (сверху) и снятый с дампа в WinDBG (внизу)
Заключение
Современное программное обеспечение совмещает в себе как Native, так и Managed программные модули. Кроме того, компилятор Microsoft Visual C++ позволяет формировать Mixed код, хранящий в одном исполняемом модуле и платформозависимый и байт-код. Существующие инструменты анализа смешанного (Native и Managed) кода являются в значительной степени разрозненными и не совершенными. С другой стороны, программное обеспечение, несмотря на наличие байт-кода в некоторых программных модулях, с помощью ряда методик организуется в однообразный (с точки зрения процессора) исполняемый машинный код. .NET сборки содержат в себе метаданные и код на языке CIL.
В ряде случаев доступ к исходным кодам исследуемого проекта затруднителен или невозможен. Кроме того, важно учитывать именно особенности фактически выполняемого кода, полученного, в том числе, после JIT компиляции. В связи с этим видится важным развитие средств анализа программного обеспечения, способных обрабатывать не только Native или Managed, но и Mixed сборки.
Список литературы
[1] http://ru.wikipedia.org/wiki/.NET_Framework (дата обращения: 01.10.2013)
[2] http://developer.android.com/sdk/index.html (дата обращения: 01.10.2013)
[3] https://www.hex-rays.com/products/ida/index.shtml (дата обращения: 01.10.2013)
[4] http://www.red-gate.com/products/dotnet-development/reflector/ (дата обращения: 01.10.2013)
[5] http://www.jetbrains.com/decompiler/ (дата обращения: 01.10.2013)
[6] Fuzzing. Исследование уязвимостей методом грубой силы, Автор: Майкл Саттон, Адам Грин, Педрам Амини, Издательство: Символ-Плюс, ISBN 978-5-93286-147-9; 2009 г.
[7] http://en.wikipedia.org/wiki/Assembler_(computing)#Assembler (дата обращения: 01.10.2013)
[8] http://msdn.microsoft.com/ru-ru/library/c1h23y6c.aspx (дата обращения: 02.10.2013)
[9] http://en.wikipedia.org/wiki/Common_Intermediate_Language (дата обращения: 02.10.2013)
[10] http://msdn.microsoft.com/ru-ru/library/f7dy01k1.aspx (дата обращения: 02.10.2013)
[11] http://technet.microsoft.com/en-us/sysinternals/bb842062.aspx (дата обращения: 02.10.2013)
[12] http://msdn.microsoft.com/en-us/magazine/cc163791.aspx (дата обращения: 02.10.2013)
[13] http://technet.microsoft.com/en-us/sysinternals/bb842062.aspx (дата обращения: 02.10.2013)
[14] http://ru.wikipedia.org/wiki/NX_bit (дата обращения: 02.10.2013)
Приложение 1. Программа из рис.1.
#include <Windows.h>
int main(int argc, char** argv)
{
MessageBox(0, TEXT("WinAPI message from unmanaged C/C++."), TEXT("Message"), MB_YESNO | MB_ICONQUESTION);
return 0;
}
Приложение 2. Программа из рис.2-3.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace CSharp_MessageBoxWinAPI
{
class Program
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
enum MsgType : ulong
{
MB_ABORTRETRYIGNORE = 0x00000002L,
MB_CANCELTRYCONTINUE = 0x00000006L,
MB_HELP = 0x00004000L,
MB_OK = 0x00000000L,
MB_OKCANCEL = 0x00000001L,
MB_RETRYCANCEL = 0x00000005L,
MB_YESNO = 0x00000004L,
MB_YESNOCANCEL = 0x00000003L
};
enum IconType : ulong
{
MB_ICONEXCLAMATION = 0x00000030L,
MB_ICONWARNING = 0x00000030L,
MB_ICONINFORMATION = 0x00000040L,
MB_ICONASTERISK = 0x00000040L,
MB_ICONQUESTION = 0x00000020L,
MB_ICONSTOP = 0x00000010L,
MB_ICONERROR = 0x00000010L,
MB_ICONHAND = 0x00000010L
};
static void Main(string[] args)
{
MessageBox(IntPtr.Zero, "WinAPI message from c#.", "Message", (uint)((ulong)MsgType.MB_YESNO | (ulong)IconType.MB_ICONQUESTION));
}
}
}
Приложение 3. Программа из рис.4.
#using <mscorlib.dll>
#include <stdio.h>
using namespace System;
#pragma unmanaged
void print(char *message)
{
printf("%s\n", message);
}
#pragma managed
int main()
{
Console::WriteLine("Managed write line.");
print("Unmanaged write line.");
Console::ReadLine();
return 0;
}