blabberer

DbgEng based Kernel Debugger

Rating: 2 votes, 5.00 average.
DbgEng based Kernel Debugger

refer to the prior articles discussing how to use dbgeng functions and make a simple user mode debugger in part 1
building upon it attaching to a local kernel and peeking around kernel memory was discussed in part 2 and part 2 contd...

lets see how to make a real kernel debugger (KD clone) using dbgeng interfaces
remember the steps we used to create a user mode debugger ? if not refresh by visiting
http://www.woodmann.com/forum/entry.php?246-A-Simple-Dbgeng-Based-User-Mode-Debugger
Creating A client using DebugCreate querying other required interfaces ,implementing the callbacks Setting a console ctrl+c handler a common exit releasing the interfaces and a main
where a process was created and an infinte loop waiting for events and exiting when debuggee exits


now copy paste the whole folder you created in the part 1 example
(dbgengdbg.cpp , sources file , makefile , prebuild.bat the test folder that contains all windbg dlls )
rename dbgengdbg.cpp to dbgengKD.cpp change the sources file to reflect the renaming

and make one simple change in the dbgengKD.cpp

Code:

Code:
Comparing files dbgengdbg.cpp and ..\DBGENGKD\DBGENGKD.CPP
***** dbgengdbg.cpp
        SetConsoleCtrlHandler(&HandlerRoutine,TRUE);  // Set control +c handler 
        if ((Status = g_Client->CreateProcess(0, Argv[1], DEBUG_ONLY_THIS_PROCESS)) != S_OK)  // create a process 
        {
***** ..\DBGENGKD\DBGENGKD.CPP
        SetConsoleCtrlHandler(&HandlerRoutine,TRUE);  // Set control +c handler 
        if ((Status = g_Client->AttachKernel(DEBUG_ATTACH_KERNEL_CONNECTION, "com:pipe,port=\\\\.\\pipe\\debugPipe,resets=0,recnnect")) != S_OK)  // create a process 
        {
*****
and build

the resulting binary is now capable of attaching to a real machine via any of the connection methods

the connection method i have provided in this example is to connect to a virtual machine using serial interface redirected to a NamedPipe
"com:pipe,port=\\\\.\\pipe\\debugPipe,resets=0,recnnect"

provide appropriate connection strings and you can connect via 1394 \ vmkd \ usb\ the new win8 network protocol apart from the trusty old serial and null modem cable

just start the binary and it will wait for a kernel connection on com port 1
start the vm on kernel debugging mode and connect for real kernel debugging session

or if you have a vm running in debug mode hit the ctrl+sysreq in the vm and connect to the binary for a kd session

see below

Code:
dbgengkd:\>dbgengKD.exe

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

Opened \\.\pipe\debugPipe
Waiting to reconnect...
Connected to Windows XP 2600 x86 compatible target at (Sat Jan 26 03:46:12.656 2
013 (UTC + 5:30)), ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: SRV*F:\symbols*http://msdl.microsoft.com/download/symbols

Executable search path is:
Windows XP Kernel Version 2600 (Service Pack 3) UP Free x86 compatible
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 2600.xpsp.080413-2111
Machine Name:
Kernel base = 0x804d7000 PsLoadedModuleList = 0x8055b1c0
Debug session time: Sat Jan 26 03:40:03.650 2013 (UTC + 5:30)
System Uptime: 0 days 2:54:32.638
Break instruction exception - code 80000003 (first chance)
*******************************************************************************
*                                                                             *
*   You are seeing this message because you pressed either                    *
*       CTRL+C (if you run kd.exe) or,                                        *
*       CTRL+BREAK (if you run WinDBG),                                       *
*   on your debugger machine's keyboard.                                      *
*                                                                             *
*                   THIS IS NOT A BUG OR A SYSTEM CRASH                       *
*                                                                             *
* If you did not intend to break into the debugger, press the "g" key, then   *
* press the "Enter" key now.  This message might immediately reappear.  If it *
* does, press "g" and "Enter" again.                                          *
*                                                                             *
*******************************************************************************
nt!RtlpBreakWithStatusInstruction+0x1:
804e3593 c20400          ret     4
kd>!process 0 0 letsreversewomen.exe
PROCESS ffad4960  SessionId: 0  Cid: 01d8    Peb: 7ffde000  ParentCid: 0114
    DirBase: 03d77000  ObjectTable: e119d860  HandleCount:  48.
    Image: letsreversewomen.exe

kd>.process /p /r ffad4960
Implicit process is now ffad4960
.cache forcedecodeuser done
Loading User Symbols
............................
nt!RtlpBreakWithStatusInstruction+0x1:
804e3593 c20400          ret     4
kd>s -[l 7]sa 12f500 L?9000
0012fa34  "{FF24CF9A-EE48-4cde-AC10-15D1CE2"
0012fa54  "C272C}.dat"
0012fa97  "|{83D33F3A-9482-446f-ABFF-7B69D5"
0012fab7  "8C1634}"
0012fc78  "C:\DOCUME~1\rr\LOCALS~1\Temp\"
0012fd2c  "C:\Documents and Settings\rr\Des"
0012fd4c  "ktop\letsreversewomen\letsrevers"
0012fd6c  "ewomen.exe"
0012fdcc  "{A37340FD-F043-41e3-9C16-2F26323"
0012fdec  "87199}"
kd>.echo ollydbgs ghost is creating a file named <guid>.dat in temp folder and

ollydbgs ghost is creating a file named <guid>.dat in temp folder and
kd>
i have attached the cpp . sources and make file tweak the connection string to your preference build and enjoy your own KD.exe:)

code for ready reference

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->AttachKernel(DEBUG_ATTACH_KERNEL_CONNECTION, "com:pipe,port=\\\\.\\pipe\\debugPipe,resets=0,reconnect")) != 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
}










Attached Thumbnails Attached Files

Submit "DbgEng based Kernel Debugger" to Digg Submit "DbgEng based Kernel Debugger" to del.icio.us Submit "DbgEng based Kernel Debugger" to StumbleUpon Submit "DbgEng based Kernel Debugger" to Google

Categories
Uncategorized

Comments