Авторские статьи Just-In-Time Debugging

Discussion in 'Статьи' started by _Great_, 5 Jan 2007.

  1. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    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. Это обеспечит следующая функция:
    Code:
    // Получение инфы о процессе по 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, который, очевидно, в пояснениях не нуждается.

    Code:
    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;
    }
    Результат:
    [​IMG]

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

    На этом позвольте откланиться, пока :)
     
    #1 _Great_, 5 Jan 2007
    Last edited: 13 Jan 2008
    4 people like this.
  2. ProTeuS

    ProTeuS --

    Joined:
    26 Nov 2004
    Messages:
    1,239
    Likes Received:
    542
    Reputations:
    445
    найс. намного удобнее, 4ем крутить окно от4етов мелкософта в поисках регистров (кто пробовал - поймут)

    ЗЫ: +
     
    1 person likes this.
  3. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
  4. hidden

    hidden 7H3 0N3

    Joined:
    23 Apr 2006
    Messages:
    550
    Likes Received:
    332
    Reputations:
    386
    Да, этот отчёт конечно гораздо более удобечетаемый чем стандартный, но предпочитаю:
    OllyDbg -> Options -> Just-In-Time Debugging
     
    1 person likes this.