A forum for reverse engineering, OS internals and malware analysis 

Discussion on reverse-engineering and debugging.
 #11156  by lorddoskias
 Fri Jan 20, 2012 10:26 am
This thing is null in x64 - does anyone have information how it got changed/relocated/completely unsupported? Even for reading purposes?
 #11160  by EP_X0FF
 Fri Jan 20, 2012 1:52 pm
Check it out.

get pointer to structure
Code: Select all
dq KdDebuggerDataBlock l1
list header to get actual pointer to data
Code: Select all
dt _LIST_ENTRY (address_of_header)
now lets check with KernelBase and PsLoadedModuleList
Code: Select all
dq (flink_address)+KernBase_offset
dq (flink_address)+PsLoadedModulesList_offset
for example Windows 7
Code: Select all
dq address+18 l1
dq address+48 l1
 #11172  by lorddoskias
 Fri Jan 20, 2012 6:55 pm
You are right. But does that mean KdVersionBlock variable is exported?

Also one more thing which I noticed is that it is not possible to get the address of the KPCR structure using the gs register:
Code: Select all
kd> !pcr
KPCR for Processor 0 at fffff800027ecd00:
    Major 1 Minor 1
	NtTib.ExceptionList: fffff80000b95000
	    NtTib.StackBase: fffff80000b96080
	   NtTib.StackLimit: 0000000001f89558
	 NtTib.SubSystemTib: fffff800027ecd00
	      NtTib.Version: 00000000027ece80
	  NtTib.UserPointer: fffff800027ed4f0
	      NtTib.SelfTib: 000007fffffd7000

	            SelfPcr: 0000000000000000
	               Prcb: fffff800027ece80
	               Irql: 0000000000000000
	                IRR: 0000000000000000
	                IDR: 0000000000000000
	      InterruptMode: 0000000000000000
	                IDT: 0000000000000000
	                GDT: 0000000000000000
	                TSS: 0000000000000000

	      CurrentThread: fffff800027fac40
	         NextThread: 0000000000000000
	         IdleThread: fffff800027fac40

	          DpcQueue: 
SelfPcr is 0 ? Why is that - how to get the address of KPCR?
 #11177  by EP_X0FF
 Sat Jan 21, 2012 4:17 am
lorddoskias wrote:SelfPcr is 0 ? Why is that - how to get the address of KPCR?
!pcr command for x64 is totally bugged.

As you know KPCR for x64 and KPCR for x86 are different. They have differently named fields. kdexts.dll seems to be broken as it tries to read fields that are not exists on x64 resulting in bugged output for !pcr command.

Take a simple example. This is inline macro KeGetPcr

for x86 it reads field named SelfPcr which is flat address of this Pcr
Code: Select all
FORCEINLINE
PKPCR
NTAPI
KeGetPcr(VOID)
{
#if NT_UP
    return (PKPCR)KIP0PCRADDRESS;
#else

#if (_MSC_FULL_VER >= 13012035)
    return (PKPCR) (ULONG_PTR) __readfsdword (FIELD_OFFSET (KPCR, SelfPcr));
#else
    __asm {  mov eax, _PCR KPCR.SelfPcr  }
#endif

#endif
}

for x64 it reads field named Self
Code: Select all
__forceinline
PKPCR
KeGetPcr (
    VOID
    )

{
    return (PKPCR)__readgsqword(FIELD_OFFSET(KPCR, Self));
}
Now lets check how it possible that !pcr gives NULL for IDT, GDT, TSS and SelfPcr?

Checking the first function which comes into my mind regarding to PCR.
Code: Select all
Microsoft (R) Windows Debugger Version 6.12.0002.633 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.


Loading Dump File [C:\Windows\livekd.dmp]
Kernel Complete Dump File: Full address space is available

Comment: 'LiveKD live system view'
Symbol search path is: srv*c:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Windows 7 Kernel Version 7601 (Service Pack 1) MP (16 procs) Free x64
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 7601.17640.amd64fre.win7sp1_gdr.110622-1506
Machine Name:
Kernel base = 0xfffff800`02c1e000 PsLoadedModuleList = 0xfffff800`02e63670
Debug session time: Sun Feb 13 10:34:57.897 17420 (UTC + 1:00)
System Uptime: 0 days 0:27:22.580
Loading Kernel Symbols
...............................................................
................................................................
....................................................
Loading User Symbols

Loading unloaded module list

0: kd> u PsGetCurrentProcess
nt!PsGetCurrentProcess:
fffff800`02cad150 65488b042588010000 mov   rax,qword ptr gs:[188h]
fffff800`02cad159 488b4070           mov   rax,qword ptr [rax+70h]
gs in place.

Lets list KPCR data by address given from !pcr
Code: Select all
0: kd> !pcr
KPCR for Processor 0 at fffff80002e10d00:
    Major 1 Minor 1
	NtTib.ExceptionList: fffff80000b95000
	    NtTib.StackBase: fffff80000b96080
	   NtTib.StackLimit: 000000000229f648
	 NtTib.SubSystemTib: fffff80002e10d00
	      NtTib.Version: 0000000002e10e80
	  NtTib.UserPointer: fffff80002e114f0
	      NtTib.SelfTib: 000007fffffdd000

	            SelfPcr: 0000000000000000
	               Prcb: fffff80002e10e80
	               Irql: 0000000000000000
	                IRR: 0000000000000000
	                IDR: 0000000000000000
	      InterruptMode: 0000000000000000
	                IDT: 0000000000000000
	                GDT: 0000000000000000
	                TSS: 0000000000000000

	      CurrentThread: fffff80002e1ecc0
	         NextThread: 0000000000000000
	         IdleThread: fffff80002e1ecc0

	          DpcQueue: 
Code: Select all
0: kd> dt nt!_KPCR fffff80002e10d00
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : 0xfffff800`00b95000 _KGDTENTRY64
   +0x008 TssBase          : 0xfffff800`00b96080 _KTSS64
   +0x010 UserRsp          : 0x229f648
   +0x018 Self             : 0xfffff800`02e10d00 _KPCR
   +0x020 CurrentPrcb      : 0xfffff800`02e10e80 _KPRCB
   +0x028 LockArray        : 0xfffff800`02e114f0 _KSPIN_LOCK_QUEUE
   +0x030 Used_Self        : 0x000007ff`fffdd000 Void
   +0x038 IdtBase          : 0xfffff800`00b95080 _KIDTENTRY64
   +0x040 Unused           : [2] 0
   +0x050 Irql             : 0 ''
   +0x051 SecondLevelCacheAssociativity : 0x8 ''
   +0x052 ObsoleteNumber   : 0 ''
   +0x053 Fill0            : 0 ''
   +0x054 Unused0          : [3] 0
   +0x060 MajorVersion     : 1
   +0x062 MinorVersion     : 1
   +0x064 StallScaleFactor : 0x703
   +0x068 Unused1          : [3] (null) 
   +0x080 KernelReserved   : [15] 0
   +0x0bc SecondLevelCacheSize : 0x200000
   +0x0c0 HalReserved      : [16] 0x6b063950
   +0x100 Unused2          : 0
   +0x108 KdVersionBlock   : (null) 
   +0x110 Unused3          : (null) 
   +0x118 PcrAlign1        : [24] 0
   +0x180 Prcb             : _KPRCB
As you see IdtBase, GdtBase, TssBase and Self are not NULL (and cant be). Probably this is kdexts.dll bug and it seems exists for years (6.12.0002.633 AMD64 output here).
Do not blindly trust Windbg :)
 #11182  by kmd
 Sat Jan 21, 2012 6:27 am
interesting if this is bug then why not fixed?
KdDebuggerDataBlock unexported, how find (in code)?
 #11184  by EP_X0FF
 Sat Jan 21, 2012 7:04 am
kmd wrote:KdDebuggerDataBlock unexported, how find (in code)?
You can extract it address from KeCapturePersistentThreadState by ldasm signature pattern search, think yourself.

x64
Code: Select all
.text:000000014017BD18                 lea     rax, KdDebuggerDataBlock
.text:000000014017BD1F                 lea     rcx, [rbx+2080h]
.text:000000014017BD26                 mov     [rbx+80h], rax
.text:000000014017BD2D                 mov     [rbx+2070h], esi
.text:000000014017BD33                 mov     [rbx+2074h], r11d
.text:000000014017BD3A                 call    KdCopyDataBlock
x86
Code: Select all
.text:004CE4BA                 mov     dword ptr [ebx+60h], offset _KdDebuggerDataBlock
.text:004CE4C1                 lea     eax, [ebx+1068h]
.text:004CE4C7                 mov     dword ptr [ebx+1058h], 1068h
.text:004CE4D1                 mov     [ebx+105Ch], esi
.text:004CE4D7                 call    _KdCopyDataBlock
kmd wrote:interesting if this is bug then why not fixed?
Since it is obvious bug then because of

Image
 #11185  by redp
 Sat Jan 21, 2012 8:44 am
EP_X0FF wrote:
kmd wrote:KdDebuggerDataBlock unexported, how find (in code)?
You can extract it address from KeCapturePersistentThreadState by ldasm signature pattern search, think yourself.

x64
Code: Select all
.text:000000014017BD18                 lea     rax, KdDebuggerDataBlock
.text:000000014017BD1F                 lea     rcx, [rbx+2080h]
.text:000000014017BD26                 mov     [rbx+80h], rax
.text:000000014017BD2D                 mov     [rbx+2070h], esi
.text:000000014017BD33                 mov     [rbx+2074h], r11d
.text:000000014017BD3A                 call    KdCopyDataBlock
x86
Code: Select all
.text:004CE4BA                 mov     dword ptr [ebx+60h], offset _KdDebuggerDataBlock
.text:004CE4C1                 lea     eax, [ebx+1068h]
.text:004CE4C7                 mov     dword ptr [ebx+1058h], 1068h
.text:004CE4D1                 mov     [ebx+105Ch], esi
.text:004CE4D7                 call    _KdCopyDataBlock
I am sure that there is much more safe way to find it
Lets see structure of KD_DEBUGGER_DATA_BLOCK
It contains at least 3 ptrs to exported symbols: MmHighestUserAddress, MmSystemRangeStart & MmUserProbeAddress
So it`s possible just to scan .data section for this pointers in right order
 #11212  by lorddoskias
 Sun Jan 22, 2012 8:37 pm
Hi, I set out to implement what redp has suggested and here is what I've got so far:

void findKdDebuggerBlock(PDRIVER_OBJECT DriverObject) {
Code: Select all
	DWORD bytesScanned;
	DWORD sectionSize = 0;
	PVOID kernelbase = getNtKrnlBase(DriverObject);
	PVOID sectionAddress = GetDataSectionAddress(kernelbase, &sectionSize);
	PKDDEBUGGER_DATA64 debugData = sectionAddress;

	DbgPrint("Size of debugger data is %d", sizeof(KDDEBUGGER_DATA64));
	for(bytesScanned = 0; bytesScanned < sectionSize; bytesScanned += sizeof(KDDEBUGGER_DATA64)) {
		if(debugData->MmHighestUserAddress == MmHighestUserAddress &&
		   debugData->MmSystemRangeStart == MmSystemRangeStart &&
		   debugData->MmUserProbeAddress == MmUserProbeAddress ) {
			   DbgPrint("Found KDEVERSIOIN BLOCK AT 0x%p\n", debugData);
			   break;
		} else {
			DbgPrint("Couldn't find it\n");
		}

		debugData++;
	}
}
But I always hit the "Couldn't find it" branch? Any ideas what I'm doing wrong? Getting kernel base is working and getting data section start is also working. Furthermore, the exported Mm* variables are declared as PVOID whereas teh Mm* in the KDEBUGGER_DATA are ULONG64 why is it so?
 #11214  by redp
 Sun Jan 22, 2012 9:29 pm
lorddoskias wrote:Any ideas what I'm doing wrong?
Error is in line
Code: Select all
debugData++;
you scan not whole memory of section but only small part with step sizeof(KDDEBUGGER_DATA64)
lorddoskias wrote:Furthermore, the exported Mm* variables are declared as PVOID whereas teh Mm* in the KDEBUGGER_DATA are ULONG64 why is it so?
To have the same size of fields under 32 & 64 bit