分类 调试 下的文章

什么是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.

简介

STATUS_WX86_BREAKPOINT,值为0x4000001F,称为中断指令异常,表示在系统未附加内核调试器时遇到断点或断言。其定义如下:

/
// MessageId: STATUS_WX86_BREAKPOINT
//
// MessageText:
//
// Exception status code used by Win32 x86 emulation subsystem.
//
#define STATUS_WX86_BREAKPOINT           ((NTSTATUS)0x4000001FL)

说明

当断点(int 3)异常来自在WOW64模式下执行的代码(64位Windows中的32位代码)时,64位调试器确实获得了状态WX86。从64位代码中断点时-状态中断点。同样,在单步异常中,如果此异常来自64位代码,64位调试器将获得STATUS_single_step;如果异常来自WOW64代码,则获得STATUS_WX86_single_step。

可以像处理STATUS_BREAKPOINT(0x80000003)一样处理它。WX86提供了从哪个模式(WOW64或本机)中断的附加信息。但在这两种情况下,这都是断点异常。以及如何处理它的逻辑。但是,这是您的选择,决定如何处理断点、单步或其他异常。

还要注意,STATUS_WX86_BREAKPOINT和STATUS_WX86_SINGLE_STEP只得到64位调试器。32位调试器始终获取状态断点,64位调试器获取状态断点,而x64调试器获取状态断点时不获取任何信息。例如,在WOW64进程启动时-64位调试器获取了2个断点-第一个状态断点来自64位模式(在64位ntdll.LdrpDoDebugBreak中),然后是状态断点来自32位ntdll.LdrpDoDebugBreak。而32位调试器只有第二个断点(来自32位代码)具有状态断点。

异常结构填充

ExceptionAddress: 03264043 (libcef!GetHandleVerifier+0x00845ba3)
ExceptionCode: 4000001f (WOW64 breakpoint)
ExceptionFlags: 00000001
NumberParameters: 1
   Parameter[0]: 00000000

简介

允许用户模式调试工作的内部机制很少得到充分的解释。更糟糕的是,这些机制在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;
}

**“异常助手”**是一项用于调试 Visual Basic 和 C# 应用程序的功能。 **“异常助手”比其他语言(例如 C/C++)所使用的“异常”**对话框提供了更多有关异常的信息。这样便更易于查找异常原因并解决问题。

**“异常助手”**在发生运行时异常时出现,它显示异常的类型、故障排除提示以及纠正操作。 **“异常助手”**还可以用来查看异常对象的详细信息。异常是从 Exception 类继承的对象。 异常在出现问题时由代码引发,并在堆栈中向上传递,直到应用程序对其进行处理或者程序失败。

单击**“操作”窗格中的“查看详细信息”**。 即出现一个对话框,显示异常的属性。

下表列出并介绍了一个异常对象的属性。 并非所有属性都会出现,具体取决于异常的类型。

表 2

Property

说明

数据

包含用户定义的键/值对的 IDictionary 对象。 默认值为空集合。

FileName

导致异常的文件的名称。

FusionLog

描述程序集加载失败的原因的日志文件。

HelpLink

指向与异常相关联的帮助文件的链接。

HResult

赋给特定异常的编码数值。

InnerException

导致当前异常的 Exception 实例。 有时,在帮助器例程中捕获引发的异常,并引发对错误更具描述性的新异常,从而提供更多信息,这么做很有用。 在这种情况下,InnerException 属性将设置为原始异常。

Message

与异常相关联的消息。 消息以引发异常的线程的 CurrentUICulture 属性所指定的语言显示。

Source

导致异常的应用程序或对象的名称。 如果未设置 Source,将返回产生异常的程序集的名称。

StackTrace

引发当前异常时调用堆栈上的方法调用的字符串表示形式。 如果有可用的调试信息,则堆栈跟踪包含源文件名和程序行号。 由于优化期间发生代码转换,StackTrace 报告的方法调用可能没有预期的多。 堆栈跟踪被捕获后,将立即引发异常。

TargetSite

引发当前异常的方法。 如果引发异常的方法不可用并且堆栈跟踪不是空引用(Visual Basic 中的 Nothing),TargetSite 将从堆栈跟踪获取该方法。 如果堆栈跟踪为空引用,TargetSite 也返回空引用。

简介

将详细分析Windows调试的内核模式接口。希望读者对C和通用NT内核体系结构和语义有一些基本的了解。此外,这并不是介绍什么是调试或如何编写调试器。它可以作为经验丰富的调试器编写人员或好奇的安全专家的参考。

内核用户模式调试支持

最后一块拼图以内核模式存在,并提供了我们到目前为止看到的事件和结构,以便调试可以工作。Dbgk不依赖KD,它是一个完全不同的组件,因为Windows XP提供了自己的对象和系统调用来管理它。以前的Windows版本没有这样的对象,而是依赖于静态数据结构,该结构将在内存中进行分析,然后用于通过Windows的本地过程调用(LPC)机制发送的各种通知。

这些系统调用的可用性和调试对象的存在的一个好处是内核模式驱动程序也可以参与用户模式调试。虽然这可能不是这个新设计的目标之一,但它是一些人应该感兴趣的特性。虽然没有导出实际的Nt*调用,但是它们仍然可以被知道其系统调用ID的驱动程序访问。即使这个数字在每个OS版本之间发生变化,在驱动程序中保留一个表还是相对容易的。通过将TDI接口添加到这样的驱动程序中,可以开发一个高速远程调试器驱动程序,它将完全没有用户模式组件,从而允许远程调试机器上的每个进程。

我们要做的第一件事是查看实现用户模式调试的实际对象,DEBUG_OBJECT:

//
//Debug Object//typedef struct_DEBUG_OBJECT
{
KEVENT EventsPresent;
FAST_MUTEX Mutex;
LIST_ENTRY EventList;
union
{
ULONG Flags;
struct{
UCHAR DebuggerInactive:
1;
UCHAR KillProcessOnExit:
1;
};
};
} DEBUG_OBJECT,
*PDEBUG_OBJECT;