Thursday, September 10, 2009

[DOC] Understanding DKOM with WinDBG

Process hiding can be achieved by using a technique called DKOM (Direct Kernel Object Manipulation). I started to discover this when I first read the book Rootkit: Subverting the Windows Kernel.


Using Task Manager (Ctrl + Alt + Del) and we see the list of processes:

We can find the chain of active processes by looking at the global kernel variable which is not exported by the kernel and undocumented PsActiveProcessHead.

PsActiveProcessHead contains a linked list LIST_ENTRY for the current active processes being processed by the kernel.

The "system" process is always in the FIRST ENTRY which can be found by calling kernel API PsInitialSystemProcess. The function will return a pointer to the EPROCESS structure of "system" process. The EPROCESS.ActiveProcessLinks.Blink is the PsActiveProcessHead. To verify that:

Let's see the EPROCESS and LIST_ENTRY structure

kd> dt _eprocess

+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : Uint4B
+0x114 ForkInProgress : Ptr32 _ETHREAD
+0x118 HardwareTrigger : Uint4B
+0x11c VadRoot : Ptr32 Void
+0x120 VadHint : Ptr32 Void
+0x124 CloneRoot : Ptr32 Void
+0x128 NumberOfPrivatePages : Uint4B
+0x12c NumberOfLockedPages : Uint4B
+0x130 Win32Process : Ptr32 Void
+0x134 Job : Ptr32 _EJOB
+0x138 SectionObject : Ptr32 Void
+0x13c SectionBaseAddress : Ptr32 Void
+0x140 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x144 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x148 Win32WindowStation : Ptr32 Void
+0x14c InheritedFromUniqueProcessId : Ptr32 Void
+0x150 LdtInformation : Ptr32 Void
+0x154 VadFreeHint : Ptr32 Void
+0x158 VdmObjects : Ptr32 Void
+0x15c DeviceMap : Ptr32 Void
+0x160 PhysicalVadList : _LIST_ENTRY
+0x168 PageDirectoryPte : _HARDWARE_PTE_X86
+0x168 Filler : Uint8B
+0x170 Session : Ptr32 Void
+0x174 ImageFileName : [16] UChar
+0x184 JobLinks : _LIST_ENTRY
+0x18c LockedPagesList : Ptr32 Void
+0x190 ThreadListHead : _LIST_ENTRY
+0x198 SecurityPort : Ptr32 Void
+0x19c PaeTop : Ptr32 Void
+0x1a0 ActiveThreads : Uint4B
+0x1a4 GrantedAccess : Uint4B
+0x1a8 DefaultHardErrorProcessing : Uint4B
+0x1ac LastThreadExitStatus : Int4B
+0x1b0 Peb : Ptr32 _PEB
+0x1b4 PrefetchTrace : _EX_FAST_REF
+0x1b8 ReadOperationCount : _LARGE_INTEGER
+0x1c0 WriteOperationCount : _LARGE_INTEGER
+0x1c8 OtherOperationCount : _LARGE_INTEGER
+0x1d0 ReadTransferCount : _LARGE_INTEGER
+0x1d8 WriteTransferCount : _LARGE_INTEGER
+0x1e0 OtherTransferCount : _LARGE_INTEGER
+0x1e8 CommitChargeLimit : Uint4B
+0x1ec CommitChargePeak : Uint4B
+0x1f0 AweInfo : Ptr32 Void
+0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1f8 Vm : _MMSUPPORT
+0x238 LastFaultCount : Uint4B
+0x23c ModifiedPageCount : Uint4B
+0x240 NumberOfVads : Uint4B
+0x244 JobStatus : Uint4B
+0x248 Flags : Uint4B
+0x248 CreateReported : Pos 0, 1 Bit
+0x248 NoDebugInherit : Pos 1, 1 Bit
+0x248 ProcessExiting : Pos 2, 1 Bit
+0x248 ProcessDelete : Pos 3, 1 Bit
+0x248 Wow64SplitPages : Pos 4, 1 Bit
+0x248 VmDeleted : Pos 5, 1 Bit
+0x248 OutswapEnabled : Pos 6, 1 Bit
+0x248 Outswapped : Pos 7, 1 Bit
+0x248 ForkFailed : Pos 8, 1 Bit
+0x248 HasPhysicalVad : Pos 9, 1 Bit
+0x248 AddressSpaceInitialized : Pos 10, 2 Bits
+0x248 SetTimerResolution : Pos 12, 1 Bit
+0x248 BreakOnTermination : Pos 13, 1 Bit
+0x248 SessionCreationUnderway : Pos 14, 1 Bit
+0x248 WriteWatch : Pos 15, 1 Bit
+0x248 ProcessInSession : Pos 16, 1 Bit
+0x248 OverrideAddressSpace : Pos 17, 1 Bit
+0x248 HasAddressSpace : Pos 18, 1 Bit
+0x248 LaunchPrefetched : Pos 19, 1 Bit
+0x248 InjectInpageErrors : Pos 20, 1 Bit
+0x248 VmTopDown : Pos 21, 1 Bit
+0x248 Unused3 : Pos 22, 1 Bit
+0x248 Unused4 : Pos 23, 1 Bit
+0x248 VdmAllowed : Pos 24, 1 Bit
+0x248 Unused : Pos 25, 5 Bits
+0x248 Unused1 : Pos 30, 1 Bit
+0x248 Unused2 : Pos 31, 1 Bit
+0x24c ExitStatus : Int4B
+0x250 NextPageColor : Uint2B
+0x252 SubSystemMinorVersion : UChar
+0x253 SubSystemMajorVersion : UChar
+0x252 SubSystemVersion : Uint2B
+0x254 PriorityClass : UChar
+0x255 WorkingSetAcquiredUnsafe : UChar
+0x258 Cookie : Uint4B

You can also check the structure on this page:
kd> dt _list_entry

+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY

We first start to verify PsActiveProcessHead by checking the linked list:

kd> dl nt!psactiveprocesshead 100 2

Addr Flink Blink

805627b8 825c68b8 82499c68
825c68b8 821f2818 805627b8
821f2818 8211b680 825c68b8
8211b680 8229fe28 821f2818
8229fe28 820bde28 8211b680
820bde28 8220fe28 8229fe28
8220fe28 82290910 820bde28
82290910 821c90a8 8220fe28
821c90a8 8223e3a0 82290910
8223e3a0 82102c98 821c90a8
82102c98 8216b3a0 8223e3a0
8216b3a0 820f8680 82102c98
820f8680 82176aa8 8216b3a0
82176aa8 821611b8 820f8680
821611b8 8221ce28 82176aa8
8221ce28 8218f5b0 821611b8
8218f5b0 822484e8 8221ce28
822484e8 82252aa8 8218f5b0
82252aa8 821fb258 822484e8
821fb258 82147770 82252aa8
82147770 821cfc40 821fb258
821cfc40 8218daa8 82147770
8218daa8 820f7538 821cfc40
820f7538 822153a0 8218daa8
822153a0 8211fe28 820f7538
8211fe28 825cb908 822153a0
825cb908 82128e28 8211fe28
82128e28 820f3510 825cb908
820f3510 8210ce28 82128e28
8210ce28 820fd7c8 820f3510
820fd7c8 82234630 8210ce28
82234630 821da510 820fd7c8
821da510 821a9658 82234630
821a9658 82143c40 821da510
82143c40 822a80a8 821a9658
822a80a8 8230b0a8 82143c40
8230b0a8 8231d2d8 822a80a8
8231d2d8 821f4b20 8230b0a8
821f4b20 82499c68 8231d2d8
82499c68 805627b8 821f4b20

We can then use Kernel API PsInitialSystemProcess to look for the address of PsActiveProcessHead which should be 0x805627b8

kd> dt _eprocess activeprocesslinks.blink poi(psinitialsystemprocess)
+0x088 ActiveProcessLinks : [ 0x821f2818 - 0x805627b8 ]
+0x004 Blink : 0x805627b8 _LIST_ENTRY [ 0x825c68b8 - 0x82499c68 ]

We prove that 0x805627b8 is the address of PsActiveProcessHead that contains linked list of ActiveProcessLinks [Flink & Blink] for "system" process [Flink=0x825c68b8, Blink=0x82499c68] process, in order to verify that:

We take "system" process Flink address 0x825c68b8-0x88 in order to get its EPROCESS structure (please refer to "dt _eprocess" and check the offset of ActiveProcessLinks=Flink):

kd> dt _eprocess 0x825c68b8-0x88

+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER 0x0
+0x078 ExitTime : _LARGE_INTEGER 0x0
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : 0x00000004
+0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x821f2818 - 0x805627b8 ]

+0x174 ImageFileName : [16] "System"
The ImageFileName gives us the process name which is "system" and the "system" process always has the process id 4 (UniqueProcessId: 0x00000004)

Hiding Processes

So we know that [ 0x821f2818 - 0x805627b8 ] is the start of the active process chain. In order to hide the processes, we can remove the EPROCESS structure from this chain. For simplicity, I'll "remove" all the chains that's mean hiding all the processes:

kd> ed 805627b8
805627b8 825c68b8
Input> 805627b8

Use ed command to edit the memory address @ 0x805627b8 (remember this is PsActiveProcessHead and it is always start with "system" process) and the debugger is waiting for your input. So just edit the address to point back to itself 0x805627b8

The result would be:


Rootkit: Subverting the Windows Kernel (Page 169) - Direct Kernel Object Manipulation - Finding Kernel Global Variables - 获取Windows 系统的内核变量

No comments: