воскресенье, 31 июля 2011 г.

Конкурс от журнала ПРОграммист. Начало отладчика для VM

Функцию проверки пароля я успешно изучил, и привел к такому виду:


В "Show Final Titles" я не вполне уверен, но коль скоро туда ведет проверка пароля на финальные титры, то буду думать что это так. Теперь буду смотреть, что бывает, когда функция проверки пароля возвращает 1. Кто ее вызывает, проверяет, и что после этого делает. Здесь уже не обойтись без отладчика. Так как, идущий в комплекте с эмулятором - довольно убогий, а трассировка с помощью OllyDbg довольно медленная, то придется писать свой. Конечно, полноценный отладчик для моих целей совсем не нужен, будут реализована только трассировка, и точки останова.
Ядро отладчика будет DLL, которую я внедрю в процесс эмулятора с помощью microsoft detours. Управляться отладчик будет из консоли. Обмен консоли с ядром будет осуществляться через named pipe.
Нужно реализовать следующие функции :
1) Создать точку останова по адресу (bpx hexaddr)
2) Проверить точки останова (bl)
3) Удалить точку останова (bc index)
4) Уведомление о достижении программой точки останова (инициируется ядром)
5) Трассировка от одной точки останова до другой (t b1 b2)
5) Пошаговое выполнение

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

lpszRemoteLibName = VirtualAllocEx(hProcess, NULL, dwBytesToWrite, MEM_COMMIT,
PAGE_READWRITE);

if (lpszRemoteLibName)
{
if (WriteProcessMemory(hProcess, lpszRemoteLibName, lpszLibName, dwBytesToWrite,
&dwBytesWritten)&& dwBytesWritten == dwBytesToWrite)
{
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary,
lpszRemoteLibName, 0, NULL);
if (hThread)
{
if (WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0)
{
GetExitCodeThread(hThread, (LPDWORD)&hLib);
}
CloseHandle(hThread);
}
}
VirtualFreeEx(hProcess, lpszRemoteLibName, 0, MEM_RELEASE);
}
CloseHandle(hProcess);


В памяти эмулятора выделяется память для имени файла DLL, которую я хочу загрузить. (В моем случае - ядро отладчика). В эту память записывается строка с именем. Затем, в том же эмуляторе, создается поток, телом которого будет функция LoadLibrary, а параметром - указатель на имя файла Dll. Вызвать функцию LoadLibrary в эмуляторе возможно оттого, что kernel32.dll одинаково отображается на все процессы, и мы можем знать адреса ее функций для любого из них.
Теперь мне нужно создать процедуру, которая взаимодействует с ядром через NamedPipe. Тут нужно определиться с протоколом. Сответственно задаче, которую я перед собой поставил, определяю следующие структуры:

//Запросы терминала
struct SetBreakpointCmd
{
static unsigned char cmd = 1;
unsigned long breakAddress;
};

struct CheckBreakpoinsCmd
{
static unsigned char cmd = 2;
};

struct RemoveBreakpointCmd
{
static unsigned char cmd = 3;
unsigned char breakpointIndex;
};

struct BreakpointGainedCmd
{
static unsigned char cmd = 4;
unsigned Long breakAddress;
};

struct SetTraceCmd
{
static unsigned char cmd = 5;
unsigned char FirstBreak;
unsigned char LastBreak;
};

struct SingleStepCmd
{
static unsigned char cmd = 6;
unsigned long Steps;
};

struct RunCmd
{
static unsigned char cmd = 7;
};

//Ответы ядра. Для простоты -
//ядро просто шлет текстовые
//строки в качестве ответа.

struct ServerAnswer
{
unsigned char message[BUF_SIZE];
};


Пишу функции отправки для каждой структуры.

Завтра продолжу :-(

1 комментарий:

  1. Гравиально!!!
    >>Завтра продолжу :-(
    2 с лишним года жду с нетерпением продолжений!!!

    ОтветитьУдалить