标题: DNS系列(11)--研究Win10 FQDN解析
创建: 2021-03-07 12:08 更新: 2022-11-07 11:13 链接: https://scz.617.cn/windows/202103071208.txt
目录:
☆ 拦截DNSAPI!SyncResolverQueryRpc阻断FQDN解析
☆ 后补
1) raw socket
2) 取Unicode字符串的长整型值(little-endian序)
☆ 用RPC/ALPC调试手段分析Win10 FQDN解析过程
1) 背景介绍
2) Inside WS2_32!GetAddrInfoW
3) 拦载RPCRT4!NdrpClientCall3获取RPC Server的IID/远程过程号
4) 用RpcView定位RPC Server的PID及远程过程
5) dnsrslvr!R_ResolverQuery调用栈回溯
6) 拦截RPCRT4!DispatchToStubInCNoAvrf获取IID/远程过程号/远程过程
7) 内核态获取ALPC目标进程
8) 利用ALPCLogger获取ALPC目标进程
☆ RPC Debugging (已不适用于Win10)
1) Enabling RPC State Information
2) 已知EndPoint反查PID
3) 不灵的RPC调试命令
4) 为什么Win10中rpcexts.dll不灵了
5) !rpcexts.help
☆ Win10 FQDN解析时绕过hosts文件
☆ Win10 RPC调试中RPC Client PID的利用
☆ Windows域名解析保护机制深度分析
☆ 拦截DNSAPI!SyncResolverQueryRpc阻断FQDN解析
想阻断指定进程的FQDN解析请求。若发往53/UDP的FQDN解析请求由进程本身发出,可 用wf.msc设置出站规则进行阻断。但在Win10中,大多数进程发往53/UDP的报文实际 由DNS Client Service发出,进程试图进行FQDN解析时,底层API实际向DNS Client Service发起RPC请求,由后者进行socket通信,FQDN解析结果经RPC响应返回给普通 进程,比如ping、Firefox、Opera俱如此。如果在wf.msc中阻断ping.exe的53/UDP外 发,达不到阻断指定进程FQDN解析请求的预期效果。
Win7可以停用DNS Client Service,此时进程试图解析FQDN时由本进程发送53/UDP报 文。Win10无法停用DNS Client Service,这招不适用。
https://docs.microsoft.com/en-us/windows/win32/api/icmpapi/nf-icmpapi-icmpsendecho
顺便说一下,ping.exe外发ICMP应该也不是进程自身进行socket通信,底层API应该 是交由其他系统组件完成ICMP外发,试图在wf.msc中阻断ping.exe的ICMPv4报文外发, 达不到预期效果,除非阻断全系统的ICMPv4报文外发。bluerust说某个版本Windows 开始不支持raw socket,我好像有这个印象,但早就不关心这些事,也就模糊了,不 欲深究。
下面这段Python3代码显示Unicode字符串"www.baidu.com"前16字节的长整数形式,2 个长整数,little-endian序:
x = 'www.baidu.com' x = "".join([c+'\0' for c in x]).encode( 'latin-1' ).hex() y = x[:16] ''.join(map(str.add, y[-2::-2], y[-1::-2])) y = x[16:32] ''.join(map(str.add, y[-2::-2], y[-1::-2]))
'002e007700770077' '0064006900610062'
我只会这么矬的办法,不知更好的办法是啥?
cdb.exe -noinh -snul -hd -o ping.exe www.baidu.com
.prompt_allow +reg +ea +dis;rm 0xa
条件断点,试图解析"www.baidu.com"时断下来:
bu RPCRT4!NdrClientCall3 "r $t9=poi(@rsp+0x28);.if(@rdx==4 and @r8==0 and qwo(@$t9)==0x002e007700770077 and qwo(@$t9+8)==0x0064006900610062){du @$t9}.else{gc}"
条件断点可以弄成字符串匹配的,但那样写起来有点复杂,快速演示时就这样吧。
00000273c13026fc "www.baidu.com"
RPCRT4!NdrClientCall3:
00007ff8
477346e0 4c89442418 mov qword ptr [rsp+18h],r8 ss:000000ef4176cc10=000000ef4176d9a0
0:000> kpn
# Child-SP RetAddr Call Site
00 000000ef
4176cbf8 00007ff8452e82e1 RPCRT4!NdrClientCall3
01 000000ef
4176cc00 00007ff8452e7fe3 DNSAPI!SyncResolverQueryRpc+0x171
02 000000ef
4176ce00 00007ff8452ebcb7 DNSAPI!Rpc_ResolverQuery+0xc3
03 000000ef
4176ced0 00007ff8452eb466 DNSAPI!Query_PrivateExW+0x7a7
04 000000ef
4176d700 00007ff8455bb163 DNSAPI!DnsQueryEx+0x166
05 000000ef
4176d970 00007ff8455baf34 mswsock!SaBlob_Query+0xcb
06 000000ef
4176da40 00007ff8455ba60e mswsock!Rnr_DoDnsLookup+0x1ac
07 000000ef
4176dae0 00007ff84792ba88 mswsock!Dns_NSPLookupServiceNext+0x1de
08 000000ef
4176def0 00007ff84792bc9c WS2_32!NSPROVIDER::NSPLookupServiceNext+0x78
09 000000ef
4176dfc0 00007ff84792bbbd WS2_32!NSQUERY::LookupServiceNext+0xa0
0a 000000ef
4176e040 00007ff84792bf7a WS2_32!WSALookupServiceNextW+0xdd
0b 000000ef
4176e090 00007ff84792c316 WS2_32!QueryDnsForFamily+0x1ae
0c 000000ef
4176ea40 00007ff8479234da WS2_32!QueryDns+0x172
0d 000000ef
4176eb00 00007ff847925eac WS2_32!LookupAddressForName+0x122
0e 000000ef
4176ec10 00007ff7bc5e11dc WS2_32!GetAddrInfoW+0x38c
0f 000000ef
4176edb0 00007ff7bc5e1fa3 ping!ResolveTarget+0xe0
10 000000ef
4176ee40 00007ff7bc5e356d ping!wmain+0x447
11 000000ef
4176f960 00007ff8484d7034 ping!__wmainCRTStartup+0x14d
12 000000ef
4176f9a0 00007ff8487a2651 KERNEL32!BaseThreadInitThunk+0x14
13 000000ef
4176f9d0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
上述调用栈回溯已是ping能到达的极限,RPCRT4!NdrClientCall3()是向DNS Client Service发起RPC请求。
下面给出各函数FQDN形参位置:
RPCRT4!NdrClientCall3( rcx, rdx, r8, r9, FQDN, ... )
r rcx,rdx,r8,r9
du poi(@rsp+0x28) // FQDN
DNSAPI!SyncResolverQueryRpc( FQDN, ... )
du @rcx // FQDN
DNSAPI!Rpc_ResolverQuery( rcx, FQDN, ... )
du @rdx // FQDN
DNSAPI!Query_PrivateExW( FQDN, QueryType, ... )
du @rcx // FQDN
// QueryName
@rdx // QueryType
DNSAPI!DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest, PDNS_QUERY_RESULT pQueryResults, PDNS_QUERY_CANCEL pCancelHandle)
du poi(@rcx+8) // FQDN
// pQueryRequest->QueryName
mswsock!SaBlob_Query( FQDN, ... )
du @rcx // FQDN
WS2_32!GetAddrInfoW( FQDN, ... )
du @rcx // FQDN
我是怎么找到前述调用栈回溯的呢?如果已经对DNSAPI.dll很熟,可以关注:
DNSAPI!DnsQueryEx DNSAPI!DnsQuery_A DNSAPI!DnsQuery_W DNSAPI!DnsQuery_UTF8 DNSAPI!DnsQueryExA DNSAPI!DnsQueryExW DNSAPI!DnsQueryExUTF8 DNSAPI!SyncResolverQueryRpc // 同步查询 DNSAPI!AsyncResolverQueryRPC // 异步查询
最初我简单断了一下DNSAPI!DnsQuery_A(),没有命中。
假设之前完全没有积累,不知流程最后会去DNSAPI!SyncResolverQueryRpc(),怎么 摸过来?大概说一下,不保证最优解。
首先IDA看ping.exe,大概找个位置在处理命令行上指定的FQDN。然后cdb调ping.exe, 在前一步找到的位置设断,同时Wireshark抓53/UDP的包。接下来一路pc (Step to Next Call),看哪个call会触发53/UDP通信。假设某个call会触发53/UDP 通信,重新用cdb调ping.exe,直接断在这个call上,t进去,继续pc,继续Wireshark。 重复这个过程,直至DNSAPI!SyncResolverQueryRpc()。有些call可能是第N次经过时 才触发53/UDP通信,这种得把前N-1次命中略过,我碰上的情形N最多等于2。
若碰上通过_guard_dispatch_icall_fptr/ntdll!LdrpDispatchUserCallTarget调用 目标函数,用"u @rax l 1"检查目标函数。ntdll!LdrpDispatchUserCallTarget通过 "jmp rax"转移到目标函数,中间没有call指令,使用pc时如果发现已经离开原函数, 不一定是跑飞了。
cdb.exe -noinh -snul -hd -o ping.exe www.baidu.com
就ping.exe而言,如下断点将阻断所有FQDN解析:
bu DNSAPI!SyncResolverQueryRpc+0x16a "r $t9=poi(@rsp+0x20);du @$t9;ezu @$t9 \".\";gc"
这是RPCRT4!NdrClientCall3()的主调位置,将所有FQDN替换成"."。
Opera开起来后台有十几个同名进程,先在一个标签页中访问www.baidu.com,用 Tcpview通过TCP连接找出对应的PID。有啥别的办法从十几个同名Opera进程中找出实 际进行页面浏览的那个进程?Message Analyzer、Network Monitor、ETW都太重型, 后来找了些快速办法回答此问题。
wmic process where name="opera.exe" get CommandLine,Name,ProcessId | findstr network.mojom.NetworkService
cdb.exe -noinh -snul -hd -o -p 16036 bu DNSAPI!SyncResolverQueryRpc+0x16a "r $t9=poi(@rsp+0x20);du @$t9;ezu @$t9 \".\";gc"
之后Opera无法访问任何FQDN;即使有/etc/hosts加持也不行,对hosts的处理由DNS Client Service进行,前述断点在Opera进程空间。
本文未从工程角度解决实际问题,仅为探索性质。
☆ 后补
1) raw socket
2021-03-07 huyuguang
huyuguang指出,Win10仍然支持raw socket,ping不用的原因也许是不想需要管理员 权限才能运行。关于不支持raw socket的传闻大概来自从某个版本起,限制了源IP不 是本机的raw socket通信。有很多协议仍然需要raw socket,例如pptp(gre)等等。
2021-03-08 scz
huyuguang这个说法合理。我去查了一下自己的历史文档:
《12.3 XP SP2对raw socket所做的改动》
《12.7 去除XP SP2/SP3对raw socket的一些限制》 https://scz.617.cn/windows/200701091750.txt
历史上陆续出现的针对raw socket的限制有:
a) 不能通过raw socket发送TCP报文。做此尝试时会得到10004号错误。 b) 不能通过raw socket发送伪造源IP的UDP报文。 c) 不能通过raw socket发送IP碎片。做此尝试时会得到10004号错误。 d) 不能通过raw socket发送全部IP碎片,只有第一个碎片可被发送出去。试图发送 后续碎片时会得到10004号错误。 e) 不允许用raw socket发送IP-ENCAP、IPv6报文
这是十几年前的事儿,现在的情形可能有变。
2) 取Unicode字符串的长整型值(little-endian序)
2020-03-07 bluerust
Python3
import struct
x = "www.baidu.com" x = bytes( x, encoding='utf-16' )[2:] [hex(struct.unpack( "<Q", x[i:i+8] )[0]) for i in range(0 ,len(x)//8*8, 8)]
['0x2e007700770077', '0x64006900610062', '0x6f0063002e0075']
import hexdump
x = "www.baidu.com" x = bytes( x, encoding='utf-16' )[2:] ['0x' + ''.join(map(str.add, y[-2::-2], y[-1::-2])) for y in hexdump.dump( x, size=16, sep=',' ).split( ',' )]
import hexdump
x = "www.baidu.com" x = bytes( x, encoding='utf-16' )[2:] ['0x' + ''.join(map(str.add, y[-2::-2], y[-1::-2])) for y in hexdump.dump( x, size=16 ).split()]
['0x002E007700770077', '0x0064006900610062', '0x006F0063002E0075', '0x006D']
☆ 用RPC/ALPC调试手段分析Win10 FQDN解析过程
见
https://scz.617.cn/windows/202111161210.txt
☆ RPC Debugging (已不适用于Win10)
见
https://scz.617.cn/windows/202111152036.txt
☆ Win10 FQDN解析时绕过hosts文件
见
https://scz.617.cn/windows/202111181224.txt
☆ Win10 RPC调试中RPC Client PID的利用
见
https://scz.617.cn/windows/202111190952.txt
☆ Windows域名解析保护机制深度分析
见
https://scz.617.cn/windows/202111241129.txt