lyyyuna 的小花园

动静中之动, by

RSS

Writing Your Own Windows Debugger - Debug Event

发表于 2017-05

We have introduced the debug loop last time, in this post, I will talk about various debug events.

RIP_EVENT

I find very few documents about this event, only mentioned with words like system error or internal error. So I decide to print a error message and skip it. As my project is not fully tested, I have never encountered such a situation.

OUTPUT_DEBUG_STRING_EVENT

When the debuggee calls the OutpuDebugString function, it will raise this debug event. The following structure describes the detail of this event:

typedef struct _OUTPUT_DEBUG_STRING_INFO {
  LPSTR lpDebugStringData;
  WORD  fUnicode;
  WORD  nDebugStringLength;
} OUTPUT_DEBUG_STRING_INFO, *LPOUTPUT_DEBUG_STRING_INFO;

With ReadProcessMemory function, the debugger can obtain the value of the string:

void OnOutputDebugString(const OUTPUT_DEBUG_STRING_INFO* pInfo) 
{
    BYTE* pBuffer = (BYTE*)malloc(pInfo->nDebugStringLength);

    SIZE_T bytesRead;

    ReadProcessMemory(
        g_hProcess,
        pInfo->lpDebugStringData,
        pBuffer, 
        pInfo->nDebugStringLength,
        &bytesRead);

    int requireLen = MultiByteToWideChar(
        CP_ACP,
        MB_PRECOMPOSED,
        (LPCSTR)pBuffer,
        pInfo->nDebugStringLength,
        NULL,
        0);

    TCHAR* pWideStr = (TCHAR*)malloc(requireLen * sizeof(TCHAR));

    MultiByteToWideChar(
        CP_ACP,
        MB_PRECOMPOSED,
        (LPCSTR)pBuffer,
        pInfo->nDebugStringLength,
        pWideStr,
        requireLen);

    std::wcout << TEXT("Debuggee debug string: ") << pWideStr <<  std::endl;

    free(pWideStr);
    free(pBuffer);
}

LOAD_DLL_DEBUG_EVENT

After the debuggee loads a dll, this debug event will be triggered. The following structure describes the detail of this event:

typedef struct _LOAD_DLL_DEBUG_INFO {
  HANDLE hFile;
  LPVOID lpBaseOfDll;
  DWORD  dwDebugInfoFileOffset;
  DWORD  nDebugInfoSize;
  LPVOID lpImageName;
  WORD   fUnicode;
} LOAD_DLL_DEBUG_INFO, *LPLOAD_DLL_DEBUG_INFO;

You may want to use the member lpImageName to retrieve the dll file name, however, it doesn't work. According the explaination on MSDN, this member is pointer to the file name of the associated hFile, it may, in turn, either be NULL or point to the actual filename. Even it is not NULL, ReadProcessMemory may also return a NULL. As a result, this membor is not reliable.

It seems that there is no direct Windows API to get the filename from the file handle. Someone has tried this way.

UNLOAD_DLL_DEBUG_EVENT

When a dll module is unloaded, this event will be triggered, nothing needs handled, just skip it.

CREATE_PROCESS_DEBUG_EVENT

After the process is created, this is the first debug event. The following structure describes the detail of this event:

typedef struct _CREATE_PROCESS_DEBUG_INFO {
  HANDLE                 hFile;
  HANDLE                 hProcess;
  HANDLE                 hThread;
  LPVOID                 lpBaseOfImage;
  DWORD                  dwDebugInfoFileOffset;
  DWORD                  nDebugInfoSize;
  LPVOID                 lpThreadLocalBase;
  LPTHREAD_START_ROUTINE lpStartAddress;
  LPVOID                 lpImageName;
  WORD                   fUnicode;
} CREATE_PROCESS_DEBUG_INFO, *LPCREATE_PROCESS_DEBUG_INFO;

We can use this structure to get the symbols of the debuggee program.

EXIT_PROCESS_DEBUG_EVENT

When debuggee process exits, this event will be triggered. The following structure describe the detail of the event:

typedef struct _EXIT_PROCESS_DEBUG_INFO {
  DWORD dwExitCode;
} EXIT_PROCESS_DEBUG_INFO, *LPEXIT_PROCESS_DEBUG_INFO;

What we can do is to print the exit code.

CREATE_THREAD_DEBUG_EVENT

It is similar to the process create debug event.

EXIT_THREAD_DEBUG_EVENT

It is similar to the process exit debug event.

EXCEPTION_DEBUG_EVENT

It is the most important event of our debugger, I will cover it in the next post.