General security

Dive into Microsoft DBG API

June 26, 2014 by SecRat

Microsoft provides an inbuilt API for debugging Windows executables. With the power of Win32 Debugging API, you can create a custom debugger according to your needs. You can handle breakpoints, create breakpoints, handle Dll loads and unloads, and more.

Windows Debugging API works in the basics of debugging events. On each event in the debugged process, an event is called. For example, when a break point is hit, EXCEPTION_BREAKPOINT is called. Similarly, when an exception is raised, EXCEPTION_ACCESS_VIOLATION is called. All of these cases are handled either by the debugger or debugged process.

Setting Up the Executable

In order to debug a program, we need to create the process in a DEBUG_ONLY_THIS_PROCESS flag, which will create the process in debugging mode. We can also combine it with a suspended flag in order to set up breakpoints at the entry point of the loaded application.

[c]
BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);

(http://msdn.microsoft.com/en-us/library/windows/desktop/ms682512(v=vs.85).aspx)

STARTUPINFO si;
PROCESS_INFORMATION pi;

ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );

if( argc != 2 )
{
printf("Usage: %s [cmdline]n", argv[0]);
return;
}

// Start the child process.
if( !CreateProcess( NULL, // No module name (use command line)
argv[1], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_SUSPENDED | DEBUG_ONLY_THIS_PROCESS, // No creation flags
NULL, // Use parent’s environment block
NULL, // Use parent’s starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
[/c]

This sample code snippet will create a suspended process in debugging mode.

Attaching to a Process

Attachment is similar to creating one and the debugger gets its notification in the DebugBreak Function, which contains the int 0xcc code to tonitify to the debugger. This should be treated as the first case chance in the process. DebugActiveProcess() can be used to attach to a running process, and we need to supply the process ID to it.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms679295(v=vs.85).aspx

Parameters

DwProcessId [in]

The identifier for the process to be debugged. The debugger is granted debugging access to the process as if it created the process with the DEBUG_ONLY_THIS_PROCESS flag. For more information, see the Remarks section of this topic.

Getting the EP (Entry Point)

For getting the entry point of the process, we can either go though the PEB or parse the required PE structure. The PE structure is well defined in winnt.h. We can create instances of those structures and parse the PE file into a structure to get the desired entry point.

[c]
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//

WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
[/c]

AddressOfEntryPoint can be used to get the address of entry point of the debugged application.

Or alternatively we can use PEB to entry point.

[c]
typedef struct _LDR_DATA_TABLE_ENTRY {
PVOID Reserved1[2];
LIST_ENTRY InMemoryOrderLinks;
PVOID Reserved2[2];
PVOID DllBase;
PVOID EntryPoint;
PVOID Reserved3;
UNICODE_STRING FullDllName;
BYTE Reserved4[8];
PVOID Reserved5[3];
union {
ULONG CheckSum;
PVOID Reserved6;
};
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
[/c]

The entry point variable can be used to get the EP of the main module during loadtime.

Please take care of the order of loaded modules in different OS versions.

Before the execution of entry point, a special function DebugBreak() is executed by Windows loader in order to give control over to the debugger. This function raises a software breakpoint. A special variable known as first_occurrence is initialized to keep track of this breakpoint. This breakpoint is known as SYSTEM BREAKPOINT. It happens in kernel32.dll.

The following code snippet can be used to get the EP:

[c]
IMAGE_DOS_HEADER DosHdr = {0};
IMAGE_FILE_HEADER FileHdr = {0};
IMAGE_OPTIONAL_HEADER OptHdr = {0};

fread(&DosHdr, sizeof(IMAGE_DOS_HEADER), 0x01, fp);

fseek(fp, (unsigned int)DosHdr.e_lfanew + 4,SEEK_SET);

fseek(fp, sizeof(IMAGE_FILE_HEADER), SEEK_CUR);

fread( &OptHdr, sizeof(IMAGE_OPTIONAL_HEADER) , 0x01, fp);

return (unsigned int*)(OptHdr.AddressOfEntryPoint + OptHdr.ImageBase);
[/c]

This function will return the address of the EP of the debugged application. In order to catch the application at the EP, we will setup a software break point at the EP.

WriteProcessMemory(<processHandle>,<enrtypoint>,”xcc”, 0x01, NULL);

Register Manipulation

We can manipulate registers using SetThreadContext and GetThreadContext API calls.

lcContext.ContextFlags = CONTEXT_ALL;

GetThreadContext(pi.hThread, &lcContext);

The parameter to these functions is the pointer to CONTEXT. CONTEXT is defined as:

[c]
http://www.nirsoft.net/kernel_struct/vista/CONTEXT.html

typedef struct _CONTEXT
{
ULONG ContextFlags;
ULONG Dr0;
ULONG Dr1;
ULONG Dr2;
ULONG Dr3;
ULONG Dr6;
ULONG Dr7;

<a href="http://www.nirsoft.net/kernel_struct/vista/FLOATING_SAVE_AREA.html">FLOATING_SAVE_AREA</a> FloatSave;
ULONG SegGs;
ULONG SegFs;
ULONG SegEs;
ULONG SegDs;
ULONG Edi;
ULONG Esi;
ULONG Ebx;
ULONG Edx;
ULONG Ecx;
ULONG Eax;
ULONG Ebp;
ULONG Eip;
ULONG SegCs;
ULONG EFlags;
ULONG Esp;
ULONG SegSs;
UCHAR ExtendedRegisters[512];
} CONTEXT, *PCONTEXT;
[/c]

We can manipulate and retrieve all these registers using those two functions. Using SetThreadContext:

lcContext.EAX = 0xdeadfeef;

SetThreadContext(pi.hThread, &lcContext);

In this example we changed EAX to 0xdeafbeef. This is how we can manipulate registers in the remote process. Similarly we can change Stack pointers like ESP and EBP.

Handling Breakpoints

We can also use software breakpoints on the remote process. For software breakpoints we can

replace the instruction with 0xcc op code, which stands for a software breakpoint. It is handled by the EXCEPTION_BREAKPOINT case statement.

One thing to notice here is before replacing the original instruction by 0xcc opcode, we need to save the original instruction, and when the breakpoint is hit, we need to replace it back and decrement the EIP so that the original instruction can be executed again.

ReadProcessMemory(pi.hProcess ,pEntryPoint, &OrgByte, 0x01, NULL);

WriteProcessMemory(pi.hProcess ,pEntryPoint,”xcc”, 0x01, NULL);

Similarly, when the breakpoint is hit, we can change the instruction to original instruction.

lcContext.ContextFlags = CONTEXT_ALL;

(pi.hThread, &lcContext);

lcContext.Eip–;

SetThreadContext(pi.hThread, &lcContext);

FlushInstructionCache(pi.hProcess,DebugEv->u.Exception.ExceptionRecord.ExceptionAddress,1);

Posted: June 26, 2014
SecRat
View Profile

SecRat works at a start-up. He's interested in Windows Driver Programming.