标题: Windows域名解析保护机制深度分析 创建: 2021-11-24 11:29 更新: 链接: https://scz.617.cn/windows/202111241129.txt 2006年4月Dave Korn在[Full-disclosure]邮件列表中发言,指责微软干扰hosts文件 的使用。一般FQDN的解析顺序都是hosts文件优先,但XP SP2在解析某些FQDN时不再 尊重hosts中的设置。XP SP2的dnsapi.dll中固化了一批FQDN,据Dave Korn测试,至 少如下两个FQDN被特殊对待了 go.microsoft.com office.microsoft.com 即使在hosts文件中将这两个FQDN解析成127.0.0.1,也会成功访问它们。当年我测过, XP SP1尚无此行为。 现在要是放狗搜,有很多相关讨论,不过都流于表象,并未从逆向工程角度阐述此事。 本来我也不关心这事的技术细节,但前几天搞Win10 RPC调试时拿Dnscache服务做靶 子,遇到hosts中的"download.windowsupdate.com"无法生效,好奇心起,逆向工程 了一把。本文没有直接给结论,展示一下探索过程。 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) -------------------------------------------------------------------------- 在Guest中 $ notepad c:\windows\system32\drivers\etc\hosts 127.0.0.1 settings-win.data.microsoft.com 127.0.0.1 sls.update.microsoft.com 127.0.0.1 fe2.update.microsoft.com 127.0.0.1 download.windowsupdate.com 127.0.0.1 au.download.windowsupdate.com 127.0.0.1 ctldl.windowsupdate.com 127.0.0.1 geo.prod.do.dsp.mp.microsoft.com 无法阻止Win10更新。简单测试表明,如下FQDN不受hosts影响 sls.update.microsoft.com fe2.update.microsoft.com download.windowsupdate.com au.download.windowsupdate.com ctldl.windowsupdate.com 下列FQDN不受保护 settings-win.data.microsoft.com geo.prod.do.dsp.mp.microsoft.com 假设Dnscache服务位于svchost(564)中,调试之 bp dnsrslvr!Cache_FindEntry "du @rcx" 该函数第1形参是FQDN。假设断下时正在请求解析"download.windowsupdate.com", gu之后rax为0,表示没找到。从而确认DNS Cache中没有相应FQDN,表明hosts中条目 未被加入DNS Cache。可以换其他非微软FQDN测试,hosts中条目正常进入DNS Cache。 编辑hosts,保存,让文件产生时间戳变动。用Process Monitor监控svchost(564), 发现有 svchost.exe 564 NotifyChangeDirectory C:\Windows\System32\drivers\etc SUCCESS Filter: FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_DIR_NAME 应该是提醒Dnscache重读hosts文件。 ipconfig /flushdns ping download.windowsupdate.com 用Process Monitor监控svchost(564),发现Dnscache服务读取hosts,调用栈如下 -------------------------------------------------------------------------- 0 FLTMGR.SYS FltpPerformPreCallbacksWorker+0x480 0xfffff8054c611bdc 1 FLTMGR.SYS FltpPassThroughInternal+0x88 0xfffff8054c6039e8 2 FLTMGR.SYS FltpPassThrough+0x1a6 0xfffff8054c6030c6 3 FLTMGR.SYS FltpDispatch+0x9e 0xfffff8054c602e6e 4 ntoskrnl.exe IopSynchronousServiceTail+0x1a0 0xfffff802b80150f0 5 ntoskrnl.exe NtReadFile+0x670 0xfffff802b8013480 6 ntoskrnl.exe KiSystemServiceCopyEnd+0x13 0xfffff802b7d75103 7 ntdll.dll NtReadFile+0x14 0x7fff88d95d44 8 KERNELBASE.dll ReadFile+0x74 0x7fff86095014 9 DNSAPI.dll hostsFileSniffBom+0x5b 0x7fff8483d627 10 DNSAPI.dll HostsFile_Open+0x14e 0x7fff8483d47e 11 dnsrslvr.dll LoadHostFileIntoCache+0x53 0x7fff7c356473 12 dnsrslvr.dll Cache_Initialize+0xda 0x7fff7c3563ba 13 dnsrslvr.dll Cache_Lock+0x80 0x7fff7c353110 14 dnsrslvr.dll Cache_GetRecordsForRpc+0xa0 0x7fff7c3524b0 15 dnsrslvr.dll Cache_Query+0xab 0x7fff7c3523eb 16 DNSAPI.dll Query_SingleNamePrivate+0xf4 0x7fff848330f4 17 DNSAPI.dll Query_SingleNameDualAddr+0xf6 0x7fff84835446 18 DNSAPI.dll Query_NextName+0x38b 0x7fff8482d93b 19 DNSAPI.dll Query_Main+0x904 0x7fff848315e4 20 dnsrslvr.dll ResolverQuery+0x103 0x7fff7c354863 21 dnsrslvr.dll R_ResolverQuery+0x233 0x7fff7c354733 22 RPCRT4.dll Invoke+0x73 0x7fff8817a583 23 RPCRT4.dll Ndr64AsyncServerWorker+0x392 0x7fff881d6162 24 RPCRT4.dll DispatchToStubInCNoAvrf+0x24 0x7fff8814a284 25 RPCRT4.dll RPC_INTERFACE::DispatchToStubWorker+0x1bd 0x7fff8814919d 26 RPCRT4.dll RPC_INTERFACE::DispatchToStub+0xcb 0x7fff88149a4b 27 RPCRT4.dll LRPC_SCALL::DispatchRequest+0x34c 0x7fff881310ac 28 RPCRT4.dll LRPC_SCALL::HandleRequest+0x2bc 0x7fff8813152c 29 RPCRT4.dll LRPC_ADDRESS::HandleRequest+0x36c 0x7fff8811ae1c 30 RPCRT4.dll LRPC_ADDRESS::ProcessIO+0x91b 0x7fff8811c67b 31 RPCRT4.dll LrpcIoComplete+0xaa 0x7fff88143a2a 32 ntdll.dll TppAlpcpExecuteCallback+0x25e 0x7fff88d0d35e 33 ntdll.dll TppWorkerThread+0x8d9 0x7fff88d0ecc9 34 KERNEL32.DLL BaseThreadInitThunk+0x14 0x7fff885b84d4 35 ntdll.dll RtlUserThreadStart+0x21 0x7fff88d41791 -------------------------------------------------------------------------- 从前述调用栈中关注到"DNSAPI!HostsFile_*" > x DNSAPI!HostsFile_* 00007fff`84841c10 DNSAPI!HostsFile_Query (void) 00007fff`8483cda0 DNSAPI!HostsFile_ReadLine (void) 00007fff`8483d710 DNSAPI!HostsFile_Close (void) 00007fff`8483d330 DNSAPI!HostsFile_Open (void) 00007fff`8485bfe8 DNSAPI!hostsFile_ScreenName (hostsFile_ScreenName) IDA中静态分析DNSAPI!HostsFile_ReadLine,注意到DNSAPI!BuildHostfileRecords。 用wt分析DNSAPI!BuildHostfileRecords内部执行情况,搞个对比实验,一次是微软 域名,另一次是非微软域名,用.logopen/.logclose记录wt输出。 对比BuildHostfileRecords_bad.txt、BuildHostfileRecords_ok.txt,很容易发现 分叉点 -------------------------------------------------------------------------- 25 0 [ 0] DNSAPI!BuildHostfileRecords call at 00007fff8483bfed 777 0 [ 1] DNSAPI!Dns_NameCompareEx rax = 0 37 777 [ 0] DNSAPI!BuildHostfileRecords call at 00007fff8483bfed 821 0 [ 1] DNSAPI!Dns_NameCompareEx rax = 2 -------------------------------------------------------------------------- 前者第二次调用DNSAPI!Dns_NameCompareEx时返回2,后者5次调用 DNSAPI!Dns_NameCompareEx均返回0。 用IDA查看DNSAPI!BuildHostfileRecords+0x7d附近代码,DNSAPI!Dns_NameCompareEx 对源自hosts文件的RightName进行检查,LeftName源自DNSAPI!DomainScreenList[5] windowsupdate.microsoft.com windowsupdate.com microsoftupdate.com download.microsoft.com update.microsoft.com 前述DNSAPI!Dns_NameCompareEx返回2那次 LeftName windowsupdate.com // 源自DNSAPI!DomainScreenList[5] RightName download.windowsupdate.com // 源自hosts CharSet DnsCharSetUnicode(1) Return DnsNameCompareLeftParent(2) 正是这次匹配使得download.windowsupdate.com未能进入DNS Cache。 除了DNSAPI!DomainScreenList[5],还有DNSAPI!HostsScreenList[18] microsoft.com www.microsoft.com support.microsoft.com wustats.microsoft.com download.microsoft.com microsoftupdate.microsoft.com office.microsoft.com msdn.microsoft.com go.microsoft.com msn.com www.msn.com msdn.com www.msdn.com technet.microsoft.com technet.com www.technet.com activation-v2.sls.microsoft.com validation-v2.sls.microsoft.com 当DNSAPI!DomainScreenList[5]均不匹配时,继续检查DNSAPI!HostsScreenList[18]。 -------------------------------------------------------------------------- > u DNSAPI!BuildHostfileRecords+0x41 l 6 00007fff`8483bfb1 488b05f8430500 mov rax,qword ptr [DNSAPI!DomainScreenList (00007fff`848903b0)] ... 00007fff`8483bfcb 4885c0 test rax,rax 00007fff`8483bfce 743b je DNSAPI!BuildHostfileRecords+0x9b (00007fff`8483c00b) -------------------------------------------------------------------------- > u DNSAPI!BuildHostfileRecords+0x9b l 5 00007fff`8483c00b 4c8b05ce430500 mov r8,qword ptr [DNSAPI!HostsScreenList (00007fff`848903e0)] ... 00007fff`8483c01e 4d85c0 test r8,r8 00007fff`8483c021 7466 je DNSAPI!BuildHostfileRecords+0x119 (00007fff`8483c089) -------------------------------------------------------------------------- 从上述两段代码逻辑看,只要DNSAPI!DomainScreenList[0]、 DNSAPI!HostsScreenList[0]为NULL,这两种微软域名保护检查将被绕过。 Patch eq DNSAPI!DomainScreenList 0 eq DNSAPI!HostsScreenList 0 假设hosts修改如下 $ notepad c:\windows\system32\drivers\etc\hosts 127.0.0.1 sls.update.microsoft.com 127.0.0.1 fe2.update.microsoft.com 127.0.0.1 download.windowsupdate.com ipconfig /flushdns 之后Guest的"Checking for updates"报错 We couldn't connect to the update service. We'll try again later, or you can check now. If it still doesn't work, make sure you're connected to the Internet. 热Patch绕过微软域名保护需要上调试器。若不想上调试器,可在Guest上启动一个定 制版DNS Server,由它负责Guest的所有FQDN解析;当它发现需要劫持的FQDN出现, 直接返回指定IP,否则转发给其他正常DNS Server。这种工具很多,以前我分享过一 个superdns.exe。 $ vi some.hosts -------------------------------------------------------------------------- 127.0.0.1 ^.+\.update\.microsoft\.com.$ 127.0.0.1 ^.+\.windowsupdate\.com.$ -------------------------------------------------------------------------- 将Guest的DNS Server指向127.0.0.1,执行superdns superdns.exe -q -m udp -i 192.168.65.2 some.hosts 假设192.168.65.2是其他正常DNS Server,若是VMware环境,这就是Host所在。 some.hosts中劫持了正则表达式匹配之FQDN。-q表示安静模式,去掉后可以看到调试 信息,比如请求解析的FQDN以及响应报文解码后的内容。这样干,效果同热Patch。 去掉-q,确认Win10自动更新首先检查 sls.update.microsoft.com 实际只需劫持这一个FQDN即可干扰Win10自动更新。该FQDN会被解析成不同IP,并不 固定,否则可在PFW中对出站请求的目标IP设置阻断规则。 bluerust 有意思啊,我都不知道还有这回事 scz 你看,虽然我没有MCSE证,也能勉强用用Windows