标题: DNS系列(10)--开启DNS Client Service日志 创建: 2017-05-11 15:19 更新: 2024-01-29 09:07 链接: https://scz.617.cn/windows/201705111519.txt 假设当前OS是64-bits Win7。 至少从2002.11.21开始,有些文章提到DNS Client Service自带一种日志,对应: %systemroot%\system32\dnsrslvr.log Google for dnsrslvr.log,估计能找到一些对它的介绍。我本来对这个没啥兴趣, 因为我常年禁用这个服务。但周大因故问起,我就用IDA逆了一下: %systemroot%\system32\dnsrslvr.dll 为啥逆它呢?因为从注册表里看到: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Dnscache\Parameters ServiceDll REG_EXPAND_SZ %systemroot%\system32\dnsrslvr.dll 好吧,承认装X了,其实我是在Process Explorer中看到相应svchost.exe加载了它。 那么多svchost.exe,怎么快速知道哪个是该服务的宿主?我可不是一个个找过去的, 至少可以这样: tasklist /svc /fi "services eq dnscache" 这是个64-bits进程,加载的是64-bits dll。IDA64虽然用于反汇编64-bits PE,但 其本身是个32-bits PE。假设OS是64-bits Win7,IDA64访问 %systemroot%\system32\dnsrslvr.dll时会报告无此文件。 64-bits OS上32-bits程序会碰上"File System Redirector",比如: %systemroot%\system32 %systemroot%\syswow64 如果自己写程序,可以禁用这种文件系统重定向: Wow64DisableWow64FsRedirection Wow64RevertWow64FsRedirection IDA64是已经存在的PE,用不上这招。有个很矬的办法,因为cmd是64-bits的,从中 copy时不会重定向,可以复制dnsrslvr.dll到其他不涉及重定向的普通目录,比如 x:\tmp\,然后再用IDA64打开。 只是这样真地好吗?我们是谁,我们是非正常人类研究中心的被研究人员,达文西是 我们的同类。说人话: 32-bits程序用%systemroot%\sysnative\可以访问原始的%systemroot%\system32\ TNND,说了半天已经离题万里了,现在回到IDA64逆向dnsrslvr.dll。在Strings中搜 dnsrslvr.log,查看交叉引用,定位如下代码: -------------------------------------------------------------------------- ResolverInitialize ( x ) { ... DnsLogInit( "dnsrsvlr.log", "Logging for DNS resolver service" ); DnsLogIt( "dnsrslvr.log", "DNS Caching Resolver Service - ResolverInitialize" ); ... } -------------------------------------------------------------------------- 顾名思义,初始化日志文件、向日志文件写入日志,一切多么美好。等等,这两个破 函数的第一形参有那么一点点不同: dnsrsvlr.log // 这是哪位大哥手抖了一下? dnsrslvr.log // 这是我们期望的 DnsLogInit()、DnsLogIt()不在dnsrslvr.dll中,而在dnsapi.dll中,继续逆后者。 -------------------------------------------------------------------------- int __fastcall DnsLogInit ( char * logfile ) { ... LoggingMode = 0; LineCount = 0; ... /* * 读写打开 */ logfd = fopen( logfile, "r+" ); if ( logfd ) { /* * 开启日志 */ LoggingMode = 1; fclose( logfd ); ... /* * 追加模式 */ logfd = fopen( logfile, "a" ); if ( logfd ) { /* * 输出日志 */ fprintf( logfd, ... ); fclose( logfd ); } } ... } -------------------------------------------------------------------------- void DnsLogIt ( char * logfile, char *fmtstr, ... ) { ... va_list va; va_start( va, fmtstr ); /* * 这是个全局变量,位于dnsapi.dll中 */ if ( LoggingMode ) { if ( LineCount <= 10000 ) { /* * 追加模式 */ logfd = fopen( logfile, "a" ); } else { /* * 超过10000行,清零重来 */ logfd = fopen( logfile, "w" ); LineCount = 0; } if ( logfd ) { /* * 输出日志 */ fputs( ..., logfd ); LineCount++; fclose( logfd ); } } } -------------------------------------------------------------------------- 对dnsapi.dll中的这两个函数,看完之后比较无语。有人说开启这种日志后性能不好, 那可不咋地,调用一次DnsLogIt()就对应一组fopen/fputs/fclose()。 现在来看重头戏。那个svchost.exe的当前目录是%systemroot%\system32\,因此 DnsLogInit实际在访问: %systemroot%\system32\dnsrsvlr.log 必须fopen(...,"r+")成功,才会将全局变量LoggingMode置1。从XP、2003开始, dnscache服务不再以"NT AUTHORITY\SYSTEM"身份运行,而是以 "NT AUTHORITY\NETWORK SERVICE"身份运行,用Process Explorer很容易确认。 后者这个身份的权限不足: $ cacls.exe %systemroot%\system32 C:\Windows\System32 BUILTIN\Administrators:F BUILTIN\Administrators:(OI)(CI)(IO)F NT AUTHORITY\SYSTEM:F NT AUTHORITY\SYSTEM:(OI)(CI)(IO)F NT AUTHORITY\Authenticated Users:C NT AUTHORITY\Authenticated Users:(OI)(CI)(IO)C BUILTIN\Users:R BUILTIN\Users:(OI)(CI)(IO)(特殊访问:) GENERIC_READ GENERIC_EXECUTE 于是对于现代OS来说,DnsLogInit()中无法读写打开: %systemroot%\system32\dnsrsvlr.log 从而导致全局变量LoggingMode恒为0。解决办法自然是: type nul > %systemroot%\system32\dnsrsvlr.log cacls %systemroot%\system32\dnsrsvlr.log /E /G "NETWORK SERVICE":W 用Administrator提前创建dnsrsvlr.log,让"NETWORK SERVICE"对之有写权限。验证 一下: $ cacls %systemroot%\system32\dnsrsvlr.log C:\Windows\system32\dnsrsvlr.log BUILTIN\Administrators:F NT AUTHORITY\SYSTEM:F NT AUTHORITY\Authenticated Users:C BUILTIN\Users:R NT AUTHORITY\NETWORK SERVICE:(special access:) READ_CONTROL SYNCHRONIZE FILE_GENERIC_WRITE FILE_WRITE_DATA FILE_APPEND_DATA FILE_WRITE_EA FILE_EXECUTE FILE_WRITE_ATTRIBUTES 对付dnsrsvlr.log只是让LoggingMode为1。真正写日志是输出到dnsrslvr.log,同样 需要提前处理dnsrslvr.log: type nul > %systemroot%\system32\dnsrslvr.log cacls %systemroot%\system32\dnsrslvr.log /E /G "NETWORK SERVICE":W 提前处理dnsrsvlr.log、dnsrslvr.log只涉及NTFS的DACLs,保持空文件就足够了, 不需要提前填任何内容。 在dnsrslvr.dll中查看DnsLogInit()的交叉引用,看到两个: ResolverInitialize() Areg_RegisterInit() 前者已经分析过了,后者有如下片段: -------------------------------------------------------------------------- Areg_RegisterInit () { ... DnsLogInit( "asyncreg.log", "Logging for DNS client registration" ); DnsLogIt( "asyncreg.log", "Inside function Areg_RegisterInit" ); ... } -------------------------------------------------------------------------- 这次这位大哥没有手抖,只有一个文件: %systemroot%\system32\asyncreg.log 考虑到DnsLogInit()中一上来就有一句"LoggingMode = 0",我不确认是否存在竞争 环境。想像这样一种情况,ResolverInitialize()成功,LoggingMode置1, Areg_RegisterInit()因为asyncreg.log的权限问题而失败,LoggingMode清零。因此 保险起见,也提前处理一下asyncreg.log好了: type nul > %systemroot%\system32\asyncreg.log cacls %systemroot%\system32\asyncreg.log /E /G "NETWORK SERVICE":W 测试时碰上过dnsrsvlr.log、dnsrslvr.log被写入初始化日志,然后就没有下文了, 再怎么刺激它,都没有新的日志被写入。用64-bits cdb调试它: $ tasklist /svc /fi "services eq dnscache" $ "C:\Program Files\Windows Kits\8.0\Debuggers\x64\cdb.exe" -noinh -snul -hd -o -p > dd dnsapi!LoggingMode l 1 发现不知何故LoggingMode为0,在cdb中手工将之置1即可达到预期效果: > ed dnsapi!LoggingMode 1 当然,前提是相应文件权限已经调整到位。这种现象只出现过一次,再未复现,记录 于此备忘。 在dnsapi.dll中检查LoggingMode的交叉引用,只在DnsLogInit()中有机会将之置1。 简单看一下日志长什么样: dnsrslvr.log -------------------------------------------------------------------------- DNS_INFO_NO_RECORDSDNS Caching Resolver Service - R_ResolverQuery Arguments: Name: www.nsfocus.net Type: 1 Flags: 0x40006000 R_ResolverQuery - Returning status: 0x000025E5 DNS_ERROR_RECORD_DOES_NOT_EXISTDNS Caching Resolver Service - R_ResolverQuery Arguments: Name: www.nsfocus.net Type: 28 Flags: 0x40006000 R_ResolverQuery - Returning status: 0x00000000 -------------------------------------------------------------------------- asyncreg.log -------------------------------------------------------------------------- Inside function Areg_RegisterInitDNS client registrationSystem Time Information -------------------------------------------------------------------------- R_ResolverQuery()是dnsrslvr.dll中的函数,里面大量调用DnsLogIt()。 这种日志开销太大,不推荐。ETW机制有DNS日志支持,有兴趣者放狗搜: Microsoft-Windows-DNS-Client {1C95126E-7EEA-49A9-A3FE-A378B03DDB4D} 至此技术细节都已到位,来个小结: -------------------------------------------------------------------------- Q: 如何开启DNS Client Service日志 A: 以Administrator权限执行 net stop dnscache type nul > %systemroot%\system32\dnsrsvlr.log type nul > %systemroot%\system32\dnsrslvr.log type nul > %systemroot%\system32\asyncreg.log cacls %systemroot%\system32\dnsrsvlr.log /E /G "NETWORK SERVICE":W cacls %systemroot%\system32\dnsrslvr.log /E /G "NETWORK SERVICE":W cacls %systemroot%\system32\asyncreg.log /E /G "NETWORK SERVICE":W net start dnscache -------------------------------------------------------------------------- Q: 假设已经启用这种日志,如何重新禁用 A: 以Administrator权限执行 net stop dnscache del %systemroot%\system32\dnsrsvlr.log del %systemroot%\system32\dnsrslvr.log del %systemroot%\system32\asyncreg.log net start dnscache --------------------------------------------------------------------------