PDA

Просмотр полной версии : Just-In-Time Debugging


_Great_
05.01.2007, 01:16
Title: Just-In-Time Debugging
Author: Great
Descr: Небольшая статья, в которой я опишу возможности jit-отладки процессов в Windows.

1. Что такое Just-In-Time Debugging?
В винде есть возможность установить специальную программу, которая будет "ловить" упавшие процессы (например, по исключению) и которая будет запускаться по кнопочке Debug в назойливом окне "The program has encountered a problem and needs to close". За путь к этой программе отвечает параметр реестра Debugger в ветке HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug. Там же есть и параметр Auto, задающий тип запуска программки. Подробнее об этом можно прочитать, например, в отличной книге М.Руссиновича и Д.Соломона "Внутреннее устройство MS Windows", а мы больше не будем акцентировать этому внимание.
Мы созданим свою программку, которая будет выполнять эту функцию. Скажете, сложно? Отнюдь!
Для начала пропишем путь к будущей программке, у меня он таков:
Debugger = "D:\Progs\dbgdump\Release\dbgdump.exe" -p %ld
причем в параметрах передадим ей число - PID (Process ID) упавшего процесса. Винда сама заполнит этот параметр. Приступим к написанию программы.

2. Недры нашей программы
Возьмем функции из моей статьи "Анализ стека" и воспользуемся ими здесь для того, чтобы вывести результаты анализа стека рухнувшего процесса. Еще мы выведем информацию о процессе, о потоке и дамп стека. Поехали!
Получение инфы о процессе будет очень простым. Мы сделаем снимок всех процессов системы и найдем наш рухнувший по его PID. Это обеспечит следующая функция:
// Получение инфы о процессе по ID
PROCESSENTRY32* GetProcess(DWORD pid)
{
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hSnapShot == INVALID_HANDLE_VALUE)
{
MessageBox(0, "CreateToolhelp32Snapshot() failed", "Dbgdump", MB_ICONERROR);
return 0;
}

PROCESSENTRY32* pe = new PROCESSENTRY32;
ZeroMemory(pe, sizeof(pe));
pe->dwSize = sizeof(*pe);
if(!Process32First(hSnapShot, pe))
{
MessageBox(0, "Unable to find any processes in the system", "Thread snapshot", MB_ICONERROR);
return 0;
}

bool found=0;
int count=0;
do
{
if(pe->th32ProcessID == pid)
return pe;
}
while(Process32Next(hSnapShot, pe));

return 0;
}

В основной функции WinMain нашей программы мы сделаем следующее:
- выделим из командной строки pid
- сделаем снимок потоков и найдем первый поток нашего процесса
- снимем контекст этого потока
- получим информацию о процессе
- выведем все это в окошке с прокручиваемым текстовым полем

Ниже приведен код функции WinMain, который, очевидно, в пояснениях не нуждается.

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR lpCmdLine,int)
{
stack = new char[10240];
DWORD dwProcessId;

sscanf(lpCmdLine, "-p %d", &dwProcessId);
if(!*lpCmdLine || !dwProcessId)
return MessageBox(0, "dbgdump usage:\ndbgdump -p [pid]\n\nCoded by Great Icq#893-894 (C) 2006", "Dbgdump", MB_ICONINFORMATION);

// Создаем снимок всех потоков системы
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if(hSnapShot == INVALID_HANDLE_VALUE)
return MessageBox(0, "CreateToolhelp32Snapshot() failed", "Dbgdump", MB_ICONERROR);

THREADENTRY32 te = {sizeof(te)};
if(!Thread32First(hSnapShot, &te))
return MessageBox(0, "Unable to find any threads in the system", "Thread snapshot", MB_ICONERROR);

bool found=0;
int count=0;
do
{
if(te.th32OwnerProcessID == dwProcessId)
{
found=1;
break;
}
}
while(Thread32Next(hSnapShot, &te));

if(!found)
return MessageBox(0, "Unable to find any thread belongs to the debugged process", "Dbgdump", MB_ICONERROR);

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, dwProcessId);
if(!hProcess)
return MessageBox(0, "Unable to open debuggee process", "Dbgdump", MB_ICONERROR);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, te.th32ThreadID);
if(!hThread)
return MessageBox(0, "Unable to open first thread belongs to the debugged process", "Dbgdump", MB_ICONERROR);

SuspendThread(hThread);
CONTEXT ctx = {CONTEXT_FULL};
GetThreadContext(hThread, &ctx);
ResumeThread(hThread);

char buf[10240];
PROCESSENTRY32* pe = GetProcess(dwProcessId);

DWORD exception=0;

wsprintf(buf, "Module '%s' has encountered a problem and needs to close\n\n"
"Number of threads: %d\n"
"Process ID: %d (0x%08x)\n"
"Exception code: 0x%08x\n"
//"Event code: 0x%08x\n"
"First thread ID: %d (0x%08x)\n"
"\n"
"First thread context:\n"
"EAX = 0x%08x\tEBX = 0x%08x\n"
"ECX = 0x%08x\tEDX = 0x%08x\n"
"EBP = 0x%08x\tESP = 0x%08x\n"
"ESI = 0x%08x\tEDI = 0x%08x\n"
"EIP = 0x%08x\tEFL = 0x%08x\n"
"\n"
,
pe->szExeFile,

pe->cntThreads,
dwProcessId, dwProcessId,
exception,
//event.dwDebugEventCode,
te.th32ThreadID, te.th32ThreadID,

ctx.Eax, ctx.Ebx,
ctx.Ecx, ctx.Edx,
ctx.Ebp, ctx.Esp,
ctx.Esi, ctx.Edi,
ctx.Eip, ctx.EFlags
);

wsprintf(buf+strlen(buf),
"Stack unwind for the first thread:\n"
"%s\n",
StackRemoteUnwind(dwProcessId, te.th32ThreadID, 150)
);

wsprintf(buf+strlen(buf), "Stack dump for the first thread:\n");
strcat(buf, Dump((LPVOID)stack, 256, ctx.Esp));
strcat(buf, "\n");

AlternateMessageBox(buf, "Dbgdump", 0, "Lucida console", 14, 750, 600);

TerminateProcess(hProcess, 0);

delete stack;
return 0;
}

Результат:
http://gr8.cih.ms/data/jit.png

Полный код можно найти тут: http://gr8.cih.ms/dbgdump.cpp

На этом позвольте откланиться, пока :)

ProTeuS
05.01.2007, 01:31
найс. намного удобнее, 4ем крутить окно от4етов мелкософта в поисках регистров (кто пробовал - поймут)

ЗЫ: +

_Great_
05.01.2007, 01:39
Кому надо, я залил бинарник моего творения: http://cribble.by.ru/dbgdump.exe (4k, packed by upack)

Юзаем :)

hidden
06.01.2007, 01:22
Да, этот отчёт конечно гораздо более удобечетаемый чем стандартный, но предпочитаю:
OllyDbg -> Options -> Just-In-Time Debugging