上次主要是给大家讲了一个用户层SEH调试的技巧,被一些牛人笑话了~~这篇文章给大家讲讲内核层的SEH,不过SEH真的是太强大了,我菜鸟一个,研究再三也不能领语到其中的真谛,如果有哪位达人愿意给各位菜鸟们一点分享,欢迎跟贴!还可以参考--A Crash Course on the Depths of Win32、《加密与解密》第三版,老罗的Win32汇编
编程工具:RadAsm
调试工具:WinDbg
编程语言:Win32汇编
先用RadAsm建立Driver类型的工程seh,然后会生成seh.inc和seh.asm两个文件
seh.inc的源代码如下:
_SEH STRUCT
SafeEip dd ? ;The offset where it's safe to continue execution
PrevEsp dd ? ;The previous value of esp
PrevEbp dd ? ;The previous value of ebp
_SEH ends
SEH_SafePlaceCounter = 0
SEH_INSTALLED = 0
;@CurSeg---The name of the current segment(text macro)
SEH_CurrentSegmentName TEXTEQU @CurSeg
;@SizeStr---Macro function that returns the length of the given string.Returns an integer
SEH_CurrentSegmentNameLenght TEXTEQU %@SizeStr(%@CurSeg)
;%---Treats the value of expression in a macro argument as text
.data?
_seh _SEH <>
@CurSeg ENDS
IF SEH_CurrentSegmentNameLenght NE 0
SEH_CurrentSegmentName SEGMENT
ENDIF
IF SEH_INSTALLED EQ 0
echo ERROR!: _finally without _try
.ERR
ELSE
SEH_INSTALLED = SEH_INSTALLED - 1
ENDIF
sp_lbl TEXTEQU @CatStr(<SEH_SafePlace>, %(SEH_SafePlaceCounter)) ;Macro function that concatenates one or more strings.Returns a string
SEH_SafePlaceCounter = SEH_SafePlaceCounter + 1
sp_lbl:
PUSHCONTEXT ASSUMES
assume fs:nothing
pop fs:[0] ; restore next ERR structure to FS:[0]
add esp, sizeof DWORD ; throw away rest of ERR structure
POPCONTEXT ASSUMES
ENDM
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.code
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DefaultExceptionHandler proc C pExcept:DWORD, pFrame:DWORD, pContext:DWORD, pDispatch:DWORD ;默认的异常处理
_SEH STRUCT
SafeEip dd ? ;The offset where it's safe to continue execution
PrevEsp dd ? ;The previous value of esp
PrevEbp dd ? ;The previous value of ebp
_SEH ends
先自定义一个SEH结构体,因为这个程序用到了SEH异常处理链表,所以在这里定义数据结构供以后方便使用!
;@CurSeg---The name of the current segment(text macro)
SEH_CurrentSegmentName TEXTEQU @CurSeg
;@SizeStr---Macro function that returns the length of the given string.Returns an integer
;%---Treats the value of expression in a macro argument as text
SEH_CurrentSegmentNameLenght TEXTEQU %@SizeStr(%@CurSeg)
.data?
_seh _SEH <>
@CurSeg ENDS
IF SEH_CurrentSegmentNameLenght NE 0
SEH_CurrentSegmentName SEGMENT
ENDIF
include D:\RadASM\masm32\include\w2k\ntstatus.inc ;根据自己安装的RadASM的路径进行设置
include D:\RadASM\masm32\include\w2k\ntddk.inc
includelib D:\RadASM\masm32\lib\w2k\ntoskrnl.lib
include D:\RadASM\masm32\include\w2k\ntoskrnl.inc
include D:\RadASM\masm32\macros\Strings.mac
include seh.inc ;包含自定义的宏和默认的异常处理函数
SEH STRUCT ;定义一个SEH相关的数据结构
SafeEip dd ? ; The offset where it's safe to continue execution
PrevEsp dd ? ; The previous value of esp
PrevEbp dd ? ; The previous value of ebp
SEH ENDS
invoke DbgPrint, $CTA0(" The code tried to write to address %08X\n\n"), \
[esi][EXCEPTION_RECORD.ExceptionInformation][4]
.else
invoke DbgPrint, $CTA0(" The code tried to read from address %08X\n\n"), \
[esi][EXCEPTION_RECORD.ExceptionInformation][4]
.endif
.endif
SEH: An exception C0000005 has occured
Access violation at address: FA0B72C1
The code tried to read from address 00000000
Exception: C0000005 at address: FA0B72CD
SEH: Leaving DriverEntry
Driver:Unloading...
下面我就调试一下这个程序,看为什么会得到如下结果!详细讲解一下WinDbg的简单使用方法!
步骤如下:
启动虚拟机,然后启运WinDbg:
这时虚拟机不动了,只看到一个很亮的横线,因为它被我们WinDbg挂接上并设置了断点断下来了,然后WinDbg也停在f9ff02d4 cc int 3的地方,我们在下面的Comand框中输入G/g,即可以启动调试器!
当调试器正常运行之后,我们将seh.sys拖到虚拟机中,然后使用KmdManager加载seh.sys驱动,这里WinDbg会断下来,这时就是我们在DriverEntry中的int 3起作用了,注意这个操作一定要在调试机上操作,不然会Blue Screen~~呵呵!
至于WinDbg中出现几个错误,我也不明白,不管了,如有高手请指点一下!
这时WinDbg中出现fa0dd367 cc int 3这条指令其实就是我们汇编代码中DriverEntry函数前面的那条指令,接下来我就只看WinDbg的内容,进行讲解(因为我的电脑实在太破,无法在这两个程序中不停的Alt+Tab)