筛选器异常
概念
异常默认是由操作系统处理的,现在用API设置由用户自己定义处理异常(内存访问无效,修改到常量区等错误),当发生异常的时候,系统将调用这个回调函数,并根据回调函数的返回值决定如何进行下一步操作
在进程范围内,筛选器异常处理回调函数是惟一的,设置了一个新的回调函数后,原来的就失效了
语法
设置异常发生处理器
invoke SetUnhandledExceptionFilter, offset MyFilter //参数为异常回调处理函数
自定义异常处理异常回调处理函
.const
ERRMSG db "由于用户操作失误!导致软件出现异常", 0
OKMSG db "由正常操作", 0
FORMAT db "ExceptionAddress=%p ExceptionCode=%p ExceptionFlags=%p NumberParameters:%d", 0
MyFilter proc ,ExceptionInfo:ptr EXCEPTION_POINTERS
mov eax, ExceptionInfo
mov eax, [eax]
assume eax :ptr EXCEPTION_RECORD ;拿到错误记录内容
invoke wsprintf, offset BUFFER, offset FORMAT, ;格式化输出错误信息
[eax].ExceptionAddress,
[eax].ExceptionCode,
[eax].ExceptionFlags,
[eax].NumberParameters
invoke MessageBox,NULL, offset BUFFER, NULL, MB_OK ;弹窗提示用户
mov eax, EXCEPTION_EXECUTE_HANDLER ;异常处理了,处理完直接退出进程
;mov eax, EXCEPTION_CONTINUE_SEARCH ;自己处理不了也就是没有处理继续查找让其他筛选器处理
;mov eax, EXCEPTION_CONTINUE_EXECUTION ;异常处理了,bug修复了,继续从原出错的代码行继续执行
ret
MyFilter endp
异常回调的参数信息
;异常信息结构体
typedef struct _EXCEPTION_POINTERS
{
PEXCEPTION_RECORD ExceptionRecord; //保存异常信息
PCONTEXT ContextRecord; //保存了异常发生寄存器信息
} EXCEPTION_POINTERS;
typedef struct _EXCEPTION_RECORD
{
DWORD ExceptionCode; //异常错误码
DWORD ExceptionFlags; //标志异常发生后是否还可以继续执行
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress; //异常发生内存位置
DWORD NumberParameters; //为ExceptionInformation数组的个数
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //异常附加参数信息,只有当ExceptionCode为EXCEPTION_ACCESS_VIOLATION,此参数才有
} EXCEPTION_RECORD;
应用场景
反调试代码加密(加密是因为可以防止他人改eip跳过你的异常,加了密之后就算跳过看到的也是加密后的代码,执行加密的代码段也是会错的)
设计思路
代码段进行加密
代码开始产生异常报错
异常筛选器的回调内解密代码,重设eip信息
程序继续运行
优势
非调试状态能正常执行程序
调试状态不能正常执行程序(注册异常后用调试工具的话,异常会被调试工具接收,断点不了异常处理函数,也就调试不了程序,除非找到你的异常处理函数的,肉眼找代码可挺烦的)
代码示例
.data
;用一个全局变量来保存异常函数指针
ERRPRO dd offset IinitInfo
;异常处理回调函数
MyFilter proc ,ExceptionInfo:ptr EXCEPTION_POINTERS
;重新设置eip位置
mov eax, [eax].ContextRecord
assume eax :ptr CONTEXT
add [eax].regEip, 6
;解密代码段
mov ebx, [eax].regEip
mov ecx, 0
LOOP1:
xor byte ptr [ebx+ecx], 90h ;将8个字节的代码与90h异或拿到实际代码
inc ecx
cmp ecx, 8
jl LOOP1
mov eax, EXCEPTION_CONTINUE_EXECUTION ;解密完成,执行正确段的代码
ret
MyFilter endp
;包装一个函数来注册并产生异常
IinitInfo proc
invoke SetUnhandledExceptionFilter, offset MyFilter
mov eax, 1
mov dword ptr [eax], 0
MyFilter endp
START:
;注册并产生异常
mov eax,ERRPRO
call eax
mov eax, eax
invoke MessageBox,NULL, offset OKMSG, NULL, MB_OK
ret
end START
保存异常日志
设计思路
利用异常处理函数的参数获取参数信息
保存异常信息到文件中
示例代码
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive
include windows.inc
include kernel32.inc
include user32.inc
include Stdlib.Inc
includelib KERNEL32.LIB
includeLIB user32.lib
includelib Stdlib.lib
MyTime STRUCT ;时间结构体
m_Year dd 0
m_Month dd 0
m_Date dd 0
m_Hour dd 0
m_Minutue dd 0
MyTime ends
.const
ERRMSG db "由于用户操作失误!导致软件出现异常", 0
FORMAT db "=====exception===== ",0ah,0dh,
" OccurTime: %d-%d-%d %d:%d",0ah,0dh,
" FilePath: %s",0ah,0dh,
" Address: %p",0ah,0dh,
" Code: %p",0ah,0dh,
" Flags: %p",0ah,0dh,
" NumberParameters: %d", 0ah,0dh,0
REGFORMAT db "======reginfo======= ",0ah,0dh,
"eax: %d ebx: %d ecx: %d edx: %d esi: %d edi: %d ebp: %d eip: %d EFlags: %d",0ah,0dh,0
STKTITLE db 0ah,0dh,"======stack======= ",0ah,0dh,0
MEMTITLE db "======memory====== ",0ah,0dh,0
FILENAME db "ErrorRecord.text",0
.data
g_Test dd(0cccccccch)
g_Test2 db 50 dup(0ch)
g_Buffer db 300 dup(0)
g_FileName db 100 dup(0)
g_RegInfo db 100 dup(0)
; 异常处理函数
MyFilter proc ,ExceptionInfo:ptr EXCEPTION_POINTERS
LOCAL @tTime:SYSTEMTIME
LOCAL @tagMyTime:MyTime
LOCAL @ats:SECURITY_ATTRIBUTES
LOCAL @hFile:HANDLE
LOCAL @dwWrite:DWORD
LOCAL @dwBytesToWrited:DWORD
LOCAL @dwStackLocate:DWORD
;@nSize:DWORD
;初始化文件句柄
mov @hFile,0
;拿到发生错误的时间
invoke GetLocalTime,addr @tTime
;拿到发生错误的文件路径
invoke GetModuleFileName,NULL,offset g_FileName,100
;拿到错误信息结构体
mov eax,ExceptionInfo
mov eax, [eax]
assume eax :ptr EXCEPTION_RECORD
lea ebx,@tTime
assume ebx:ptr SYSTEMTIME
mov ecx,0
mov cx,[ebx].wYear
mov @tagMyTime.m_Year,ecx
mov cx,[ebx].wMonth
mov @tagMyTime.m_Month,ecx
mov cx,[ebx].wDay
mov @tagMyTime.m_Date,ecx
mov cx,[ebx].wHour
mov @tagMyTime.m_Hour,ecx
mov cx,[ebx].wMinute
mov @tagMyTime.m_Minutue,ecx
invoke wsprintf, offset g_Buffer, offset FORMAT,
@tagMyTime.m_Year,@tagMyTime.m_Month,@tagMyTime.m_Date, @tagMyTime.m_Hour,@tagMyTime.m_Minutue,
offset g_FileName,
[eax].ExceptionAddress, ;异常发生内存位置
[eax].ExceptionCode, ;异常错误码
[eax].ExceptionFlags, ;标志异常发生后是否还可以继续执行
[eax].NumberParameters ;异常附加参数信息,只有当ExceptionCode为EXCEPTION_ACCESS_VIOLATION,此参数才有用
;invoke MessageBox,NULL, offset g_Buffer, NULL, MB_OK
;取消假设
assume ebx:nothing
;拿到寄存器信息结构体
mov eax,ExceptionInfo
assume eax:ptr EXCEPTION_POINTERS
mov eax,[eax].ContextRecord
assume eax:ptr CONTEXT
;保存栈的位置
mov ebx,[eax].regEbp
mov @dwStackLocate,ebx
;保存寄存器信息
invoke wsprintf,offset g_RegInfo,offset REGFORMAT,
[eax].regEax,[eax].regEbx,[eax].regEcx,[eax].regEdx,
[eax].regEsi,[eax].regEdi,[eax].regEbp,[eax].regEsp,
[eax].regFlag
assume eax:nothing
;invoke MessageBox,NULL, offset g_RegInfo, NULL, MB_OK
;新建日志文件
mov @ats.nLength,sizeof @ats
mov @ats.bInheritHandle,TRUE
mov @ats.lpSecurityDescriptor,NULL
invoke CreateFile,offset FILENAME,GENERIC_WRITE,FILE_SHARE_WRITE,addr @ats,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
.if eax == NULL
jmp SAFE_EXIT
.endif
mov @hFile,eax
;写入异常信息
invoke StrLen,offset g_Buffer
mov @dwWrite,eax
invoke WriteFile,@hFile,offset g_Buffer,@dwWrite,addr @dwBytesToWrited,NULL
;写入寄存器信息
invoke StrLen,offset g_RegInfo
mov @dwWrite,eax
invoke WriteFile,@hFile,offset g_RegInfo,@dwWrite,addr @dwBytesToWrited,NULL
;写入内存信息
invoke StrLen,offset MEMTITLE
mov @dwWrite,eax
invoke WriteFile,@hFile,offset MEMTITLE,@dwWrite,addr @dwBytesToWrited,NULL
mov @dwWrite,50
invoke WriteFile,@hFile,offset g_Test,@dwWrite,addr @dwBytesToWrited,NULL
;写入堆栈信息
invoke StrLen,offset STKTITLE
mov @dwWrite,eax
invoke WriteFile,@hFile,offset STKTITLE,@dwWrite,addr @dwBytesToWrited,NULL
mov @dwWrite,50h
invoke WriteFile,@hFile,@dwStackLocate,@dwWrite,addr @dwBytesToWrited,NULL
SAFE_EXIT:
.if @hFile != NULL
invoke CloseHandle,@hFile ;关闭文件
.endif
mov eax, EXCEPTION_EXECUTE_HANDLER ;退出进程
ret
MyFilter endp