我在Windows内部进行一些摸索,以了解一般情况,并且试图了解Image File Execution Options背后的机制。具体来说,我为calc.exe设置了一个调试器条目,其中"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -NoExit -Command "& { start-process -filepath $args[0] -argumentlist $args[1..($args.Length - 1)] -nonewwindow -wait}"
为有效负载。这导致递归,启动了许多powershell实例,考虑到我正在拦截他们对calc.exe
的调用,这是有道理的。
但是,这引出了一个问题:普通调试器如何在不引起这种递归行为的情况下启动被测程序?
答案 0 :(得分:2)
真的,没有参与者吗?
无论如何,这是Windows内部的一个很好的问题,但是它之所以引起我的兴趣,是因为它已成为我的一个实际问题。我负责的工作是三台计算机,每台计算机具有不同的Windows版本,甚至还有不同的调试器,使用IFEO技巧进行调试的调试器本身将被调试,显然陷入了困扰OP的同一个循环中。
调试器通常如何避免这种循环性?好吧,他们自己没有。 Windows为他们避免使用它。
但是首先让我们看一下圆度。 PowerShell混合几乎无法帮助进行简单的演示,而calc.exe不再是以前的样子。而是将notepad.exe的调试器值设置为“ c:\ windows \ system32 \ cmd.exe / k”(不带引号)。 Windows会将其解释为意味着尝试运行notepad.exe通常应运行“ c:\ windows \ system32 \ cmd.exe / k notepad.exe”。 CMD会将其解释为运行notepad.exe并保持运行状态。但是notepad.exe的执行也将变成“ c:\ windows \ system32 \ cmd.exe / k notepad.exe”,依此类推。任务管理器将很快向您显示数百个cmd.exe实例。 (好消息是它们都在一个控制台上,可以一起杀死。)
OP的问题是,为什么CMD及其用于运行子级的/ k(或/ c)开关在Debugger值中循环显示,而WinDbg却没有。
从某种意义上讲,答案是在未记录的结构PS_CREATE_INFO中降到一位,该结构在NtCreateUserProcess函数的用户模式和内核模式之间交换。这种结构已在某些圈子中广为人知,而不是他们似乎从未说过。我认为该结构可以追溯到Windows Vista,但是直到Windows 8才从Microsoft的公共符号文件中得知,甚至从Internet组件URLMON.DLL之类的内核中也无法得知。
无论如何,以现代形式的PS_CREATE_INFO结构,偏移量0x08(32位)或0x10(64位)处的0x04位控制内核是否检查Debugger值。符号文件告诉我们此位被Microsoft称为IFEOSkipDebugger。如果该位清除并且有Debugger值,则NtCreateUserProcess失败。通过PS_CREATE_INFO结构的其他反馈告诉KERNELBASE处理CreateProcessInternalW,以自己查看Debugger值并再次调用NtCreateUserProcess,但(可能)是其他可执行文件和命令行。
相反,当该位置1时,内核将不在乎Debugger值,并且NtCreateUserProcess可以成功。 KERNELBASE通常设置该位的方式,因为调用者不仅要求创建一个进程,而且还专门要求成为新进程的调试器,即在进程创建标志中设置了DEBUG_PROCESS或DEBUG_ONLY_THIS_PROCESS。我的意思是说调试器自己不会做任何事情来避免这种循环性。 Windows只是为他们想要调试可执行文件而为他们做的。
将调试器值视为可执行文件X的映像文件执行选项的一种方式是,该值的存在意味着X只能在调试器下执行,并且值的内容可以说明如何执行此操作。正如黑客早已注意到的那样,内核的程序员早就已经注意到了,该内容无需指定调试器,并且可以修改该值,以便尝试运行X而不是运行Y。较少注意到的是Y将无法执行除非Y调试X(或禁用Debugger值),否则运行X。还不太引起注意的是,并非所有运行X的尝试都将转而运行Y:调试器将X作为调试对象运行的尝试不会被转移。
好的,足够了。现在,我必须为我的网站写PS_CREATE_INFO。
答案 1 :(得分:1)
Geoff的最佳答案的TLDR-使用DEBUG_PROCESS
或DEBUG_ONLY_THIS_PROCESS
绕过Debugger
/ Image File Execution Options (IFEO)
全局标志并避免递归。
在C#中,使用出色的Vanara.PInvoke.Kernel32 NuGet:
var startupInfo = new STARTUPINFO();
var creationFlags = Kernel32.CREATE_PROCESS.DEBUG_ONLY_THIS_PROCESS;
CreateProcess(path, null, null, null, false, creationFlags, null, null, startupInfo, out var pi);
DebugActiveProcessStop(pi.dwProcessId);
请注意,DebugActiveProcessStop
是我的关键(否则,打开notepad.exe
时将看不到窗口)-如果您的程序不是真正的调试器,而您只想绕过,则仍然有意义。