Debugging programs with multiple processes with windbg’s kernel mode debugger

末蓝、 2022-05-11 03:46 266阅读 0赞

转载自:http://www.vallejo.cc/2015/04/debugging-programs-with-multiple.html

It’s common to reverse malware (or any type of software) that creates multiple processes or loads drivers, and it is useful to be able to debug the new created processes or loaded drivers from entry point.

To break at the entry point of the processes you can modify CreateProcess parameters to create child processes suspended (to attach debugger later), or introduce 0xCC at entrypoint on disk of the PE file that is going to be launched, or use plugins for debuggers to attach to child processes… There are lot of methods. From kernel mode you can set conditions that break into the debugger: sxe ld ntdll.dll, sxe cpr, etc… For breaking at a driver entry point you can use: bu !DriverEntry (though, i don’t know why, sometimes it doesn’t work for me).

I will talk here about a couple of ways to do this from windbg kernel mode debugger, without needing to restart computer to enable exceptions that break into debugger, or modifying PEs in disk,…

To break at entry point of the main module of a new process, we start setting a breakpoint at CreateProcessA, CreateProcessW, CreateProcessAsUserA, CreateProcessAsUserW, … of the process that will create the child process. If you don’t know what function is called to create the child process, set the bp at ZwCreateSection (it must be called always when a process is created). Step over the call (let the new process to be loaded). Now the new process’s image is loaded and you can set a bp for a target process with bp /p :

When the process is loaded, search it with !process 0 0 to get the address of the EPROCESS.

  1. ...
  2. PROCESS 8223d020 SessionId: 0 Cid: 0dd8 Peb: 7ffd9000 ParentCid: 0120
  3. DirBase: 093c0220 ObjectTable: e1224c08 HandleCount: 0.
  4. Image: calc.exe
  5. ...

With the EPROCESS address, enum process info:

kd> !process 8223d020

  1. PROCESS 8223d020 SessionId: 0 Cid: 0dd8 Peb: 7ffd9000 ParentCid: 0120
  2. DirBase: 093c0220 ObjectTable: e1224c08 HandleCount: 0.
  3. Image: calc.exe
  4. VadRoot 82235f60 Vads 11 Clone 0 Private 8. Modified 0. Locked 0.
  5. DeviceMap e1b2b1b8
  6. Token e1274998
  7. ElapsedTime 00:00:00.000
  8. UserTime 00:00:00.000
  9. KernelTime 00:00:00.000
  10. QuotaPoolUsage[PagedPool] 7860
  11. QuotaPoolUsage[NonPagedPool] 440
  12. Working Set Sizes (now,min,max) (19, 50, 345) (76KB, 200KB, 1380KB)
  13. PeakWorkingSetSize 19
  14. VirtualSize 2 Mb
  15. PeakVirtualSize 2 Mb
  16. PageFaultCount 12
  17. MemoryPriority BACKGROUND
  18. BasePriority 8
  19. CommitCharge 19
  20. THREAD 824304c0 Cid 0dd8.0dec Teb: 7ffdf000 Win32Thread: 00000000 READY on processor 0
  21. Not impersonating
  22. DeviceMap e1b2b1b8
  23. Owning Process 0 Image: <Unknown>
  24. Attached Process 8223d020 Image: calc.exe
  25. Wait Start TickCount 70583 Ticks: 0
  26. Context Switch Count 0 IdealProcessor: 0
  27. UserTime 00:00:00.000
  28. KernelTime 00:00:00.000
  29. Win32 Start Address Explorer!CClockCtl::_GetMaxTimeSize (0x0101e23a)
  30. Start Address kernel32!BaseProcessStartThunk (0x7c810735)
  31. Stack Init f6b04000 Current f6b03d48 Base f6b04000 Limit f6b01000 Call 0
  32. Priority 8 BasePriority 8 PriorityDecrement 0 DecrementCount 0
  33. ChildEBP RetAddr
  34. f6b03e28 7c920222 nt!KiThreadStartup
  35. f6b03eb0 7c924d12 ntdll!RtlpAllocateFromHeapLookaside+0x42 (FPO: [Non-Fpo])
  36. f6b03eb0 00000000 ntdll!RtlConvertSidToUnicodeString+0x1b5 (FPO: [Non-Fpo])

We can see the thread and use it to change the context to this thread:

  1. kd> .thread 824304c0
  2. Implicit thread is now 824304c0
  3. kd> r
  4. Last set context:
  5. eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
  6. eip=80541f4c esp=f6b03d54 ebp=805c623e iopl=0 nv up di pl nz na po nc
  7. cs=001b ss=0023 ds=0000 es=0000 fs=0000 gs=0000 efl=00000000
  8. nt!KiThreadStartup:
  9. 001b:80541f4c 33db xor ebx,ebx

Now we see the ETHREAD struct of the main thread of the new process at address 0x824304c0, and we can see the fields of this structure. We are interesed on Win32StartAddress field, the entrypoint of the thread in the recently created process:

  1. kd> .process /i 8223d020
  2. You need to continue execution (press 'g' <enter>) for the context
  3. to be switched. When the debugger breaks in again, you will be in
  4. the new process context.
  5. kd> dt _ETHREAD 824304c0
  6. ntdll!_ETHREAD
  7. +0x000 Tcb : _KTHREAD
  8. +0x1c0 CreateTime : _LARGE_INTEGER 0x0e82fb8c`bed0e210
  9. +0x1c0 NestedFaultCount : 0y00
  10. +0x1c0 ApcNeeded : 0y0
  11. +0x1c8 ExitTime : _LARGE_INTEGER 0x82430688`82430688
  12. +0x1c8 LpcReplyChain : _LIST_ENTRY [ 0x82430688 - 0x82430688 ]
  13. +0x1c8 KeyedWaitChain : _LIST_ENTRY [ 0x82430688 - 0x82430688 ]
  14. +0x1d0 ExitStatus : 0n0
  15. +0x1d0 OfsChain : (null)
  16. +0x1d4 PostBlockList : _LIST_ENTRY [ 0x82430694 - 0x82430694 ]
  17. +0x1dc TerminationPort : (null)
  18. +0x1dc ReaperLink : (null)
  19. +0x1dc KeyedWaitValue : (null)
  20. +0x1e0 ActiveTimerListLock : 0
  21. +0x1e4 ActiveTimerListHead : _LIST_ENTRY [ 0x824306a4 - 0x824306a4 ]
  22. +0x1ec Cid : _CLIENT_ID
  23. +0x1f4 LpcReplySemaphore : _KSEMAPHORE
  24. +0x1f4 KeyedWaitSemaphore : _KSEMAPHORE
  25. +0x208 LpcReplyMessage : (null)
  26. +0x208 LpcWaitingOnPort : (null)
  27. +0x20c ImpersonationInfo : (null)
  28. +0x210 IrpList : _LIST_ENTRY [ 0x824306d0 - 0x824306d0 ]
  29. +0x218 TopLevelIrp : 0
  30. +0x21c DeviceToVerify : (null)
  31. +0x220 ThreadsProcess : 0x8223d020 _EPROCESS
  32. +0x224 StartAddress : 0x7c810735 Void
  33. +0x228 Win32StartAddress : 0x0101e23a Void
  34. +0x228 LpcReceivedMessageId : 0x101e23a
  35. +0x22c ThreadListEntry : _LIST_ENTRY [ 0x8223d1b0 - 0x8223d1b0 ]
  36. +0x234 RundownProtect : _EX_RUNDOWN_REF
  37. +0x238 ThreadLock : _EX_PUSH_LOCK
  38. +0x23c LpcReplyMessageId : 0
  39. +0x240 ReadClusterSize : 7
  40. +0x244 GrantedAccess : 0x1f03ff
  41. +0x248 CrossThreadFlags : 0
  42. +0x248 Terminated : 0y0
  43. +0x248 DeadThread : 0y0
  44. +0x248 HideFromDebugger : 0y0
  45. +0x248 ActiveImpersonationInfo : 0y0
  46. +0x248 SystemThread : 0y0
  47. +0x248 HardErrorsAreDisabled : 0y0
  48. +0x248 BreakOnTermination : 0y0
  49. +0x248 SkipCreationMsg : 0y0
  50. +0x248 SkipTerminationMsg : 0y0
  51. +0x24c SameThreadPassiveFlags : 0
  52. +0x24c ActiveExWorker : 0y0
  53. +0x24c ExWorkerCanWaitUser : 0y0
  54. +0x24c MemoryMaker : 0y0
  55. +0x250 SameThreadApcFlags : 0
  56. +0x250 LpcReceivedMsgIdValid : 0y0
  57. +0x250 LpcExitThreadCalled : 0y0
  58. +0x250 AddressSpaceOwner : 0y0
  59. +0x254 ForwardClusterOnly : 0 ''
  60. +0x255 DisablePageFaultClustering : 0 ''

The field Win32StartAddress of ETHREAD contains the entrypoint, so we set a breakpoint in that process at that address with:

bp /p

or

ba e1

In this way we can debug from entrypoint of the process.

Usually, it’s interesting to suspend the new processes until you see what other processes are created, and want to debug them, etc… Windbg user-mode debugger let you to suspend threads easily (~Thread n) but you can’t do this from kernel-mode debugger. You can read a couple of articles about threads suspension and resumption here:

Windows Thread Suspension Internals Part 1

Windows Thread Suspension Internals Part 2

Windows Internals – Thread resumption and synchronization objects

I asked the author of these articles about suspending threads from kernel mode session and he answered me that “windbg raises the IRQL to its highest value to stop all threads. I don’t think u can do it for one thread only, many stuff done”. I would like to investigate it in deep in the future.

I tried to force the thread to call SuspendThread to suspend itself:

  1. kd> r @esp = @esp - 8
  2. kd> ed @esp @eip
  3. kd> ed @esp+4 fffffffe
  4. kd> r @eip = kernel32!SuspendThread

But it worked sometimes, other times the thread ended up in an unestable state.

About breaking at DriverEntry of a driver, it is possible to set a breakpoint at symbol with bu. But i noticed it doesn’t work sometimes, i don’t know why. You can set bp at MmLoadSystemImage function too. This function will load the driver, so when the bp is hit, you step out of the function, and, in that moment, the driver is mapped in memory. Now you can load symbols and set breakpoint at driver entry with bp !DriverEntry,…

发表评论

表情:
评论列表 (有 0 条评论,266人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Log Processing With Storm

    有代码的书籍看起来就是爽,看完顺便跑个demo,感觉很爽! 场景分析 主要是利用apache的访问日志来进行分析统计 如用户的IP来源,来自哪个国家或地区,用户使用的Os