Watch & Learn

Debugwar Blog

Step in or Step over, this is a problem ...

BSOD dump:INVALID_PROCESS_ATTACH_ATTEMPT

2024-09-18 @ UTC+0

Time flies, nearly a year has passed since the last article analyzing the blue screen issue. Recently, an interesting dump file was collected, which has some noteworthy aspects—this article will use this dump file to delve into some details related to Windows process termination.

Crash Scene

First, let's take a look at the dump scene:

  1. Microsoft (R) Windows Debugger Version 10.0.26100.1 AMD64  
  2. Copyright (c) Microsoft Corporation. All rights reserved.  
  3.   
  4.   
  5. Loading Dump File [\\VBOXSVR\SharedFolder\MEMORY.DMP]  
  6. Kernel Bitmap Dump File: Kernel address space is available, User address space may not be available.  
  7.   
  8.   
  9. ************* Path validation summary **************  
  10. Response                         Time (ms)     Location  
  11. Deferred                                       srv*\\VBOXSVR\Symbols*http://msdl.blackint3.com:88/download/symbols  
  12. Symbol search path is: srv*\\VBOXSVR\Symbols*http://msdl.blackint3.com:88/download/symbols  
  13. Executable search path is:   
  14. Unable to load image \SystemRoot\system32\ntoskrnl.exe, Win32 error 0n2  
  15. Windows 10 Kernel Version 19041 MP (8 procs) Free x64  
  16. Product: WinNt, suite: TerminalServer SingleUserTS  
  17. Edition build lab: 19041.1.amd64fre.vb_release.191206-1406  
  18. Kernel base = 0xfffff806`71c1b000 PsLoadedModuleList = 0xfffff806`72845820  
  19. Debug session time: Sat Sep 14 17:42:23.681 2024 (UTC + 8:00)  
  20. System Uptime: 1 days 2:24:42.212  
  21. Unable to load image \SystemRoot\system32\ntoskrnl.exe, Win32 error 0n2  
  22. Loading Kernel Symbols  
  23. ...............................................................  
  24. ...........Page 6f0a3 not present in the dump file. Type ".hh dbgerr004" for details  
  25. ........Page 6992d not present in the dump file. Type ".hh dbgerr004" for details  
  26. .............................................  
  27. ............................Page 441b5 not present in the dump file. Type ".hh dbgerr004" for details  
  28. ....................................  
  29.   
  30. Loading User Symbols  
  31. PEB is paged out (Peb.Ldr = 00000000`05701018).  Type ".hh dbgerr001" for details  
  32. Loading unloaded module list  
  33. .................  
  34. For analysis of this file, run !analyze -v  
  35. 3: kd> !analyze -v  
  36. *******************************************************************************  
  37. *                                                                             *  
  38. *                        Bugcheck Analysis                                    *  
  39. *                                                                             *  
  40. *******************************************************************************  
  41.   
  42. INVALID_PROCESS_ATTACH_ATTEMPT (5)  
  43. Arguments:  
  44. Arg1: ffff860f2b5c0240  
  45. Arg2: ffff860f289f0080  
  46. Arg3: 0000000000000000  
  47. Arg4: 0000000000000000  
  48.   
  49. Debugging Details:  
  50. ------------------  
  51.   
  52. Unable to load image \??\C:\Windows\system32\drivers\constantine64.sys, Win32 error 0n2  
  53.   
  54. BUGCHECK_CODE:  5  
  55.   
  56. BUGCHECK_P1: ffff860f2b5c0240  
  57.   
  58. BUGCHECK_P2: ffff860f289f0080  
  59.   
  60. BUGCHECK_P3: 0  
  61.   
  62. BUGCHECK_P4: 0  
  63.   
  64. FILE_IN_CAB:  MEMORY.DMP  
  65.   
  66. BLACKBOXBSD: 1 (!blackboxbsd)  
  67.   
  68.   
  69. BLACKBOXNTFS: 1 (!blackboxntfs)  
  70.   
  71.   
  72. BLACKBOXWINLOGON: 1  
  73.   
  74. PROCESS_NAME:  QDoctor.exe  
  75.   
  76. STACK_TEXT:    
  77. fffff90a`6e396188 fffff806`72094f52     : 00000000`00000005 ffff860f`2b5c0240 ffff860f`289f0080 00000000`00000000 : nt!KeBugCheckEx  
  78. fffff90a`6e396190 fffff809`9692b74b     : ffff860f`2b5c0240 00000000`00000002 ffff860f`2d1805d0 ffff860f`2c0f981e : nt!KeAttachProcess+0x154482  
  79. fffff90a`6e3961d0 fffff809`969311c0     : ffff860f`2c377680 ffff860f`2b5c0240 ffff860f`2c0f981e ffff860f`00000000 : constantine64+0x3b74b  
  80. fffff90a`6e3962a0 fffff809`9692f1d8     : ffff860f`2c5326e0 ffff860f`2b5c0240 00000000`00000000 00000000`00000000 : constantine64+0x411c0  
  81. fffff90a`6e396360 fffff809`96fd835f     : ffff860f`21d16cd0 0000002b`00000000 0000002b`00000053 0c91f070`0000002b : constantine64+0x3f1d8  
  82. fffff90a`6e3963e0 fffff809`96fd6416     : 00000000`00000000 00000000`00000000 00000000`00000000 fffff90a`6e397118 : constantine64+0x6e835f  
  83. fffff90a`6e3970e0 fffff806`71e65d55     : ffff860f`28d9c810 ffff860f`281f6100 fffff90a`6e397500 00000000`00000000 : constantine64+0x6e6416  
  84. fffff90a`6e397170 fffff806`722482fc     : 00000000`00000001 00000000`0022e228 ffff860f`2d1805d0 fffff806`00000000 : nt!IofCallDriver+0x55  
  85. fffff90a`6e3971b0 fffff806`72247f4a     : ffff860f`00000000 fffff90a`6e397500 00000000`00010000 00000000`0022e228 : nt!IopSynchronousServiceTail+0x34c  
  86. fffff90a`6e397250 fffff806`72247226     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!IopXxxControlFile+0xd0a  
  87. fffff90a`6e3973a0 fffff806`7202d305     : 00000000`000005ac 00000000`0c81e428 00000000`0c81e438 00000000`00000008 : nt!NtDeviceIoControlFile+0x56  
  88. fffff90a`6e397410 00000000`771a1cfc     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x25  
  89. 00000000`0c81ed38 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x771a1cfc  
  90.   
  91.   
  92. SYMBOL_NAME:  constantine64+3b74b  
  93.   
  94. MODULE_NAME: constantine64  
  95.   
  96. IMAGE_NAME:  constantine64.sys  
  97.   
  98. STACK_COMMAND:  .cxr; .ecxr ; kb  
  99.   
  100. BUCKET_ID_FUNC_OFFSET:  3b74b  
  101.   
  102. FAILURE_BUCKET_ID:  0x5_constantine64!unknown_function  
  103.   
  104. OS_VERSION:  10.0.19041.1  
  105.   
  106. BUILDLAB_STR:  vb_release  
  107.   
  108. OSPLATFORM_TYPE:  x64  
  109.   
  110. OSNAME:  Windows 10  
  111.   
  112. FAILURE_ID_HASH:  {bf2cf753-0fe1-6d0c-e7f3-1d053b3a225a}  
  113.   
  114. Followup:     MachineOwner  
  115. ---------  

The cause of this blue screen is relatively easy to analyze; the output from WinDbg has already provided a straightforward answer: INVALID_PROCESS_ATTACH_ATTEMPT—which means we attempted to attach to an invalid process. From the stack trace output, we can see that the process attached by KeAttachProcess is ffff860f`2b5c0240.

Let's examine this process:

  1. 3: kd> !process ffff860f`2b5c0240  
  2. PROCESS ffff860f2b5c0240  
  3.     SessionId: 0  Cid: 1874    Peb: 2502873000  ParentCid: 0d50  
  4.     DirBase: 597fb000  ObjectTable: 00000000  HandleCount:   0.  
  5.     Image: SearchFilterHo  
  6.     VadRoot 0000000000000000 Vads 0 Clone 0 Private 16. Modified 548. Locked 0.  
  7.     DeviceMap 0000000000000000  
  8.     Token                             0000000000000000  
  9.     ElapsedTime                       00:02:48.123  
  10.     UserTime                          00:00:00.000  
  11.     KernelTime                        00:00:00.031  
  12.     QuotaPoolUsage[PagedPool]         0  
  13.     QuotaPoolUsage[NonPagedPool]      0  
  14.     Working Set Sizes (now,min,max)  (1, 50, 345) (4KB, 200KB, 1380KB)  
  15.     PeakWorkingSetSize                1892  
  16.     VirtualSize                       0 Mb  
  17.     PeakVirtualSize                   2101324 Mb  
  18.     PageFaultCount                    3328  
  19.     MemoryPriority                    BACKGROUND  
  20.     BasePriority                      8  
  21.     CommitCharge                      4  
  22.   
  23. No active threads  

The critical parts are highlighted in red. From the output of the !process command, it's evident that this process is highly abnormal, with data related to its memory, handles, kernel objects, etc., all set to NULL. This situation arises because the process has already exited, but the EPROCESS data structure representing the process still lingers in memory.

Let's confirm this further through the debugger:

  1. 3: kd> dt _EPROCESS ffff860f`2b5c0240  
  2. nt!_EPROCESS  
  3.    +0x000 Pcb              : _KPROCESS  
  4.    +0x438 ProcessLock      : _EX_PUSH_LOCK  
  5.    +0x440 UniqueProcessId  : 0x00000000`00001874 Void  
  6.    +0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffff860f`286634c8 - 0xffff860f`2b694688 ]  
  7.    +0x458 RundownProtect   : _EX_RUNDOWN_REF  
  8.    +0x460 Flags2           : 0xd81f  
  9.    +0x460 JobNotReallyActive : 0y1  
  10.    +0x460 AccountingFolded : 0y1  
  11.    +0x460 NewProcessReported : 0y1  
  12.    +0x460 ExitProcessReported : 0y1  
  13.    +0x460 ReportCommitChanges : 0y1  
  14.    +0x460 LastReportMemory : 0y0  
  15.    +0x460 ForceWakeCharge  : 0y0  
  16.    +0x460 CrossSessionCreate : 0y0  
  17.    +0x460 NeedsHandleRundown : 0y0  
  18.    +0x460 RefTraceEnabled  : 0y0  
  19.    +0x460 PicoCreated      : 0y0  
  20.    +0x460 EmptyJobEvaluated : 0y1  
  21.    +0x460 DefaultPagePriority : 0y101  
  22.    +0x460 PrimaryTokenFrozen : 0y1  
  23.    +0x460 ProcessVerifierTarget : 0y0  
  24.    +0x460 RestrictSetThreadContext : 0y0  
  25.    +0x460 AffinityPermanent : 0y0  
  26.    +0x460 AffinityUpdateEnable : 0y0  
  27.    +0x460 PropagateNode    : 0y0  
  28.    +0x460 ExplicitAffinity : 0y0  
  29.    +0x460 ProcessExecutionState : 0y00  
  30.    +0x460 EnableReadVmLogging : 0y0  
  31.    +0x460 EnableWriteVmLogging : 0y0  
  32.    +0x460 FatalAccessTerminationRequested : 0y0  
  33.    +0x460 DisableSystemAllowedCpuSet : 0y0  
  34.    +0x460 ProcessStateChangeRequest : 0y00  
  35.    +0x460 ProcessStateChangeInProgress : 0y0  
  36.    +0x460 InPrivate        : 0y0  
  37.    +0x464 Flags            : 0x524c0c2d  
  38.    +0x464 CreateReported   : 0y1  
  39.    +0x464 NoDebugInherit   : 0y0  
  40.    +0x464 ProcessExiting   : 0y1  
  41.    +0x464 ProcessDelete    : 0y1  
  42.    +0x464 ManageExecutableMemoryWrites : 0y0  
  43.    +0x464 VmDeleted        : 0y1  
  44.    +0x464 OutswapEnabled   : 0y0  
  45.    +0x464 Outswapped       : 0y0  
  46.    +0x464 FailFastOnCommitFail : 0y0  
  47.    +0x464 Wow64VaSpace4Gb  : 0y0  
  48.    +0x464 AddressSpaceInitialized : 0y11  
  49.    +0x464 SetTimerResolution : 0y0  
  50.    +0x464 BreakOnTermination : 0y0  
  51.    +0x464 DeprioritizeViews : 0y0  
  52.    +0x464 WriteWatch       : 0y0  
  53.    +0x464 ProcessInSession : 0y0  
  54.    +0x464 OverrideAddressSpace : 0y0  
  55.    +0x464 HasAddressSpace  : 0y1  
  56.    +0x464 LaunchPrefetched : 0y1  
  57.    +0x464 Background       : 0y0  
  58.    +0x464 VmTopDown        : 0y0  
  59.    +0x464 ImageNotifyDone  : 0y1  
  60.    +0x464 PdeUpdateNeeded  : 0y0  
  61.    +0x464 VdmAllowed       : 0y0  
  62.    +0x464 ProcessRundown   : 0y1  
  63.    +0x464 ProcessInserted  : 0y0  
  64.    +0x464 DefaultIoPriority : 0y010  
  65.    +0x464 ProcessSelfDelete : 0y1  
  66.    +0x464 SetTimerResolutionLink : 0y0  
  67.    +0x468 CreateTime       : _LARGE_INTEGER 0x01db068a`028c7a84  
  68.    +0x470 ProcessQuotaUsage : [2] 0  
  69.    +0x480 ProcessQuotaPeak : [2] 0x2418  
  70.    +0x490 PeakVirtualSize  : 0x00000201`04cf7000  
  71.    +0x498 VirtualSize      : 0  
  72.    +0x4a0 SessionProcessLinks : _LIST_ENTRY [ 0xffff860f`2decb7a0 - 0xffff860f`2b6946e0 ]  
  73.    +0x4b0 ExceptionPortData : (null)   
  74.    +0x4b0 ExceptionPortValue : 0  
  75.    +0x4b0 ExceptionPortState : 0y000  
  76.    +0x4b8 Token            : _EX_FAST_REF  
  77.    +0x4c0 MmReserved       : 0  
  78.    +0x4c8 AddressCreationLock : _EX_PUSH_LOCK  
  79.    +0x4d0 PageTableCommitmentLock : _EX_PUSH_LOCK  
  80.    +0x4d8 RotateInProgress : (null)   
  81.    +0x4e0 ForkInProgress   : (null)   
  82.    +0x4e8 CommitChargeJob  : (null)   
  83.    +0x4f0 CloneRoot        : _RTL_AVL_TREE  
  84.    +0x4f8 NumberOfPrivatePages : 0x10  
  85.    +0x500 NumberOfLockedPages : 0  
  86.    +0x508 Win32Process     : (null)   
  87.    +0x510 Job              : (null)   
  88.    +0x518 SectionObject    : (null)   
  89.    +0x520 SectionBaseAddress : 0x00007ff6`1fa10000 Void  
  90.    +0x528 Cookie           : 0xf75d4a2  
  91.    +0x530 WorkingSetWatch  : (null)   
  92.    +0x538 Win32WindowStation : 0x00000000`00000170 Void  
  93.    +0x540 InheritedFromUniqueProcessId : 0x00000000`00000d50 Void  
  94.    +0x548 OwnerProcessId   : 1  
  95.    +0x550 Peb              : 0x00000025`02873000 _PEB  
  96.    +0x558 Session          : 0xffffd780`d11fc000 _MM_SESSION_SPACE  
  97.    +0x560 Spare1           : (null)   
  98.    +0x568 QuotaBlock       : 0xfffff806`7286eac0 _EPROCESS_QUOTA_BLOCK  
  99.    +0x570 ObjectTable      : (null)   
  100.    +0x578 DebugPort        : (null)   
  101.    +0x580 WoW64Process     : (null)   
  102.    +0x588 DeviceMap        : (null)   
  103.    +0x590 EtwDataSource    : (null)   
  104.    +0x598 PageDirectoryPte : 0  
  105.    +0x5a0 ImageFilePointer : (null)   
  106.    +0x5a8 ImageFileName    : [15]  "SearchFilterHo"  
  107.    +0x5b7 PriorityClass    : 0x2 ''  
  108.    +0x5b8 SecurityPort     : 0x00000000`00000001 Void  
  109.    +0x5c0 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO  
  110.    +0x5c8 JobLinks         : _LIST_ENTRY [ 0xffff860f`2749a088 - 0xffff860f`2749a088 ]  
  111.    +0x5d8 HighestUserAddress : 0x00007fff`ffff0000 Void  
  112.    +0x5e0 ThreadListHead   : _LIST_ENTRY [ 0xffff860f`2b5c0820 - 0xffff860f`2b5c0820 ]  
  113.    +0x5f0 ActiveThreads    : 0  
  114.    +0x5f4 ImagePathHash    : 0x44162447  
  115.    +0x5f8 DefaultHardErrorProcessing : 0  
  116.    +0x5fc LastThreadExitStatus : 0n0  
  117.    +0x600 PrefetchTrace    : _EX_FAST_REF  
  118.    +0x608 LockedPagesList  : (null)   
  119.    +0x610 ReadOperationCount : _LARGE_INTEGER 0x1  
  120.    +0x618 WriteOperationCount : _LARGE_INTEGER 0x0  
  121.    +0x620 OtherOperationCount : _LARGE_INTEGER 0x34  
  122.    +0x628 ReadTransferCount : _LARGE_INTEGER 0x119a  
  123.    +0x630 WriteTransferCount : _LARGE_INTEGER 0x0  
  124.    +0x638 OtherTransferCount : _LARGE_INTEGER 0xa0  
  125.    +0x640 CommitChargeLimit : 0  
  126.    +0x648 CommitCharge     : 4  
  127.    +0x650 CommitChargePeak : 0x1b1  
  128.    +0x680 Vm               : _MMSUPPORT_FULL  
  129.    +0x7c0 MmProcessLinks   : _LIST_ENTRY [ 0xffff860f`28663840 - 0xffff860f`2b694a00 ]  
  130.    +0x7d0 ModifiedPageCount : 0x224  
  131.    +0x7d4 ExitStatus       : 0n0  
  132.    +0x7d8 VadRoot          : _RTL_AVL_TREE  
  133.    +0x7e0 VadHint          : (null)   
  134.    +0x7e8 VadCount         : 0  
  135.    +0x7f0 VadPhysicalPages : 0  
  136.    +0x7f8 VadPhysicalPagesLimit : 0  
  137.    +0x800 AlpcContext      : _ALPC_PROCESS_CONTEXT  
  138.    +0x820 TimerResolutionLink : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]  
  139.    +0x830 TimerResolutionStackRecord : (null)   
  140.    +0x838 RequestedTimerResolution : 0  
  141.    +0x83c SmallestTimerResolution : 0  
  142.    +0x840 ExitTime         : _LARGE_INTEGER 0x01db068a`5311a5fc  
  143.    +0x848 InvertedFunctionTable : (null)   
  144.    +0x850 InvertedFunctionTableLock : _EX_PUSH_LOCK  
  145.    +0x858 ActiveThreadsHighWatermark : 7  
  146.    +0x85c LargePrivateVadCount : 0  
  147.    +0x860 ThreadListLock   : _EX_PUSH_LOCK  
  148.    +0x868 WnfContext       : 0xffff9c0f`c9fdb870 Void  
  149.    +0x870 ServerSilo       : 0xffffd780`cc19a000 _EJOB  
  150.    +0x878 SignatureLevel   : 0 ''  
  151.    +0x879 SectionSignatureLevel : 0 ''  
  152.    +0x87a Protection       : _PS_PROTECTION  
  153.    +0x87b HangCount        : 0y000  
  154.    +0x87b GhostCount       : 0y000  
  155.    +0x87b PrefilterException : 0y0  
  156.    +0x87c Flags3           : 0x40c008  
  157.    +0x87c Minimal          : 0y0  
  158.    +0x87c ReplacingPageRoot : 0y0  
  159.    +0x87c Crashed          : 0y0  
  160.    +0x87c JobVadsAreTracked : 0y1  
  161.    +0x87c VadTrackingDisabled : 0y0  
  162.    +0x87c AuxiliaryProcess : 0y0  
  163.    +0x87c SubsystemProcess : 0y0  
  164.    +0x87c IndirectCpuSets  : 0y0  
  165.    +0x87c RelinquishedCommit : 0y0  
  166.    +0x87c HighGraphicsPriority : 0y0  
  167.    +0x87c CommitFailLogged : 0y0  
  168.    +0x87c ReserveFailLogged : 0y0  
  169.    +0x87c SystemProcess    : 0y0  
  170.    +0x87c HideImageBaseAddresses : 0y0  
  171.    +0x87c AddressPolicyFrozen : 0y1  
  172.    +0x87c ProcessFirstResume : 0y1  
  173.    +0x87c ForegroundExternal : 0y0  
  174.    +0x87c ForegroundSystem : 0y0  
  175.    +0x87c HighMemoryPriority : 0y0  
  176.    +0x87c EnableProcessSuspendResumeLogging : 0y0  
  177.    +0x87c EnableThreadSuspendResumeLogging : 0y0  
  178.    +0x87c SecurityDomainChanged : 0y0  
  179.    +0x87c SecurityFreezeComplete : 0y1  
  180.    +0x87c VmProcessorHost  : 0y0  
  181.    +0x87c VmProcessorHostTransition : 0y0  
  182.    +0x87c AltSyscall       : 0y0  
  183.    +0x87c TimerResolutionIgnore : 0y0  
  184.    +0x87c DisallowUserTerminate : 0y0  
  185.    +0x880 DeviceAsid       : 0n0  
  186.    +0x888 SvmData          : (null)   
  187.    +0x890 SvmProcessLock   : _EX_PUSH_LOCK  
  188.    +0x898 SvmLock          : 0  
  189.    +0x8a0 SvmProcessDeviceListHead : _LIST_ENTRY [ 0xffff860f`2b5c0ae0 - 0xffff860f`2b5c0ae0 ]  
  190.    +0x8b0 LastFreezeInterruptTime : 0  
  191.    +0x8b8 DiskCounters     : 0xffff860f`2b5c0c80 _PROCESS_DISK_COUNTERS  
  192.    +0x8c0 PicoContext      : (null)   
  193.    +0x8c8 EnclaveTable     : (null)   
  194.    +0x8d0 EnclaveNumber    : 0  
  195.    +0x8d8 EnclaveLock      : _EX_PUSH_LOCK  
  196.    +0x8e0 HighPriorityFaultsAllowed : 0  
  197.    +0x8e8 EnergyContext    : 0xffff860f`2b5c0ca8 _PO_PROCESS_ENERGY_CONTEXT  
  198.    +0x8f0 VmContext        : (null)   
  199.    +0x8f8 SequenceNumber   : 0x53a  
  200.    +0x900 CreateInterruptTime : 0x000000dd`10df825f  
  201.    +0x908 CreateUnbiasedInterruptTime : 0x000000dd`10df825f  
  202.    +0x910 TotalUnbiasedFrozenTime : 0  
  203.    +0x918 LastAppStateUpdateTime : 0x000000dd`61613aec  
  204.    +0x920 LastAppStateUptime : 0y0000000000000000000000000000001010000100000011011100010001101 (0x5081b88d)  
  205.    +0x920 LastAppState     : 0y011  
  206.    +0x928 SharedCommitCharge : 0  
  207.    +0x930 SharedCommitLock : _EX_PUSH_LOCK  
  208.    +0x938 SharedCommitLinks : _LIST_ENTRY [ 0xffff860f`2b5c0b78 - 0xffff860f`2b5c0b78 ]  
  209.    +0x948 AllowedCpuSets   : 0  
  210.    +0x950 DefaultCpuSets   : 0  
  211.    +0x948 AllowedCpuSetsIndirect : (null)   
  212.    +0x950 DefaultCpuSetsIndirect : (null)   
  213.    +0x958 DiskIoAttribution : (null)   
  214.    +0x960 DxgProcess       : (null)   
  215.    +0x968 Win32KFilterSet  : 0  
  216.    +0x970 ProcessTimerDelay : _PS_INTERLOCKED_TIMER_DELAY_VALUES  
  217.    +0x978 KTimerSets       : 0  
  218.    +0x97c KTimer2Sets      : 0  
  219.    +0x980 ThreadTimerSets  : 0  
  220.    +0x988 VirtualTimerListLock : 0  
  221.    +0x990 VirtualTimerListHead : _LIST_ENTRY [ 0xffff860f`2b5c0bd0 - 0xffff860f`2b5c0bd0 ]  
  222.    +0x9a0 WakeChannel      : _WNF_STATE_NAME  
  223.    +0x9a0 WakeInfo         : _PS_PROCESS_WAKE_INFORMATION  
  224.    +0x9d0 MitigationFlags  : 0xa0021  
  225.    +0x9d0 MitigationFlagsValues : <anonymous-tag>  
  226.    +0x9d4 MitigationFlags2 : 0x40000000  
  227.    +0x9d4 MitigationFlags2Values : <anonymous-tag>  
  228.    +0x9d8 PartitionObject  : 0xffff860f`222c17a0 Void  
  229.    +0x9e0 SecurityDomain   : 0x00000001`0000041b  
  230.    +0x9e8 ParentSecurityDomain : 0  
  231.    +0x9f0 CoverageSamplerContext : (null)   
  232.    +0x9f8 MmHotPatchContext : (null)   
  233.    +0xa00 DynamicEHContinuationTargetsTree : _RTL_AVL_TREE  
  234.    +0xa08 DynamicEHContinuationTargetsLock : _EX_PUSH_LOCK  
  235.    +0xa10 DynamicEnforcedCetCompatibleRanges : _PS_DYNAMIC_ENFORCED_ADDRESS_RANGES  
  236.    +0xa20 DisabledComponentFlags : 0  
  237.    +0xa28 PathRedirectionHashes : (null)   
  238.    +0xa30 MitigationFlags3 : 0  
  239.    +0xa30 MitigationFlags3Values : <anonymous-tag>  

As we can see, apart from most fields being NULL or zero, this EPROCESS data structure remains relatively intact. Let's observe the following two fields:

  1. 3: kd> dt _EPROCESS ffff860f`2b5c0240 RundownProtect  
  2. nt!_EPROCESS  
  3.    +0x458 RundownProtect : _EX_RUNDOWN_REF  
  4. 3: kd> dx -id 0,0,ffff860f289f0080 -r1 (*((ntkrnlmp!_EX_RUNDOWN_REF *)0xffff860f2b5c0698))  
  5. (*((ntkrnlmp!_EX_RUNDOWN_REF *)0xffff860f2b5c0698))                 [Type: _EX_RUNDOWN_REF]  
  6.     [+0x000] Count            : 0x1 [Type: unsigned __int64]  
  7.     [+0x000] Ptr              : 0x1 [Type: void *]  
  8. 3: kd> dt _EPROCESS ffff860f`2b5c0240 Flags  
  9. nt!_EPROCESS  
  10.    +0x464 Flags : 0x524c0c2d  
  11. 3: kd> ?0x524c0c2d&0x4  
  12. Evaluate expression: 4 = 00000000`00000004  

We can see that the RundownProtect counter is already at 1, and Flags & 0x4 is not zero.

Let's present the conclusion first before diving deeper into the analysis: these current values of the two fields indicate that the process has already exited. The RundownProtect is a lock used during object destruction, and Windows increments this counter when destroying a process. The Flags field, on the other hand, is a flag that is set to the value 0x4 when the last thread of the process exits.

Solution

Now that we have identified the root cause of this blue screen, let's discuss the solution.

To prevent a blue screen when attaching to a process using the KeAttachProcess function, there appear to be the following necessary conditions:

  1. The first parameter of KeAttachProcess, the memory address of EPROCESS, must be valid.
  2. The memory of EPROCESS location contains an object of type Process.
  3. The process has not exited.

Since EPROCESS is a kernel object, the above condition 1 can be constrained using the following relationship:

  1. TargetMemory > MmHighestUserAddress && MmIsAddressValid(TargetMemory)  

For condition 2, before providing a solution, we need to understand something called DISPATCHER_HEADER.

It's well known that Windows processes can be waited upon using WaitForXxxObject series functions. Not only processes, but also objects like EVENT, TIMER, etc., can be waited upon. Let's first look at the definitions for these types of objects:

  1. typedef struct _DISPATCHER_HEADER {  
  2.     union {  
  3.         struct {  
  4.             UCHAR Type;  
  5.             UCHAR Absolute;  
  6.             UCHAR Size;  
  7.             union {  
  8.                 UCHAR Inserted;  
  9.                 BOOLEAN DebugActive;  
  10.             };  
  11.         };  
  12.   
  13.         volatile LONG Lock;  
  14.     };  
  15.   
  16.     LONG SignalState;  
  17.     LIST_ENTRY WaitListHead;  
  18. } DISPATCHER_HEADER;  
  19.   
  20. //  
  21. // Event object  
  22. //  
  23.   
  24. typedef struct _KEVENT {  
  25.     DISPATCHER_HEADER Header;  
  26. } KEVENT, *PKEVENT, *RESTRICTED_POINTER PRKEVENT;  
  27.   
  28. //  
  29. // Timer object  
  30. //  
  31.   
  32. typedef struct _KTIMER {  
  33.     DISPATCHER_HEADER Header;  
  34.     ULARGE_INTEGER DueTime;  
  35.     LIST_ENTRY TimerListEntry;  
  36.     struct _KDPC *Dpc;  
  37.     LONG Period;  
  38. } KTIMER, *PKTIMER, *RESTRICTED_POINTER PRKTIMER;  
  39.   
  40. //  
  41. // Process object  
  42. //  
  43.   
  44. typedef struct _KPROCESS {  
  45.   
  46.     //  
  47.     // The dispatch header and profile listhead are fairly infrequently  
  48.     // referenced.  
  49.     //  
  50.   
  51.     DISPATCHER_HEADER Header;  
  52.     LIST_ENTRY ProfileListHead;  
  53.     …………  
  54. };  
  55. typedef struct _EPROCESS {  
  56.     KPROCESS Pcb;  
  57.     …………  
  58. };  

As we can see, their definitions all start with DISPATCHER_HEADER, which is the key to why these objects can be waited upon Reference1. The Type member of DISPATCHER_HEADER indicates the object type, with the following defined types:

  1. typedef enum _KOBJECTS {  
  2.     EventNotificationObject = 0,  
  3.     EventSynchronizationObject = 1,  
  4.     MutantObject = 2,  
  5.     ProcessObject = 3,  
  6.     QueueObject = 4,  
  7.     SemaphoreObject = 5,  
  8.     ThreadObject = 6,  
  9.     Spare1Object = 7,  
  10.     TimerNotificationObject = 8,  
  11.     TimerSynchronizationObject = 9,  
  12.     Spare2Object = 10,  
  13.     Spare3Object = 11,  
  14.     Spare4Object = 12,  
  15.     Spare5Object = 13,  
  16.     Spare6Object = 14,  
  17.     Spare7Object = 15,  
  18.     Spare8Object = 16,  
  19.     Spare9Object = 17,  
  20.     ApcObject,  
  21.     DpcObject,  
  22.     DeviceQueueObject,  
  23.     EventPairObject,  
  24.     InterruptObject,  
  25.     ProfileObject,  
  26.     ThreadedDpcObject,  
  27.     MaximumKernelObject  
  28. } KOBJECTS;  

Alright, let's return to the second critical issue at the beginning of this section—we can quickly determine if a block of memory is an EPROCESS by checking the DISPATCHER_HEADER->Type field for ProcessObject (0x3) type.

  1. 0x03 == ((DISPATCHER_HEADER *)TargetMemory)->Type  

Now, only one question remains—how do we determine if a process has exited? Based on the previous analysis, we can use the RundownProtect field or the Flags field to make this determination. The new question is how to obtain the values of these two fields? After rummaging through the kernel, I found the following function:

  1. #define PS_PROCESS_FLAGS_PROCESS_EXITING        0x00000004UL // PspExitProcess entered  
  2.   
  3. BOOLEAN PsGetProcessExitProcessCalled(PEPROCESS Process)  
  4. {  
  5.     return (BOOLEAN) ((Process->Flags & PS_PROCESS_FLAGS_PROCESS_EXITING) != 0);  
  6. }  

And this function is exported across different versions of the operating system's kernel:


Therefore, the third issue can be resolved using this exported function to check if the process has exited.

Here is the final code for attaching to a process:

  1. BOOLEAN AttachProcessSafe (PEPROCESS TargetMemory)  
  2. {  
  3.     if (TargetMemory > MmHighestUserAddress && MmIsAddressValid(TargetMemory))  
  4.     {  
  5.         if (0x03 == ((DISPATCHER_HEADER *)TargetMemory)->Type)  
  6.         {  
  7.             if (!PsGetProcessExitProcessCalled((PEPROCESS)TargetMemory))  
  8.             {  
  9.                 KeAttachProcess((PEPROCESS)TargetMemory;  
  10.                 return TRUE;  
  11.             }  
  12.         }  
  13.     }  
  14.     return FALSE;  
  15. }  

Digging Deeper

In the previous section, we analyzed the root cause of the blue screen and provided a solution. However, the author's compulsion demands an explanation of why we can use Process->Flags & PS_PROCESS_FLAGS_PROCESS_EXITING to determine if a process has exited. Let's clarify this issue...

Before we start, let's take a look at a diagram:


As we know, although various ExitProcess functions are provided in the kernel, they ultimately call NtTerminateProcess. Therefore, the most important call path in the diagram is:

  1. NtTerminateProcess -> PspTerminateThreadByPointer -> PspExitThread -> PspExitProcess  

In the PspExitProcess function, the PS_PROCESS_FLAGS_PROCESS_EXITING flag is set right at the beginning:

  1. VOID PspExitProcess(IN BOOLEAN LastThreadExit, IN PEPROCESS Process)  
  2. {  
  3.     …………  
  4.     PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_PROCESS_EXITING);  
  5.     …………  
  6. }  

This is the fundamental reason why we can determine if a process has exited using PsGetProcessExitProcessCalled.

By quickly reviewing the functions in the aforementioned call chain, we can summarize as follows:

  • NtTerminateProcess: This function acquires the process that needs to exit via the passed handle, then iterates through all threads in the process and calls PspTerminateThreadByPointer to end these threads.
  • PspTerminateThreadByPointer: This function determines whether to directly use PspExitThread to exit the thread or to exit the thread via the APC queue based on certain conditions. The APC queue method will ultimately also call PspExitThread, so this function can be simplified as calling PspExitThread to exit the passed thread.
  • PspExitThread: This function includes extensive state checks and event notifications (such as those set by PsSetCreateThreadNotifyRoutine). If the exiting thread is the last one in the current process, it will call PspExitProcess.
  • PspExitProcess: This function is called when the last thread exits and invokes the process termination callback set by PsSetCreateProcessNotifyRoutine, performing final notifications and cleanup. At the beginning, it sets the process flags to PS_PROCESS_FLAGS_PROCESS_EXITING.

These functions contain very interesting details. It is recommended that those involved in low-level system development take a detailed look at this part of the code.

References

  1. DISPATCHER_HEADER
Catalog
Crash Scene
Solution
Digging Deeper
References

CopyRight (c) 2020 - 2025 Debugwar.com

Designed by Hacksign