PDA

View Full Version : Dbgeng based Handles


blabberer
January 29th, 2013, 21:15

Dbgeng based Handles



most of those who are reading this article would be aware of Sysinternal's (mark russinovich) handle.exe
it provides the details of all handles that are open in a system

this article is an attempt to retrieve the same information using dbgeng interfaces and its allied extensions

this article also builds upon previous articles that uses dbgeng interfaces and has components copy pasted
as it is so if you are suggested to glance these articles too for ease of understanding

http://www.woodmann.com/forum/entry.php?246-A-Simple-Dbgeng-Based-User-Mode-Debugger
http://www.woodmann.com/forum/entry.php?248-DbgEng-Based-Debugger-(PART2)
http://www.woodmann.com/forum/entry.php?249-DbgEng-Based-Debugger-(PART2-Contd-)
http://www.woodmann.com/forum/entry.php?250-DbgEng-based-Kernel-Debugger



so you are required to make a DbgEngHandle.cpp use visual studio to make a new project from existing code
and use wdk7 xpfre build to compile and link and have a test folder filled with windbg dlls to test the resulting binary

in the cpp
we create a client query for interfaces implement an output callback attach non invasively to each running process
recursively and get the handle details using an extension call

handle details in usermode are exported from ExtensionFunction handle in ntsdexts.dll
handle details in kernelmode are exported from ExtensionFunction handle in kdexts.dll

lets see the implementations

these are standard global declarations we want a client and a control interface only to get handle details

Code:

#include <stdio.h>
#include <dbgeng.h>

IDebugClient2* g_Client2 = NULL;
IDebugControl* g_Control = NULL;
HRESULT Status = NULL;



these below are standard exit and output callback implementation (resusable code]

Code:


void Exit(int Code, PCSTR Format, ...)
{
if (g_Control != NULL)
{
g_Control->Release();
g_Control = NULL;
}
if (g_Client2 != NULL)
{
g_Client2->EndSession(DEBUG_END_PASSIVE);
g_Client2->Release();
g_Client2 = NULL;
}
if (Format != NULL)
{
va_list Args;
va_start(Args, Format);
vfprintf(stderr, Format, Args);
va_end(Args);
}
exit(Code);
};

class StdioOutputCallbacks : public IDebugOutputCallbacks
{
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 );
};
STDMETHODIMP StdioOutputCallbacks::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface )
{
*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 )
{
return 1;
}
STDMETHODIMP_(ULONG) StdioOutputCallbacks::Release( THIS )
{
return 0;
}
STDMETHODIMP StdioOutputCallbacks::Output( THIS_ IN ULONG Mask, IN PCSTR Text )
{
UNREFERENCED_PARAMETER(Mask);
fputs(Text, stdout);
return S_OK;
}

StdioOutputCallbacks g_OutputCb;


we start out main function by creating interface

Code:

void CreateInterfaces (void)
{
if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client2)) != S_OK)
{
Exit(1, "DebugCreate failed, 0x%X\n", Status);
}
if ((Status = g_Client2->QueryInterface(__uuidof(IDebugControl), (void**)&g_Control)) != S_OK )
{
Exit(1, "g_Client2->QueryInterface(__uuidof(IDebugControl) failed, 0x%X\n", Status);
}
return ;
}


then use the

Code:

g_Client2->GetRunningProcessSystemIds
g_Client2->GetRunningProcessDescription
we skip system idle process from attaching and querying the handle details

if( Ids[I] == 0)
{
continue;
}
for all other process that are currently running we attach to them non invasively non suspending
g_Client2->AttachProcess(NULL,
Ids[I],DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND ))
and wait for event
g_Control->WaitForEvent

then add the extension
g_Control->AddExtension("ntsdexts.dll",0,&Handle))

call the extension function
g_Control->CallExtension (the output call back recieves the output from this call)

and detach from the process
g_Client2->DetachCurrentProcess(
in a for loop for getting handle details from all running process



it is then built using wdk xp fre environemnt

and the resulting binary is executed from test folder that contains all the windbg dlls

full source

Code:

#include <stdio.h>
#include <dbgeng.h>

IDebugClient2* g_Client2 = NULL;
IDebugControl* g_Control = NULL;
HRESULT Status = NULL;

void Exit(int Code, PCSTR Format, ...)
{
if (g_Control != NULL)
{
g_Control->Release();
g_Control = NULL;
}
if (g_Client2 != NULL)
{
g_Client2->EndSession(DEBUG_END_PASSIVE);
g_Client2->Release();
g_Client2 = NULL;
}
if (Format != NULL)
{
va_list Args;
va_start(Args, Format);
vfprintf(stderr, Format, Args);
va_end(Args);
}
exit(Code);
};

class StdioOutputCallbacks : public IDebugOutputCallbacks
{
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 );
};
STDMETHODIMP StdioOutputCallbacks::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface )
{
*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 )
{
return 1;
}
STDMETHODIMP_(ULONG) StdioOutputCallbacks::Release( THIS )
{
return 0;
}
STDMETHODIMP StdioOutputCallbacks::Output( THIS_ IN ULONG Mask, IN PCSTR Text )
{
UNREFERENCED_PARAMETER(Mask);
fputs(Text, stdout);
return S_OK;
}

StdioOutputCallbacks g_OutputCb;

void CreateInterfaces (void)
{
if ((Status = DebugCreate(__uuidof(IDebugClient), (void**)&g_Client2)) != S_OK)
{
Exit(1, "DebugCreate failed, 0x%X\n", Status);
}
if ((Status = g_Client2->QueryInterface(__uuidof(IDebugControl), (void**)&g_Control)) != S_OK )
{
Exit(1, "g_Client2->QueryInterface(__uuidof(IDebugControl) failed, 0x%X\n", Status);
}
return ;
}

void __cdecl main(int Argc, char* Argv[])
{
ULONG Ids[0x100];
char Exename[0x100];
ULONG ActualCount = 0;
ULONG64 Handle = 0;

memset(&Ids,0,sizeof(Ids));
memset(&Exename,0,sizeof(Exename));
CreateInterfaces();
if ((Status = g_Client2->GetRunningProcessSystemIds(NULL,
Ids,_countof(Ids),&ActualCount) ) != S_OK)
{
Exit(1,"g_Client2->GetRunningProcessSystemIds failed, 0x%X\n", Status);
}
printf ("No of Running Process is 0n%d\n\nSRNo\tPID\tProcessName\n\n", ActualCount);
for (ULONG i = 0; i < ActualCount;i++)
{
if (( g_Client2->GetRunningProcessDescription(
NULL, Ids[I],
DEBUG_PROC_DESC_NO_PATHS | DEBUG_PROC_DESC_NO_SERVICES |
DEBUG_PROC_DESC_NO_MTS_PACKAGES | DEBUG_PROC_DESC_NO_COMMAND_LINE,
Exename, sizeof(Exename),NULL,NULL,NULL,NULL ) ) != S_OK)
{
Exit(1,"g_Client2->GetRunningProcessDescription Failed, 0x%X\n", Status);
}
printf("%02d\t0n%04d\t%s\n",i+1,Ids[I],Exename);
if( Ids[I] == 0)
{
continue;
}
if (( Status = g_Client2->AttachProcess(NULL,
Ids[I],DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND )) != S_OK)
{
Exit(1,"g_Client2->AttachProcess Failed, 0x%X\n", Status);
}
if (( Status = g_Control->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE))!=S_OK)
{
Exit(1,"g_Control->WaitForEvent Failed, 0x%X\n", Status);
}
if (( Status = g_Control->AddExtension("ntsdexts.dll",0,&Handle)) != S_OK)
{
Exit(1,"g_Control->AddExtension failed,0x%X\n",Status);
}
if ((Status = g_Client2->SetOutputCallbacks (&g_OutputCb)) != S_OK)
{
Exit(1,"g_Client2->SetOutputCallbacks failed, 0x%X\n", Status);
}
if (( Status = g_Control->CallExtension(Handle,"handle","0 5") != S_OK)
{
Exit(1,"g_Control->CallExtension failed,0x%X\n",Status);
}
if ((Status = g_Client2->SetOutputCallbacks (0)) != S_OK)
{
Exit(1,"g_Client2->SetOutputCallbacks failed, 0x%X\n", Status);
}
if (( Status = g_Client2->DetachCurrentProcess() ) != S_OK)
{
Exit(1,"g_Client2->DetachCurrentProcess failed,0x%X\n",Status);
}
}
Exit(0, "Finished Debugging Quitting\n";
}


since there is no error handling and graceful recovery but fatal exit on every failure
run the binary when the process count is stable race conditions arent handled in the code
so attach may happen but the process might have exited before handle enumeration or symbol loading
and it may fail during callExtension() or process id might be there in the array but process might have exited even
before attaching happens and may result in failure

in normal cases where process ids match running process from start of execution of this binary till end
it will get you all the handles from all the running process and it will match the results of sysinternals handle -a output

use redirection to capture the output

result

Code:


dbgenghandle:\>DbgEngHandle.exe > res.txt
Finished Debugging Quitting

dbgenghandle:\>

No of Running Process is 0n32

SRNo PID ProcessName

01 0n0000 System Process
02 0n0004 System

Handle 4
Type Process
Name <none>
Handle 8
Type Thread
Name <none>
Handle c
Type Key
Name \REGISTRY\MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Memory Management\PrefetchParameters
Handle 10
Type Key
Name \REGISTRY
Handle 14
Type Key
Name \REGISTRY\MACHINE\SYSTEM\Setup
Handle 18
Type Key
Name \REGISTRY\MACHINE\HARDWARE\DESCRIPTION\System\MultifunctionAdapter
Handle 1c
Type Key
Name \REGISTRY\MACHINE\SYSTEM\WPA\MediaCenter
Handle 20
Type Key
Name \REGISTRY\MACHINE\SYSTEM\WPA\PnP
Handle 24
Type Key
Name \REGISTRY\MACHINE\SYSTEM\WPA\Key-4F3B2RFXKC9C637882MBM
Handle 28
Type Key
Name \REGISTRY\MACHINE\SYSTEM\WPA\SigningHash-V44KQMCFXKQCTQ
Handle 2c
Type Key
Name \REGISTRY\MACHINE\SYSTEM\ControlSet001\Control\ProductOptions
Handle 30
Type Key
Name \REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\Eventlog
Handle 34
Type Event
Name \Security\TRKWKS_EVENT

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Handle 2b98
Type SymbolicLink
Name \GLOBAL??\C:
Handle 2bc0
Type File
2593 Handles
Type Count
None 4
Event 6
Section 1
File 2400
Port 4
Directory 5
SymbolicLink 101
Key 30
Token 1
Process 17
Thread 23
Desktop 1
03 0n0512 smss.exe
Handle 4
Type KeyedEvent
Name \KernelObjects\CritSecOutOfMemoryEvent
Handle 8
Type File
Handle c
Type Port
Name \SmApiPort
Handle 10
Type Port
Name <none>
Handle 14
Type Directory
Name \GLOBAL??
Handle 18
Type Directory
Name \Sessions
Handle 1c
Type File
Handle 20
Type SymbolicLink
Name \KnownDlls\KnownDllPath
Handle 24
Type Directory
Name \KnownDlls
Handle 28
Type Event
Name <none>
Handle 2c
Type Event
Name \UniqueSessionIdEvent
Handle 30
Type Process
Name <none>
Handle 34
Type Process
Name <none>



Handle v3.41
Copyright (C) 1997-2008 Mark Russinovich
Sysinternals - www.sysinternals.com

------------------------------------------------------------------------------
System pid: 4 NT AUTHORITY\SYSTEM
4: Process System(4)
8: Thread System(4): 12
C: Key HKLM\SYSTEM\ControlSet001\Control\Session Manager\Memory Management\PrefetchParameters
10: Key \REGISTRY
14: Key HKLM\SYSTEM\Setup
18: Key HKLM\HARDWARE\DESCRIPTION\System\MultifunctionAdapter
1C: Key HKLM\SYSTEM\WPA\MediaCenter
20: Key HKLM\SYSTEM\WPA\PnP
24: Key HKLM\SYSTEM\WPA\Key-4F3B2RFXKC9C637882MBM
28: Key HKLM\SYSTEM\WPA\SigningHash-V44KQMCFXKQCTQ
2C: Key HKLM\SYSTEM\ControlSet001\Control\ProductOptions
30: Key HKLM\SYSTEM\ControlSet001\Services\Eventlog
34: Event \Security\TRKWKS_EVENT

XXXXXXXXXXXXXXXXXXXXXXXXX
2B98: SymbolicLink \GLOBAL??\C:
2BC0: File (---) \Device\Tcp
------------------------------------------------------------------------------
smss.exe pid: 512 NT AUTHORITY\SYSTEM
4: KeyedEvent \KernelObjects\CritSecOutOfMemoryEvent
8: File (RW-) C:\WINDOWS
C: Port \SmApiPort
10: Port
14: Directory \GLOBAL??
18: Directory \Sessions
1C: File (RW-) C:\WINDOWS\system32
20: SymbolicLink \KnownDlls\KnownDllPath
24: Directory \KnownDlls
28: Event
2C: Event \UniqueSessionIdEvent
30: Process csrss.exe(564)
34: Process csrss.exe(564)



part 2 continued for kernel mode handles (it is same details just a different implementation

attached below is source and a compiled binaryDbgEngHandle.zip (7.1 KB)