blabberer

A Simple Dbgeng Based User Mode Debugger

Rate this Entry


A Simple Dbgeng Based User Mode Debugger


I wrote a simple plugin to ollydbg v 2.0 it is called olly_lkd it mimics the windbg local kernel debugging functionality I simply had hooked and hacked and used lot of magic constants while I wrote it though it works ( I hope so no real feedback on its bugs / problems were received in spite of it being downloaded quite a few times ) I did not post the source as it was a mess

this tutorial is an attempt to demystify the magic behind dbgeng and its com based sorcery
once you shed the fear of com /c++ / its magic interfaces and classes you will appreciate
how good the framework is and why windbg survives the latest tech when its contemporaries have withered away

the documentation to these functions are also dry in either the help (debugger.chm) that comes along with the windbg documentation or in MSDN and you don�t get a lot of help in the wild alleys of internet on how to use these functions only obscure
*i<template foo **ppv pWhatever = QuerySomecrap (** Scooby doo) snippets surface and understanding them is a task in itself and even if you believe you understood the snippet you are still left scratching your head on implementing it to your present task.

Okay enough whining.
Lets get to the task in hand

As they say the whole journey starts with a single step (yep single stepping is basic requirement for traveling any where or debugging anything )

The first step you put out is IDebugClient I wont bore you by copy pasting the lines from windbg.chm or msdn read them yourself and read it several times they aren�t easy to digest if you have coded only asm or simple c programs and aren�t familiar with the component object model (COM ) interfaces

See what the document says about client objects

Almost all interaction with the debugger engine is through client objects, often simply referred to as clients. Each client provides an implementation of the top-level engine interfaces. Each interface provides a different set of methods, which can be used to interact with the engine and, through the engine, the targets An instance of the engine can have many clients, each with its own state.
so you first create a client and to create a client you use DebugCreate ();


if you have a bit of reversing spirit in your blood stream then you might know that DebugCreate() is one of the few Functions Exported by Dbgeng.dll
Code:
HRESULT
  DebugCreate(
    IN REFIID  InterfaceId,
    OUT PVOID *  Interface
    );
Code:
ParametersInterfaceIdSpecifies the interface identifier (IID) of the desired debugger engine client interface. This is the type of the interface that will be returned in Interface. For information about the interface identifier, see COM Interfaces 
Interface
Receives an interface pointer for the new client. The type of this interface is specified by InterfaceId.


this is how you call it
#include <dbgeng.h>
declare a pointer to IDebugClient and receive the interface pointer from DebugCreate()

Code:
IDebugClient*    g_Client                        = NULL;
HRESULT                   Status                           = 0;
 
            if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client)) != S_OK)
            {
                        Exit(1, "DebugCreate failed, 0x%X\n", Status);
            }
so the lines above call DebugCreate with the interface id of IDebugClient and receive a pointer to it in g_Client if the Function failed it exits the program by returning a code
freeing any prior resources that were allotted and printing a message

the exit function is shown below


Code:
void Exit(int Code, PCSTR Format, ...)
{
       if (g_Client != NULL)
       {
              g_Client->EndSession(DEBUG_END_PASSIVE);
              g_Client->Release();
              g_Client = NULL;
       }
       if (Format != NULL)
       {
              va_list Args;
              va_start(Args, Format);
              vfprintf(stderr, Format, Args);
              va_end(Args);
       }
       exit(Code);
}
at the moment it is enough to say you have an IUnknown Com Interface in g_Client

Code:
DEFINE_GUID(IID_IDebugClient, 0x27fe5639, 0x8407, 0x4f47, 0x83, 0x64, 0xee, 0x11, 0x8f, 0xb0, 0x8a, 0xc8);
/* edbed635-372e-4dab-bbfe-ed0d2f63be81 */
 
typedef interface DECLSPEC_UUID("27fe5639-8407-4f47-8364-ee118fb08ac8")
    IDebugClient* PDEBUG_CLIENT;

and you can use QueryInterface to query these com interfaces from g_Client

Code:
IDebugAdvanced
IDebugClient
IDebugControl
IDebugDataSpaces
IDebugRegisters
IDebugSymbols
IDebugSystemObjects
so if you want IDebugControl

you simply query for it by declaring a pointer to IDebugControl

Code:
            IDebugControl* g_Control = NULL;
 
       if ((Status = g_Client->QueryInterface(__uuidof(IDebugControl),      (void**)&g_Control)) != S_OK )
       {
              Exit(1, "QueryInterface failed, 0x%X\n", Status);
       }
notice we exit using the same old Exit() Function
so obviously you need to release g_Control as well when you are done with it

Code:
void Exit(int Code, PCSTR Format, ...)
{
       if (g_Control != NULL)
       {
              g_Control->Release();
              g_Control = NULL;
       }
       if (g_Client != NULL)
       {
              g_Client->EndSession(DEBUG_END_PASSIVE);
              g_Client->Release();
              g_Client = NULL;
       }
       if (Format != NULL)
       {
              va_list Args;
              va_start(Args, Format);
              vfprintf(stderr, Format, Args);
              va_end(Args);
       }
       exit(Code);
}
each of these interfaces implement few methods
one of it is Addref() and another one is Release()

if you have noticed g_Control is released first and g_Client is released later
the reason behind being quoted verbatim from docs


The reference count will be initialized to one when the client object is created using DebugCreate

When IUnknown::QueryInterface is called, the reference count is incremented, so when a client interface pointer is no longer needed IUnknown::Release should be called to decrement the reference count.
we start by calling DebugCreate() for a Client and then call QueryInterface for further interfaces

Apart from
Code:
#define INTERFACE IDebugClient
DECLARE_INTERFACE_(IDebugClient, IUnknown)
{
    // IUnknown.
    STDMETHOD(QueryInterface)(
        THIS_
        __in REFIID InterfaceId,
        __out PVOID* Interface
        ) PURE;
    STDMETHOD_(ULONG, AddRef)(
        THIS
        ) PURE;
    STDMETHOD_(ULONG, Release)(
        THIS
        ) PURE;
IDebugClient has a few other methods too read the doc or MSDN for various methods
There are also enhanced version of
IDebugClient named

DECLARE_INTERFACE_(IDebugClient2, IUnknown) till
DECLARE_INTERFACE_(IDebugClient5, IUnknown)


We will discuss IDebugClient::CreateProcess in this tutorial

You will absorb further methods and enhancements when you start traveling down the road

Lets now try coding a simple exe that gets only g_Client and uses one of its methods.
GetIdentity();

Code:
 
HRESULT
  IDebugClient::GetIdentity(
    OUT OPTIONAL PSTR  Buffer,
    IN ULONG  BufferSize,
    OUT OPTIONAL PULONG  IdentitySize
    );


create a new folder named JustClient
create a new file named JustClient.cpp inside the newly created folder
you can use notepad / wordpad / vim / your own text editor/ but I would use Visual Studio Express to code and will compile and link it using a batch file and wdk separately

C:\>md JustClient
C:\>cd JustClient
C:\JustClient>copy con JustClient.cpp
^Z
1 file(s) copied.
C:\JustClient>"C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\VCExpress.exe"
C:\JustClient>
In vcexpress2010 using
new
project from existing code
project file location c:\justClient
project name JustClient
next next....... finish
in the source files you will have JustClient.cpp already added and you can start coding

#include <windows.h>
#include <DbgEng.h>

void __cdecl main(void)
{
IDebugClient * g_Client = NULL;
HRESULT Status = NULL;
if (( Status = g_Client->Get
and at this point using a visual studio project for coding shows up
Name:  vchelp.JPG
Views: 7353
Size:  24.8 KB

lets finish the code

Code:
#include <stdio.h>
#include <dbgeng.h>
 
void __cdecl main (void)
{
        IDebugClient*   g_Client = NULL;
        HRESULT                 Status  = 0;
        if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client)) != S_OK)
        {
                printf("DebugCreate failed, 0x%X\n", Status);
                exit(1);
        }
        char    IDBuffer [0x100];
        unsigned long   IdentitySize;
        if (( Status = g_Client->GetIdentity((PSTR)&IDBuffer,sizeof(IDBuffer),&IdentitySize) ) != S_OK)
        {
                printf("g_Client->GetIdentity, 0x%X\n", Status);
                exit(1);
        }
        printf("%s\n",IDBuffer);
        exit(0);
}
now to compile and link it we will use wdk and not visual studio

add two more files to c:\JustClient named
Sources (no extension) and Prebuild.bat


The contents of bat file should be as follows

C:\JustClient>type prebuild.bat
pushd ..
set DBGSDK_INC_PATH=F:\windbg\sdk\inc
set DBGSDK_LIB_PATH=F:\windbg\sdk\lib
set DBGLIB_LIB_PATH=F:\windbg\sdk\lib
@call C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ fre x86 WXP
popd
build -cZMg
copy .\objfre_wxp_x86\i386\*.exe .\test\. /y

C:\JustClient>

All it does is sets the lib path calls xp free build environemt from wdk 7 and builds the target
And copies the built executable to another folder named test
The last step is done because we need to have the latest windbg dlls always in a local folder or the executable might pull some old dlls in system32 directory and fail as they may not contain the needed advanced interfaces that we might use in the dlls that were shipped inbox
In the folder named test copy paste all the dlls from windbg installation folder
dbgeng.dll dbghelp.dll ext.dll exts.dll kdexts.dll kext.dll ntsdexts.dll symsrv.dll uext.dll wdfkd.dll


in the file name sources you should have

TARGETNAME = JustClient
TARGETTYPE = PROGRAM
_NT_TARGET_VERSION=$(_NT_TARGET_VERSION_WINXP)
!if "$(DBGSDK_INC_PATH)" != ""
INCLUDES = $(DBGSDK_INC_PATH);$(INCLUDES)
!endif
!if "$(DBGSDK_LIB_PATH)" == ""
DBGSDK_LIB_PATH = $(SDK_LIB_PATH)
!else
DBGSDK_LIB_PATH = $(DBGSDK_LIB_PATH)\$(TARGET_DIRECTORY)
!endif

TARGETLIBS = \
$(DBGSDK_LIB_PATH)\dbgeng.lib\
$(SDK_LIB_PATH)\user32.lib\
$(SDK_LIB_PATH)\gdi32.lib\
$(SDK_LIB_PATH)\kernel32.lib

USE_MSVCRT = 1

SOURCES = \
JustClient.cpp

MSC_WARNING_LEVEL = /W3 /WX

UMTYPE = console
UMENTRY = main




On completing the above steps your tree will be as follows

C:\JustClient>tree /f /a
C:.
| JustClient.cpp
| JustClient.sln
| JustClient.vcxproj
| JustClient.vcxproj.filters
| JustClient.vcxproj.user
| prebuild.bat
| sources
|
\---test
dbgeng.dll
dbghelp.dll
ext.dll
exts.dll
kdexts.dll
kext.dll
ntsdexts.dll
symsrv.dll
uext.dll
wdfkd.dll



C:\JustClient>run the prebuild.bat file and you should have a compiled binary available in test folder


C:\JustClient>tree /f /a
C:.
| buildfre_wxp_x86.log
| JustClient.cpp
| JustClient.sln
| JustClient.vcxproj
| JustClient.vcxproj.filters
| JustClient.vcxproj.user
| prebuild.bat
| sources
|
+---objfre_wxp_x86
| \---i386
| JustClient.exe
| justclient.obj
| JustClient.pdb
| vc90.pdb
| _objects.mac
|
\---test
dbgeng.dll
dbghelp.dll
ext.dll
exts.dll
JustClient.exe
kdexts.dll
kext.dll
ntsdexts.dll
symsrv.dll
uext.dll
wdfkd.dll



C:\JustClient> navigate to test directory and run the JustClient.exe


C:\JustClient\test>JustClient.exe
NonPrimaryClient\NoIdentity

C:\JustClient\test>

g_Client returned the Identity successfully and it is correct as per document
So we are now a step closer to use the other interfaces

Primary Clients

A primary client is a client that has joined the current debugging session. Initially, when a new client object is created, it is not a primary client. A client becomes a primary client when it is used to acquire a target (for example, by calling CreateProcess2 or is connected to the debugging session using ConnectSession[/URL].


so unless we acquire a target this isn�t going to return anything usefull lets see if g_Client has any other methods that can be called independent of other interfaces

these 3 methods doesn't seem to be dependent on any other interfaces

GetRunningProcessDescription
GetRunningProcessSystemIds
GetRunningProcessSystemIdByExecutableName[/URL]


Lets try them


Open the JustClient.sln

Split the GetIdentity to a separate function so that we can reuse it
Make the IDebugClient * g_Client Global
so that the split function as well as the new function which we are going to code
can use it

the JustClient.cpp will look like this
we do it like this so that we can use the same old sources file and makefile and also we can reuse the functions when we want it

Code:
#include <stdio.h>
#include <dbgeng.h>
 
void MyGetIdentity (void);
void MyGetRunningProcessSystemIds (void);
 
IDebugClient* g_Client = NULL;
HRESULT                    Status = 0;
 
void __cdecl main (void)
{
       if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client)) != S_OK)
       {
              printf("DebugCreate failed, 0x%X\n", Status);
              exit(1);
       }
       MyGetIdentity();
       MyGetRunningProcessSystemIds();
       exit(0);
}
 
void MyGetIdentity (void)
{
       char   IDBuffer [0x100];
       unsigned long IdentitySize;
       if (( Status = g_Client->GetIdentity((PSTR)&IDBuffer,sizeof(IDBuffer),&IdentitySize) ) != S_OK)
       {
              printf("g_Client->GetIdentity, 0x%X\n", Status);
              exit(1);
       }
       printf("%s\n",IDBuffer);
}
 
void MyGetRunningProcessSystemIds (void)
{
       ULONG Ids[0x30]                   = {0};
       ULONG Count                = 0x30;
       ULONG ActualCount    = 0;
       if ((g_Client->GetRunningProcessSystemIds(NULL,Ids,Count,&ActualCount) ) != S_OK) 
       {
              printf("DebugCreate failed, 0x%X\n", Status);
              exit(1);
       }
       printf (
              "ActualCount of Process Ids For Local Computer is 0n%d \n",
              ActualCount
              );
       for (ULONG i = 0; i< ActualCount;i++)
       {
              printf("process id 0n%02d is\t0n%04d\n",i,Ids[i]);
       }
}
run the prebuild.bat to compile and link and run the exe that was copied to test folder

the results as follows

C:\JustClient\test>JustClient.exe
NonPrimaryClient\NoIdentity

ActualCount of Process Ids For Local Computer is 0n33
process id 0n00 is 0n0000
process id 0n01 is 0n0033
process id 0n02 is 0n0512
XXXXXXXXXXXXXXXXXXXXXXXXxx
process id 0n31 is 0n3504
process id 0n32 is 0n0952

C:\JustClient\test>

That worked fine and now you can understand how to use the IDebugClient Methods lets code GetRunningProcessDescription based on the Ids

Code:
#include <stdio.h>
#include <dbgeng.h>
 
void MyGetIdentity (void);
void MyGetRunningProcessSystemIds (void);
void MyGetRunningProcessDescription (void);
 
IDebugClient* g_Client = NULL;
HRESULT        Status = 0;
 
void __cdecl main (void)
{
       if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client)) != S_OK)
       {
              printf("DebugCreate failed, 0x%X\n", Status);
              exit(1);
       }
       //MyGetIdentity();
       //MyGetRunningProcessSystemIds();
       MyGetRunningProcessDescription();
       exit(0);
}
 
void MyGetIdentity (void)
{
       char   IDBuffer [0x100];
       unsigned long IdentitySize;
       if (( Status = g_Client->GetIdentity((PSTR)&IDBuffer,sizeof(IDBuffer),&IdentitySize) ) != S_OK)
       {
              printf("g_Client->GetIdentity Failed, 0x%X\n", Status);
              exit(1);
       }
       printf("%s\n",IDBuffer);
}
 
void MyGetRunningProcessSystemIds (void)
{
       ULONG Ids[0x50]            = {0};
       ULONG Count                = 0x50;
       ULONG ActualCount    = 0;
       if ((g_Client->GetRunningProcessSystemIds(NULL,Ids,Count,&ActualCount) ) != S_OK) 
       {
              printf("g_Client->GetRunningProcessSystemIds Failed, 0x%X\n", Status);
              exit(1);
       }
       printf (
              "ActualCount of Process Ids For Local Computer is 0n%d \n",
              ActualCount
              );
       for (ULONG i = 0; i< ActualCount;i++)
       {
              printf("process id 0n%02d is\t0n%04d\n",i,Ids[i]);
       }
}
 
void MyGetRunningProcessDescription (void)
{
       ULONG Ids[0x50]            = {0};
       ULONG Count                = 0x50;
       ULONG ActualCount    = 0;
       char  ExeName[0x100] = {0};
       ULONG ActualExeNameSize = 0;
       char Description[0x1000] = {0};
       ULONG ActualDescriptionSize = 0;
 
       if ((g_Client->GetRunningProcessSystemIds(NULL,Ids,Count,&ActualCount) ) != S_OK) 
       {
              printf("g_Client->GetRunningProcessSystemIds Failed, 0x%X\n", Status);
              exit(1);
       }
       printf (
              "ActualCount of Process Ids For Local Computer is 0n%d \n",
              ActualCount
              );
       for (ULONG i = 0; i < ActualCount;i++)
       {
              if (( g_Client->GetRunningProcessDescription(
                     NULL,
                     Ids[i],
                     NULL,
                     ExeName,
                     sizeof(ExeName),
                     &ActualExeNameSize,
                     Description,
                     sizeof(Description),
                     &ActualDescriptionSize
                     ) ) != S_OK) 
              {
                     printf("g_Client->GetRunningProcessDescription Failed, 0x%X\n", Status);
                     return;
              }
              printf("process id 0n%02d is\t0n%04d\n%s\n%s\n",i,Ids[i],ExeName,Description);
       }
}
and you get result as follows

C:\JustClient\test>JustClient.exe
ActualCount of Process Ids For Local Computer is 0n37
process id 0n00 is 0n0000
System Process

process id 0n01 is 0n0004
System

process id 0n02 is 0n0512
\SystemRoot\System32\smss.exe
Session: 0 User: NT AUTHORITY\SYSTEM Command Line: \SystemRoot\System32\smss.exe

process id 0n03 is 0n0564
\??\C:\WINDOWS\system32\csrss.exe
Session: 0 User: NT AUTHORITY\SYSTEM Command Line: C:\WINDOWS\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 Windows=On SubSystemType
=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 Serv
erDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16

process id 0n04 is 0n0588
\??\C:\WINDOWS\system32\winlogon.exe
Session: 0 User: NT AUTHORITY\SYSTEM Command Line: winlogon.exe

process id 0n05 is 0n0632
C:\WINDOWS\system32\services.exe
Session: 0 User: NT AUTHORITY\SYSTEM Services: Eventlog,PlugPlay Command Line
: C:\WINDOWS\system32\services.exe
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
C:\WINDOWS\system32\dllhost.exe
Session: 0 User: NT AUTHORITY\SYSTEM Services: COMSysApp MTS Packages: System Application Command Line: C:\WINDOWS\system32\dllhost.exe /Processid:{02D4B3F1-FD88-11D1-960D-00805FC79235}



Read the documentation / dbgeng.h include /MSDN for other methods of IDebugClient
Enhanced IDebugClient Interfaces like 1,2,3,4,5 offers few more refinements like Unicode enabled functions option to add Process options etc

Lets wrap up IDebugClient Here and concentrate on other COM INTERFACES like
IDebugControl
Summary Until Here


  1. You Start With IDebugClient
  2. To get an InterFace Pointer to IDebugClient you use DebugCreate Function which initializes the reference count to 1
  3. Further interfaces can be Queried from IDebugClient Interface using QueryInterface Function which increments the reference count
  4. So we should Release all other interfaces we queried from IDebugClient before we release IDebugClient IDebugClient must be Released last
  5. IDebugClient methods that don�t depend on other interfaces for delivering proper results can be called for immediately
  6. IDebugClient Methods that depend on the presence of other interfaces can be called after Querying the appropriate Interfaces




Lets Create a process now we will use argv[] to input the name of exe to be used for create process
So lets change void main (void) to

void __cdecl main(int Argc, char* Argv[])

lets first createprocess and see if GetIdentity() returns an Identity or not and if the process is created or not

Code:
#include <stdio.h>
#include <dbgeng.h>
 
void MyGetIdentity (void);
void MyGetRunningProcessSystemIds (void);
void MyGetRunningProcessDescription (void);
void MyCreateProcess (char *exename);
 
IDebugClient* g_Client = NULL;
HRESULT                    Status = 0;
 
void __cdecl main(int Argc, char* Argv[])
{
       if (Argc != 2)
       {
              printf("provide path of exe\n");
              exit(0);
       }
       if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client)) != S_OK)
       {
              printf("DebugCreate failed, 0x%X\n", Status);
              exit(1);
       }
       MyCreateProcess(Argv[1]);
       MyGetIdentity();
       //MyGetRunningProcessSystemIds();
       //MyGetRunningProcessDescription();
       if (g_Client != NULL)
       {
              g_Client->EndSession(DEBUG_END_PASSIVE);
              g_Client->Release();
              g_Client = NULL;
       }
       exit(0);
}
 
void MyGetIdentity (void)
{
       char   IDBuffer [0x100];
       unsigned long IdentitySize;
       if (( Status = g_Client->GetIdentity((PSTR)&IDBuffer,sizeof(IDBuffer),&IdentitySize) ) != S_OK)
       {
              printf("g_Client->GetIdentity Failed, 0x%X\n", Status);
              exit(1);
       }
       printf("%s\n",IDBuffer);
}
 
void MyGetRunningProcessSystemIds (void)
{
       ULONG Ids[0x50]            = {0};
       ULONG Count                = 0x50;
       ULONG ActualCount    = 0;
       if ((g_Client->GetRunningProcessSystemIds(NULL,Ids,Count,&ActualCount) ) != S_OK) 
       {
              printf("g_Client->GetRunningProcessSystemIds Failed, 0x%X\n", Status);
              exit(1);
       }
       printf (
              "ActualCount of Process Ids For Local Computer is 0n%d \n",
              ActualCount
              );
       for (ULONG i = 0; i< ActualCount;i++)
       {
              printf("process id 0n%02d is\t0n%04d\n",i,Ids[i]);
       }
}
 
void MyGetRunningProcessDescription (void)
{
       ULONG Ids[0x50]            = {0};
       ULONG Count                = 0x50;
       ULONG ActualCount    = 0;
       char  ExeName[0x100] = {0};
       ULONG ActualExeNameSize = 0;
       char Description[0x1000] = {0};
       ULONG ActualDescriptionSize = 0;
 
       if ((g_Client->GetRunningProcessSystemIds(NULL,Ids,Count,&ActualCount) ) != S_OK) 
       {
              printf("g_Client->GetRunningProcessSystemIds Failed, 0x%X\n", Status);
              exit(1);
       }
       printf (
              "ActualCount of Process Ids For Local Computer is 0n%d \n",
              ActualCount
              );
       for (ULONG i = 0; i < ActualCount;i++)
       {
              if (( g_Client->GetRunningProcessDescription(
                     NULL,
                     Ids[i],
                     NULL,
                     ExeName,
                     sizeof(ExeName),
                     &ActualExeNameSize,
                     Description,
                     sizeof(Description),
                     &ActualDescriptionSize
                     ) ) != S_OK) 
              {
                     printf("g_Client->GetRunningProcessDescription Failed, 0x%X\n", Status);
                     return;
              }
              printf("process id 0n%02d is\t0n%04d\n%s\n%s\n",i,Ids[i],ExeName,Description);
       }
}
 
void MyCreateProcess (char *exename)
{
 
       if ((Status = g_Client->CreateProcess(0, exename, DEBUG_ONLY_THIS_PROCESS)) != S_OK)
       {
              printf("CreateProcess failed, 0x%X\n", Status);
              exit(1);
       }
       printf("Createprocess suceeded we can now start waiting for events\n");
}
result


C:\JustClient\test>JustClient.exe msgbox.exe

Createprocess suceeded we can now start waiting for events
HostMachine\HostUser


C:\JustClient\test>

Yes the createprocess succeeded and the Getidentity returned an Identity this time
But where is the created process ?? we didn�t see the msgbox popping up did we ?
It doesn�t show up in taskmgr too why ??


That is because we have not called WaitForEvent() as documented.

Note The engine doesn't completely attach to the process until the WaitForEvent method has been called. Only after the process has generated an event for example, the process creation event does it become available in the debugger session.
WaitForEvent is a method of IDebugControl so to call it we need to use QueryInterface
Also if an event occurs we need to have an event callback handler so that we can handle the event

Lets start doing we create a event callback class

A basic event callback implementation consist of
A class with methods the implementation of methods and a declaration

AddRef(); and Release(); are methods that are required
We can implement other methods as we need
We will implement CreateProcess method and GetInterestMask method which is needed if we have to implement CreateProcess method

It will be as follows and we will register this eventcallback using SetEventCallback method in IDebugControl

Code:
class EventCallbacks : public DebugBaseEventCallbacks
{
public:
       STDMETHOD_(ULONG, AddRef)         ( THIS );
 
       STDMETHOD_(ULONG, Release)        ( THIS );
 
       STDMETHOD(CreateProcess)          ( THIS_ 
IN ULONG64 ImageFileHandle, 
IN ULONG64 Handle,
IN ULONG64 BaseOffset,
IN ULONG ModuleSize, 
IN PCSTR ModuleName, 
IN PCSTR ImageName, 
IN ULONG CheckSum, 
IN ULONG TimeDateStamp,
IN ULONG64 InitialThreadHandle,
IN ULONG64 ThreadDataOffset,
IN ULONG64 StartOffset                   );
 
STDMETHOD(GetInterestMask)        ( THIS_ OUT PULONG Mask );
};
 
STDMETHODIMP_(ULONG) EventCallbacks::AddRef(    THIS )
{
       return 1;
};
STDMETHODIMP_(ULONG) EventCallbacks::Release( THIS )
{
       return 0;
};
 
STDMETHODIMP EventCallbacks::CreateProcess( THIS_ 
IN ULONG64 ImageFileHandle,
IN ULONG64 Handle,
IN ULONG64 BaseOffset,
IN ULONG ModuleSize,
IN PCSTR ModuleName,
IN PCSTR ImageName,
IN ULONG CheckSum,
IN ULONG TimeDateStamp,
IN ULONG64 InitialThreadHandle,
IN ULONG64 ThreadDataOffset,
IN ULONG64 StartOffset                          )
{
       printf (
              "EventCallbacks::CreateProcess called\n"
              "%s\n%s\n",
              ModuleName,
              ImageName
              );
       return DEBUG_STATUS_BREAK;
};
 
STDMETHODIMP EventCallbacks::GetInterestMask( THIS_ OUT PULONG Mask )
{
       *Mask = DEBUG_EVENT_CREATE_PROCESS
              return S_OK;
}
 
EventCallbacks                    g_EventCb;
If you want to implement Breakpoint Event call back
You need to add it to class and implement the method
And so on there are 14 methods that can be implemented in event callbacks
Read the documentation for the details of the methods
Like

Code:
STDMETHOD(CreateProcess)          ( THIS_  XXX ,XXX. XXX ) inside class
Implementation like
STDMETHODIMP EventCallbacks::CreateProcess( THIS_ XXX,XXX,XXX )
{
XXXX
XXX
}


the whole code with all above included is as below

Code:
#include <stdio.h>
#include <dbgeng.h>
 
void MyGetIdentity (void);
void MyCreateProcess (char *exename);
 
IDebugClient* g_Client = NULL;
IDebugControl*  g_Control = NULL;
HRESULT                    Status = 0;
 
class EventCallbacks : public DebugBaseEventCallbacks
{
public:
       STDMETHOD_(ULONG, AddRef)         ( THIS );
       STDMETHOD_(ULONG, Release)        ( THIS );
       STDMETHOD(CreateProcess)          ( THIS_ IN ULONG64 ImageFileHandle, 
              IN ULONG64 Handle, IN ULONG64 BaseOffset, IN ULONG ModuleSize,
              IN PCSTR ModuleName, IN PCSTR ImageName, IN ULONG CheckSum, IN ULONG TimeDateStamp, 
              IN ULONG64 InitialThreadHandle,   IN ULONG64 ThreadDataOffset, IN ULONG64 StartOffset );
       STDMETHOD(GetInterestMask)        ( THIS_ OUT PULONG Mask );
};
STDMETHODIMP_(ULONG) EventCallbacks::AddRef(    THIS )
{
       return 1;
};
STDMETHODIMP_(ULONG) EventCallbacks::Release( THIS )
{
       return 0;
};
STDMETHODIMP EventCallbacks::CreateProcess(
       THIS_ IN ULONG64 ImageFileHandle, IN ULONG64 Handle, IN ULONG64 BaseOffset,IN ULONG ModuleSize,
       IN PCSTR ModuleName, IN PCSTR ImageName,IN ULONG CheckSum, IN ULONG TimeDateStamp,
       IN ULONG64 InitialThreadHandle, IN ULONG64 ThreadDataOffset,  IN ULONG64 StartOffset 
       )
{
       printf (
              "EventCallbacks::CreateProcess called\n"
              "module name is %s\nimage name is %s\n",
              ModuleName,
              ImageName
              );
       return DEBUG_STATUS_NO_CHANGE;
};
STDMETHODIMP EventCallbacks::GetInterestMask( THIS_ OUT PULONG Mask )
{
       *Mask = DEBUG_EVENT_CREATE_PROCESS;
       return S_OK;
}
EventCallbacks                    g_EventCb;
void __cdecl main(int Argc, char* Argv[])
{
       if (Argc != 2)
       {
              printf("provide path of exe\n");
              exit(0);
       }
       if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client)) != S_OK)
       {
              printf("DebugCreate failed, 0x%X\n", Status);
              exit(1);
       }
       if ((Status = g_Client->QueryInterface(__uuidof(IDebugControl),      (void**)&g_Control)) != S_OK )
       {
              printf( "QueryInterface failed, 0x%X\n", Status);
              exit(1);
       }
       if ((Status = g_Client->SetEventCallbacks       (&g_EventCb)) != S_OK)
       {
              printf("g_Client->SetEventCallbacks failed\n");
              exit(1);
       }
       MyCreateProcess(Argv[1]);
 
       if (g_Control != NULL)
       {
              g_Control->Release();
              g_Control = NULL;
       }
       if (g_Client != NULL)
       {
              g_Client->EndSession(DEBUG_END_PASSIVE);
              g_Client->Release();
              g_Client = NULL;
       }
       exit(0);
}
void MyGetIdentity (void)
{
       char   IDBuffer [0x100];
       unsigned long IdentitySize;
       if (( Status = g_Client->GetIdentity((PSTR)&IDBuffer,sizeof(IDBuffer),&IdentitySize) ) != S_OK)
       {
              printf("g_Client->GetIdentity Failed, 0x%X\n", Status);
              exit(1);
       }
       printf("%s\n",IDBuffer);
}
 
void MyCreateProcess (char *exename)
{
       if ((Status = g_Client->CreateProcess(0, exename, DEBUG_ONLY_THIS_PROCESS)) != S_OK)
       {
              printf("CreateProcess failed, 0x%X\n", Status);
              exit(1);
       }
       printf("Createprocess suceeded we can now start waiting for events\n");
       MyGetIdentity();
       if ( ( Status = g_Control->WaitForEvent( DEBUG_WAIT_DEFAULT, INFINITE ) )!=S_OK)
       {
              if (Status == E_UNEXPECTED)
              {
                     printf("target finished execution and exited\n");
              }
              else
              {
                     printf("g_Control->WaitForEvent failed, 0x%X\n", Status);
              }             
       }      
}
this time the msgbox popped up right
we have also hit out createprocess event callback which returned DEBUG_STATUS_NO_CHANGE



result

C:\JustClient\test>JustClient.exe msgbox.exe
Createprocess suceeded we can now start waiting for events
HostMachine\HostUser
EventCallbacks::CreateProcess called
module name is msgbox
image name is msgbox.exe
target finished execution and exited


C:\JustClient\test>


Since we have correctly created a process and have hit our callbacks
We can start implementing OutputCallbacks and Inputcallbacks
We can set a handler to catch ctrl+c and break into our debugger
And we can let the debugger engine interpret our input and execute the input
And all other things a good debugger should be capable of

The collapsed code snap below shows
The declaration for globals IDebugClient,IDebugControl,IDebugBreakpoint,and HRESULT
3 class for 3 callbacks event , output, input
16 methods for event callbacks (1) AddRef,2) Release, 3) breakpoint, 4) CreateProcess, 5)ExitProcess,6) exception, 7)createThread,8)Exitthread,9)LoadModule, 10)UnloadModule,
11)SystemError,12)GetInterestMask,13)changeEngineState,14ChangeDebuggeeState,15)ChangeSessionState,1 6)ChangeSymbolState
3 methods for output callbacks 1)AddRef,2)Release,3)Output
4 methods for input callbacks 1) Addref, 2) Release , StartInput, Endinput

handler for console control event
and a
main that takes an argument for exename
creates interfaces
sets event callbacks
creates the process
wait for events
and process the events in an loop taking input and delivering output as appropriate
till the debuggee quits


the full code copy pasted below he snap shot

and the result of a dry run pasted below the code

use the same sources file and makefile posted earlier in the article to compile and build this code

run the compiled binary in a test folder that has all the latest windbg dlls
or run it in windbg installation folder
Name:  a simple debugger.JPG
Views: 1614
Size:  150.2 KB

Code:
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <dbgeng.h>
IDebugClient*        g_Client              = NULL; // globals
IDebugControl*      g_Control            = NULL; // globals
IDebugBreakpoint* g_Breakpoint        = NULL; // globals
bool                       State                  = NULL; // globals
void Exit(int Code, PCSTR Format, ...)          // release the interfaces on last in first out  basis
{
       if (g_Control != NULL)                                 // print message if given 
       {
              g_Control->Release();                           // and exit the program
              g_Control = NULL;
       }
       if (g_Client != NULL)
       {
              g_Client->EndSession(DEBUG_END_PASSIVE);
              g_Client->Release();
              g_Client = NULL;
       }
       if (Format != NULL)
       {
              va_list Args;
              va_start(Args, Format);
              vfprintf(stderr, Format, Args);
              va_end(Args);
       }
       exit(Code);
};
class EventCallbacks : public DebugBaseEventCallbacks   // event callback class has 16 methods
{
public:
       STDMETHOD_(ULONG, AddRef)         ( THIS );
       STDMETHOD_(ULONG, Release)        ( THIS );
       STDMETHOD(Breakpoint)                  ( THIS_ IN PDEBUG_BREAKPOINT Bp );
       STDMETHOD(ChangeDebuggeeState) ( THIS_ IN ULONG Flags, IN ULONG64  Argument );
       STDMETHOD(ChangeEngineState)      ( THIS_ IN ULONG Flags, IN ULONG64  Argument );
       STDMETHOD(ChangeSymbolState)     ( THIS_ IN ULONG Flags, IN ULONG64  Argument );
       STDMETHOD(CreateThread)              ( THIS_ IN ULONG64  Handle, IN ULONG64  DataOffset,IN ULONG64  StartOffset);
       STDMETHOD(Exception)              ( THIS_ IN PEXCEPTION_RECORD64 Exception, IN ULONG FirstChance );
       STDMETHOD(ExitProcess)                   ( THIS_ IN ULONG  ExitCode );
       STDMETHOD(ExitThread)                    ( THIS_ IN ULONG  ExitCode );
       STDMETHOD(GetInterestMask)        ( THIS_ OUT PULONG Mask );
       STDMETHOD(SessionStatus)          ( THIS_ IN ULONG Status );
       STDMETHOD(SystemError)                   ( THIS_ IN ULONG  Error, IN ULONG  Level );
       STDMETHOD(UnloadModule)                  ( THIS_ IN PCSTR  ImageBaseName, IN ULONG64  BaseOffset );
       STDMETHOD(LoadModule)                    (  THIS_ IN ULONG64 ImageFileHandle, IN ULONG64 BaseOffset, 
              IN ULONG ModuleSize,  IN PCSTR ModuleName,IN PCSTR ImageName,  IN ULONG CheckSum, 
              IN ULONG TimeDateStamp );
       STDMETHOD(CreateProcess)          ( THIS_ IN ULONG64 ImageFileHandle, IN ULONG64 Handle, IN ULONG64 BaseOffset, IN ULONG ModuleSize, IN PCSTR ModuleName, IN PCSTR ImageName, IN ULONG CheckSum, 
IN ULONG TimeDateStamp,  IN ULONG64 InitialThreadHandle,   IN ULONG64 ThreadDataOffset, 
IN ULONG64 StartOffset );
};
class StdioOutputCallbacks : public IDebugOutputCallbacks  // output callback class 3 methods
{
public:
       STDMETHOD(QueryInterface)( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface );
       STDMETHOD_(ULONG, AddRef)( THIS );
       STDMETHOD_(ULONG, Release)(THIS );
       STDMETHOD(Output)( THIS_ IN ULONG Mask, IN PCSTR Text );
};
class StdioInputCallbacks : public IDebugInputCallbacks  // input callback class 4 methods
{
public:
       STDMETHOD(QueryInterface)( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface );
       STDMETHOD_(ULONG, AddRef)( THIS );
       STDMETHOD_(ULONG, Release)(THIS );
       STDMETHOD(StartInput)( THIS_ IN ULONG  BufferSize );
       STDMETHOD(EndInput)( THIS_ void );
};
STDMETHODIMP_(ULONG) EventCallbacks::AddRef(    THIS )  // event callback method 1 
{
       return 1;
}
STDMETHODIMP_(ULONG) EventCallbacks::Release( THIS )  //event callback method 2 
{
       return 0;
}
STDMETHODIMP EventCallbacks::Breakpoint( THIS_ IN PDEBUG_BREAKPOINT Bp ) //event callback method 3 
{
       return DEBUG_STATUS_BREAK;
};
STDMETHODIMP EventCallbacks::CreateProcess(     THIS_ IN ULONG64 ImageFileHandle, IN ULONG64 Handle, 
       IN ULONG64 BaseOffset,IN ULONG ModuleSize,IN PCSTR ModuleName,IN PCSTR ImageName, 
       IN ULONG CheckSum, IN ULONG TimeDateStamp,IN ULONG64 InitialThreadHandle, 
       IN ULONG64 ThreadDataOffset,  IN ULONG64 StartOffset 
       ) //event callback method 4  and so on till method 16
{
       HRESULT Status             = 0; //we are setting a break point in Address Of Entrypoint of the debuggee
       if (( Status = g_Control->AddBreakpoint(DEBUG_BREAKPOINT_CODE,DEBUG_ANY_ID,&g_Breakpoint)) == S_OK)
       {
              if (( Status = g_Breakpoint->SetOffset(StartOffset)) == S_OK)
              {
                     if (( Status = g_Breakpoint->SetFlags(DEBUG_BREAKPOINT_ENABLED)) == S_OK)
                     {
                           if (( Status = g_Breakpoint->SetCommand(
                                  ".echo This is a Bp From DBGENGDBG on Address of EntryPoint")) == S_OK)
                           {
                                  return DEBUG_STATUS_NO_CHANGE;
                           }
                           printf("SetFlags failed %x\n",Status);
                     }
                     printf("Setoffset failed %x\n",Status);
              }
              printf("AddBp failed %x\n",Status);
       }
       return DEBUG_STATUS_NO_CHANGE;
}

STDMETHODIMP EventCallbacks::CreateThread( THIS_ IN ULONG64  Handle, IN ULONG64  DataOffset, IN ULONG64  StartOffset )
{
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP EventCallbacks::Exception( THIS_ IN PEXCEPTION_RECORD64 Exception, IN ULONG FirstChance )
{
       return DEBUG_STATUS_BREAK;
} 
STDMETHODIMP EventCallbacks::ExitProcess (THIS_ IN ULONG  ExitCode )
{
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP EventCallbacks::ExitThread (THIS_ IN ULONG  ExitCode )
{
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP EventCallbacks::GetInterestMask( THIS_ OUT PULONG Mask )
{
       *Mask =
              DEBUG_EVENT_BREAKPOINT |
              DEBUG_EVENT_EXCEPTION |
              DEBUG_EVENT_CREATE_THREAD |
              DEBUG_EVENT_EXIT_THREAD |
              DEBUG_EVENT_CREATE_PROCESS |
              DEBUG_EVENT_EXIT_PROCESS |
              DEBUG_EVENT_LOAD_MODULE |
              DEBUG_EVENT_UNLOAD_MODULE |
              DEBUG_EVENT_SYSTEM_ERROR |
              DEBUG_EVENT_SESSION_STATUS |
              DEBUG_EVENT_CHANGE_DEBUGGEE_STATE |
              DEBUG_EVENT_CHANGE_ENGINE_STATE |
              DEBUG_EVENT_CHANGE_SYMBOL_STATE;
       return S_OK;
}
STDMETHODIMP EventCallbacks::LoadModule( THIS_ IN ULONG64 ImageFileHandle, IN ULONG64 BaseOffset, 
       IN ULONG ModuleSize,IN PCSTR ModuleName, IN PCSTR ImageName, IN ULONG CheckSum, IN ULONG TimeDateStamp )
{
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP EventCallbacks::SystemError( THIS_ IN ULONG  Error, IN ULONG  Level )
{
       return DEBUG_STATUS_BREAK;
}
STDMETHODIMP EventCallbacks::UnloadModule( THIS_ IN PCSTR  ImageBaseName, IN ULONG64  BaseOffset )
{
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP EventCallbacks::SessionStatus( THIS_ IN ULONG SessionStatus )
{
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP EventCallbacks::ChangeDebuggeeState( THIS_ IN ULONG Flags, IN ULONG64 Argument )
{
       State = 1;
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP EventCallbacks::ChangeEngineState( THIS_ IN ULONG Flags, IN ULONG64 Argument )
{
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP EventCallbacks::ChangeSymbolState( THIS_ IN ULONG Flags, IN ULONG64 Argument ) // method 16
{
       return DEBUG_STATUS_NO_CHANGE;
}
STDMETHODIMP StdioOutputCallbacks::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface ) 
{
       //outputevent callbacks 3 methods
       *Interface = NULL;
       if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
              IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))
       {
              *Interface = (IDebugOutputCallbacks *)this;
              AddRef();
              return S_OK;
       }
       else
       {
              return E_NOINTERFACE;
       }
}
STDMETHODIMP_(ULONG) StdioOutputCallbacks::AddRef( THIS       )  // method 1
{
       return 1;
}
STDMETHODIMP_(ULONG) StdioOutputCallbacks::Release(    THIS ) // method 2
{
       return 0;
}
STDMETHODIMP StdioOutputCallbacks::Output( THIS_ IN ULONG Mask, IN PCSTR Text ) // method 3
{
       UNREFERENCED_PARAMETER(Mask);
       fputs(Text, stdout);
       return S_OK;
}
STDMETHODIMP StdioInputCallbacks::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface )
{
       //inputevent callbacks 4 methods
       *Interface = NULL;
       if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
              IsEqualIID(InterfaceId, __uuidof(IDebugInputCallbacks)))
       {
              *Interface = (IDebugInputCallbacks *)this;
              AddRef();
              return S_OK;
       }
       else
       {
              return E_NOINTERFACE;
       }
}
STDMETHODIMP_(ULONG) StdioInputCallbacks::AddRef( THIS )
{
       return 1;
}
STDMETHODIMP_(ULONG) StdioInputCallbacks::Release(     THIS )
{
       return 0;
}
STDMETHODIMP StdioInputCallbacks::StartInput( THIS_ IN ULONG  BufferSize )
{
       char *Inbuff = (char *)malloc(BufferSize+10);
       memset(Inbuff,0,BufferSize+10);
       fgets(Inbuff,BufferSize,stdin);
       g_Control->ReturnInput(Inbuff);
       free(Inbuff);
       return S_OK;
}
STDMETHODIMP StdioInputCallbacks::EndInput( THIS_ void) // method 4
{
       return S_OK;
}
StdioOutputCallbacks g_OutputCb;
StdioInputCallbacks        g_InputCb;
EventCallbacks                    g_EventCb;
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)  // control + c handler
{
       switch ( dwCtrlType )
       {
              case CTRL_C_EVENT:
                     g_Control->SetInterrupt(DEBUG_INTERRUPT_ACTIVE);
                     printf( 
                           "You Pressed Ctrl-C\n"
                           "if you did not intend to break enter g to continue execution"
                           );
                     return TRUE;
              default:
                     return FALSE;
       }
}
void __cdecl main(int Argc, char* Argv[])
{
       HRESULT              Status        = 0;
       ULONG         InputSize     = 0;
       ULONG         ExecStatus    = 0;
       char          Buff[0x105];
       memset(Buff,0,sizeof(Buff));  
       if (Argc != 2) // looks for exe name
       {
              Exit(0, "provide path of exe\n");
       }
       if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client)) != S_OK) // Createinterfaces
       {
              Exit(1, "DebugCreate failed, 0x%X\n", Status);
       }
       if ((Status = g_Client->QueryInterface(__uuidof(IDebugControl),      (void**)&g_Control)) != S_OK ) // Createinterfaces
       {
              Exit(1, "QueryInterface failed, 0x%X\n", Status);
       }
       if (
              ((Status = g_Client->SetEventCallbacks   (&g_EventCb)) != S_OK) ||   // Register Callbacks
              ((Status = g_Client->SetOutputCallbacks  (&g_OutputCb))       != S_OK) ||
              ((Status = g_Client->SetInputCallbacks   (&g_InputCb)) != S_OK)
              )
       {
              Exit(1, "Setting of Callbacks failed\n");
       }
       SetConsoleCtrlHandler(&HandlerRoutine,TRUE);  // Set control +c handler 
       if ((Status = g_Client->CreateProcess(0, Argv[1], DEBUG_ONLY_THIS_PROCESS)) != S_OK)  // create a process 
       {
              Exit(1, "CreateProcess failed, 0x%X\n", Status);
       }
       Status = g_Control->WaitForEvent( DEBUG_WAIT_DEFAULT, INFINITE );  // wait for event
       while (TRUE) // spin till debuggee exits for processing events
       {
              if ( ( g_Control->GetExecutionStatus(&ExecStatus) )!=S_OK)
              {
                     printf("GetExecutionStatus failed\n");
                     break;                                                 // quit while loop if GetExecutionStatus failed
              }
              if (ExecStatus == DEBUG_STATUS_NO_DEBUGGEE) 
              {
                     printf ("no Debuggee\n");
                     break;                                                 // quit while loop if no debugee
              }
              if (
                     ExecStatus == DEBUG_STATUS_GO                   || 
                     ExecStatus == DEBUG_STATUS_STEP_OVER     || 
                     ExecStatus == DEBUG_STATUS_STEP_OVER     ||
                     ExecStatus == DEBUG_STATUS_STEP_INTO     || 
                     ExecStatus == DEBUG_STATUS_STEP_BRANCH
                     )
              {
                     if ( ( Status = g_Control->WaitForEvent( DEBUG_WAIT_DEFAULT, INFINITE )) == S_OK)
                     {
                           continue;  // do not prompt do not stop on the above execution status
                     }
                     if ( Status != E_UNEXPECTED )
                     {
                           printf ("Wait For Event in mainloop failed\n");
                           break;  // quit while loop on a fatal error in waitforevent
                     }
              }
              if (State == 1)  // print register disassembly for 1 line , effective address and symbols
              {
                     State = 0;
                     g_Control->OutputCurrentState(DEBUG_OUTCTL_THIS_CLIENT, DEBUG_CURRENT_DEFAULT);
              }
              g_Control->OutputPrompt(DEBUG_OUTCTL_THIS_CLIENT,NULL); // set the prompt
              if (( g_Control->Input(Buff,(sizeof(Buff)-5),&InputSize) ) != S_OK)  // get input when broken
              {
                     Exit( 0,"g_Control->Input Failed\n");
              }
              else if (InputSize)
              {
                     g_Control->Execute(DEBUG_OUTCTL_THIS_CLIENT,Buff,DEBUG_EXECUTE_DEFAULT); // execute the input 
              }
       }
       Exit(0, "Finished Debugging Quitting\n"); // print this msg and exit program
}
result


F:\dbgengdbg\test>dbgengdbg.exe msgbox.exe

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: msgbox.exe
Symbol search path is: SRV*F:\symbols*http://msdl.microsoft.com/download/symbols

Executable search path is:
ModLoad: 00400000 00404000 msgbox.exe
ModLoad: 7c900000 7c9b2000 ntdll.dll
ModLoad: 7c800000 7c8f6000 C:\WINDOWS\system32\kernel32.dll
ModLoad: 64d00000 64d3c000 C:\Program Files\Alwil Software\Avast5\snxhk.dll
ModLoad: 7e410000 7e4a1000 C:\WINDOWS\system32\user32.dll
ModLoad: 77f10000 77f59000 C:\WINDOWS\system32\GDI32.dll
ModLoad: 5cb70000 5cb96000 C:\WINDOWS\system32\ShimEng.dll
ModLoad: 76390000 763ad000 C:\WINDOWS\system32\IMM32.DLL
ModLoad: 77dd0000 77e6b000 C:\WINDOWS\system32\ADVAPI32.dll
ModLoad: 77e70000 77f02000 C:\WINDOWS\system32\RPCRT4.dll
ModLoad: 77fe0000 77ff1000 C:\WINDOWS\system32\Secur32.dll
This is a Bp From DBGENGDBG on Address of EntryPoint
eax=00000000 ebx=7ffdd000 ecx=0013ffb0 edx=7c90e514 esi=00000000 edi=00000000
eip=00401000 esp=0013ffc4 ebp=0013fff0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
msgbox!start:
00401000 6a00 push 0
0:000>bp 401013
0:000>bl
0 e 00401000 0001 (0001) 0:**** msgbox!start ".echo This is a Bp From DBGE
NGDBG on Address of EntryPoint"
1 e 00401013 0001 (0001) 0:**** msgbox!start+0x13
0:000>kb
ChildEBP RetAddr Args to Child
0013fff0 00000000 00401000 00000000 78746341 msgbox!start
0:000>g
ModLoad: 5ad70000 5ada8000 C:\WINDOWS\system32\uxtheme.dll
ModLoad: 77c10000 77c68000 C:\WINDOWS\system32\msvcrt.dll
ModLoad: 77c00000 77c08000 C:\WINDOWS\system32\version.dll
ModLoad: 77b40000 77b62000 C:\WINDOWS\system32\apphelp.dll
ModLoad: 755c0000 755ee000 C:\WINDOWS\system32\msctfime.ime
ModLoad: 774e0000 7761d000 C:\WINDOWS\system32\ole32.dll
You Pressed Ctrl-C
if you did not intend to break enter g to continue execution(f4c.f18): Break ins
truction exception - code 80000003 (first chance)

eax=7ffdd000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c90120e esp=003dffcc ebp=003dfff4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
ntdll!DbgBreakPoint:
7c90120e cc int 3
0:001>kb
ChildEBP RetAddr Args to Child
003dffc8 7c951e40 00000005 00000004 00000001 ntdll!DbgBreakPoint
003dfff4 00000000 00000000 00000000 00000000 ntdll!DbgUiRemoteBreakin+0x2d
0:001>~* kb

0 Id: f4c.f30 Suspend: 1 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr Args to Child

0013fa74 7e419418 7e42770a 00000000 00000000 ntdll!KiFastSystemCallRet
0013faac 7e4249c4 000d018c 00000000 00000001 user32!NtUserWaitMessage+0xc
0013fad4 7e43a956 7e410000 00163310 00000000 user32!InternalDialogBox+0xd0
0013fd94 7e43a2bc 0013fef0 00000000 ffffffff user32!SoftModalMessageBox+0x938
0013fee4 7e4663fd 0013fef0 00000028 00000000 user32!MessageBoxWorker+0x2ba
0013ff3c 7e4664a2 00000000 00162e58 00162ea8 user32!MessageBoxTimeoutW+0x7a
0013ff70 7e450877 00000000 00403019 00403000 user32!MessageBoxTimeoutA+0x9c
0013ff90 7e45082f 00000000 00403019 00403000 user32!MessageBoxExA+0x1b
0013ffac 00401013 00000000 00403019 00403000 user32!MessageBoxA+0x45
0013fff0 00000000 00401000 00000000 78746341 msgbox!start+0x13

# 1 Id: f4c.f18 Suspend: 1 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr Args to Child

003dffc8 7c951e40 00000005 00000004 00000001 ntdll!DbgBreakPoint
003dfff4 00000000 00000000 00000000 00000000 ntdll!DbgUiRemoteBreakin+0x2d
0:001>g
Breakpoint 1 hit
eax=00000001 ebx=7ffdd000 ecx=7c91005d edx=00160608 esi=00000000 edi=00000000
eip=00401013 esp=0013ffc4 ebp=0013fff0 iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
msgbox!start+0x13:
00401013 6a00 push 0
0:000>p
eax=00000001 ebx=7ffdd000 ecx=7c91005d edx=00160608 esi=00000000 edi=00000000
eip=00401015 esp=0013ffc0 ebp=0013fff0 iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
msgbox!start+0x15:
00401015 e806000000 call msgbox!ExitProcess (00401020)
0:000>t
eax=00000001 ebx=7ffdd000 ecx=7c91005d edx=00160608 esi=00000000 edi=00000000
eip=00401020 esp=0013ffbc ebp=0013fff0 iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
msgbox!ExitProcess:
00401020 ff2500204000 jmp dword ptr [msgbox!_imp__ExitProcess (00402000)]
ds:0023:00402000={kernel32!ExitProcess (7c81cb12)}
0:000>t
eax=00000001 ebx=7ffdd000 ecx=7c91005d edx=00160608 esi=00000000 edi=00000000
eip=7c81cb12 esp=0013ffbc ebp=0013fff0 iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
kernel32!ExitProcess:
7c81cb12 8bff mov edi,edi
0:000>g
NoTarget>q
WARNING: The debugger does not have a current process or thread
WARNING: Many commands will not work
quit:
no Debuggee
Finished Debugging Quitting


F:\dbgengdbg\test>











Submit "A Simple Dbgeng Based User Mode Debugger" to Digg Submit "A Simple Dbgeng Based User Mode Debugger" to del.icio.us Submit "A Simple Dbgeng Based User Mode Debugger" to StumbleUpon Submit "A Simple Dbgeng Based User Mode Debugger" to Google

Categories
Uncategorized

Comments