Windows Process Internals with WinDbg
Last modified: 2024-05-21
We can examine the Windows process using WinDbg.
Preparation
Here we’re going to start notepad
process as example.
Click File
→ Launch Executable
then select C:\Windows\System32\notepad.exe
.
After that, if we’ve not already set symbol file path, run the following commands:
.sympath SRV*c:\symbols*http://msdl.microsoft.com/download/symbols
.reload /f
Examine DOS Header
1. Get Address of Module Base Address
Firstly, we need to get the module (notepad
here) base address. To do that, list loaded modules with the following command:
lm
Output:
start end module name
00007ff6`52990000 00007ff6`529ea000 notepad (pdb symbols) C:\ProgramData\Dbg\sym\notepad.pdb\123456789ABCDEF123456789ABCDEF123\notepad.pdb
00007ffb`0e1a0000 00007ffb`0e433000 COMCTL32 (deferred)
00007ffb`29c10000 00007ffb`29d21000 ucrtbase (deferred)
As output above, the address of the notepad
is 00007ff6`52990000 so take a note of this address.
2. Examine IMAGE_DOS_HEADER Structure
To get inside the DOS header, run the following command with specified address that we retrieved in the previous section:
dt ntdll!_IMAGE_DOS_HEADER 00007ff6`52990000
Output:
+0x000 e_magic : 0x5a4d
...
+0x03c e_lfanew : 0n240
Maybe what data we should check are e_magic
and e_lfanew
.
e_magic
: It contains 2-byte (0x5A4D
:MZ
in ASCII) data.e_lfanew
: It holds an offset of NT Headers.
3. Examine e_magic
dd 00007ff6`52990000 L1
Output:
00007ff6`52990000 00905a4d
As above, we can see the value contains 5A4D
.
4. Examine e_lfanew
To see the value of e_lfanew
, run the following command with specifying the offset 0x03C
:
dd 00007ff6`52990000+0x03C L1
Output:
00007ff6`5299003c 000000f0
As above, we get 000000f0
. This value is an offset of PE Header.
5. Examine PE Header
To get information of PE header, run the following command with specifying the offset 0x0F0
:
dd 00007ff6`52990000+0x0F0 L1
Output:
00007ff6`529900f0 00004550
The result value is 00004550
that is \0\0EP
(little-endian, so it means PE\0\0
in ASCII.
Examine PEB (Process Environment Block)
1. Get Address of PEB
At first, we need to know the PEB (Process Environment Block) address with the following command:
!peb
Output:
PEB at 00000055d7905000
...
Although the output is large, we can get the address at the first line as above.
2. PEB Structure
To see the inside of the PEB, run dt
command with the specified address:
dt _PEB 00000055d7905000
Output:
ntdll!_PEB
+0x000 InheritedAddressSpace : 0 ''
+0x001 ReadImageFileExecOptions : 0 ''
...
+0x018 Ldr : 0x00007ffb`2ca76440 _PEB_LDR_DATA
...
What we should pay attention to is the Ldr
line because the Ldr
has important roles in loading APIs required by the executable file when the process is started.
So take a note of this LDR address (0x00007ffb`2ca76440 here).
3. Examine Ldr Structure
Same ways as above, run dt
command to see the Ldr
structure. Please note that we should specify the _PEB_LDR_DATA
.
dt _PEB_LDR_DATA 0x00007ffb`2ca76440
Output:
ntdll!_PEB_LDR_DATA
+0x000 Length : 0x58
+0x004 Initialized : 0x1 ''
+0x008 SsHandle : (null)
+0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x000001c4`b1ef28c0 - 0x000001c4`b1eff510 ]
+0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x000001c4`b1ef28d0 - 0x000001c4`b1eff520 ]
+0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x000001c4`b1ef2740 - 0x000001c4`b1ef75e0 ]
+0x040 EntryInProgress : (null)
+0x048 ShutdownInProgress : 0 ''
+0x050 ShutdownThreadId : (null)
We’re going to see the details of InLoadOrderModuleList
in the next, so take a note of the address (0x000001c4`b1ef28c0).
4. Examine InLoadOrderModuleList Structure
To see the information in the InLoadOrderModuleList
, run dt
command as below. Please note that we should set _LDR_DATA_TABLE_ENTRY
instead of _LIST_ENTRY
to see the desired information.
dt _LDR_DATA_TABLE_ENTRY 0x000001c4`b1ef28c0
Output:
ntdll!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x000001c4`b1ef2720 - 0x00007ffb`2ca76450 ]
+0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x000001c4`b1ef2730 - 0x00007ffb`2ca76460 ]
+0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
+0x030 DllBase : 0x00007ff6`52990000 Void
+0x038 EntryPoint : 0x00007ff6`529919a0 Void
+0x040 SizeOfImage : 0x5a000
+0x048 FullDllName : _UNICODE_STRING "C:\Windows\System32\notepad.exe"
+0x058 BaseDllName : _UNICODE_STRING "notepad.exe"
...
As above, the notepad.exe
itself is loaded in here by seeing the BaseDllName
line.
5. Examine Next InLoadOrderModuleList Structure
To examine the next loaded module, we should get the next InLoadOrderModuleList
structure. To do that, run the following command with specifying the address of the InLoadOrderModuleList
that we took a note of in the previous section. Please note that we should set _LIST_ENTRY
to see the next entry:
dt _LIST_ENTRY 0x000001c4`b1ef28c0
Output:
ntdll!_LIST_ENTRY
[ 0x000001c4`b1ef2720 - 0x00007ffb`2ca76450 ]
+0x000 Flink : 0x000001c4`b1ef2720 _LIST_ENTRY [ 0x000001c4`b1ef75c0 - 0x000001c4`b1ef28c0 ]
+0x008 Blink : 0x00007ffb`2ca76450 _LIST_ENTRY [ 0x000001c4`b1ef28c0 - 0x000001c4`b1eff510 ]
This output contains the next entry (Flink
) and the last entry (Blink
). Naturally, what we want is the Flink
!. So please take a note of this address (0x000001c4`b1ef2720)
Now we can get the next data entry with the following command:
dt _LDR_DATA_TABLE_ENTRY 0x000001c4`b1ef2720
Output:
ntdll!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x000001c4`b1ef75c0 - 0x000001c4`b1ef28c0 ]
+0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x000001c4`b1ef75d0 - 0x000001c4`b1ef28d0 ]
+0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x000001c4`b1ef7bd0 - 0x00007ffb`2ca76470 ]
+0x030 DllBase : 0x00007ffb`2c8f0000 Void
+0x038 EntryPoint : (null)
+0x040 SizeOfImage : 0x217000
+0x048 FullDllName : _UNICODE_STRING "C:\Windows\SYSTEM32\ntdll.dll"
+0x058 BaseDllName : _UNICODE_STRING "ntdll.dll"
...
Seeing at the BaseDllName
line, it loads the ntdll.dll
module.
If we repeat the steps above, we should see that the KERNEL32.DLL
is loaded in the next entry.
6. Examine BaseDllName Structure
Up to this point, we have been able to see the information of InloadOrderModuleList
structure. Next, let's take a look inside the BaseDllName
.
With the example above, the offset of the BaseDllName
from the InLoadOrderModuleList
is +0x058
. So we can use this in the following command:
dt _UNICODE_STRING 000001c4`b1ef2720+0x058
Output:
ntdll!_UNICODE_STRING
"ntdll.dll"
+0x000 Length : 0x12
+0x002 MaximumLength : 0x14
+0x008 Buffer : 0x00007ffb`2ca2e5f0 "ntdll.dll"