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.

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



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

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

    // 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

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.


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)

Want to learn more?? The InfoSec Institute CISSP Training course trains and prepares you to pass the premier security certification, the CISSP. Professionals that hold the CISSP have demonstrated that they have deep knowledge of all 10 Common Body of Knowledge Domains, and have the necessary skills to provide leadership in the creation and operational duties of enterprise wide information security programs.

InfoSec Institute's proprietary CISSP certification courseware materials are always up to date and synchronized with the latest ISC2 exam objectives. Our industry leading course curriculum combined with our award-winning CISSP training provided by expert instructors delivers the platform you need in order to pass the CISSP exam with flying colors. You will leave the InfoSec Institute CISSP Boot Camp with the knowledge and domain expertise to successfully pass the CISSP exam the first time you take it. Some benefits of the CISSP Boot Camp are:

  • Dual Certification - CISSP and ISSEP/ISSMP/ISSAP
  • We have cultivated a strong reputation for getting at the secrets of the CISSP certification exam
  • Our materials are always updated with the latest information on the exam objectives: This is NOT a Common Body of Knowledge review-it is intense, successful preparation for CISSP certification.
  • We focus on preparing you for the CISSP certification exam through drill sessions, review of the entire Common Body of Knowledge, and practical question and answer scenarios, all following a high-energy seminar approach.

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.

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;

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

Or alternatively we can use PEB to entry point.

typedef struct _LDR_DATA_TABLE_ENTRY {
    PVOID Reserved1[2];
    LIST_ENTRY InMemoryOrderLinks;
    PVOID Reserved2[2];
    PVOID DllBase;
    PVOID EntryPoint;
    PVOID Reserved3;
    BYTE Reserved4[8];
    PVOID Reserved5[3];
    union {
        ULONG CheckSum;
        PVOID Reserved6;
    ULONG TimeDateStamp;

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:


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);

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”, 0×01, 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:

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

		<a href="">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];

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, 0×01, NULL);

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

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

lcContext.ContextFlags = CONTEXT_ALL;

(pi.hThread, &lcContext);


SetThreadContext(pi.hThread, &lcContext);