使用NSP机制解决windows7 dns溯源问题
挂钩(hook)具体的接口函数,比如挂钩(hook)gethostbyname接口等函数。
这些接口函数可不止gethostbyname,还包括 getaddrinfo,
WSALookupServiceBegin,WSALookupServiceNext,
DnsQuery_Xxx
至少我知道的起码有以上这些接口函数,应该还有更多,不过到时可以根据具体程序来查漏补缺。
至于如何hook具体程序的具体这些接口函数,这里不再赘述,反正是能实现的。
有兴趣可以去查阅我讲述的远程桌面系列文章中关于dxhook的相关文章,里边有阐述如何hook接口函数的内容。
除了hook具体的接口函数之外,难道没有别的办法了吗?
自然是有的,windows系统提供了NSP (Name Space Provider) 机制,类似于winsock中的LSP机制。
其实性质也是做具体接口函数的hook。
NSP的大致实现办法:
我们需要开发一个dll动态库,这个动态库必须导出 NSPStartup 接口函数,
而在这个接口函数中,会提供 NSP_ROUTINE 结构的参数,
里边我们必须填写正确对应的回调函数,其中包括三个核心函数:
NSPLookupServiceBegin
NSPLookupServiceNext
NSPLookupServiceEnd
只要dns查询接口函数发起调用,这三个核心函数就会被调用。
于是我们在此回调函数中就能找到dns域名和具体进程的对应关系。
并且把查询dns的请求正确路由到我们的代理中。
但是这里的回调函数调用与我们通常理解的hook又有些不同,
具体下面会讲述,这个也是把dns请求正确路由到我们的代理中的必须考虑的。
而且细心就会发现,系统导出的win32 api函数
WSALookupServiceBegin, WSALookupServiceNext, WSALookupServiceEnd
与上面的三个核心回调函数很像。
确实如此,比如 WSALookupServiceBegin调用的时候,就是查询nsp服务列表,然后调用这些nsp服务的回调函数,
也就是如果我们注册了自己的nsp服务,最终WSALookupServiceBegin 也会调用我们自己NSPLookupServiceBegin回调函数。
而系统的gethotbyname,getaddrinfo等其他函数,内部是通过调用 WSALookupServiceBegin 接口函数实现的。
这样就等于是一环扣一环,最终都会正确路由到nsp的回调函数中。
接着我们在程序中,调用 WSCInstallNameSpace 函数,安装我们的这个dll动态库,
这样我们nsp就能正常工作了。
是不是很简单!
不过也别高兴的太早,让nsp老老实实的为我们服务,也不大容易。
系统中存在一个系统默认的nsp服务,这个服务就是沟通dnscache,发起真正的dns请求nsp服务。
也就是如果不出意外的话,系统默认的nsp都会被调用。
举个例子,有三个NSP服务:
myself(M)我们自己的nsp; other(O)其他NSP; System(S)系统默认的NSP。
现在假设这三个服务的安装顺序是:M->O->S
按照我们通常hook的理解,当某个函数返回成功,就不再继续调用下一链条的函数。
比如 如果M.NSPLookupServiceBegin成功之后,
就不调用 O.NSPLookupServiceBegin 函数和S.NSPLookupServiceBegin 函数。
但实际情况是: 不管成功与否,都会按如下顺序,所有nsp的函数都会调用一遍:
M.NSPLookupServiceBegin -> O.NSPLookupServiceBegin ->S.NSPLookupServiceBegin
同样的,
M.NSPLookupServiceNext -> O.NSPLookupServiceNext -> S.NSPLookupServiceNext
也会跟着这样顺序调用一遍,并且最要命的是,会把 *Next函数的调用结果进行合并:
比如 M查询到地址是 1.1.1.1, S查询到的地址是 2.2.2.2,于是返回的DNS结果就有 1.1.1.1和2.2.2.2两个。
我们实现NSP的目标是完全拦截对dns请求,也就是只要处于设置的规则范围内的域名解析,
都交给我们自己处理,也不能再次通过dnscache缓存,再去查询一遍。
其他nsp我们暂时可以不必去关心,最讨厌的是如何让系统的nsp别调用,或者调用了也让它返回空值。
好在还是有办法解决这个问题:
在NSPLookupServiceBegin 函数中第2个参数,是 WSAQUERYSETW 结构的参数。
WSAQUERYSETW 里边有个参数 dwNameSpace, 这个就是关键所在。
这个dwNameSpace 有多个选项,比如NS_DNS就是dns查询,具体含义可以去msdn查询。
系统的nsp在调用NSPLookupServiceBegin 时候是会检测这个参数的。
如果我们把这个参数改成别的值,那系统nsp就会错误的调用。
最通用的就是设置 dwNameSpace = NS_TCPIP_HOSTS ,
这样让系统默认的nsp只查询 本地 /etc/hosts文件中域名对应关系,自然大部分情况下都查不到。
光这样设置还不够,因为上面说了,NSPLookupServiceBegin 是按照上面的安装顺序来调用的。
如果系统默认nsp在我们的nsp之前调用,其实是没效果的。
而WSCInstallNameSpace 函数安装后,我们的nsp是排到最后的,也就是 O->S->M 这样的顺序。
因此我们还得排个序,调用WSCWriteNameSpaceOrder来排序,排到最前面,变成 M->O->S 这样的顺序。
这样,我们的nsp才算基本上为我们服务。
至于如何截取dns域名和具体进程关系,我们在xFsRedir中如何再次处理dns请求,
这里不再赘述。
有兴趣可以下载 xfsredir使用,最新版本以后会更新上去。
GitHub - fanxiushu/xFsRedir
————————————————
原文链接:https://blog.csdn.net/fanxiushu/article/details/127371222