标题: MSDN系列(43)--从僵死的调试器中抢救被调试进程 创建: 2021-12-07 15:04 更新: 2021-12-23 11:48 链接: https://scz.617.cn/windows/202112071504.txt -------------------------------------------------------------------------- 目录: ☆ 背景介绍 ☆ Skywing的技术方案 ☆ 调试lsass引发的死锁 ☆ 利用kd detach ☆ detach.nasm ☆ 使用示例 ☆ 后记 -------------------------------------------------------------------------- ☆ 背景介绍 云海在测试机中误操作,直接Attach lsass了,然后windbg僵死。碰上过这事的可能 知道,此时大概率杀不掉windbg,也不敢杀啊,杀了lsass也挂了。同时没法Detach, 因为windbg失去响应了。他来问我有啥办法旁路Detach,否则只能重启系统了。我还 真在十三年前看过Skywing的神操作,这事某些情况下有解,但云海来问时,我没想 起细节。 本文在Win10企业版2016 LTSB 1607(OS Build 14393.4704)上测试。 ☆ Skywing的技术方案 Recovering a process from a hung debugger - Ken Johnson [2009-02-21] http://www.nynaeve.net/?p=274 要点是给windbg指定"-pe"二次Attach,然后Resume线程,最后qd退出。 二十一世纪初时,Skywing和Skape这对双S组合在安全领域很是拉风。Skape真名是 Matt Miller,大约在2008年底加入微软,负责MSRC?这我不确认,反正后来给微软 报漏洞、报缓解措施的可能跟他打过交道。他的个人主页还在维护中。 Skywing的真名是Ken Johnson,大约在2009年初加入微软,比Skape晚了没几个月, 去了同一个安全团队吧。这是不输Alex Ionescu的顶级神人,曾经从他那儿学了好多 东西,其个人主页停更于2010年,但仍活着。 云海说Skywing应该还在微软。Matt Miller提过一次,说他们试图让Skywing用 Twitter,失败了,但他们说服Skywing重新写blog,下面这篇就是Skywing写的 https://msrc-blog.microsoft.com/2018/03/23/kva-shadow-mitigating-meltdown-on-windows/ 总的来说,Skywing进入了神隐模式,不更新自己的旧blog,也不用Twitter。他这种 神仙范儿,真心佩服。只是有些遗憾,再也不能从他那里学东西了。 ☆ 调试lsass引发的死锁 在管理员级cmd中执行 C:\temp\cdb.exe -sins -y "srv*http://msdl.microsoft.com/download/symbols" -noinh -snul -hd -o -pn lsass.exe 依次尝试访问任务栏上的"桌面"、开始菜单、资源管理器,尝试Ctrl-Shift-Esc、 Win-R,确保整个系统进入死锁状态。 若此刻Host与Guest之间有kd接入,可以看到发生了什么。 kd> !process 0 0x1f lsass.exe PROCESS ffffda884002a800 SessionId: 0 Cid: 0314 Peb: e6548a7000 ParentCid: 02ac FreezeCount 1 ... DebugPort ffffda8843215970 THREAD ffffda8840027080 Cid 0314.0324 Teb: 000000e6548ac000 Win32Thread: 0000000000000000 WAIT: (Suspended) KernelMode Non-Alertable SuspendCount 1 FreezeCount 1 ffffda8840027360 NotificationEvent Not impersonating DeviceMap ffffa08d0bc17c50 Owning Process ffffda884002a800 Image: lsass.exe Attached Process N/A Image: N/A Wait Start TickCount 98653130 Ticks: 10534 (0:00:02:44.593) Context Switch Count 583 IdealProcessor: 0 UserTime 00:00:00.000 KernelTime 00:00:00.000 Win32 Start Address lsass!LsapRmServerThread (0x00007ff772ec3570) Stack Init ffffb10165dddc90 Current ffffb10165ddd180 Base ffffb10165dde000 Limit ffffb10165dd8000 Call 0000000000000000 Priority 11 BasePriority 9 PriorityDecrement 0 IoPriority 2 PagePriority 5 Child-SP RetAddr Call Site ffffb101`65ddd1c0 fffff802`b7c751bd nt!KiSwapContext+0x76 ffffb101`65ddd300 fffff802`b7c74c5f nt!KiSwapThread+0x17d ffffb101`65ddd3b0 fffff802`b7c76a37 nt!KiCommitThreadWait+0x14f ffffb101`65ddd450 fffff802`b7cb95ad nt!KeWaitForSingleObject+0x377 ffffb101`65ddd500 fffff802`b7c77c4a nt!KiSchedulerApc+0x231 ffffb101`65ddd620 fffff802`b7c753a4 nt!KiDeliverApc+0x22a ffffb101`65ddd6b0 fffff802`b7c74c5f nt!KiSwapThread+0x364 ffffb101`65ddd760 fffff802`b7c76a37 nt!KiCommitThreadWait+0x14f ffffb101`65ddd800 fffff802`b7cfb18a nt!KeWaitForSingleObject+0x377 ffffb101`65ddd8b0 fffff802`b80079cd nt!AlpcpWaitForSingleObject+0x3e ffffb101`65ddd8f0 fffff802`b7ffd59b nt!AlpcpReceiveMessagePort+0x44d ffffb101`65ddd980 fffff802`b7ffd3fe nt!AlpcpReceiveLegacyMessage+0x10b ffffb101`65ddda20 fffff802`b7ffd323 nt!NtReplyWaitReceivePortEx+0xce ffffb101`65dddac0 fffff802`b7d75103 nt!NtReplyWaitReceivePort+0xf ffffb101`65dddb00 00007fff`88d95de4 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffb101`65dddb00) 000000e6`54b7f3f8 00000000`00000000 ntdll!NtReplyWaitReceivePort+0x14 ... THREAD ffffda8841a8b600 Cid 0314.1928 Teb: 000000e654962000 Win32Thread: 0000000000000000 WAIT: (Executive) KernelMode Non-Alertable SuspendCount 1 FreezeCount 1 ffffb10166027d80 SynchronizationEvent Not impersonating DeviceMap ffffa08d0bc17c50 Owning Process ffffda884002a800 Image: lsass.exe Attached Process N/A Image: N/A Wait Start TickCount 98653131 Ticks: 10533 (0:00:02:44.578) Context Switch Count 9 IdealProcessor: 0 UserTime 00:00:00.000 KernelTime 00:00:00.000 Win32 Start Address ntdll!DbgUiRemoteBreakin (0x00007fff88dc0480) Stack Init ffffb10166028c90 Current ffffb101660279b0 Base ffffb10166029000 Limit ffffb10166023000 Call 0000000000000000 Priority 9 BasePriority 9 PriorityDecrement 0 IoPriority 2 PagePriority 5 Child-SP RetAddr Call Site ffffb101`660279f0 fffff802`b7c751bd nt!KiSwapContext+0x76 ffffb101`66027b30 fffff802`b7c74c5f nt!KiSwapThread+0x17d ffffb101`66027be0 fffff802`b7c76a37 nt!KiCommitThreadWait+0x14f ffffb101`66027c80 fffff802`b821f79c nt!KeWaitForSingleObject+0x377 ffffb101`66027d30 fffff802`b8220985 nt!DbgkpQueueMessage+0x230 ffffb101`66027f30 fffff802`b80fe7da nt!DbgkpSendApiMessage+0xa9 ffffb101`66027f80 fffff802`b7cbb521 nt!DbgkForwardException+0x14e ffffb101`66028100 fffff802`b7d75702 nt!KiDispatchException+0x331 ffffb101`66028920 fffff802`b7d70298 nt!KiExceptionDispatch+0xc2 ffffb101`66028b00 00007fff`88d994f1 nt!KiBreakpointTrap+0x2d8 (TrapFrame @ ffffb101`66028b00) 000000e6`671ffd08 00007fff`88dc04ca ntdll!DbgBreakPoint+0x1 000000e6`671ffd10 00007fff`885b84d4 ntdll!DbgUiRemoteBreakin+0x4a 000000e6`671ffd40 00007fff`88d41791 KERNEL32!BaseThreadInitThunk+0x14 000000e6`671ffd70 00000000`00000000 ntdll!RtlUserThreadStart+0x21 lsass.exe中有很多线程,重点看上面列出来的两个线程。 第一个线程在调nt!AlpcpReceiveMessagePort,该线程负责处理RPC/ALPC的读取。第 二个线程对应ntdll!DbgUiRemoteBreakin,这是cdb调试lsass时远程注入的调试线程。 现在看看cdb里发生了什么 kd> !process 0 0x1f cdb.exe PROCESS ffffda8844326800 SessionId: 1 Cid: 1680 Peb: f607a9c000 ParentCid: 1b0c ... THREAD ffffda884146e080 Cid 1680.0e80 Teb: 000000f607a9d000 Win32Thread: ffffda883f8b6370 WAIT: (WrLpcReply) UserMode Non-Alertable ffffda884146e6c0 Semaphore Limit 0x1 Waiting for reply to ALPC Message ffffa08d2120ccf0 : queued at port ffffda8840064ad0 : owned by process ffffda884002a800 Not impersonating DeviceMap ffffa08d1dc71550 Owning Process ffffda8844326800 Image: cdb.exe Attached Process N/A Image: N/A Wait Start TickCount 98653131 Ticks: 10541 (0:00:02:44.703) Context Switch Count 252 IdealProcessor: 0 UserTime 00:00:00.000 KernelTime 00:00:00.046 Win32 Start Address cdb!wmainCRTStartup (0x00007ff7a7ddc8b0) Stack Init ffffb10165242c90 Current ffffb10165242300 Base ffffb10165243000 Limit ffffb1016523d000 Call 0000000000000000 Priority 13 BasePriority 13 PriorityDecrement 0 IoPriority 2 PagePriority 5 Kernel stack not resident. Child-SP RetAddr Call Site ffffb101`65242340 fffff802`b7c751bd nt!KiSwapContext+0x76 ffffb101`65242480 fffff802`b7c74c5f nt!KiSwapThread+0x17d ffffb101`65242530 fffff802`b7c76a37 nt!KiCommitThreadWait+0x14f ffffb101`652425d0 fffff802`b7c78048 nt!KeWaitForSingleObject+0x377 ffffb101`65242680 fffff802`b8002b28 nt!AlpcpSignalAndWait+0x1d8 ffffb101`65242720 fffff802`b80db059 nt!AlpcpReceiveSynchronousReply+0x58 ffffb101`65242780 fffff802`b806412d nt!AlpcpProcessConnectionRequest+0x241 ffffb101`65242890 fffff802`b80fb39c nt!AlpcpConnectPort+0x2c5 ffffb101`65242a10 fffff802`b7d75103 nt!NtAlpcConnectPortEx+0x70 ffffb101`65242a90 00007fff`88d96b34 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffb101`65242b00) 000000f6`07c75bf8 00007fff`8813dc12 ntdll!NtAlpcConnectPortEx+0x14 000000f6`07c75c00 00007fff`8813e5e7 RPCRT4!LRPC_CASSOCIATION::AlpcConnect+0x28a 000000f6`07c75dc0 00007fff`881386ae RPCRT4!LRPC_CASSOCIATION::Connect+0x17f 000000f6`07c75e90 00007fff`8812ebf9 RPCRT4!LRPC_BASE_BINDING_HANDLE::DriveStateForward+0x47e 000000f6`07c75f20 00007fff`881d7072 RPCRT4!LRPC_BINDING_HANDLE::NegotiateTransferSyntax+0xa79 000000f6`07c76020 00007fff`881d6652 RPCRT4!NdrpClientCall3+0x7e2 000000f6`07c763c0 00007fff`84e34a5d RPCRT4!NdrClientCall3+0xf2 000000f6`07c76750 00007fff`84e344f1 SSPICLI!CreateRpcConnection+0x69 000000f6`07c767d0 00007fff`84e37a26 SSPICLI!InitState+0x55 000000f6`07c76820 00007fff`84e37790 SSPICLI!SspipGetUserName+0x1d6 000000f6`07c76910 00007fff`84e3a8ab SSPICLI!GetUserNameExW+0x50 000000f6`07c76960 00007fff`74b8fb9c SSPICLI!GetUserNameExA+0x6b 000000f6`07c769b0 00007fff`74b8fa66 wininet!WininetGetUserName+0x28 000000f6`07c769e0 00007fff`88d1a491 wininet!_InitOnceGlobalUserName+0x56 000000f6`07c76b60 00007fff`860bc7aa ntdll!RtlRunOnceExecuteOnce+0x91 000000f6`07c76ba0 00007fff`74b964ff KERNELBASE!InitOnceExecuteOnce+0xa 000000f6`07c76bd0 00007fff`74b3fa76 wininet!InitializeGlobalUserName+0x2f 000000f6`07c76c00 00007fff`74b42c7d wininet!CacheClientGlobalInitialize+0x56 000000f6`07c76c40 00007fff`74b4038e wininet!GetCurrentSettingsVersion+0x55 000000f6`07c76c70 00007fff`74bac303 wininet!GlobalDataInitialize+0xde 000000f6`07c76d30 00007fff`74bac0f9 wininet!InternetOpenA+0x183 000000f6`07c76e70 00007fff`73df87dc wininet!InternetOpenW+0x129 000000f6`07c76f10 00007fff`73df416e symsrv!StoreWinInet::connect+0x39c 000000f6`07c77430 00007fff`73deac47 symsrv!StoreHTTP::find+0x5e 000000f6`07c77b50 00007fff`73debb69 symsrv!cascade+0xe7 000000f6`07c77fa0 00007fff`73debcc6 symsrv!SymbolServerByIndexWorkerW+0x369 000000f6`07c78b70 00007fff`73deb49a symsrv!SymbolServerByIndexAndChecksumsW+0x66 000000f6`07c78bc0 00007fff`7063fb6f symsrv!SymbolServerW+0xba 000000f6`07c78e50 00007fff`70629be1 dbghelp!symsrvGetFile+0x2ef 000000f6`07c79710 00007fff`7062aa49 dbghelp!diaLocatePdb+0x595 ... cdb.exe中有多个线程。第一个线程正在试图加载PDB,由此发起RPC调用,这个调用的 RPC服务端在lsass.exe里。lsass正被cdb调试,无法响应RPC请求,cdb又等着lsass 响应自己发起的RPC请求,典型的死锁。 "Waiting for reply"这一行已经指明死锁关键,包括引起死锁的进程 可以利用调用栈上的数据获取cdb发起的RPC请求的更多信息 IID 4f32adc8-6052-4a04-8701-293ccf2096f0 ProcNum 0 (sspisrv!SspirConnectRpc) ☆ 利用kd detach 假设在Guest中cdb调试lsass.exe陷入死锁,但Host与Guest间有kd接入,如何用kd进 行抢救?这个问题有现实意义。我一般用cdb,不怎么用GUI的windbg。调试lsass需 要管理员级cmd,我只有一个管理员级cmd,还被cdb占用了,Skywing的技术方案不适 用于此真实场景,这可能是一个用windbg而不用cdb的理由吧。当时我有kd,所以很 不甘心重启或恢复快照,想利用kd直接抢救,后来找到一个可用方案。 我问了一下杨军锋、王宇,二位给了大致类似的思路,设法让cdb走detach的流程。 杨军锋建议利用kd在cdb进程空间注入shellcode,调用DebugActiveProcessStop。 后来证明,思路可行,但有一些细节要处理。 KERNELBASE!DebugActiveProcessStop(PID) KERNELBASE!ProcessIdToHandle(PID) ntdll!DbgUiStopDebugging(ProcessHandle) ntdll!NtRemoveProcessDebug(ProcessHandle,DebugObjectHandle) 假设中断在NtRemoveProcessDebug,kpn看调用栈,看不到DbgUiStopDebugging,因 为在汇编层面有个优化,DbgUiStopDebugging是jmp到NtRemoveProcessDebug,不是 call过去的。 真正进行detach操作的是NtRemoveProcessDebug,它需要两个参数,一个是被调试进 程句柄,另一个是DebugObject句柄,这是被调试进程DebugPort在调试器进程空间的 索引。虽然DebugActiveProcessStop只需要指定目标PID,但实际上要用到 DebugObjectHandle,该值来自线程变量"NtCurrentTeb()->DbgSsReserved[1]"。 KERNELBASE!DebugActiveProcess ntdll!DbgUiConnectToDbg ntdll!NtCreateDebugObject DbgUiConnectToDbg中调用NtCreateDebugObject设置DbgSsReserved[1]。cdb进程有 多个线程,应该在调用DebugActiveProcess创建调试会话的那个线程中调用配对的 DebugActiveProcessStop,因为它们共用同一个线程变量DbgSsReserved[1]。 但现代cdb实现,或者说dbgeng实现,不再使用DbgSsReserved[1]。detach时调用栈 回溯如下 # Call Site 00 ntdll!NtRemoveProcessDebug 01 dbgeng!LiveUserDebugServices::DetachProcess+0x77 02 dbgeng!LiveUserTargetInfo::DetachProcess+0xe0 03 dbgeng!ProcessInfo::Separate+0x354 04 dbgeng!ParseSeparateCurrentProcess+0xf5 05 dbgeng!DotCommand+0xf1 06 dbgeng!ProcessCommands+0xc90 07 dbgeng!ProcessCommandsAndCatch+0x86 08 dbgeng!Execute+0x346 09 dbgeng!DebugClient::ExecuteWide+0x94 0a cdb!MainLoop+0x532 0b cdb!wmain+0x4df 0c cdb!__wmainCRTStartup+0x14d 0d KERNEL32!BaseThreadInitThunk+0x14 0e ntdll!RtlUserThreadStart+0x21 detach时甚至没有调用DebugActiveProcessStop,直接调了NtRemoveProcessDebug。 现代dbgeng实现有几个类,ProcessInfo、LiveUserTargetInfo、 LiveUserDebugServices,原来保存在DbgSsReserved[1]的DebugObjectHandle现在应 该是某个类成员变量。手工检查cdb所有线程TEB,所有DbgSsReserved[]都是NULL, 无一被使用。 不必找那个保存DebugObjectHandle的类成员变量,"!process 0 1 lsass.exe"得到 DebugPort地址,"!findhandle "得到DebugObjectHandle。咱有 kd,干嘛不用? 既然有DebugObjectHandle,就想直接调NtRemoveProcessDebug来detach。起初用 "!findhandle "得到一个ProcessHandle,测试发现这个Handle不能用 于NtRemoveProcessDebug。看ProcessIdToHandle实现,应该与NtOpenProcess第2形 参有关,是不是得指定PROCESS_SET_PORT啊?未进一步测试。不想自己打开进程,还 是用DebugActiveProcessStop吧。不就是从DbgSsReserved[1]取DebugObjectHandle 吗,提前填上有效值就是。 dx @$teb->DbgSsReserved[1]=(void*)0x16c 可以修改cdb!wmainCRTStartup附近的代码,用来存放定制的汇编代码,反正也不会 用到了。 如果不在kd里,还可以用.dvalloc分配一块内存存放汇编代码。接下来的问题是,怎 么让cdb的流程转向修改过的cdb!wmainCRTStartup?由于对Windows内核不熟,我用 了个最直白的办法,杀掉cmd导致杀掉cdb,流程转ntdll!NtTerminateProcess。 在用户态用cdb调notepad,然后杀cmd间接杀cdb,有个调用栈回溯 # Call Site 00 ntdll!NtTerminateProcess 01 ntdll!RtlExitUserProcess+0x5b 02 KERNELBASE!DefaultHandler+0xf 03 KERNELBASE!CtrlRoutine+0xa3 04 KERNEL32!BaseThreadInitThunk+0x14 05 ntdll!RtlUserThreadStart+0x21 用cdb调lsass时也一样。可以先对NtTerminateProcess设断,命中后强制修改rip到 cdb!wmainCRTStartup,这样定制的汇编代码得到执行。 不要在kd中.kill杀cdb,那样的话流程不过ntdll!NtTerminateProcess。得去用户态 杀cmd间接杀cdb。 为了验证思路可行,先用cdb调notepad,用kd帮cdb detach,即使出幺蛾子也没啥影 响。起初在cdb中只调DebugActiveProcessStop,发现notepad的DebugPort确实关闭 了,杀cmd间接杀cdb后,notepad没跟着一块儿完蛋。但是,notepad呈僵死状态。在 kd里"!process 0 3 notepad.exe",一堆的"SuspendCount 1"、"FreezeCount 1"。 没去查SuspendCount、FreezeCount的意义,前者应该是KeSuspendThread导致的,后 者是啥概念?正常运行的notepad,这两种值都是0。 想了想,除了DebugActiveProcessStop,可能还得ResumeThread。实验表明,确实如 此。又一次用了笨办法 .shell -ci "!process 0 2 notepad.exe" findstr "THREAD " awk -F' ' '{print "!findhandle " $2 " ffffda883eaef800";}' some.txt 得到notepad所有线程在cdb进程空间的对应句柄,不是TID,ResumeThread用的是线 程句柄。循环调用ResumeThread,继续执行notepad的所有线程。之后,notepad恢复 正常,attach上来的cdb已经被杀,notepad还活着。相当于在cdb失去响应的情况下 旁路detach成功。把同样的思路用于lsass死锁场景,成功解除死锁,系统恢复响应。 有无可能利用kd强制cdb走.detach对应的流程,直接使用现有代码,而不是定制一段 汇编,尤其是lsass死锁场景。这也是各种遗留问题之一,留待有缘人解答。 ☆ detach.nasm 很久不写Intel汇编代码,下面这段写得相当扯淡,但能用。这只是个模板,需要填 写有效值再编译。 -------------------------------------------------------------------------- ; nasm -f bin -o detach.bin detach.nasm BITS 64 %macro pushaq 0 pushfq push rcx push rdx push rbx push rbp push rsi push rdi push r8 push r9 push r10 push r11 push r12 push r13 push r14 push r15 %endmacro %macro popaq 0 pop r15 pop r14 pop r13 pop r12 pop r11 pop r10 pop r9 pop r8 pop rdi pop rsi pop rbp pop rbx pop rdx pop rcx popfq %endmacro DebugActiveProcessStop equ 0 ResumeThread equ 8 TerminateProcess equ 0x10 ; dt nt!_TEB DbgSsReserved DbgSsReserved equ 0x16a0 _start: pushaq ; get rip relative to the next instruction after the lea lea rbp, [rel $+7] base: movzx rax, word [rbp+DebugPort_handle-base] mov rdx, [gs:0x30] mov qword[rdx+DbgSsReserved+1*8], rax movzx rcx, word [rbp+target_pid-base] call qword [rbp+func_array+DebugActiveProcessStop-base] xor rbx, rbx loop: movzx rcx, word [rbp+thread_handle_array-base+rbx*2] call qword [rbp+func_array+ResumeThread-base] inc rbx cmp bx, word [rbp+thread_handle_count-base] jne loop popaq lea rax, [rel $+7] jmp qword [rax+func_array+TerminateProcess-$] func_array: ; KERNELBASE!DebugActiveProcessStop dq 0x7fff86136e10 ; KERNELBASE!ResumeThread dq 0x7fff860dd770 ; KERNELBASE!TerminateProcess dq 0x7fff860e1130 thread_handle_count: dw ( thread_handle_end - thread_handle_array ) / 2 DebugPort_handle: dw 0x1314 target_pid: dw 0x1315 thread_handle_array: dw 0x1316 dw 0x1317 dw 0x1318 dw 0x1319 dw 0x131a dw 0x131b dw 0x131c thread_handle_end: -------------------------------------------------------------------------- ☆ 使用示例 在管理员级cmd中执行 C:\temp\cdb.exe -sins -y "srv*http://msdl.microsoft.com/download/symbols" -noinh -snul -hd -o -pn lsass.exe 在kd中".process /i"切到cdb进程空间,执行如下操作 .readmem detach_lsass.bin cdb!wmainCRTStartup l 0n159 u cdb!wmainCRTStartup cdb!wmainCRTStartup+0n159 ba e1 /1 /p @$proc KERNELBASE!CtrlRoutine "r rip=cdb!wmainCRTStartup;gc" 回Guest对着僵死的cdb按下Ctrl-C解除死锁,管理员级cmd仍在。 上例假设detach_lsass.bin大小是159字节。 ☆ 后记 相比Skywing的方案,我的方案没啥优势,但有时没机会用Skywing的方案,我碰上了, 就研究了一下。绝大多数人这辈子都不需要了解这些东西,也用不上。 2021-12-22 06:04 bluerust kill cmd来终止cdb这步我不一定会想到,如果是我的话,可能会想着塞一段kernel shellcode进去,然后KeStackAttachProcess,在内核分配用户进程空间,写入用户 态shellcode,最后KeInsertQueueApc在cdb里执行shellcode,应该也行。当然,我 这还是YY,实操不知道可行不。我很久没有用汇编写shellcode了,我都是用C。 参 Loading and Debugging Windows Kernel Shellcodes with Windbg - Javier Vicente Vallejo [2017-07-23] https://vallejocc.wordpress.com/2017/06/23/loading-and-debugging-windows-kernel-shellcodes-with-windbg-debugging-doublepulsar-shellcode/