How to get field names and offsets of a struct using dbghlp and pdb - windows

I would like to dump the fields and offsets of structures in the same way as windbg's dt command. Let's say for example I would like to dump the _PEB structure which is in the Microsoft Public symbols (since windbg's DT command works).
From MSDN documentation I understood that the SymFromName function should be able to do this, below the is the code I've tried that fails on SymFromName with LastError 126 (The specified module could not be found).
From the registered Callback I get the following output:
CBA_SET_OPTIONS
CBA_SET_OPTIONS
CBA_SET_OPTIONS
CBA_EVENT: code 0 desc DBGHELP: Symbol Search Path: symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols
DBGHELP: Symbol Search Path: symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols
CBA_DEFERRED_SYMBOL_LOAD_START: C:\Windows\Sysnative\ntdll.dll
CBA_DEFERRED_SYMBOL_LOAD_PARTIAL: C:\Windows\Sysnative\ntdll.dll
CBA_EVENT: code 0 desc DBGHELP: No header for C:\Windows\Sysnative\ntdll.dll. Searching for image on disk
DBGHELP: No header for C:\Windows\Sysnative\ntdll.dll. Searching for image on disk
CBA_EVENT: code 0 desc DBGHELP: C:\Windows\Sysnative\ntdll.dll - OK
DBGHELP: C:\Windows\Sysnative\ntdll.dll - OK
CBA_DEFERRED_SYMBOL_LOAD_COMPLETE: C:\Windows\Sysnative\ntdll.dll
CBA_EVENT: code 0 desc DBGHELP: ntdll - public symbols
C:\Symbols\ntdll.pdb\823B51C37A764AF7BA1558B42B627FAC2\ntdll.pdb
DBGHELP: ntdll - public symbols
C:\Symbols\ntdll.pdb\823B51C37A764AF7BA1558B42B627FAC2\ntdll.pdb
The Code:
const
Index: THandle =1;
Size = (SizeOf(SYMBOL_INFO)-1 + MAX_SYM_NAME * SizeOf(TCHAR) + SizeOf(ULONG64) -1) div SizeOf(ULONG64);
var
Symbol: String;
Filename: String;
Path: String;
dwBaseAddress: DWORD;
im: IMAGEHLP_MODULE64;
Buffer: array[0..Size] of ULONG64;
pSymbol: PSYMBOL_INFO;
SymbolName: array[0..MAX_SYM_NAME-1] of Char;
begin
ZeroMemory(#SymbolName, SizeOf(SymbolName));
SymbolName := '_PEB';
Filename := 'C:\Windows\Sysnative\ntdll.dll';
Path := 'symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols';
{ Initialize }
Win32Check(SymInitialize(Index, nil, False));
{ Register callback to get some debug info }
Win32Check(SymRegisterCallback64(Index, DbgHelpCallback, 0));
{ Set Options }
SymSetOptions(SymGetOptions or SYMOPT_UNDNAME);
SymSetOptions(SymGetOptions or SYMOPT_DEBUG);
SymSetOptions(SymGetOptions or SYMOPT_LOAD_ANYTHING);
{ Set Symbol Path }
Win32Check(SymSetSearchPathW(Index, PChar(Path)));
{ Load Module }
dwBaseAddress := SymLoadModuleExW(Index, 0, PChar(Filename), nil, 0, 0, nil, 0);
Win32Check(dwBaseAddress > 0);
ZeroMemory(#im, SizeOf(im));
im.SizeOfStruct := SizeOf(im);
Win32Check(SymGetModuleInfoW64(Index, dwBaseAddress, im));
ZeroMemory(#Buffer, SizeOf(Buffer));
pSymbol := PSYMBOL_INFO(#Buffer);
pSymbol^.SizeOfStruct := SizeOf(SYMBOL_INFO);
pSymbol^.MaxNameLen := MAX_SYM_NAME;
Win32Check(SymFromNameW(Index, Symbolname, pSymbol));
Win32Check(SymUnloadModule64(Index, dwBaseAddress));
Win32Check(SymCleanup(Index));

I got it to work by using SymGetTypeFromName to get the Symbol Index and then use SymGetTypeInfo to get the details:
const
Index: THandle =1;
Size = (SizeOf(SYMBOL_INFO)-1 + MAX_SYM_NAME * SizeOf(TCHAR) + SizeOf(ULONG64) -1) div SizeOf(ULONG64);
var
Filename: String;
Path: String;
dwBaseAddress: array[0..0] of DWORD;
im: IMAGEHLP_MODULE64;
Buffer: array[0..Size] of ULONG64;
pSymbol: PSYMBOL_INFO;
SymbolName: array[0..MAX_SYM_NAME-1] of Char;
i: Integer;
ChildParams: TI_FINDCHILDREN_PARAMS;
dwOffset: DWORD;
pSymName: PChar;
begin
ZeroMemory(#SymbolName, SizeOf(SymbolName));
SymbolName := '_PEB';
Filename := 'C:\Windows\System32\ntdll.dll';
Path := 'symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols';
{ Initialize }
Win32Check(SymInitialize(Index, nil, False));
{ Register callback to get some debug info }
Win32Check(SymRegisterCallback64(Index, DbgHelpCallback, 0));
{ Set Options }
SymSetOptions(SymGetOptions or SYMOPT_UNDNAME);
SymSetOptions(SymGetOptions or SYMOPT_DEBUG);
SymSetOptions(SymGetOptions or SYMOPT_LOAD_ANYTHING);
{ Set Symbol Path }
Win32Check(SymSetSearchPathW(Index, PChar(Path)));
{ Load Module }
dwBaseAddress[0] := SymLoadModuleExW(Index, 0, PChar(Filename), nil, 0, 0, nil, 0);
ZeroMemory(#im, SizeOf(im));
im.SizeOfStruct := SizeOf(im);
for i := 0 to Length(dwBaseAddress)-1 do
begin
SymGetModuleInfoW64(Index, dwBaseAddress[i], im);
end;
ZeroMemory(#Buffer, SizeOf(Buffer));
pSymbol := PSYMBOL_INFO(#Buffer);
pSymbol^.SizeOfStruct := SizeOf(SYMBOL_INFO);
pSymbol^.MaxNameLen := MAX_SYM_NAME;
{ Get Type Info by Symbol Name (we need the index) }
Win32Check(SymGetTypeFromNameW(Index, dwBaseAddress[0], SymbolName, pSymbol));
{ Get Child Count }
ZeroMemory(#ChildParams, SizeOf(ChildParams));
Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], pSymbol^.TypeIndex, TI_GET_CHILDRENCOUNT, #ChildParams.Count));
{ Get Child Info }
// TODO: Caller must reserve memory for the ChildId array (Count * SizeOf(ULONG))
Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], pSymbol^.TypeIndex, TI_FINDCHILDREN, #ChildParams));
for i := ChildParams.Start to ChildParams.Count - 1 do
begin
{ Get Child Name }
Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], {pSymbol^.TypeIndex + }ChildParams.ChildId[i], TI_GET_SYMNAME, #pSymName));
{ Get Child Offset }
Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], {pSymbol^.TypeIndex + }ChildParams.ChildId[i], TI_GET_OFFSET, #dwOffset));
Memo1.Lines.Add(Format('+0x%.3x %s', [dwOffset, pSymName]));
LocalFree(Cardinal(pSymName));
end;
for i := 0 to Length(dwBaseAddress)-1 do
begin
Win32Check(SymUnloadModule64(Index, dwBaseAddress[i]));
end;
Win32Check(SymCleanup(Index));
end;
and this is the output:
+0x000 InheritedAddressSpace
+0x001 ReadImageFileExecOptions
+0x002 BeingDebugged
+0x003 BitField
+0x003 ImageUsesLargePages
+0x003 IsProtectedProcess
+0x003 IsLegacyProcess
+0x003 IsImageDynamicallyRelocated
+0x003 SkipPatchingUser32Forwarders
+0x003 SpareBits
+0x004 Mutant
+0x008 ImageBaseAddress
+0x00C Ldr
+0x010 ProcessParameters
+0x014 SubSystemData
+0x018 ProcessHeap
+0x01C FastPebLock
+0x020 AtlThunkSListPtr
+0x024 IFEOKey
+0x028 CrossProcessFlags
+0x028 ProcessInJob
+0x028 ProcessInitializing
+0x028 ProcessUsingVEH
+0x028 ProcessUsingVCH
+0x028 ProcessUsingFTH
+0x028 ReservedBits0
+0x02C KernelCallbackTable
+0x02C UserSharedInfoPtr
+0x030 SystemReserved
+0x034 AtlThunkSListPtr32
+0x038 ApiSetMap
+0x03C TlsExpansionCounter
+0x040 TlsBitmap
+0x044 TlsBitmapBits
+0x04C ReadOnlySharedMemoryBase
+0x050 HotpatchInformation
+0x054 ReadOnlyStaticServerData
+0x058 AnsiCodePageData
+0x05C OemCodePageData
+0x060 UnicodeCaseTableData
+0x064 NumberOfProcessors
+0x068 NtGlobalFlag
+0x070 CriticalSectionTimeout
+0x078 HeapSegmentReserve
+0x07C HeapSegmentCommit
+0x080 HeapDeCommitTotalFreeThreshold
+0x084 HeapDeCommitFreeBlockThreshold
+0x088 NumberOfHeaps
+0x08C MaximumNumberOfHeaps
+0x090 ProcessHeaps
+0x094 GdiSharedHandleTable
+0x098 ProcessStarterHelper
+0x09C GdiDCAttributeList
+0x0A0 LoaderLock
+0x0A4 OSMajorVersion
+0x0A8 OSMinorVersion
+0x0AC OSBuildNumber
+0x0AE OSCSDVersion
+0x0B0 OSPlatformId
+0x0B4 ImageSubsystem
+0x0B8 ImageSubsystemMajorVersion
+0x0BC ImageSubsystemMinorVersion
+0x0C0 ActiveProcessAffinityMask
+0x0C4 GdiHandleBuffer
+0x14C PostProcessInitRoutine
+0x150 TlsExpansionBitmap
+0x154 TlsExpansionBitmapBits
+0x1D4 SessionId
+0x1D8 AppCompatFlags
+0x1E0 AppCompatFlagsUser
+0x1E8 pShimData
+0x1EC AppCompatInfo
+0x1F0 CSDVersion
+0x1F8 ActivationContextData
+0x1FC ProcessAssemblyStorageMap
+0x200 SystemDefaultActivationContextData
+0x204 SystemAssemblyStorageMap
+0x208 MinimumStackCommit
+0x20C FlsCallback
+0x210 FlsListHead
+0x218 FlsBitmap
+0x21C FlsBitmapBits
+0x22C FlsHighIndex
+0x230 WerRegistrationData
+0x234 WerShipAssertPtr
+0x238 pContextData
+0x23C pImageHeaderHash
+0x240 TracingFlags
+0x240 HeapTracingEnabled
+0x240 CritSecTracingEnabled
+0x240 SpareTracingBits
Now on to the next step: use Delphi 2010's RTTI and use this mechanism to compare offsets (this helps me converting headers for the Jedi ApiLib).

I'm not exactly an expert in these things, but the leading underscore in C namemangling is sometimes supposed to be part of the binary format.
Does it work if you remove the leading underscore?

Related

From Raku/Perl6, how to read this registry key?

Windows
With Raku/Perl6, how do I use NativeCall to read the value of
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ "EnableLUA"]
with RegQueryValueExW?
https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexw
Many thanks,
-T
edit 12-27-2019 #1: removing bad code and inserting new code
This is how far I have gotten.
Test run string:
K:\Windows\NtUtil>perl6 -I. -e "use WinMount :GetLUA; say GetLUA();"
RegOpenKeyExW
RegOpenKeyExW RtnCode 0
RegQueryValueExW
1
2
RegQueryValueExW RtnCode 87 (87 = ERROR_INVALID_PARAMETER)
lpData pointer 0
lpcbData data length 0
RegCloseKey
RegCloseKey RtnCode 0
True
WinMount.pm6
# unit module WinMount;
# WinMount.pm6#`{
Utilities to mount and dismound drive partitions
Note: LUA must be unset (0x00000000) for mount to function prpoperly
raku -I. -c WinMount.pm6
}
use NativeCall;
use WinPopUps :WinMsg;
# Reference to types and values: http://dsource.org/projects/tango/ticket/820
constant BYTE := uint8;
constant WCHAR := uint16;
constant DWORD := int32;
constant REGSAM := int32;
constant WCHARS := CArray[WCHAR];
constant BYTES := CArray[BYTE];
constant HKEY_CURRENT_USER = 0x80000001;
constant HKEY_LOCAL_MACHINE = 0x80000002;
constant KEY_QUERY_VALUE = 1;
constant ERROR_SUCCESS = 0; # Yeah, I know. The Win-Api uses 0 for success and other values to indicate errors
constant REG_SZ = 1;
constant KEY_READ = 0x20019;
constant KEY_SET_VALUE = 0x0002;
constant REG_DWORD = 0x00000004;
sub to-c-str( Str $str ) returns CArray[WCHAR] is export( :to-c-str ) {
my #str := CArray[WCHAR].new;
for ( $str.comb ).kv -> $i, $char { #str[$i] = $char.ord; }
#str[ $str.chars ] = 0;
#str;
}
sub wstr( Str $str ) returns WCHARS is export( :wstr ) {
CArray[WCHAR].new( $str.comb.map: *.ord )
}
sub GetLUA() is export( :GetLUA ) {
#`{
Returns the LUA value in the registry to True (0x00000001) or False (0x00000000)
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
"EnableLUA"=dword:00000000
https://docs.perl6.org/language/nativecall
Win32 return codes:
https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
}
my Str $SubName = &?ROUTINE.name;
my Str $OS = $*KERNEL.name;
if not $OS eq "win32" { say "Sorry, $SubName only work in Windows."; exit; }
my Bool $LUA = True;
my $RtnCode;
my Str $SubKey = Q[SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\];
my Str $Key = Q[EnableLUA];
my $lpSubKey = wstr( $SubKey );
my $lpValueName = wstr( $Key );
# my $lpSubKey = CArray[uint8].new($Key.encode.list);
# my $lpValueName = CArray[uint8].new($SubKey.encode.list);
my int32 $Handle;
my int32 $ulOptions = 0;
my int32 $lpData;
my int32 $lpcbData;
my int32 $lpReserved = 1;
#`{
Open the key:
https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexw
https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-key-security-and-access-rights
C++
LSTATUS RegOpenKeyExW(
HKEY hKey, # Hive name (HKEY_LOCAL_MACHINE)
LPCWSTR lpSubKey, # path to the key(/SOFTWARE/Microsoft/Windows/CurrentVersion/Policies/System/EnableLUA)
DWORD ulOptions, # 0
REGSAM samDesired, # KEY_READ (0x20019), KEY_SET_VALUE (0x0002)
PHKEY phkResult # A pointer to a variable that receives a handle to the opened key
);
}
say "RegOpenKeyExW";
sub RegOpenKeyExW( DWORD, WCHARS, DWORD, DWORD, DWORD is rw) is native("Kernel32.dll") returns DWORD { * };
$RtnCode = RegOpenKeyExW( HKEY_LOCAL_MACHINE, $lpSubKey, $ulOptions, KEY_READ, $Handle );
say "RegOpenKeyExW RtnCode $RtnCode\n";
#`{
Read the key:
use RegQueryValueExW if you know key and value name
https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexw
C++
LSTATUS RegQueryValueExW(
HKEY hKey, # Hive name (HKEY_LOCAL_MACHINE)
LPCWSTR lpValueName, # path to the key(\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA)
LPDWORD lpReserved, # give it "int32" without the quotes to give it a NULL
LPDWORD lpType, # Registry Value Type (REG_DWORD which is 32 bit)
LPBYTE lpData, # Pointer to the return value
LPDWORD lpcbData # number of bytes in the return value
);
}
say "RegQueryValueExW";
sub RegQueryValueExW( DWORD, WCHARS, DWORD, DWORD, DWORD is rw, DWORD is rw ) is native("Kernel32.dll") returns DWORD { * };
say "1";
$RtnCode = RegQueryValueExW( $Handle, $lpValueName, $lpReserved, REG_DWORD, $lpData, $lpcbData );
say "2";
say "RegQueryValueExW RtnCode $RtnCode (87 = ERROR_INVALID_PARAMETER)\nlpData pointer $lpData\nlpcbData data length $lpcbData\n";
#`{
Close the key
https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
C++
LSTATUS RegCloseKey(
HKEY hKey # handle to the open key to be closed. See RegOpenKeyExW phkResult
);
}
say "RegCloseKey";
sub RegCloseKey( DWORD ) is native("Kernel32.dll") returns DWORD { * };
$RtnCode = RegCloseKey( $Handle );
say "RegCloseKey RtnCode $RtnCode\n";
return $LUA;
}
Here's what I have so far. I am able to successfully retrieve the handle for the registry key, but when I try to read the key it throws "Native call expected argument that references a native integer, but got P6int". And I don't know why yet. But I figured I post this already, maybe someone else can shed some light.
use NativeCall;
constant BYTE := uint8;
constant WCHAR := uint16;
constant DWORD := int32;
constant REGSAM := int32;
constant WCHARS := CArray[WCHAR];
constant BYTES := CArray[BYTE];
constant HKEY_CURRENT_USER = 0x80000001;
constant HKEY_LOCAL_MACHINE = 0x80000002;
constant KEY_QUERY_VALUE = 1;
constant ERROR_SUCCESS = 0; # Yeah, I know. The Win-Api uses 0 for success and other values to indicate errors
constant REG_SZ = 1;
sub RegOpenKeyExW( DWORD, WCHARS, DWORD, REGSAM, DWORD is rw) is native("Kernel32.dll") returns DWORD { * };
#DWORD RegOpenKeyExW(
# HKEY hKey,
# LPCWSTR lpSubKey,
# DWORD ulOptions,
# REGSAM samDesired,
# PHKEY phkResult
#);
sub RegQueryValueExW( DWORD, WCHARS, DWORD is rw, DWORD is rw, BYTE is rw, DWORD is rw) is native("Kernel32.dll") returns DWORD { * };
#DWORD RegQueryValueExW(
# HKEY hKey,
# LPCWSTR lpValueName,
# LPDWORD lpReserved,
# LPDWORD lpType,
# LPBYTE lpData,
# LPDWORD lpcbData
#);
my $key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System';
my DWORD $hkey;
my $length = 1024;
if RegOpenKeyExW(HKEY_LOCAL_MACHINE, wstr($key), 0, KEY_QUERY_VALUE, $hkey) == ERROR_SUCCESS
{
say "Got handle: $hkey";
my BYTES $buffer = CArray[BYTE].new( 0 xx 1024 );
# throws "Native call expected argument that references a native integer, but got P6int"
if RegQueryValueExW( $hkey, WCHARS, DWORD, REG_SZ, $buffer, $length ) == ERROR_SUCCESS
{
say "Got data of length $length";
say gather for 0 .. $length { take $buffer[$_] };
}
}
sub wstr( Str $str ) returns WCHARS {
CArray[WCHAR].new( $str.comb.map: *.ord )
}

Interpreting Section object in kernel dump

I'm trying to track down issues with a 3thParty application. The path currently being investigated is to look into a Section object that get's created in each process: rpsPdf10.mutex.
If the name of the object is any indication for it's intended usage, I'm not sure why they choose a Section object and use it as a Mutex but that's likely largely irrelevant.
Using LiveKd I've issued following command's trying to get detailed info of the Section object
0: kd>!process 0 0 3thParty.exe
...
PROCESS fffffa800ea80060
SessionId: 0 Cid: 0a00 Peb: fffdf000 ParentCid: 014c
DirBase: 99349000 ObjectTable: fffff8a004448bf0 HandleCount: 338.
Image: 3thParty.exe
...
0: kd> !handle 0 7 fffffa800ea80060
...
08 fffff8a012e26710 Section rpsPdf10.mutex
...
0: kd> !object fffff8a012e26710
Object: fffff8a012e26710 Type: (fffffa800cd7cea0) Section
ObjectHeader: fffff8a012e266e0 (new version)
HandleCount: 38 PointerCount: 39
Directory Object: fffff8a00a980080 Name: rpsPdf10.mutex
0: kd> dt nt!_FILE_OBJECT fffff8a012e26710
+0x000 Type : 0n256
+0x002 Size : 0n0
+0x008 DeviceObject : 0x000000000008dfb0 _DEVICE_OBJECT
+0x010 Vpb : 0xfffffa80c0000001 _VPB
+0x018 FsContext : (null)
+0x020 FsContext2 : 0xfffffa8000000034 Void
+0x028 SectionObjectPointer : 0xfffff8a0102d7820 _SECTION_OBJECT_POINTERS
+0x030 PrivateCacheMap : 0x0000000000001000 Void
+0x038 FinalStatus : 0n73728
+0x040 RelatedFileObject : 0x63536153030a040c _FILE_OBJECT
+0x048 LockOperation : 0x74 't'
+0x049 DeletePending : 0 ''
+0x04a ReadAccess : 0x65 'e'
+0x04b WriteAccess : 0 ''
+0x04c DeleteAccess : 0x73 's'
+0x04d SharedRead : 0 ''
+0x04e SharedWrite : 0x74 't'
The string 't' 'e' 's' 't' in the output definitely sticks out so
either I'm following a wrong path -> tx to Blabb, this is certain. It's not a fileobject but the question remains how to find out more information about the Section object. It's still curious and/or a rather unfortunate coincidence that following the section and control area pointers I derived from the fileobject info do seem correct?!
or there's something wrong with the Section object
or ... ?
tldr;
Following the _SECTION_OBJECT_POINTERS of the _FILE_OBJECT structure above, I arrive at a
NumberOfMappedViews of 0x26 (= HandleCount: 38)
NumberOfUserReferences of 0x27 (= PointerCount: 39)
so for the moment I assume the path I've followed is correct.
0: kd> dt nt!_SECTION_OBJECT_POINTERS 0xfffff8a0102d7820
+0x000 DataSectionObject : 0xfffffa800fbed900 Void
+0x008 SharedCacheMap : 0x0008000000000001 Void
+0x010 ImageSectionObject : 0x0000000000000001 Void
0: kd> dt nt!_CONTROL_AREA 0xfffffa800fbed900
+0x000 Segment : 0xfffff8a0102d7820 _SEGMENT
+0x008 DereferenceList : _LIST_ENTRY [ 0x0000000000000000 - 0x0000000000000000 ]
+0x018 NumberOfSectionReferences : 1
+0x020 NumberOfPfnReferences : 0
+0x028 NumberOfMappedViews : 0x26
+0x030 NumberOfUserReferences : 0x27
Edit
The object header looks like this
0: kd> dt nt!_OBJECT_HEADER fffff8a012e266e0
+0x000 PointerCount : 0n39
+0x008 HandleCount : 0n38
+0x008 NextToFree : 0x00000000`00000026 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0x21 '!'
+0x019 TraceFlags : 0 ''
+0x01a InfoMask : 0xa ''
+0x01b Flags : 0 ''
+0x020 ObjectCreateInfo : 0xfffffa80`0e505140 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xfffffa80`0e505140 Void
+0x028 SecurityDescriptor : 0xfffff8a0`1ba076a8 Void
+0x030 Body : _QUAD
Edit 2
following #blabb's answer adjusting for architecture
0: kd> ? #$proc
Evaluate expression: -6047068061600 = fffffa80`0ea80060
0: kd> dx (char *)#$proc->ImageFileName
(char *)#$proc->ImageFileName : 0xfffffa800ea80340 : [Type: char *] : "3thParty.exe"
0: kd> !handle 0 0 #$proc section
...
0474: Object: fffff8a012e26710 GrantedAccess: 000f0007
...
0: kd> !object fffff8a012e26710
Object: fffff8a012e26710 Type: (fffffa800cd7cea0) Section
ObjectHeader: fffff8a012e266e0 (new version)
HandleCount: 38 PointerCount: 39
Directory Object: fffff8a00a980080 Name: rpsPdf10.mutex
0: kd> ?? (unsigned long) (#FIELD_OFFSET(nt!_OBJECT_HEADER, Body))
unsigned long 0x30
0: kd> dt nt!_object_header 0xfffff8a012e26710-0x30
+0x000 PointerCount : 0n39
+0x008 HandleCount : 0n38
+0x008 NextToFree : 0x00000000`00000026 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0x21 '!'
+0x019 TraceFlags : 0 ''
+0x01a InfoMask : 0xa ''
+0x01b Flags : 0 ''
+0x020 ObjectCreateInfo : 0xfffffa80`0e505140 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xfffffa80`0e505140 Void
+0x028 SecurityDescriptor : 0xfffff8a0`1ba076a8 Void
+0x030 Body : _QUAD
0: kd> x nt!ObTypeIndexTable
fffff800`01a70c00 nt!ObTypeIndexTable = <no type information>
0: kd> dt -r1 nt!_SECTION_OBJECT 0xfffff8a012e26710
+0x000 StartingVa : 0x00000022`00000100 Void
+0x008 EndingVa : 0x00000000`0008dfb0 Void
+0x010 Parent : 0xfffffa80`c0000001 Void
+0x018 LeftChild : (null)
+0x020 RightChild : 0xfffffa80`00000034 Void
+0x028 Segment : 0xfffff8a0`102d7820 _SEGMENT_OBJECT
+0x000 BaseAddress : 0xfffffa80`0fbed900 Void
+0x008 TotalNumberOfPtes : 1
+0x010 SizeOfSegment : _LARGE_INTEGER 0x1
+0x018 NonExtendedPtes : 0x1000
+0x01c ImageCommitment : 0
+0x020 ControlArea : (null)
+0x028 Subsection : (null)
+0x030 MmSectionFlags : 0xfffffa80`10987b10 _MMSECTION_FLAGS
+0x038 MmSubSectionFlags : 0x00000000`03400000 _MMSUBSECTION_FLAGS
0: kd> dc 0xfffff8a012e26710-0x30-0x50
fffff8a0`12e26690 030c0408 f4636553 0e1a02e0 fffffa80 ....Sec.........
fffff8a0`12e266a0 00000048 000000b8 0000001c fffffa80 H...............
fffff8a0`12e266b0 0e505140 fffffa80 00000000 00000000 #QP.............
fffff8a0`12e266c0 0a980080 fffff8a0 001c001c 00000000 ................
fffff8a0`12e266d0 10eb8770 fffff8a0 00000000 00000008 p...............
fffff8a0`12e266e0 00000027 00000000 00000026 00000000 '.......&.......
fffff8a0`12e266f0 00000000 00000000 000a0021 fffff8a0 ........!.......
fffff8a0`12e26700 0e505140 fffffa80 1ba076a8 fffff8a0 #QP......v......
0: kd> !pool 0xfffff8a012e26710-0x30-0x50 2
Pool page fffff8a012e26690 region is Paged pool
*fffff8a012e26690 size: c0 previous size: 80 (Allocated) *Sect (Protected)
Pooltag Sect : Section objects
This is a 32 bit machine running windows 7
Commands used are architecture agnostic but pointer arithmetic are arch dependent
Current process
kd> ? #$proc
Evaluate expression: -2061895528 = 8519f898
Process Name From EPROCESS->ImageFileName
kd> dx (char *)#$proc->ImageFileName
(char *)#$proc->ImageFileName : 0xffffffff8519fa04 : "windbg.exe" [Type: char *]
lets Search For Some Section Handles in this process
the TypeName is CaseSensitive
kd> !handle 0 3 #$proc Section
Searching for handles of type Section
PROCESS 8519f898 SessionId: 1 Cid: 0138 Peb: 7ffd8000 ParentCid: 0d04
DirBase: 7e257560 ObjectTable: b91a3520 HandleCount: 254.
Image: windbg.exe
Handle table at b91a3520 with 254 entries in use
00c0: Object: 9a10bc58 GrantedAccess: 00000004 Entry: 9945b180
Object: 9a10bc58 Type: (84eb6040) Section
ObjectHeader: 9a10bc40 (new version)
HandleCount: 6 PointerCount: 6
!handle 0 3 flag dumps object specific information which can be reverified using !object {object address}
kd> !object 9a10bc58
Object: 9a10bc58 Type: (84eb6040) Section
ObjectHeader: 9a10bc40 (new version)
HandleCount: 6 PointerCount: 6
each object has an objectheader in 32 bit it is 18 bytes prior to object address that is sizeof(nt!_OBJECT_HEADER- sizeof(obheader->Body)) body is embedded in HEADER as the last member and is variable sized
kd> ?? (unsigned long ) (#FIELD_OFFSET(nt!_OBJECT_HEADER , Body))
unsigned long 0x18
_OBJECT_HEADER is as follows (though sizes haven't changed there are differences between new version header and old version header)
kd> dt nt!_object_header 9a10bc58-0x18
+0x000 PointerCount : 0n6
+0x004 HandleCount : 0n6
+0x004 NextToFree : 0x00000006 Void
+0x008 Lock : _EX_PUSH_LOCK
+0x00c TypeIndex : 0x21 '!'
+0x00d TraceFlags : 0 ''
+0x00e InfoMask : 0x8 ''
+0x00f Flags : 0 ''
+0x010 ObjectCreateInfo : 0x82f7aa00 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : 0x82f7aa00 Void
+0x014 SecurityDescriptor : (null)
+0x018 Body : _QUAD
the old version header had _OBJECT_TYPE directly in the header
the new version is an index into an array
here the type index is 0x21
the array of Type is at
kd> x nt!ObTypeIndexTable
82f88580 nt!ObTypeIndexTable = <no type information>
you can write a script like this to dump all the types
function log(instr)
{
host.diagnostics.debugLog(instr + "\n");
}
function exec (cmdstr)
{
return host.namespace.Debugger.Utility.Control.ExecuteCommand(cmdstr);
}
function dumptypeindex()
{
var cpob = host.createPointerObject
var titab = exec("x nt!ObTypeIndexTable").First().substr(0,8)
var obtype = cpob(host.parseInt64(titab , 16),"nt","_OBJECT_TYPE **")
var i = 2
while(obtype[i] !=0 )
{
log("index = "+i+"\t"+ host.memory.readWideString(obtype[i].Name.Buffer))
i++
}
}
executing this script would yield the types as follows
kd> .scriptload c:\wdscr\dumptypeindex.js
JavaScript script successfully loaded from 'c:\dumptypeindex.js'
kd> dx #$scriptContents.dumptypeindex()
index = 2 Type
index = 3 Directory
index = 4 SymbolicLink
index = 5 Token
index = 6 Job
index = 7 Process
index = 8 Thread
index = 9 UserApcReserve
index = 10 IoCompletionReserve
index = 11 DebugObject
index = 12 Event
index = 13 EventPair
index = 14 Mutant
index = 15 Callback
index = 16 Semaphore
index = 17 Timer
index = 18 Profile
index = 19 KeyedEvent
index = 20 WindowStation
index = 21 Desktop
index = 22 TpWorkerFactory
index = 23 Adapter
index = 24 Controller
index = 25 Device
index = 26 Driver
index = 27 IoCompletion
index = 28 File
index = 29 TmTm
index = 30 TmTxȂ؃扏楄
index = 31 TmRm
index = 32 TmEn
index = 33 Section
index = 34 Session
index = 35 Key
index = 36 ALPC Port
index = 37 PowerRequest
index = 38 WmiGuid
index = 39 EtwRegistration
index = 40 EtwConsumer
index = 41 FilterConnectionPort
index = 42 FilterCommunicationPort
index = 43 PcwObject
notice 0x21 = 0n33 = Section
given that we have a Section
we can dump the Section Object
kd> dt -r1 nt!_SECTION_OBJECT 9a10bc58
+0x000 StartingVa : 0x90f87b44 Void
+0x004 EndingVa : 0x82efb58a Void
+0x008 Parent : 0xc0802000 Void
+0x00c LeftChild : (null)
+0x010 RightChild : 0xc0c0a280 Void
+0x014 Segment : 0x995ed8d8 _SEGMENT_OBJECT
+0x000 BaseAddress : 0x86b65740 Void
+0x004 TotalNumberOfPtes : 0xdf
+0x008 SizeOfSegment : _LARGE_INTEGER 0x000000df`00080000
+0x010 NonExtendedPtes : 0xdf000
+0x014 ImageCommitment : 0
+0x018 ControlArea : (null)
+0x01c Subsection : (null)
+0x020 MmSectionFlags : 0x869f52a8 _MMSECTION_FLAGS
+0x024 MmSubSectionFlags : 0x02ea0000 _MMSUBSECTION_FLAGS
an object is preceded by object header which is preceded by the pool_header
kd> dc 9a10bc58-0x18-0x18
9a10bc28 060b0204 f4636553 00000720 00000070 ....Sec. ...p...
9a10bc38 00000000 00000000 00000006 00000006 ................
9a10bc48 00000000 00080021 82f7aa00 00000000 ....!...........
9a10bc58 90f87b44 82efb58a c0802000 00000000 D{....... ......
9a10bc68 c0c0a280 995ed8d8 000df000 00000000 ......^.........
9a10bc78 00012000 00000004 0670020b 6666744e . ........p.Ntff
9a10bc88 00f00702 00000a48 0000c0fe 00020000 ....H...........
9a10bc98 00000000 00000002 00000000 00000000 ................
notice the Sec tag Sect is used by SectionObjects
d> !pool 9a10bc58-0x18-0x18 2
Pool page 9a10bc28 region is Paged pool
*9a10bc28 size: 58 previous size: 20 (Allocated) *Sect (Protected)
Pooltag Sect : Section objects

how to get file name safetly and compare it in windows minifilter driver

Hello i'm new at filterdriver writing so i'm apologizing for my basic question , i try to get the file name from a pre read function and check if it's equal to my file name and do some logic according to this info.
this is my function:
FLT_PREOP_CALLBACK_STATUS SwapPreReadBuffers( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext )
{
PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb;
FLT_PREOP_CALLBACK_STATUS retValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
PVOID newBuf = NULL;
PMDL newMdl = NULL;
PVOLUME_CONTEXT volCtx = NULL;
PPRE_2_POST_CONTEXT p2pCtx;
NTSTATUS status;
ULONG readLen = iopb->Parameters.Read.Length;
PFLT_FILE_NAME_INFORMATION NameInfo = NULL;
UNICODE_STRING FILE_NAME;
//
// Skip IRP_PAGING_IO, IRP_SYNCHRONOUS_PAGING_IO and
// TopLevelIrp.
//
if ((Data->Iopb->IrpFlags & IRP_PAGING_IO) || (Data->Iopb->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) || IoGetTopLevelIrp())
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
RtlInitUnicodeString( & FILE_NAME, L"my_file.txt" );
status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED |
FLT_FILE_NAME_QUERY_DEFAULT, &NameInfo );
if (!NT_SUCCESS( status ))
{
DbgPrint("[-] SwapPreReadBuffers we couldn't extract %wZ info\n", Data->Iopb->TargetFileObject->FileName);
}
status = FltParseFileNameInformation( NameInfo );
if (!NT_SUCCESS( status ))
{
DbgPrint("[-] SwapPreReadBuffers we couldn't pars %wZ info\n", Data->Iopb->TargetFileObject->FileName);
}
DbgPrint("[+] pars gets me Name: %wZ extention: %wZ perentDir: %wZ volume: %wZ \n", NameInfo->Name, NameInfo->Extension, NameInfo->ParentDir, NameInfo->Volume);
if (RtlPrefixUnicodeString( &FILE_NAME, &NameInfo->Name, TRUE )) /* here i'm getting the blue screen*/
{
DbgPrint("[***] SwapPreReadBuffers we are at calles thats related to our file %wZ \n", Data->Iopb->TargetFileObject->FileName);
}
/* continue of the code*/
}
my problem is i getting a blue screen when i try to check if the file name is equal to my wanted file name.
why the driver get here a blue screen?
Thank you
pit
edit:
i updated my code to this:
FLT_PREOP_CALLBACK_STATUS SwapPreReadBuffers( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext )
{
PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb;
FLT_PREOP_CALLBACK_STATUS retValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
PVOID newBuf = NULL;
PMDL newMdl = NULL;
PVOLUME_CONTEXT volCtx = NULL;
NTSTATUS CbStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
PPRE_2_POST_CONTEXT p2pCtx;
NTSTATUS status;
ULONG readLen = iopb->Parameters.Read.Length;
PFLT_FILE_NAME_INFORMATION NameInfo = NULL;
UNICODE_STRING FILE_NAME;
//
// Skip IRP_PAGING_IO, IRP_SYNCHRONOUS_PAGING_IO and
// TopLevelIrp.
//
if ((Data->Iopb->IrpFlags & IRP_PAGING_IO) ||
(Data->Iopb->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) ||
IoGetTopLevelIrp()) {
DbgPrint("[-] SwapPreReadBuffers we out , this call not for us\n");
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
status = FltGetFileNameInformation( Data,
FLT_FILE_NAME_NORMALIZED
| FLT_FILE_NAME_QUERY_DEFAULT,
&NameInfo );
if (!NT_SUCCESS( status ))
{
DbgPrint("[-] SwapPreReadBuffers we couldn't extract info\n");
goto PreReadCleanup;
}
status = FltParseFileNameInformation( NameInfo );
if (!NT_SUCCESS( status ))
{
DbgPrint("[-] SwapPreReadBuffers we couldn't pars info\n");
goto PreReadCleanup;
}
if (NULL == NameInfo)
{
DbgPrint("[---] name info is actally 0\n");
}
else
{
DbgPrint("[*] address of name is %x:%x and address of extansion is %x:%x -> buffers are: name %x , extension: %x \nsize of info %d size of extension %d\n",NameInfo->Extension , &NameInfo->Extension, EXTENTION, &EXTENTION, NameInfo->Extension.Buffer , EXTENTION.Buffer ,NameInfo->Extension.Length , EXTENTION.Length);
}
if ((0 == RtlCompareUnicodeString( &EXTENTION, &NameInfo->Extension, TRUE ))) {
DbgPrint("[***] SwapPreReadBuffers we are at calles thats related to our file %wZ \n", &Data->Iopb->TargetFileObject->FileName);
DbgPrint("[+] pass parse\n");
DbgPrint("[+] pars gets me Name: %wZ\n extention: %wZ\n perentDir: %wZ\n volume: %wZ\n", &NameInfo->Name, &NameInfo->Extension, &NameInfo->ParentDir, &NameInfo->Volume);
}
else{
DbgPrint("[*] we pass compration check\n");
goto PreReadCleanup;
}
//
// Clean up
//
PreReadCleanup:
if (NameInfo) {
FltReleaseFileNameInformation( NameInfo );
}
return retValue;
}
and again i got a blue screen , this is the analized core dump:
kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except,
it must be protected by a Probe. Typically the address is just plain bad or it
is pointing at freed memory.
Arguments:
Arg1: 994af2a4, memory referenced.
Arg2: 00000000, value 0 = read operation, 1 = write operation.
Arg3: 82a72a17, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 00000000, (reserved)
Debugging Details:
------------------
READ_ADDRESS: GetPointerFromAddress: unable to read from 829a5718
Unable to read MiSystemVaType memory at 829851a0
994af2a4
FAULTING_IP:
nt!RtlCompareUnicodeStrings+3c
82a72a17 0fb706 movzx eax,word ptr [esi]
MM_INTERNAL_CODE: 0
CUSTOMER_CRASH_COUNT: 1
DEFAULT_BUCKET_ID: WIN7_DRIVER_FAULT
BUGCHECK_STR: 0x50
PROCESS_NAME: cmd.exe
CURRENT_IRQL: 0
TRAP_FRAME: a72b7974 -- (.trap 0xffffffffa72b7974)
ErrCode = 00000000
eax=00000003 ebx=994af2aa ecx=8da2064c edx=000000bf esi=994af2a4 edi=137f5b5e
eip=82a72a17 esp=a72b79e8 ebp=a72b79f4 iopl=0 nv up ei pl nz ac po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010212
nt!RtlCompareUnicodeStrings+0x3c:
82a72a17 0fb706 movzx eax,word ptr [esi] ds:0023:994af2a4=????
Resetting default scope
LAST_CONTROL_TRANSFER: from 8287c3d8 to 828c941b
STACK_TEXT:
a72b795c 8287c3d8 00000000 994af2a4 00000000 nt!MmAccessFault+0x106
a72b795c 82a72a17 00000000 994af2a4 00000000 nt!KiTrap0E+0xdc
a72b79f4 82a72b0f 994af2aa 00000003 acca4e02 nt!RtlCompareUnicodeStrings+0x3c
a72b7a10 994a9105 994ad090 acca4d6c 00000001 nt!RtlCompareUnicodeString+0x25
WARNING: Stack unwind information not available. Following frames may be wrong.
a72b7a6c 8b756aeb 87587068 a72b7a8c a72b7ab8 MyDriver2+0x1105
a72b7ad8 8b7599f0 a72b7b2c 87435e48 00000000 fltmgr!FltpPerformPreCallbacks+0x34d
a72b7af0 8b759f01 a72b7b2c 00000000 862ff240 fltmgr!FltpPassThroughInternal+0x40
a72b7b14 8b75a3ba 032b7b00 862ff240 00000000 fltmgr!FltpPassThrough+0x203
a72b7b44 82872593 862ff240 87435e48 87435e48 fltmgr!FltpDispatch+0xb4
a72b7b5c 82a6699f 87435e48 87435fd8 87590d98 nt!IofCallDriver+0x63
a72b7b7c 82a9f2da 862ff240 87590d98 00000001 nt!IopSynchronousServiceTail+0x1f8
a72b7c08 828791ea 862ff240 87435e48 00000000 nt!NtReadFile+0x644
a72b7c08 773e70b4 862ff240 87435e48 00000000 nt!KiFastCallEntry+0x12a
0013f014 00000000 00000000 00000000 00000000 0x773e70b4
STACK_COMMAND: kb
FOLLOWUP_IP:
MyDriver2+1105
994a9105 ?? ???
SYMBOL_STACK_INDEX: 4
SYMBOL_NAME: MyDriver2+1105
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: MyDriver2
IMAGE_NAME: MyDriver2.sys
DEBUG_FLR_IMAGE_TIMESTAMP: 597b0358
FAILURE_BUCKET_ID: 0x50_MyDriver2+1105
BUCKET_ID: 0x50_MyDriver2+1105
Followup: MachineOwner
---------
so the crush is indead in the RtlCompareUnicodeString function (or in prefix version before) but i have no idea why it's crush there, it's look like i did every thing o.k.
your bug not in call RtlPrefixUnicodeString( &FILE_NAME, &NameInfo->Name, TRUE )) - here all ok. your bug in previous line:
DbgPrint("[+] pars gets me Name: %wZ extention: %wZ perentDir: %wZ volume: %wZ \n",
NameInfo->Name,
NameInfo->Extension,
NameInfo->ParentDir,
NameInfo->Volume);
the %wZ format require pointer to UNICODE_STRING - so must be
DbgPrint("[+] pars gets me Name: %wZ extention: %wZ perentDir: %wZ volume: %wZ \n",
&NameInfo->Name,
&NameInfo->Extension,
&NameInfo->ParentDir,
&NameInfo->Volume);
also if FltGetFileNameInformation fail - you must not use NameInfo after that (it will be 0 or undefined). but you not do this in code
and usually swap buffers need do when and only when Data->Iopb->IrpFlags & IRP_NOCACHE - so when data readed or writed to storage
There are a lot of mistakes here.
First of all don't print things that could be NULL, check them first.
Go through the FLT_FILE_NAME_INFORMATION structure and make sure all the data you want to print is actually there.
Fix your symbols in the debugger. Make sure your debugger has the symbol path set to the folder where your .pdb is.
Don't query the name in PreRead. There are several reasons not to do this, but I will tell you what you should do:
In Post IRP_MJ_CREATE query the name of the file that has been opened. Check if it is the file you are interested in. If no, just return flt postop finished processing otherwise, if it is a file you are interested in create a context for it and store the FileNameInformation there as well while you're at it.
Now in PreRead, when you are called simply ask for the context of the file object. If one is present, that means it is a file you are interested in so then you do your processing, otherwise you simply skip the read.
Be aware of string processing routines and IRQL. Don't use static stack strings for comparison like L"my_file.txt"
Also please show how your variable EXTENSION is initialized.

How do I call the default windows screen to choose a certificate?

THTTPRIO component, in HTTPWebNode property, when you click in ClientCertificate, Delphi opens a form to choose the certificate and load it's information in the component's propertys. Is this a windows screen? If it is, how can I use it? Today I'm using SecureBlackBox to load the certificates in a combobox, but I'd like to know if is possible to use this screen.
Thanks
UPDATE
I was able to show the dialog using the ms function CryptUIDlgSelectCertificateFromStore, using JWAPI. Now I'm having problems with the result of the function, the PCCERT_CONTEXT structure.
var
P: Pointer;
Context: PCCERT_CONTEXT;
Issuer: DATA_BLOB;
function GetDataBlobText(Data: DATA_BLOB): string;
begin
SetString(Result, PAnsiChar(Data.pbData), Data.cbData div SizeOf(AnsiChar));
end;
begin
P := CertOpenSystemStore(0, 'MY');
Context := CryptUIDlgSelectCertificateFromStore(P, 0, PChar('test'), nil, CRYPTUI_SELECT_ISSUEDTO_COLUMN, 0, nil);
if Context <> nil then
begin
Issuer := Context.pCertInfo.Issuer;
ShowMessage((GetDataBlobText(Issuer)));
end;
end;
The result in ShowMessage is:
UPDATE2
Thanks #RbMm.
To get string of ASN encoding fields (Issuer and Subject)
var
P: Pointer;
Context: PCCERT_CONTEXT;
Subject: DATA_BLOB;
SubjectStr: string;
size : Cardinal;
begin
P := CertOpenSystemStore(0, PAnsiChar('MY'));
Context := CryptUIDlgSelectCertificateFromStore(P, 0, 'test', 'select certificate',
CRYPTUI_SELECT_ISSUEDTO_COLUMN, 0, nil);
if Context <> nil then
begin
Subject := Context.pCertInfo.Subject;
size := CertNameToStr(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, #Subject, CERT_X500_NAME_STR, 0, 0);
SetString(SubjectStr, PAnsiChar(Subject.pbData), size);
CertNameToStr(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, #Subject, CERT_X500_NAME_STR, PAnsiChar(SubjectStr), size);
Result := SubjectStr;
end;
To get the string of raw data block (SerialNumber):
var
SerialNumber: CRYPT_INTEGER_BLOB;
size : Cardinal;
s: PWideChar;
ss: string;
begin
SerialNumber := Context.pCertInfo.SerialNumber;
CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, nil, size);
s := AllocMem(SizeOf(Char) * size);
CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, s, size);
ss := s;
showmessage(ss);
FreeMem(s, SizeOf(Char) * size);
all data blobs in certificate is encoded. so you need decode it. in general by using CryptDecodeObjectEx api. however for Issuer ( i.e. CERT_NAME_BLOB) decode you can use also CertNameToStrW. only after converts an encoded name in a CERT_NAME_BLOB structure to a null-terminated character string you can print it. code example on c/c++:
void PrintIssuer(PCCERT_CONTEXT Context)
{
CERT_NAME_BLOB Issuer = Context->pCertInfo->Issuer;
// option #1
if (ULONG len = CertNameToStrW(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &Issuer, CERT_X500_NAME_STR, 0, 0))
{
PWSTR sz = (PWSTR)alloca( len * sizeof(WCHAR));
if (CertNameToStrW(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &Issuer, CERT_X500_NAME_STR, sz, len))
{
DbgPrint("%S\n", sz);
}
}
// option #2
PCERT_NAME_INFO pcni;
ULONG size;
if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_NAME, Issuer.pbData, Issuer.cbData,
CRYPT_DECODE_ALLOC_FLAG, 0, &pcni, &size))
{
if (DWORD cRDN = pcni->cRDN)
{
PCERT_RDN rgRDN = pcni->rgRDN;
do
{
if (DWORD cRDNAttr = rgRDN->cRDNAttr)
{
PCERT_RDN_ATTR rgRDNAttr = rgRDN->rgRDNAttr;
do
{
DbgPrint("ObjId = %s\n", rgRDNAttr->pszObjId);
switch (rgRDNAttr->dwValueType)
{
case CERT_RDN_PRINTABLE_STRING:
DbgPrint("Value = %s\n", rgRDNAttr->Value.pbData);
break;
}
} while (rgRDNAttr++, --cRDNAttr);
}
} while (rgRDN++, --cRDN);
}
LocalFree(pcni);
}
}
and output
CN=***
ObjId = 2.5.4.3
Value = ***
(the string after CN= and Value = is the same)
you can note that "2.5.4.3" is szOID_COMMON_NAME or "CN". so first api is append CN= before Issuer name. second variant return you name as is and additional ObjId = 2.5.4.3
convert SerialNumber to printable string can next way:
CRYPT_INTEGER_BLOB SerialNumber = Context->pCertInfo->SerialNumber;
DWORD cb = 0;
if (CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, 0, &cb))
{
PWSTR sz = (PWSTR)alloca( cb * sizeof(WCHAR));
if (CryptBinaryToStringW(SerialNumber.pbData, SerialNumber.cbData, CRYPT_STRING_HEX, sz, &cb))
{
DbgPrint("%S\n", sz);
}
}

Implementing DebugExtensionProvideValue corrupts WinDbg internal state?

I'm implementing DebugExtensionProvideValue in my extension so I can provide custom pseudo-registers. It works perfectly in CDB and it works fine initially in WinDbg but after stopping debugging and opening a new executable something happens and WinDbg ends up in a weird unusable state.
WinDbg prints this message to the command window when you trigger the problem:
Unable to deliver callback, 3131
and after this happens WinDbg seems to print all output twice in the command window!
My extension code is very simple:
EXTERN_C HRESULT CALLBACK DebugExtensionProvideValue(PDEBUG_CLIENT Client, ULONG Flags, IN PCWSTR Name, OUT PULONG64 Value, OUT PULONG64 TypeModBase, OUT PULONG TypeId, OUT PULONG TypeFlags)
{
HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
if (!Name || !Value || !TypeFlags)
{
hr = E_INVALIDARG;
}
else if (0 == lstrcmpiW(Name, L"$$test"))
{
*Value = 0xDeadCafeBabeBeefULL;
*TypeFlags = DEBUG_EXT_PVTYPE_IS_VALUE;
if (TypeId) *TypeId = 0; // Where are these types defined?
if (TypeModBase) *TypeModBase = 0;
hr = S_OK;
}
#if 0 // <-- ** Setting this to != 0 fixes the problem **
Client->Release(); // This does not feel right but it does seem to be required!
#endif
return hr;
}
EXTERN_C HRESULT CALLBACK DebugExtensionQueryValueNames(PDEBUG_CLIENT Client, ULONG Flags, OUT PWSTR Buffer, ULONG BufferChars, OUT PULONG BufferNeeded)
{
static const WCHAR pseregs[] = L"$$test\0";
if (BufferNeeded) *BufferNeeded = ARRAYSIZE(pseregs);
memcpy(Buffer, pseregs, min(sizeof(pseregs), BufferChars*sizeof(*Buffer)));
return ARRAYSIZE(pseregs) > BufferChars ? S_FALSE : S_OK;
}
EXTERN_C HRESULT CALLBACK DebugExtensionInitialize(OUT PULONG Version, OUT PULONG Flags)
{
*Version = DEBUG_EXTENSION_VERSION(1, 0), *Flags = 0;
return S_OK;
}
Reproducing the issue looks something like this:
0:000> $$ Press Ctrl+E to open a executable, I'm going to open WinVer.exe
0:000> .load c:\test\myext.dll
0:000> ?#$$test
Evaluate expression: -2401039830915039505 = deadcafe`babebeef
0:000> $$ Press Shift+F5 to stop debugging
0:000> $$ Press Ctrl+E and open a executable again, WinDbg will now print "Unable to deliver callback, 3131"
I was able to come up with a workaround that does seem to work but it just does not feel right because I have to Release a interface I never QI'ed nor AddRef'ed. In my minimal amount of testing this hack never seemed to crash and by peeking at IDebugClients refcount it does seem correct over multiple calls.
As far as I can tell you cannot stop debugging and open a new .exe like this in CDB so it seems the problem can only happen WinDbg.
Am I doing something wrong or is there a bug in DbgEng?
#Anders
I found some time to check it in a different code-path and it appears there is a bug in dbgeng ClientListCapture / Find / fill / list / FindExt
an incremented Reference count doesnt seem to be decremented resulting in a ref accumulation adding up several callbacks on each close and open
the code-path i used was to set the m_ProvidedValue member of the Extension Class
Note I also added code to address your other two queries TypeId and TypeModBase below
#include <engextcpp.cpp>
ExtProvidedValue pval[];
class EXT_CLASS : public ExtExtension {
public:
void
handler (
_In_ ULONG Flags, _In_ PCWSTR ValueName, _Out_ PULONG64 Value,
_Out_ PULONG64 TypeModBase, _Out_ PULONG TypeId, _Out_ PULONG TypeFlags
) {
DEBUG_CACHED_SYMBOL_INFO Info;
ZeroMemory(&Info, sizeof(Info));
PCSTR Type = "ntdll!_LDR_DATA_TABLE_ENTRY";
HRESULT Status = E_FAIL;
if((Status = m_Symbols->GetSymbolTypeId(Type,&Info.Id,&Info.ModBase)) != S_OK) {
ThrowStatus(Status, "Unable to get type ID of '%s'", Type);
}
if (0 == lstrcmpiW(ValueName, L"$$test")) {
if(Value) { *Value = 0xDeadCafeBabeBeefULL; }
if(TypeModBase) { *TypeModBase = Info.ModBase; }
if(TypeId) { *TypeId = Info.Id; }
if(TypeFlags) { *TypeFlags = DEBUG_EXT_PVTYPE_IS_POINTER; }
}
};
HRESULT Initialize(void) {
this->m_ProvidedValues = pval;
return S_OK;
}
};
EXT_DECLARE_GLOBALS();
ExtProvideValueMethod mymethod = (ExtProvideValueMethod)&Extension::handler;
ExtProvidedValue pval[] = {L"$$test\0",mymethod,NULL,NULL};
def file contains
EXPORTS
DebugExtensionInitialize
DebugExtensionQueryValueNames
DebugExtensionProvideValue
help
compiled and linked with enterprise wdk as follows
#echo off
IF "%donesetup%" == "" ( pushd .. )
IF "%donesetup%" == "" ( cd /d E:\ewdk )
IF "%donesetup%" == "" ( #call launchbuildenv.cmd )
IF "%donesetup%" == "" ( popd )
IF "%donesetup%" == "" ( set "donesetup=donesetup" )
IF "%INCLUDE%" == "" ( set "INCLUDE=%vcinstalldir%include;%windowssdkdir%Include\10.0.10586.0\ucrt;%windowssdkdir%Include\10.0.10586.0\um;%windowssdkdir%Include\10.0.10586.0\shared;%windowssdkdir%Debuggers\inc;" )
IF "%LIB%" == "" ( set "LIB=%vcinstalldir%\LIB;%WINDOWSSDKDIR%Lib\10.0.10586.0\ucrt\x86;%WINDOWSSDKDIR%Lib\10.0.10586.0\um\x86;%windowssdkdir%Debuggers\lib\x86")
IF "%LINKLIBS%" == "" ( set "LINKLIBS=user32.lib kernel32.lib dbgeng.lib dbghelp.lib" )
cl /LD /nologo /W3 /Zi /EHsc pstest.cpp /link /DEF:pstest.def /RELEASE %linklibs%
move /y pstest.dll "e:\ewdk\Program Files\Windows Kits\10\Debuggers\x86\winext"\.
loading the extension into windbg and result of invocation
Microsoft (R) Windows Debugger Version 10.0.10586.567 X86
0:000> .load pstest
0:000> ? #$$test (masm Evaluate)
Evaluate expression: -2401039830915039505 = deadcafe`babebeef
0:000> ?? #$$test (c++ Evaluate note the use of TypeId and TypeModbase
results in a type Display instead of a Int64 value)
struct _LDR_DATA_TABLE_ENTRY * 0xbabebeef
+0x000 InLoadOrderLinks : _LIST_ENTRY
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : ????
+0x01c EntryPoint : ????
+0x020 SizeOfImage : ??
+0x024 FullDllName : _UNICODE_STRING
+0x02c BaseDllName : _UNICODE_STRING
+0x034 Flags : ??
+0x038 LoadCount : ??
+0x03a TlsIndex : ??
+0x03c HashLinks : _LIST_ENTRY
+0x03c SectionPointer : ????
+0x040 CheckSum : ??
+0x044 TimeDateStamp : ??
+0x044 LoadedImports : ????
+0x048 EntryPointActivationContext : ????
+0x04c PatchInformation : ????
+0x050 ForwarderLinks : _LIST_ENTRY
+0x058 ServiceTagLinks : _LIST_ENTRY
+0x060 StaticLinks : _LIST_ENTRY
+0x068 ContextInformation : ????
+0x06c OriginalBase : ??
+0x070 LoadTime : _LARGE_INTEGER
and as posted by you if i do Shift+f5 and ctrl+e windbg complains that it cannot deliver the callback
if the procedure of shift +f5 / ctrl+e / load / invoke is repeated
the number of complaints keeps on adding to the point that windbg hangs
trying to ProcessPendingMessages in hung callstack

Resources