标题: MSDN系列(41)--调试Windows服务 创建: 2021-11-29 11:59 更新: 2023-07-20 09:52 链接: https://scz.617.cn/windows/202111291159.txt -------------------------------------------------------------------------- 目录: ☆ 调试Windows服务知识点汇总 1) 官方文档 2) Debugging Server模式 2.1) Process Servers (User Mode) 2.2) Debugging Server 2.3) ServerTransport/ClientTransport 2.4) -noio 2.5) -noshell 2.6) IcfEnable (PFW放行) 2.7) 调试示例 2.7.1) 勿在初始化断点处设置硬件断点 2.7.2) Detach 3) ServicesPipeTimeout 3.1) 热Patch让ServicesPipeTimeout生效 4) IFEO (Image File Execution Options) 4.1) cdb vs ntsd 4.2) NT AUTHORITY\SYSTEM 4.2.1) -noshell 4.2.2) _NT_SYMBOL_PATH环境变量 4.2.3) \\vmware-host\Shared Folders 4.3) .sympath/-y 4.4) 用gflags图形界面设置IFEO 4.4.1) 用gflags命令行设置IFEO (不推荐) 4.5) 可以不用IFEO 5) 远程调试 5.1) 手工启动待调试服务 5.2) 调试客户端接入 5.3) 无法完美退出远程调试 6) Isolating the Service 6.1) Moving the Service to its Own Group (推荐) 6.2) Changing the Service Type 6.3) Duplicating the SvcHost Binary 6.4) 杂议 7) Windows 2000比较特殊 8) 服务名与PID双向查找 ☆ 参考资源 -------------------------------------------------------------------------- ☆ 调试Windows服务知识点汇总 若服务源码是自己开发的,可在被调试代码逻辑中主动调用DebugBreak(),以此呼叫 "Just-In-Time Debugging";也可通过命令行参数让被调试代码逻辑以普通控制台进 程方式运行,这种没法调试SCM相关的代码。 一般调试Windows服务,并非服务源码可控的情形。通常分两种情况,一种是被调试 代码逻辑可以在Attach之后触发,一种是被调试代码逻辑只在服务启动时触发。第一 种情况和普通调试一样,第二种情况相对复杂些,要做些特别设置。以前没有过第二 种需求,只知道大概思路,未实践过。最近碰上,实践一番,有不少琐碎的知识点, 在此记录一二。 在Win10企业版2016 LTSB 1607(OS Build 14393.4704)上测试。 1) 官方文档 windbg帮助(debugger.chm) Debugging Techniques Specialized Debugging Techniques Debugging a Service Application Choosing the Best Method Preparing to Debug the Service Application Debugger Operation Remote Debugging Remote Debugging Through the Debugger Activating a Debugging Server Activating a Debugging Client Controlling a Remote Debugging Session 官方文档是最好的,看过后就差不多了,不过我意识到这点有些晚。 2) Debugging Server模式 为了调试服务启动阶段,需要用到"Debugging Server" 2.1) Process Servers (User Mode) 在Guest中以管理员身份运行 dbgsrv.exe -t tcp:port=8765,password=8765 在Host中 cdb.exe -noinh -snul -hd -o -premote tcp:server=192.168.65.136,port=8765,password=8765 -pn lsass.exe 这种组合是"Process Servers (User Mode)",不是"Debugging Server",容易搞混 的两种术语。 2.2) Debugging Server 在Guest中 cdb.exe -server tcp:port=8766,password=8766 -noinh -snul -hd -o -G notepad.exe 在Host中 cdb.exe -remote tcp:server=192.168.65.136,port=8766,password=8766 -noinh -snul -hd 这种组合是"Debugging Server"。 调试客户端用的是-remote,不是-premote。 调试服务端-server、调试客户端-remote必须是各自命令的第一个参数。调试客户端 不能指定-p、-pn,完全由调试服务端决定调试目标。 调试客户端指定"-noinh -snul -hd"可能并无意义?反正调试客户端指定不了"-o"。 一个调试服务端可以对应多个调试客户端,比如在Host中开第二个cdb接入调试服务 端。假设现在有一个调试服务端、两个调试客户端,这三端都可以输入调试命令,调 试命令产生的输出会同时出现在三端。"Debugging Server"这种搞法允许多人同时协 作调试同一个目标进程,有点意思,这与dbgsrv模式完全不同。 -------------------------------------------------------------------------- .servers 显示接入的调试服务端 .clients 显示接入的所有调试客户端 .endsrv 0 让调试服务端不再侦听8766/TCP。这个有延迟,在调试服务端用Process Explorer 查看cdb,发现8766/TCP仍在LISTEN状态,实际上调试服务端不再接受新的入连 接。新开调试客户端尝试接入,失败,再去看调试服务端,不再侦听8766/TCP。 不影响已接入的所有调试客户端,已有TCP连接均保持。 .shell whoami 无论在哪端使用.shell,真正执行的都是调试服务端的shell,一般是cmd .noshell 关闭调试服务端对.shell的支持。没有反命令,一旦生效,将保持到调试服务端 终止。 !envvar _NT_SYMBOL_PATH 可用!envvar查看被调试进程环境变量。但该命令依赖ntdll!_PEB,要求 ntdll.pdb就位。若.sympath有误,找不到ntdll.pdb,!envvar报错。 -------------------------------------------------------------------------- 2.3) ServerTransport/ClientTransport -server ServerTransport -remote ClientTransport 关于ServerTransport/ClientTransport的语法,参看windbg帮助。 "npipe:pipe"要求LanmanServer服务启动中,涉及SMB认证,无谓地引入复杂性。像 我,LanmanServer服务常年禁用中。若调试服务端、调试客户端都在本机,且 LanmanServer服务启动中,用"npipe:pipe"也行 cdb.exe -server npipe:pipe=anyname,password=8766 -noinh -snul -hd -o -G notepad.exe cdb.exe -remote npipe:server=localhost,pipe=anyname,password=8766 -noinh -snul -hd C/S不在同一主机时,并不推荐"npipe:pipe"。 2.4) -noio 调试服务端指定-noio后,调试服务端本身不接受调试命令的输入,也不同步显示调 试命令产生的输出,无法Ctrl-C终止调试服务端。调试Windows服务时,建议始终指 定。 2.5) -noshell 调试服务端指定-noshell后,一上来就关闭调试服务端对.shell的支持。调试Windows 服务时,建议始终指定。 2.6) IcfEnable (PFW放行) Win10有PFW,cdb首次侦听8766/TCP时会弹框提示是否允许入连接,必须选"允许", 让相应规则进入wf.msc。 若通过IFEO间接启动cdb,并且是cdb首次侦听8766/TCP,情况就有些微妙了。假设 wf.msc中无相应放行规则,按理要弹框选择的。但若IFEO的原始进程在Session 0中 启动,cdb导致的弹框也在Session 0中,你看不到,没法选,8766/TCP被阻断中。 为解决上述问题,可提前增设PFW规则,避免弹框提示。在Session 1中用cdb触发弹 框,选"允许",这是一种办法。另一种办法是在管理员级cmd中执行 cdb.exe -server tcp:port=8766,password=8766,icfenable -noinh -snul -hd -o -G notepad.exe -server中指定了IcfEnable,大小写不敏感。这条命令不会触发弹框,直接在wf.msc 中增设名为"Debugger RPC Port Mapping"的8766/TCP放行规则。 $ netsh advfirewall firewall show rule name="Debugger RPC Port Mapping" Rule Name: Debugger RPC Port Mapping ---------------------------------------------------------------------- Enabled: Yes Direction: In Profiles: Domain,Private,Public Grouping: LocalIP: Any RemoteIP: Any Protocol: TCP LocalPort: 8766 RemotePort: Any Edge traversal: No Action: Allow Ok. IcfEnable添加到wf.msc的放行规则不会因调试终止而自动删除,始终存在,只能手 工删除。只有在管理员级或SYSTEM级别时,指定IcfEnable才有效,否则即使指定 IcfEnable,仍将弹框提示。 windbg帮助里说,IcfEnable用于"npipe:pipe"时会放行139、445/TCP,未实测确认。 2.7) 调试示例 在Guest中以管理员身份运行 cdb.exe -server tcp:port=8766,password=8766,icfenable -noshell -noio -noinh -snul -hd -o -G notepad.exe 在Host中 cdb.exe -remote tcp:server=192.168.65.136,port=8766,password=8766 -noinh -snul -hd 调试客户端接入后,发现调试服务端断在ibp(初始化断点) # Child-SP RetAddr Call Site 00 000000c7`6437f4b0 00007fff`88d83268 ntdll!LdrpDoDebuggerBreak+0x30 01 000000c7`6437f4f0 00007fff`88d68145 ntdll!LdrpInitializeProcess+0x1be4 02 000000c7`6437f900 00007fff`88d67fae ntdll!LdrpInitialize+0x141 03 000000c7`6437f980 00000000`00000000 ntdll!LdrInitializeThunk+0xe .prompt_allow +reg +ea +dis bp /1 @$exentry "kpn" 2.7.1) 勿在初始化断点处设置硬件断点 在ibp用ba设置硬件断点,一般会失败报错 > ba e1 /1 @$exentry "kpn" ^ Unable to set breakpoint error The system resets thread contexts after the process breakpoint so hardware breakpoints cannot be set. Go to the executable's entry point and set it then. 尽量避免在ibp对任何地址设置硬件断点,因为后面会过ZwContinue,该函数会切换 CONTEXT,必然导致DR*寄存器被修改。理论上在ibp处不是不能设硬件断点,而是设 了之后,很快就会被破坏。为了避免将来这种破坏引起误会,cdb干脆禁止在ibp处使 用ba设置硬件断点,如果尝试ba命令,会提示到了@$exentry之后才可以设硬件断点。 其实在@$exentry之前很多地方都可以正常使用ba设置硬件断点,比如"sxe cpr"、 "sxe ld:ntdll"命中时、流程到达ntdll!RtlUserThreadStart时,这几处都可以ba。 反调试手段之一就是拦截ZwContinue,修改DR*、TF等。 假设前面那个测试环境是A环境,现在换到另一个B环境。在B环境中,在ibp用ba设置 硬件断点,没有失败报错,bl可以看到硬件断点,当然,后来还是被ZwContinue破坏 而失效。B环境发生的事很不友好,我在B环境中被坑了一把,当时忘了ibp处设置硬 件断点的坑,发现ba断不下来,还奇怪呢,云海提醒之后才重新想起缘由。 A、B环境都是Win10企业版2016 LTSB 1607。A打过2021.10补丁,B打过2021.11补丁, ntoskrnl.exe不同,但具体到notepad.exe、ntdll.dll这两个模块,并无差别。A环 境有kd接入,B环境未进入"Test Mode"。A、B所用cdb是同一版本。 云海在更早期的其他环境中测试,重现B环境出现的现象,看上去与最近这些补丁无 关。 后来调试cdb本身,解释了为什么A、B有差异,参看 《MSDN系列(42)--windbg禁止在ibp处设置硬件断点》 https://scz.617.cn/windows/202112030950.txt 要点是,Win10并行加载机制和CPU/Core数目共同作用,导致停留在ibp时线程数目不 同,只存在主线程时ibp+ba保护措施才会生效。 2.7.2) Detach 推荐流程 .endsrv 0 // 调试服务端不再侦听8766/TCP .detach // 让被调试进程脱离调试后运行 qqd // 离开调试客户端操作界面 这样干之后,调试服务端cdb终止,notepad保持运行。就notepad调试示例而言,直 接qqd就可以了,不需要前两步。 3) ServicesPipeTimeout 参[2] Windows的SCM(Service Control Manager)启动某个服务时缺省等待30秒,超时则认 为启动失败,会有其他动作,比如杀掉目标进程重启服务。假设需要调试服务启动阶 段代码,断点命中后的交互式调试很容易导致服务启动阶段超时被杀,可能上一步还 在kpn,下一步发现目标进程不在了。这很影响调试,幸好有注册表设置这个超时。 -------------------------------------------------------------------------- Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control] "ServicesPipeTimeout"=dword:15752a00 -------------------------------------------------------------------------- reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control" /v "ServicesPipeTimeout" /t REG_DWORD /d 0x15752a00 /f reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control" /v "ServicesPipeTimeout" reg.exe delete "HKLM\SYSTEM\CurrentControlSet\Control" /v "ServicesPipeTimeout" /f 0x15752a00是360000000,单位是毫秒,换算过来就是100小时。需要重启OS使之生效。 该值缺省30000,即30秒。 3.1) 热Patch让ServicesPipeTimeout生效 碰上一个场景,Guest接有kd,但未提前设置过ServicesPipeTimeout,因故不方便重 启OS,想找个热Patch方案让ServicesPipeTimeout生效。此时确实有热Patch方案, 简介如下 kd> !process 0 0 services.exe PROCESS ffffda884002c480 SessionId: 0 Cid: 030c Peb: 13e8b6c000 ParentCid: 02ac DirBase: 2198e3000 ObjectTable: ffffa08d0c9a0e80 HandleCount: Image: services.exe .process /i ffffda884002c480;g !process -1 0 .reload /f /user bp /p @$proc nt!NtCreateUserProcess 回到Guest,在管理员级cmd中执行 sc start spooler 前述内核态断点命中 Patch ed services!g_dwScControlMessageTimeout 0n360000000 ed services!g_dwHandlerTimeout 0n360000000 eb services!g_fDefaultControlMessageTimeout 0 UnPatch ed services!g_dwScControlMessageTimeout 0n30000 ed services!g_dwHandlerTimeout 0n30000 eb services!g_fDefaultControlMessageTimeout 1 禁用断点,继续执行 bd * g Win10无法用cdb调试services.exe,涉及PPL保护,回头单写一下此事。 4) IFEO (Image File Execution Options) -------------------------------------------------------------------------- Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\spoolsv.exe] "Debugger"="C:\\temp\\cdb.exe -server tcp:port=8766,password=8766,icfenable -noshell -noio -noinh -snul -hd -o -G -y \"srv*\\\\vmware-host\\Shared Folders\\sym*http://msdl.microsoft.com/download/symbols\"" -------------------------------------------------------------------------- reg.exe add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\spoolsv.exe" /v "Debugger" /t REG_SZ /d "C:\temp\cdb.exe -server tcp:port=8766,password=8766,icfenable -noshell -noio -noinh -snul -hd -o -G -y \"srv*\\vmware-host\Shared Folders\sym*http://msdl.microsoft.com/download/symbols\"" /f reg.exe query "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\spoolsv.exe" /v "Debugger" reg.exe delete "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\spoolsv.exe" /v "Debugger" /f 有几点微妙之处,后面逐一讨论。 4.1) cdb vs ntsd windbg帮助里有 The only difference between NTSD and CDB is that NTSD spawns a new console window while CDB inherits the window from which it was invoked. Since the start command can also be used to spawn a new console window, the following two constructions will give the same results start cdb [parameters] ntsd [parameters] 用ntsd的话,会额外产生一个conhost.exe进程。调试Windows服务时还是用cdb好了。 4.2) NT AUTHORITY\SYSTEM spoolsv.exe是以"NT AUTHORITY\SYSTEM"身份启动的,通过IFEO启动cdb(或ntsd), cdb也是以SYSTEM身份执行,将涉及几个小问题。 4.2.1) -noshell 若IFEO中未指定-noshell,客户端cdb远程接入后可以用这类命令 .shell whoami .shell echo %_NT_SYMBOL_PATH% 这是SYSTEM级别的shell。 4.2.2) _NT_SYMBOL_PATH环境变量 若_NT_SYMBOL_PATH不在系统级环境变量中,只在用户级环境变量中,SYSTEM账户就 没有_NT_SYMBOL_PATH环境变量,可用.shell或!envvar检查之。 4.2.3) \\vmware-host\Shared Folders SYSTEM账户可以访问"\\vmware-host\Shared Folders\sym",但不能访问 [net use Z: "\\vmware-host\Shared Folders"]映射的Z盘,SYSTEM账户并未映射过 盘符。可能SYSTEM账户干脆无法"net use"? 4.3) .sympath/-y 为使用符号,保险起见,可在IFEO中用-y指定符号路径。即使未在IFEO中使用-y,将 来总是可以用.sympath设置符号路径 .sympath srv*\\vmware-host\Shared Folders\sym*http://msdl.microsoft.com/download/symbols 用.sympath时不要用双引号,用-y时要用双引号,注意转义。 4.4) 用gflags图形界面设置IFEO 前面直接操作注册表设置IFEO,也可以用windbg自带的gflags设置IFEO,参[3]、[6]、 [7]。 a) 执行gflags b) 切到"Image File"页 c) 在Image中输入spoolsv.exe,按TAB键 d) 勾中Debugger,输入 C:\temp\cdb.exe -server tcp:port=8766,password=8766,icfenable -noshell -noio -noinh -snul -hd -o -G -y "srv*\\vmware-host\Shared Folders\sym*http://msdl.microsoft.com/download/symbols" 4.4.1) 用gflags命令行设置IFEO (不推荐) 参[8],有人琢磨出用gflags命令行设置IFEO的办法 gflags.exe /p /enable spoolsv.exe /debug "C:\temp\cdb.exe -server tcp:port=8766,password=8766,icfenable -noshell -noio -noinh -snul -hd -o -G -y \"srv*\\vmware-host\Shared Folders\sym*http://msdl.microsoft.com/download/symbols\"" 但上述gflags命令会额外设置几个注册表项,比如GlobalFlag、PageHeapFlags -------------------------------------------------------------------------- Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\spoolsv.exe] "Debugger"="C:\\temp\\cdb.exe -server tcp:port=8766,password=8766,icfenable -noshell -noio -noinh -snul -hd -o -G -y \"srv*\\\\vmware-host\\Shared Folders\\sym*http://msdl.microsoft.com/download/symbols\"" "GlobalFlag"="0x02000000" "PageHeapFlags"="0x2" -------------------------------------------------------------------------- reg.exe query "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\spoolsv.exe" /v "Debugger" 下列gflags命令会同时删除上述三个注册表项 gflags.exe /p /disable spoolsv.exe 我们并不想设置GlobalFlag、PageHeapFlags,未找到"干净"的gflags命令行参数。 只能用gflags的图形界面进行"干净"的IFEO设置。 4.5) 可以不用IFEO 参[9],Larry Osterman的独门技巧。 之前我以为自己对《调试Windows服务》完全清楚了,但LO在此展示了一项我从未想 过的技术 sc config binpath= "C:\temp\ntsd.exe -d " -d这个无所谓,可以换用-server技术。我没想过直接改binpath也是可以的,总是思 维定势到IFEO去了。 修改binpath可以调试服务启动阶段的代码。起初我以为这样干会导致SCM无法获取服 务当前状态,比如服务实际上已经启动成功,但SCM认为服务还在启动中。但实测发 现一切如常。 5) 远程调试 5.1) 手工启动待调试服务 在Guest中以管理员身份运行 sc qc spooler sc query spooler sc queryex spooler sc config spooler start= demand sc stop spooler sc start spooler sc启动spooler服务时,由于IFEO,实际执行的是 C:\temp\cdb.exe -server tcp:port=8766,password=8766,icfenable -noshell -noio -noinh -snul -hd -o -G -y "srv*\\vmware-host\Shared Folders\sym*http://msdl.microsoft.com/download/symbols" spoolsv.exe 这将进入Debugging Server模式,cdb位于Session 0,不可见,等待调试客户端接入。 由于未指定-g,cdb将停在ibp。 5.2) 调试客户端接入 cdb.exe -remote tcp:server=192.168.65.136,port=8766,password=8766 -noinh -snul -hd # Child-SP RetAddr Call Site 00 00000000`0103f110 00007fff`88d83268 ntdll!LdrpDoDebuggerBreak+0x30 01 00000000`0103f150 00007fff`88d68145 ntdll!LdrpInitializeProcess+0x1be4 02 00000000`0103f560 00007fff`88d67fae ntdll!LdrpInitialize+0x141 03 00000000`0103f5e0 00000000`00000000 ntdll!LdrInitializeThunk+0xe .prompt_allow +reg +ea +dis bp /1 @$exentry "kpn" bp sechost!StartServiceCtrlDispatcherW "kpn" 勿在ibp处ba设断,就用普通的bp设断 # Child-SP RetAddr Call Site 00 00000000`0087f818 00007ff7`ed6c8143 sechost!StartServiceCtrlDispatcherW 01 00000000`0087f820 00007ff7`ed6cdded spoolsv!main+0x93 02 00000000`0087f850 00007fff`885b84d4 spoolsv!__mainCRTStartup+0x14d 03 00000000`0087f890 00007fff`88d41791 KERNEL32!BaseThreadInitThunk+0x14 04 00000000`0087f8c0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 5.3) 无法完美退出远程调试 推荐流程 .endsrv 0 // 调试服务端不再侦听8766/TCP .detach // 让被调试进程脱离调试后运行 Ctrl-B 回车 // 离开调试客户端操作界面 这样干之后,被调试进程正常跑,但服务端cdb不会结束,也在那儿,只是不能再调 试目标进程。 与前面notepad调试示例不同,用IFEO调试spoolsv.exe时,如下操作序列会出幺蛾子 .endsrv 0 .detach qqd 这样干之后,服务端cdb与spoolsv.exe都彻底终止。给服务端cdb指定-pd,现象依旧; 当然,-pd实际就是.detach。是我哪里用得不对? 不知造成这种差异的原因是啥,与spoolsv.exe是服务进程相关,还是与IFEO相关? 6) Isolating the Service 打印服务本来就在单独的spoolsv.exe进程,但很多其他服务共用一个svchost.exe,比如 $ tasklist /svc /fi "services eq dnscache" Image Name PID Services ========================= ======== ============================================ svchost.exe 964 CryptSvc, Dnscache, LanmanWorkstation, NlaSvc, TermService $ sc qc dnscache SERVICE_NAME: dnscache TYPE : 20 WIN32_SHARE_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Windows\system32\svchost.exe -k NetworkService LOAD_ORDER_GROUP : TDI TAG : 0 DISPLAY_NAME : DNS Client DEPENDENCIES : Tdx : nsi SERVICE_START_NAME : NT AUTHORITY\NetworkService 将Dnscache服务隔离出来,单独使用一个svchost.exe,不与其他服务共用,是比较 稳妥的选择;好处在于,调试目标服务时不影响其他服务。官方文档提出三种隔离方 案。 6.1) Moving the Service to its Own Group (推荐) 关注"svchost.exe -k"的参数,这是原始注册表设置 -------------------------------------------------------------------------- Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SvcHost] "NetworkService"=hex(7):43,00,72,00,79,00,70,00,74,00,53,00,76,00,63,00,00,00,\ 6e,00,6c,00,61,00,73,00,76,00,63,00,00,00,6c,00,61,00,6e,00,6d,00,61,00,6e,\ 00,77,00,6f,00,72,00,6b,00,73,00,74,00,61,00,74,00,69,00,6f,00,6e,00,00,00,\ 57,00,69,00,6e,00,52,00,4d,00,00,00,57,00,45,00,43,00,53,00,56,00,43,00,00,\ 00,4d,00,61,00,70,00,73,00,42,00,72,00,6f,00,6b,00,65,00,72,00,00,00,44,00,\ 4e,00,53,00,43,00,61,00,63,00,68,00,65,00,00,00,44,00,48,00,43,00,50,00,00,\ 00,54,00,65,00,72,00,6d,00,53,00,65,00,72,00,76,00,69,00,63,00,65,00,00,00,\ 54,00,61,00,70,00,69,00,73,00,72,00,76,00,00,00,00,00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SvcHost\NetworkService] "CoInitializeSecurityParam"=dword:00000001 "DefaultRpcStackSize"=dword:0000001c -------------------------------------------------------------------------- SvcHost下有名为NetworkService的键值,类型是REG_MULTI_SZ,内容是 CryptSvc nlasvc lanmanworkstation WinRM WECSVC MapsBroker DNSCache DHCP TermService Tapisrv 这与前面tasklist的输出相符。SvcHost下还有名为NetworkService的子键。 这是改动后的注册表设置 -------------------------------------------------------------------------- Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SvcHost] "NetworkService"=hex(7):43,00,72,00,79,00,70,00,74,00,53,00,76,00,63,00,00,00,\ 6e,00,6c,00,61,00,73,00,76,00,63,00,00,00,6c,00,61,00,6e,00,6d,00,61,00,6e,\ 00,77,00,6f,00,72,00,6b,00,73,00,74,00,61,00,74,00,69,00,6f,00,6e,00,00,00,\ 57,00,69,00,6e,00,52,00,4d,00,00,00,57,00,45,00,43,00,53,00,56,00,43,00,00,\ 00,4d,00,61,00,70,00,73,00,42,00,72,00,6f,00,6b,00,65,00,72,00,00,00,44,00,\ 48,00,43,00,50,00,00,00,54,00,65,00,72,00,6d,00,53,00,65,00,72,00,76,00,69,\ 00,63,00,65,00,00,00,54,00,61,00,70,00,69,00,73,00,72,00,76,00,00,00,00,00 "TempGrp"=hex(7):44,00,4e,00,53,00,43,00,61,00,63,00,68,00,65,00,00,00,00,00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SvcHost\NetworkService] "CoInitializeSecurityParam"=dword:00000001 "DefaultRpcStackSize"=dword:0000001c [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SvcHost\TempGrp] "CoInitializeSecurityParam"=dword:00000001 "DefaultRpcStackSize"=dword:0000001c -------------------------------------------------------------------------- 根据NetworkService组的设置仿造一个新组TempGrp,从前者删除DNSCache,在后者 添加DNSCache,其余设置要保持二者间完全一样。 SvcHost下有名为TempGrp的键值,类型是REG_MULTI_SZ,内容是 DNSCache SvcHost下还有名为TempGrp的子键。 改完注册表后,执行 sc config dnscache binpath= "C:\Windows\system32\svchost.exe -k TempGrp" sc qc dnscache 重启OS使之生效。若充分理解上下文及各种边际效应,某些情况下可以只重启服务使 之生效,Dnscache服务正是如此。 sc stop dnscache && sc start dnscache sc queryex dnscache tasklist /svc /fi "services eq dnscache" Dnscache服务已经独占一个svchost.exe。 还原时,在NetworkService组中恢复DNSCache,删除TempGrp键值、子键,最后执行 sc config dnscache binpath= "C:\Windows\system32\svchost.exe -k NetworkService" sc qc dnscache 这是官方推荐套路。 6.2) Changing the Service Type 可用如下命令让Dnscache服务独占一个svchost.exe sc config dnscache type= own sc stop dnscache && sc start dnscache sc queryex dnscache tasklist /svc /fi "services eq dnscache" 外在效果同TempGrp方案。但官方文档里说这样干会改变服务行为,并不推荐。个人 觉得不妨一试,毕竟最简捷。type可选值有 type= 恢复操作 sc config dnscache type= share 6.3) Duplicating the SvcHost Binary sc stop dnscache copy C:\Windows\system32\svchost.exe X:\temp\svchost2.exe sc config dnscache binpath= "X:\temp\svchost2.exe -k NetworkService" sc qc dnscache sc start dnscache sc queryex dnscache tasklist /svc /fi "services eq dnscache" 这种方案太直白了,无需解释。但官方文档里说这样干会改变服务行为,并不推荐。 恢复操作 sc stop dnscache sc config dnscache binpath= "C:\Windows\system32\svchost.exe -k NetworkService" sc qc dnscache sc start dnscache sc queryex dnscache tasklist /svc /fi "services eq dnscache" 6.4) 杂议 前述三种服务隔离方案任选其一即可,官方推荐第一种,我觉得第二种挺好的。[5] 的作者同时用到官方并不推荐的后两种服务隔离方案。若不涉及IFEO,只用第二种 方案即可;涉及IFEO的话,像svchost.exe这种,必须同时使用第二、三种方案。 7) Windows 2000比较特殊 前面说的是Win10,Windows 2000比较特殊,参[4]。 When we connect through Terminal Services, we are in a different Window Station than the services, so we can't attach to their processes. On Windows 2003, we can cross the barrier between WinStations. [4]演示了一些特别技巧处理此问题,包括remote.exe的使用。 8) 服务名与PID双向查找 已知服务名,查找其对应PID,一般用Process Explorer,也有命令行方案: tasklist /svc /fi "services eq dnscache" sc queryex dnscache | findstr PID 都是系统自带工具。还可以 "X:\\Windows Kits\10\x64\Debuggers\x64\tlist.exe" -s | findstr Svcs | findstr Dnscache 这是windbg带的工具。 已知PID,查找其对应服务名: tasklist /svc /fi "pid eq " "X:\\Windows Kits\10\x64\Debuggers\x64\tlist.exe" -s | findstr Svcs | findstr ☆ 参考资源 [1] Debugging a Service - [2021-01-08] https://support.microsoft.com/kb/824344/en-us https://docs.microsoft.com/en-US/windows/win32/services/debugging-a-service DebugBreak function (debugapi.h) https://docs.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-debugbreak Debugging a Service Application - [2020-12-08] https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-a-service-application (就是windbg帮助) [2] A slow service does not start due to time-out error in Windows - [2021-09-24] https://support.microsoft.com/kb/922918/en-us https://docs.microsoft.com/en-US/troubleshoot/windows-server/system-management-components/service-not-start-events-7000-7011-time-out-error [3] Running a Program in a Debugger - [2021-06-17] https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/running-a-program-in-a-debugger (有gflags的图示) [4] How to debug Windows services with Windbg - Alex (Alejandro Campos Magencio) [2008-08-19] https://docs.microsoft.com/en-us/archive/blogs/alejacma/how-to-debug-windows-services-with-windbg https://www.sysadmins.lv/retired-msft-blogs/alejacma/how-to-debug-windows-services-with-windbg.aspx (现存最早的有效示例,remote.exe示例) [5] HOWTO Debug a Windows Service Using windbg - sam [2010-08-03] https://samscode.blogspot.com/2010/08/howto-debug-windows-service-using.html (-server npipe:pipe=service_name_debug) (sc config service_name type= own) [6] How to debug a Windows service - Julien Crozon [2010-10-14] https://bugslasher.net/2010/10/14/how-to-debug-a-windows-service/ (有gflags的图示,提到调试services.exe创建的子进程) How to debug a process as soon as it starts with WinDbg or Visual Studio 2010 - Julien Crozon [2011-03-26] https://bugslasher.net/2011/03/26/how-to-debug-a-process-as-soon-as-it-starts-with-windbg-or-visual-studio-2010/ [7] Debugging Windows services - Vetle kland [2019-01-25] https://bordplate.no/blog/en/post/debugging-a-windows-service/ (有gflags的图示,作者未处理ServicesPipeTimeout) [8] Debugging a Windows Service - Marc Durdin [2020-09-17] https://marc.durdin.net/2020/09/debugging-a-windows-service/ (有gflags的命令行) [9] Useful service tricks Debugging service startup - Larry Osterman [2006-03-01] https://docs.microsoft.com/en-us/archive/blogs/larryosterman/useful-service-tricks-debugging-service-startup (LO的独门技巧)