2023年1月

简介

允许用户模式调试工作的内部机制很少得到充分的解释。更糟糕的是,这些机制在Windows XP中已经发生了根本性的变化,当许多支持被重新编写时,还通过将ntdll中的大多数例程作为本地API的一部分,使更多的子系统变得可移植。希望读者对C和通用NT内核体系结构和语义有一些基本的了解。此外,这并不是介绍什么是调试或如何编写调试器。它可以作为经验丰富的调试器编写人员或好奇的安全专家的参考。

Win32 Debugging

NT的Win32子系统从第一个版本开始就允许对进程进行调试,随后的版本添加了更多与符号和其他PE信息相关的特性和调试帮助库。但是,对于外部API用户来说,除了在Windows XP中添加的停止调试进程而不杀死进程的功能之外,其他的变化相对较少。NT的这个版本还包含了对底层实现的几次彻底检查,我们将对此进行详细讨论。但是,这些更改的一个重要副作用是不再使用LPC(和csrss.exe),这允许调试这个二进制文件(以前,调试这个二进制文件是不可能的,因为它负责处理内核到用户的通知)。
处理调试进程的基本win32api很简单:DebugActiveProcess,to attach,WaitForDebugEvent,等待调试事件的出现,以便调试可以处理它们,ContinueDebugEvent,恢复线程执行。WindowsXP的发布增加了三个更有用的API:Debug活动进程,它允许您停止调试一个进程(Debug),Debug GestPosikIon退出,这允许您即使在它被拆分和Debug处理之后继续运行一个进程,它允许您执行远程DebugBreak,而无需手动创建远程线程。在Windows XP Service Pack 1中,又添加了一个API,CheckRemoteDebuggerPresent。与IsDebuggerPresent类似,此API允许您在另一个进程中检查连接的调试器,而无需远程读取PEB。
由于NT的体系结构,这些api在最新版本的Windows上(2003将被用作一个例子,但是这些信息也适用于XP)本身做不了多少工作。相反,它们执行的典型工作是调用所需的Native函数,然后处理输出,以便Win32调用方能够以与Win9x和原始Win32 API定义兼容的格式使用它。让我们看看这些非常简单的实现:

BOOL
WINAPI
DebugActiveProcess(IN DWORD dwProcessId)
{
NTSTATUS Status;
HANDLE Handle;
/*Connect to the debugger*/Status=DbgUiConnectToDbg();if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
returnFALSE;
}
/*Get the process handle*/Handle=ProcessIdToHandle(dwProcessId);if (!Handle) returnFALSE;/*Now debug the process*/Status=DbgUiDebugActiveProcess(Handle);
NtClose(Handle);
/*Check if debugging worked*/ if (!NT_SUCCESS(Status))
{
/*Fail*/SetLastErrorByStatus(Status);returnFALSE;
}
/*Success*/ returnTRUE;
}

什么是BadImageFormatException

BadImageFormatException是当动态链接库 (DLL) 或可执行程序的文件映像无效时引发的异常。

可能的原因

如果动态链接库 (.dll 文件) 或可执行文件 (.exe 文件) 的文件格式不符合公共语言运行时所需的格式, 则会引发此异常。 具体而言, 在以下情况下会引发异常:

  • 早期版本的 .NET Framework 实用工具 (如 installutil.exe 或) 与使用 .NET Framework 的更高版本开发的程序集一起使用。


    若要解决此异常, 请使用与用于开发程序集的 .NET Framework 版本相对应的工具版本。 这可能需要修改Path环境变量或为正确的可执行文件提供完全限定的路径。
  • 尝试加载非托管动态链接库或可执行文件 (如 Windows 系统 DLL), 就像它是 .NET Framework 程序集一样。
    下面的示例通过使用Assembly.LoadFile方法加载 kernel32.dll 对此进行了说明。

    //Windows DLL (non-.NET assembly)
    string filePath = Environment.ExpandEnvironmentVariables("%windir%");if (! filePath.Trim().EndsWith(@"\"))
    filePath
    += @"\";
    filePath
    += @"System32\Kernel32.dll";try{
    Assembly assem
    =Assembly.LoadFile(filePath);
    }
    catch(BadImageFormatException e) {
    Console.WriteLine(
    "Unable to load {0}.", filePath);
    Console.WriteLine(e.Message.Substring(
    0,
    e.Message.IndexOf(
    ".") + 1));
    }
    //The example displays an error message like the following://Unable to load C:\WINDOWS\System32\Kernel32.dll.//The module was expected to contain an assembly manifest.

什么是0x8013XXXX

有时您可能会遇到从.NET返回的神秘HRESULT,它以0x8013开头,例如0x80131522。不幸的是,Visual Studio附带的错误查找并不能真正处理那些奇怪的HRESULT。它们是什么?

事实上,它们是由.NET/CLR在头文件中定义的。所有这些失败/成功的HRESULT都是在平台SDK include目录的corerror.h中定义的(通常是C:\程序文件(x86)\ Microsoft SDKs\Windows\v7.0A\include)。所有这些HRESULT的FACILITY都定义为FACILITY_URT=0x13(其中URT代表通用运行时是CLR的一个旧的三字母缩写,还有其他有趣的旧名称CLR-COM+就是其中之一)。如果您不记得什么是FACILITY,可以将其视为错误的类别–HRESULT 的FACILITY=FACILITY_URT是从CLR返回的。