Delphi研究之驱动开发篇(六)--利用Section与用户模式程序通讯(上)
作 者: mickeylan
时 间: 2008-06-10,00:45
链 接: http://bbs.pediy.com/showthread.php?t=66291在进入主题之前,先来简单地看一下结构化异常处理(Structured Exception Handling, SEH),本篇的程序需要这个东东。
结构化异常处理
这里我并不打算详细讲结构化异常处理,关于SEH,在网上你能找到相关的内容,SHE能用于所有的异常处理,也就是说,SEH既能用于用户模式又能用于内核模式。但这两种模式下的异常处理有一个本质上的差别:
在内核模式下,借助于seh,并非所有的异常都能得到处理!比如说,即使使用了seh,用零作除数作除法也会使系统崩溃。最为可怕的是,引用未定义的内核内存也会导致蓝屏死机BSOD。而对未定义的用户模式内存的引用异常,seh却可以轻松处理。因此避免系统崩溃的唯一办法就是所编写的代码不要导致无法处理的异常。
以下是个使用结构化异常的例子:
代码:unit seh;inte易做图ce
uses
nt_status;function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;implementation
uses
ntoskrnl;const
SEH_SafePlaceCounter = 0;
SEH_INSTALLED = 0;type
_SEH = record
SafeEip: DWORD; { 线程继续执行的地方 }
PrevEsp: DWORD; { 以前esp的值 }
PrevEbp: DWORD; { 以前ebp的值 }
end;var
sseh: _SEH;function DefaultExceptionHandler(pExcept:PEXCEPTION_RECORD;
pFrame:DWORD;
pContext:PCONTEXT;
pDispatch:DWORD): DWORD; cdecl;
begin
DbgPrint(#13#10SEH: An exception %08X has occured#13#10,
pExcept^.ExceptionCode);
if pExcept^.ExceptionCode = $0C0000005 then
begin
{如果发生了EXCEPTION_ACCESS_VIOLATION类型的异常,}
{则输出以下信息.}
DbgPrint( Access violation at address: %08X#13#10,
pExcept^.ExceptionAddress);
if pExcept^.ExceptionInformation[0] <> nil then {试图读还是写?}
begin
DbgPrint( The code tried to write to address %08X#13#10#13#10,
DWORD(pExcept^.ExceptionInformation[4]));
end else
begin
DbgPrint( The code tried to read from address %08X#13#10#13#10,
DWORD(pExcept^.ExceptionInformation[4]));
end;
end;
asm
lea eax, sseh
push (_SEH PTR [eax]).SafeEip
push (_SEH PTR [eax]).PrevEsp
push (_SEH PTR [eax]).PrevEbpmov eax, pContext
pop (CONTEXT PTR [eax]).regEbp
pop (CONTEXT PTR [eax]).regEsp
pop (CONTEXT PTR [eax]).regEip
end;
result := 0;
end;procedure BuggyReader; assembler;
asm
xor eax, eax
mov eax, [eax] {!!! 没有SEH的话 - BSOD !!!}
end;procedure BuggyWriter; assembler;
asm
mov eax, offset MmUserProbeAddress
mov eax, [eax]
mov eax, [eax]mov byte ptr [eax], 0 {!!!没有SEH的话 - BSOD !!!}
end;function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;
label
SafePlace;
begin
DbgPrint(#13#10SEH: Entering DriverEntry#13#10);
{ "手工"安装SEH }
asm
push offset DefaultExceptionHandler {我们的SEH程序}
push fs:[0]
mov fs:[0], espmov sseh.SafeEip, offset SafePlace {SafePlace是处理完异常后继续执行的地方}
mov sseh.PrevEbp, ebp
mov sseh.PrevEsp, esp
end;
BuggyReader;
BuggyWriter;SafePlace:
asm
pop fs:[0]
add esp, 4
end;
DbgPrint(#13#10SEH: Leaving DriverEntry#10#13);
result := STATUS_DEVICE_CONFIGURATION_ERROR;
end;end.
安装SHE-Frame
由于在内核模式下我们无法直接使用Delphi自身的异常处理机制,因为在驱动程序中我们要自己手工安装SHE,这里我们使用Delphi的BASM来做这件事情,了解SHE的朋友都知道,做这件事情是非常简单的。代码:asm
push offset DefaultExceptionHandler {我们的SEH程序}
push fs:[0]
mov fs:[0], espmov sseh.SafeEip, offset SafePlace {SafePlace是处理完异常后继续执行的地方}
mov sseh.PrevEbp, ebp
mov sseh.PrevEsp, esp
end;
为了在异常处理之后我们的处理程序能恢复线程的执行,我们应该保存esp、ebp寄存器的内容以及线程继续执行的地址。我们将这三项信息保存在seh结构体中并调用函数BuggyReader。BuggyReader函数试图从地址00000000读取一个DWORD。代码:procedure BuggyReader; assembler;
asm
xor eax, eax
mov eax, [eax] {!!! 没有SEH的话 - BSOD !!!}
end;
nil指针引用是一个十分常见的错误,微软在00000000-0000FFFFh划出了64k字节的内存区,并使此区域无法访问。访问此区域中的任何一个字节都会导致EXCEPTION_ACCESS_VIOLATION类型的异常。
异常处理
函数BuggyReader从地址00000000读取引发了异常,我们就进入了我们指定的处理程序。
代码:function DefaultExceptionHandler(pExcept:PEXCEPTION_RECORD;
pFrame:DWORD;
补充:软件开发 , Delphi ,