一、背景介绍
Win10 RPC调试时,知道RPC Client PID是一件有意义的事。有些RPC Server中的断点命中太过频繁,靠RPC Client PID进行过滤能有效调试。有些RPC Server调用栈回溯中出现远程过程,想知道哪个进程发起的RPC调用,想溯源。想拦截特定进程的域名解析,想干扰Win10的自动更新,等等。非常规需求,但大千世界芸芸众生,必有人产生过类似需求。
本文演示一些Win10 RPC调试技术中的黑魔法,让Win10 RPC调试之旅更有趣些。
Guest环境如下
Win10
Win10企业版2016 LTSB 1607(OS Build 14393.4704)
dnsapi.dll
10.0.14393.4350 (rs1_release.210407-2154)
dnsrslvr.dll
10.0.14393.4350 (rs1_release.210407-2154)
ntdll.dll
10.0.14393.4704 (rs1_release.211004-1917)
rpcrt4.dll
10.0.14393.4704 (rs1_release.211004-1917)
二、阻断指定PID的FQDN解析
Win10 FQDN解析绝大多数时候都要过dnsrslvr!R_ResolverQuery,在Dnscahce服务中。对于ping这样转瞬即逝的进程,一般不易提前获知其PID,从而不易在Dnscahce服务中对ping的PID进行过滤,本小节不考虑这种情形,只考虑常驻进程的FQDN解析。
1) 获取Dnscache(DNS Client)服务所在进程PID
tasklist /svc /fi “services eq dnscache”
假设是svchost(564)。
2) 获取Firefox PID
打开Firefox,Process Explorer查看之,居然开了6个进程,1个父进程5个子进程,只有父进程(5384)有GUI。用Firefox访问”www.baidu.com”,用Tcpview查看TCP连接,5384有到百度的长连接,但这不足以说明FQDN解析之RPC请求由5384发起,理论上可能有其他子进程参与其中。
假设事先并不清楚Firefox架构,应该用其他手段确认FQDN解析之RPC请求由5384发起,比如ALPCLogger,或直接上调试器。顺便期待一下,哪天Process Monitor支持ALPCETW就好了,把ALPCLogger的功能揉进来。
3) 阻止指定客户端解析指定FQDN
dnsrslvr!R_ResolverQuery的形参中有FQDN,可以对FQDN进行过滤,但该函数形参中没有RPC Client PID。也可能通过某个形参可以间接获取RPC Client PID,只是我没逆向分析出来。
做了其他角度的逆向分析后,发现在dnsrslvr!R_ResolverQuery入口点
qwo(poi(@r12+0x68)+8) // 可能是RPC Client PID
qwo(poi(@r12+0x68)+0x10) // 可能是RPC Client TID
这是调用栈中某些底层函数留下的内存残像,与具体汇编代码相关。如遇不适用的情形,有兴趣者可自行逆向适配。
可在dnsrslvr!R_ResolverQuery入口点对PID、FQDN同时进行过滤,匹配时将FQDN替换成”.”,这将导致FQDN解析失败。
bp dnsrslvr!R_ResolverQuery “r $t0=@r8;r $t1=poi(@r12+0x68);
.if(qwo(@$t1+8)==0n5384 and qwo(@$t0)==0x2e007700770077 and
qwo(@$t0+8)==0x64006900610062 and qwo(@$t0+0x10)==0x6f0063002e0075 and
by(@$t0+0x18)=0x6d){ezu @$t0 \”.\”}.else{du @$t0};gc”
为显示方便,上面这个断点做了折行处理,在cdb中使用时,请恢复成一行。
本例阻止Firefox(5384)解析”www.baidu.com”,Firefox(5384)可以解析其他FQDN,其他进程可以正常解析任意FQDN(包括百度)。上例断点条件未使用字符串比较技巧,所以显得很野蛮,自用时换字符串比较好了。
在这个位置取RPC Client PID是Hacking方案,上下文相关,切勿用于产品。
三、干扰Win10自动更新
靠前述获取PID的黑魔法观察wuauserv服务发起的FQDN解析。wuauserv服务与很多其他服务共用svchost(872),理论上只过滤PID不够,但其他服务产生的干扰可以忽略。
$ tasklist /svc /fi “services eq wuauserv”
Image Name PID Services
========================= ========
svchost.exe 872 Appinfo, BITS, CertPropSvc, DoSvc, DsmSvc,
gpsvc, IKEEXT, iphlpsvc, lfsvc, ProfSvc,
Schedule, seclogon, SENS, SessionEnv,
Themes, UserManager, UsoSvc, Winmgmt,
wisvc, wlidsvc, WpnService, wuauserv
Wireshark抓包当然是可以的,只是干扰更多,不如调试器里直接过滤PID来得爽。
调试svchost(564)
bp dnsrslvr!R_ResolverQuery “r $t0=@r8;r $t1=poi(@r12+0x68);du @$t0;.if(qwo(@$t1+8)==0n872){}.else{? qwo(@$t1+8);? qwo(@$t1+0x10);gc}”
RPC Client PID是svchost(872)时断下,看到若干FQDN,比如
sls.update.microsoft.com
fe2.update.microsoft.com
download.windowsupdate.com
au.download.windowsupdate.com
ctldl.windowsupdate.com
可以搞个条件断点,发现svchost(872)试图解析”download.windowsupdate.com”时将FQDN替换成”.”。之后Guest的”Checking for updates”报错
There were some problems installing updates, but we’ll try again later. If
you keep seeing this and want to search the web or contact support for
information, this may help: (0x8024402f)
阻止Win10自动更新有略显”正经”的其他办法,此处只是用它演示RPC调试黑魔法。有些缺乏经验者,在此可能设问,在hosts中将”download.windowsupdate.com”解析成”127.0.0.1″不就得了,可以自己试试再说。假设能上调试器,可以热Patch绕过微软域名保护,有兴趣者不妨调试一下。
四、调试winlogon时反向溯源
有次调试主控台winlogon进程,设断winlogon!SignalManagerSetSignal。主控台出现新的密码输入界面时,点击残障人士按钮,触发断点,调用栈回溯如下
# Child-SP RetAddr Call Site
00 0000008666efee08 00007ff7
90f65902 winlogon!SignalManagerSetSignal
01 0000008666efee10 00007ff7
90f99122 winlogon!WlStateMachineSetSignal+0x2a
02 0000008666efee40 00007ff7
90f94944 winlogon!WlAccessibilityOnWin32KMessage+0x10e
03 0000008666efee70 00007ff7
90f7bbfd winlogon!WMsgKMessageHandler+0x18d04
04 0000008666efeef0 00007fff
8817a583 winlogon!I_WMsgkSendMessage+0x4d
05 0000008666efef40 00007fff
881d6162 RPCRT4!Invoke+0x73
06 0000008666efefb0 00007fff
8814a284 RPCRT4!Ndr64AsyncServerWorker+0x392
07 0000008666eff0d0 00007fff
8814919d RPCRT4!DispatchToStubInCNoAvrf+0x24
08 0000008666eff120 00007fff
88149a4b RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1bd
09 0000008666eff1f0 00007fff
881310ac RPCRT4!RPC_INTERFACE::DispatchToStub+0xcb
0a 0000008666eff250 00007fff
8813152c RPCRT4!LRPC_SCALL::DispatchRequest+0x34c
0b 0000008666eff330 00007fff
8811ae1c RPCRT4!LRPC_SCALL::HandleRequest+0x2bc
0c 0000008666eff450 00007fff
8811c67b RPCRT4!LRPC_ADDRESS::HandleRequest+0x36c
0d 0000008666eff500 00007fff
88143a2a RPCRT4!LRPC_ADDRESS::ProcessIO+0x91b
0e 0000008666eff640 00007fff
88d0d35e RPCRT4!LrpcIoComplete+0xaa
0f 0000008666eff6e0 00007fff
88d0ecc9 ntdll!TppAlpcpExecuteCallback+0x25e
10 0000008666eff790 00007fff
885b84d4 ntdll!TppWorkerThread+0x8d9
11 0000008666effb90 00007fff
88d41791 KERNEL32!BaseThreadInitThunk+0x14
12 0000008666effbc0 00000000
00000000 ntdll!RtlUserThreadStart+0x21
知道winlogon会RPC出去,没想到还有其他进程RPC到winlogon,想溯这个RPC的源。
bp winlogon!I_WMsgkSendMessage “r $t1=poi(@r12+0x68);? qwo(@$t1+8);? qwo(@$t1+0x10)”
Evaluate expression: 5760 = 0000000000001680 Evaluate expression: 5860 = 00000000
000016e4
据此确认上述IID、ProcNum的RPC Client是LogonUI(5760)。
假设正断在winlogon!I_WMsgkSendMessage,另开cdb调试LogonUI(5760),查看TID(0x16e4)的调用栈回溯
~~[16e4] kn
# Child-SP RetAddr Call Site
00 0000001cf0fff428 00007fff
860b8c1f ntdll!NtWaitForMultipleObjects+0x14
01 0000001cf0fff430 00007fff
87f7e6fb KERNELBASE!WaitForMultipleObjectsEx+0xef
02 0000001cf0fff730 00007fff
87cd78f9 user32!MsgWaitForMultipleObjectsEx+0x15b
03 0000001cf0fff810 00007fff
87cd8038 combase!ASTAWaitContext::KernelWait+0x59
04 0000001cf0fff880 00007fff
87cd5cef combase!ASTAWaitContext::Wait+0x308
05 0000001cf0fffb90 00007fff
87cd5be5 combase!ASTAWaitInNewContext+0xc3
06 0000001cf0fffc70 00007fff
87c52acb combase!ASTAThreadWaitForHandles+0x75
07 0000001cf0fffce0 00007fff
7dcd0952 combase!CoWaitForMultipleHandles+0xcb
08 0000001cf0fffd20 00007fff
7dcd0e69 Windows_UI_XamlHost!ASTAThreadHost::ASTAThreadHostThreadProc+0x76
09 0000001cf0fffda0 00007fff
85ce3fad Windows_UI_XamlHost!ASTAThreadHost::s_ASTAThreadHostThreadProc+0x19
0a 0000001cf0fffdd0 00007fff
885b84d4 SHCORE!_WrapperThreadProc+0xed
0b 0000001cf0fffec0 00007fff
88d41791 KERNEL32!BaseThreadInitThunk+0x14
0c 0000001cf0fffef0 00000000
00000000 ntdll!RtlUserThreadStart+0x21
未看到想像中的RPCRT4!NdrpClientCall3,发生了什么?
这个问题当成Win10 RPC调试的课后作业留给那些永远充满好奇心的人们。有兴趣者可以尝试回答这个问题,会让你的调试技能进阶,不用告诉我答案。
五、关于取RPC Client PID/TID的补充说明
在RPC Server侧的调用栈回溯中,某些函数通过其形参可以方便地获取RPC ClientPID/TID,但那些函数入口点处尚未进行RPC形参反序列化,无法直接取到RPC形参;远程过程本身已获取RPC形参,又缺失了获取RPC Client PID/TID的途径。换句话说,有些位置取PID/TID方便,有些位置取RPC形参方便,很少有位置取这两种信息都方便。要想在单点位置同时获取这两种信息,只能动用上下文相关的Hacking方案。
参考资源
[1] 《DNS系列(11)–研究Win10 FQDN解析》
http://scz.617.cn:8/windows/202103071208.txt
[2] 《RpcView简介》
https://blog.nsfocus.net/rpcview-nsfocus/
[3] 《利用findrpc寻找RPC接口信息》
https://blog.nsfocus.net/findrpc-rpc/
[4] 《Win10中rpcexts.dll已废》
http://scz.617.cn:8/windows/202111152036.txt
[5] 《用RPC/ALPC调试手段分析Win10 FQDN解析过程》
https://blog.nsfocus.net/rpc-alpc/
[6] 《Win10 FQDN解析时绕过hosts文件》
https://blog.nsfocus.net/win10-fqdn/
版权声明
本站“技术博客”所有内容的版权持有者为绿盟科技集团股份有限公司(“绿盟科技”)。作为分享技术资讯的平台,绿盟科技期待与广大用户互动交流,并欢迎在标明出处(绿盟科技-技术博客)及网址的情形下,全文转发。
上述情形之外的任何使用形式,均需提前向绿盟科技(010-68438880-5462)申请版权授权。如擅自使用,绿盟科技保留追责权利。同时,如因擅自使用博客内容引发法律纠纷,由使用者自行承担全部法律责任,与绿盟科技无关。