PE结构、SEH相关知识学习笔记

x64的seh PatchGuard与异常处理密切相关,所以先看看x64的SEH 关于x86中内核如何捕获异常,分发异常,可以看http://blog.youkuaiyun.com/shevacoming/article/details/7580350 上面那篇没有跟进分析RtlDispatchException,这里跟进以复习x86 seh的结构,也看经典的这一篇 http://bbs.pediy.com/ 阅读详情

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题

PE结构的学习

原文中用fasm自己构造了一个pe,这里贴一个用masm的,其实是使用WriteFile API将编写的PE数据写成文件~也没啥好说的,PE结构在这里没有仔细介绍,需要可以另外查询,剩下要说的的基本都在代码注释里了

参考:点击打开链接(PEDIY技术之新思路(二)_用'高级'编译器MASM实现自定义PE文件结构)

Pe.asm:

REMOTE_CODE_START equ this BYTE
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; DOS Header
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
PE_HEADER_START equ this BYTE
DOS_HEADER:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 事实上,从这里到e_res2无用部分可以用一句DUP(0)来完成
; 在这里编译器不会检查你结构的定义是否正确, 这些变量名也是没有意义的
; 这样写只是有利于我们认识PE结构而已,除了关键字,你可以任意写
; 因为它相当于写二进制型汇编代码, 事实上,和直接写二进制是一样的
; 只是这样就轻松多了。当然了, 这种方式的定义给我们带来的问题就是重定位
; 所以需要重定位的地方要小心了, 你也可以把PE头部提上来到DOS头部里面来变形PE头
; 或者整个最小PE什么的, 随你怎么高兴怎么玩,但是要保证e_lfanew定位的正确
; 你想怎么来就怎么来,你自由了!但是,没有绝对的自由,俗话说得好
; 任悟空本领再高,他也跳不出如来佛(OS)的手掌心啊
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	e_magic				db 'MZ'
	e_cplp				dw 0
	e_cp				dw 0
	e_crlc				dw 0
	e_cparhdr			dw 0
	e_minalloc			dw 0
	e_maxalloc			dw 0
	e_ss				dw 0
	e_sp				dw 0
	e_csum				dw 0
	e_ip				dw 0
	e_cs				dw 0
	e_lfarlc			dw 0
	e_ovno 				dw 0
	e_res				dw 4 dup(0)
	e_oemid				dw 0
	e_oeminfo			dw 0
	e_res2				dw 10 dup(0)
	e_lfanew			dd NT_HEADERS - 00401000h
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Dos小程序,写不写都行
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Dos_Stub:
	mov ah, 4ch
	int 21h
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Nt Header
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
NT_HEADERS:
	Signature			dd 4550h	; 'PE'
	Machine				dw 14ch
	NumberOfSections		dw 2
	TimeDateStamp			dd 0
	PointerToSymbolTable		dd 0
	NumberOfSymbols			dd 0
	SizeOfOptionalHeader		dw 0e0h
	Characteristics			dw 010fh
	Magic				dw 10bh
	MajorLinkerVersion		db 0
	MinorLinkerVersion		db 0
	SizeOfCode			dd 200h
	SizeOfInitializedData		dd 0
	SizeOfUninitializedData		dd 0
	AddressOfEntryPoint		dd 1000h	; oep
	BaseOfCode			dd 1000h	; Code Section RVA
	BaseOfData			dd 0		; Data Section RVA
	ImageBase			dd 00400000h
	SectionAlignment		dd 1000h	; Section Mem Align
	FileAlignment			dd 200h		; Section Disk Align
	MajorOperSystemVersion		dw 0
	MinorOperSystemVersion		dw 0
	MajorImageVersion		dw 0
	MinorImageVersion		dw 0
	MajorSubsystemVersion		dw 4
	MinorSubsystemVersion		dw 0
	Win32VersionValue		dd 0		; Reserved 0
	SizeOfImage			dd 3000h	; PE加载到内存后的映像大小
	SizeOfHeaders			dd 200h		; DosHeaders + DosStub + NtHeader + Section Header
	_CheckSum			dd 0
	SubSystem			dw 2		; GUI
	DllCharacteristics		dw 0
	SizeOfStackReserve		dd 100000h
	SizeOfStackCommit		dd 1000h	; Stack = 4kb
	SizeOfHeapReserve		dd 100000h
	SizeOfHeapCommit		dd 1000h	; Heap = 4kb
	LoaderFlags			dd 0
	NumberOfRvaAndSizes		dd 10h		; 16
	DirectoryData1			dq 0		; 没有输出表,填0
	ImportTableAddress		dd IMPORT_START - 00401000h - 400h + 2000h
	ImportTableSize			dd IMPORT_LENGTH
	DirectoryData2			dq 14 dup(0)	; 余下的数据目录填0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Section 1
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
SECTION_HEADER1:
	Name1				db 'CODE', 0, 0, 0, 0		; 节区名,8字节大小
	VirtualSize			dd CODE_LENGTH
	VirtualAddress			dd 1000h			; 内存中的偏移
	SizeOfRawData			dd 200h
	; 这里的CODE_START - 00401000h = 00401200h - 00401000h
	; 00401000h为Pe.asm, PE头200h,CODE节区在PE头后面,所以为
	; 00401200h,这里这样做可以求出代码段所在文件偏移
	PointerToRawData		dd CODE_START - 00401000h	; 文件中的偏移,也可以直接成为200h,因为定义的PE头大小为200
	PointerToRelocations		dd 0
	PointerToLinenumbers		dd 0
	NumberOfRelocations		dw 0
	NumberOfLinenumbers		dw 0
	_Characteristics		dd 0e0000020h			; 节区属性
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Section 2
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
SECTION_HEADER2:
	Name2				db 'IMPORT', 0, 0		; 这个块可以去掉,将输入表放入代码块里
	VirtualSize2			dd IMPORT_LENGTH
	VirtualAddress2			dd 2000h			; 内存中的偏移
	SizeOfRawData2			dd 200h
	; 同上,这里IMPORT_START = 00401400h
	; PE头 + CODE节区 = 400h
	PointerToRawData2		dd IMPORT_START - 00401000h	; 文件中的偏移,也可以直接写成400h
	PointerToRelocations2		dd 0
	PointerToLinenumbers2		dd 0
	NumberOfRelocations2		dw 0
	NumberOfLinenumbers2		dw 0
	_Characteristics2		dd 0e0000020h
PE_HEADER_END equ this BYTE
PE_HEADER_LENGTH equ offset PE_HEADER_END - offset PE_HEADER_START
ZeroSpace1	db 200h - PE_HEADER_LENGTH dup(0)	; 不足200h, 间隙填0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Code Start
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
CODE_START equ this BYTE
	mov eax, OFFSET IAT_1
	lea eax, [szContextR - 200h]	; 401200h为code段开始,szContextR = 401220h, 这是在PE.ASM包含在MakePe.asm时的情况
	lea ebx, [szCaptionR - 200h]	; 如果要单独作为程序,其起始地址00401000h,要减去200h
	push MB_OK
	push ebx
	push eax
	push 0
	; IAT_1(0040143ch) - 1000h为不在CODE段 = (0040043ch)
	; 0040043ch - 400h = 0040003ch这样就等于ImageBase + 在导入表(400h)中的偏移(文件偏移)
	; 再加上2000h = 0040203ch为实际内存地址
	call dword ptr [IAT_1 - 1000h - 400h + 2000h]
	push 0
	call dword ptr [IAT_2 - 1000h - 400h + 2000h]
	szContextR	db 'Congratulations! You make it!', 0dh, 0ah, 0
	szCaptionR	db 'OK', 0
CODE_END equ this BYTE
CODE_LENGTH equ offset CODE_END - offset CODE_START
ZeroSpace2	db 200h - CODE_LENGTH dup(0)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Import Start
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IMPORT_START equ this BYTE
IID_1:
	; IAT_1 - 401000h = 获得IAT_1的文件偏移, 再减400h为获得在导入表中的偏移(文件中)
	; 加2000h获得在内存中的偏移(RVA), 下面的一些和这个一个道理
	OriginalFirstThunk		dd IAT_1 - 401000h - 400h + 2000h
	TimeDateStemp			dd 0
	ForwarderChain			dd 0
	DllName				dd DllName1 - 401000h - 400h + 2000h
	; 这里为了方便,把它和OriginalFirstThunk指向同一个地址的IAT结构
	FirstThunk			dd IAT_1 - 401000h - 400h + 2000h
IID_2:
	OriginalFirstThunk2  		dd IAT_2-401000h-400h+2000h
	TimeDateStemp2   		dd 0
	ForwarderChain2  		dd 0
	DllName2         		dd _DllName2-401000h-400h+2000h
	FirstThunk2      		dd IAT_2-401000h-400h+2000h
IID_END:
	IIDEND  			dd 5 dup(0)	; 用一个全0的结构作为结束
IAT_1:
	AddressOfData1  		dd IIBN_1-401000h-400h+2000h
	AddressOfDataEnd1  		dd 0
IAT_2:
	AddressOfData2  		dd IIBN_2-401000h-400h+2000h
	AddressOfDataEnd2  		dd 0
IIBN_1:
	Hint1  				dw 0
	Nama1  				db 'MessageBoxA',0
	DllName1 			db 'user32.dll',0,0
IIBN_2:
	Hint2  				dw 0
	Nama2  				db 'ExitProcess',0
	_DllName2 			db 'kernel32.dll',0,0
IMPORT_END   equ  this BYTE 
IMPORT_LENGTH  equ offset IMPORT_END - offset IMPORT_START 
ZeroSpace3 db 200h - IMPORT_LENGTH dup(0)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; THE_PE_END
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
REMOTE_CODE_END equ this byte
REMOTE_CODE_LENGTH equ offset REMOTE_CODE_END - offset REMOTE_CODE_START
	

MakePe.asm:

	.386
	.model flat, stdcall
	option casemap:none
	
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

	.data
hOutFile	dd 0
BytesWrite	dd 0

	.const
szCaption	db 'Info', 0
szContext	db 'Success', 0
szOutFileName	db 'Pe.exe', 0

	.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 将PE数据代码引入,编译并产生Pe.exe文件
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include Pe.asm

start:
	invoke CreateFile, offset szOutFileName, GENERIC_READ or GENERIC_WRITE, \
			FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
	mov hOutFile, eax
	invoke WriteFile, hOutFile, offset REMOTE_CODE_START, REMOTE_CODE_LENGTH, \ 
			addr BytesWrite, NULL
	invoke MessageBox, NULL, offset szContext, offset szCaption, MB_OK
	invoke ExitProcess, 0
	
	end start


SEH的学习


发生异常时系统的处理顺序(by Jeremy Gordon):

    1. 系统首先判断异常是否应发送给目标程序的异常处理例程,如果决定应该发送,并且目标程序正在被调试,则系统
    挂起程序并向调试器发送EXCEPTION_DEBUG_EVENT消息.呵呵,这不是正好可以用来探测调试器的存在吗?

    2. 如果你的程序没有被调试或者调试器未能处理异常,系统就会继续查找你是否安装了线程相关的异常处理例程,如果
    你安装了线程相关的异常处理例程,系统就把异常发送给你的程序seh处理例程,交由其处理.

    3. 每个线程相关的异常处理例程可以处理或者不处理这个异常,如果他不处理并且安装了多个线程相关的异常处理例程,
    可交由链起来的其他例程处理.

    4. 如果这些例程均选择不处理异常,如果程序处于被调试状态,操作系统仍会再次挂起程序通知debugger.

    5. 如果程序未处于被调试状态或者debugger没有能够处理,并且你调用SetUnhandledExceptionFilter安装了最后异
    常处理例程的话,系统转向对它的调用.

    6. 如果你没有安装最后异常处理例程或者他没有处理这个异常,系统会调用默认的系统处理程序,通常显示一个对话框,
    你可以选择关闭或者最后将其附加到调试器上的调试按钮.如果没有调试器能被附加于其上或者调试器也处理不了,系统
    就调用ExitProcess终结程序.

    7. 不过在终结之前,系统仍然对发生异常的线程异常处理句柄来一次展开,这是线程异常处理例程最后清理的机会.


有两种类型的异常处理句柄,一种是final型的,这是在你的异常未能得到线程相关处理例程处理操作系统在即将关闭程序之前会
回调的例程,这个例程是进程相关的而不是线程相关的,因此无论是哪个线程发生异常未能被处理,都会调用这个例程.

	.386
	.model flat, stdcall
	option casemap:none
	
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

	.data
szCaption	db 'TestSEH', 0
szMsgOK		db 'OK, the exception was handled by final handler!', 0
szMsgERR1	db 'It would never Get here!', 0
buff		db 200 dup(0)

	.code
start:
	lea eax, Final_Handler
	invoke SetUnhandledExceptionFilter, eax
	xor ecx, ecx
	mov eax, 200
	cdq
	div ecx
	; 以下不会运行
	invoke MessageBox, NULL, addr szMsgERR1, addr szCaption, \
		MB_OK or MB_ICONEXCLAMATION
	invoke ExitProcess, NULL
	
Final_Handler:
	invoke MessageBox, NULL, addr szMsgOK, addr szCaption, \
		MB_OK or MB_ICONEXCLAMATION
	mov eax, EXCEPTION_EXECUTE_HANDLER	; 1, 这时不出现非法操作的对话框
	;mov eax, EXCEPTION_CONTINUE_SEARCH	; 0, 出现,这时是调用系统默认的异常处理过程,程序被终结
	;mov eax, EXCEPTION_CONTINUE_EXECUTION	; -1, 不断出现对话框,你将先去死循环
						; 因为我们并没有修复ecx,所以不断产生异常,然后不断调用这个例程

	ret
	
	end start

windows根据你的异常处理程序的返回值来决定如何进一步处理
EXCEPTION_EXECUTE_HANDLER            equ 1    表示我已经处理了异常,可以优雅地结束了
EXCEPTION_CONTINUE_SEARCH            equ 0    表示我不处理,其他人来吧,于是windows调用默认的处理程序
                                                                                           显示一个错误框,并结束
EXCEPTION_CONTINUE_EXECUTION     equ -1   表示错误已经被修复,请从异常发生处继续执行


另一种是per_Thread Exception Handler->线程相关的异常处理,通常每个线程初始化准备好运行时fs指向一个TIB结构 (THREAD INFORMATION BLOCK)

这个结构的第一个元素fs:[0]指向一个_EXCEPTION_REGISTRATION结构, 后面_EXCEPTION_REGISTRATION为了简化,用ERR来代替这个结构...不要说没见过啊...
fs:[0]->
    _EXCEPTION_REGISTRATION struc
    prev dd ?                      ;前一个_EXCEPTION_REGISTRATION结构
    handler dd ?                  ;异常处理例程入口....呵呵,现在明白该怎么作了吧
    _EXCEPTION_REGISTRATION ends
    我们可以建立一个ERR结构然后将fs:[0]换成指向他的指针,当然最常用的是堆栈,如果你用静态内存区也可以,没有人阻止你
    在asm世界,放心地干吧,除了多S几次之外,通常不会有更大的损失
    把handler域换成你的程序入口,就可以在发生异常时调用你的代码了

Ps: 下面的代码运行后会弹出异常处理例程中的对话框,然后弹出系统错误报告对话框,点击关闭后,又会弹出异常处理例程中的对话框,具体原因参见<Windows环境32位汇编语言程序设计>14.3.4节

注意和final返回值的含义不同

	.386
	.model flat, stdcall
	option casemap:none
	
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

	.data
szCaption	db 'TestSEH', 0
szMsgOK		db "It's now in the Per_Thread handler!", 0
szMsgERR1	db 'It would never Get here!', 0
buff		db 200 dup(0)

	.code
start:
	assume fs:nothing
	push offset perThread_Handler
	push fs:[0]
	mov fs:[0], esp
	xor ecx, ecx
	mov eax, 200
	cdq
	div ecx
	; 以下不会运行
	invoke MessageBox, NULL, addr szMsgERR1, addr szCaption, \
		MB_OK or MB_ICONINFORMATION
	pop fs:[0]
	add esp, 4
	invoke ExitProcess, NULL
	
perThread_Handler:
	invoke MessageBox, NULL, addr szMsgOK, addr szCaption, \
		MB_OK or MB_ICONINFORMATION
	mov eax, 1	; ExceptionContinueSearch, 不处理,由其他例程或系统处理
	;mov eax, 0	; ExceptionContinueExecution, 表示修复CONTEXT, 可以从已成发生处继续执行
			; 如果返回0,你会陷入死循环,不断跳出对话框
	ret
	
	end start

当异常发生时,系统给了我们一个处理异常的机会,他首先会调用我们自定义的seh处理例程,当然也包括
了相关信息,在调用之前,系统把包含这些信息结构的指针压入stack,供我们的异常处理例程调用,
传递给例程的参数通常是四个,其中只有三个有明确意义,另一个到现在为止还没有发现有什么作用,
这些参数是:pExcept:DWORD,pErr:DWORD,pContext:DWORD,pDispatch意义如下:
pExcept:  ---  EXCEPTION_RECORD结构的指针
pErr:    ---  前面ERR结构的指针
pContext: --- CONTEXT结构的指针
pDispatch:---没有发现有啥意义

ERR结构是前面介绍的_EXCEPTION_REGISTRATION结构,往前翻翻,Dispatch省略,下面介绍
EXCEPTION_RECORD和CONTEXT结构的定义(Windows核心编程第五版P657, Windows环境下32位汇编程序设计第二版P508):

        EXCEPTION_RECORD STRUCT
          ExceptionCode        DWORD      ?      ;//异常码
          ExceptionFlags        DWORD      ?      ;//异常标志
          pExceptionRecord      DWORD      ?      ;//指向另外一个EXCEPTION_RECORD的指针
          ExceptionAddress      DWORD      ?      ;//异常发生的地址
          NumberParameters      DWORD      ?      ;//下面ExceptionInformation所含有的dword数目
          ExceptionInformation  DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)
        EXCEPTION_RECORD ENDS                      ;//EXCEPTION_MAXIMUM_PARAMETERS ==1

ExceptionCode 异常类型,SDK里面有很多类型,你可以在windows.inc里查找STATUS_来找到更多
的异常类型,下面只给出hex值,具体标识定义请查阅windows.inc,你最可能遇到的几种类型如下:

              C0000005h----读写内存冲突
              C0000094h----非法除0
              C00000FDh----堆栈溢出或者说越界
              80000001h----由Virtual Alloc建立起来的属性页冲突
              C0000025h----不可持续异常,程序无法恢复执行,异常处理例程不应处理这个异常
              C0000026h----在异常处理过程中系统使用的代码,如果系统从某个例程莫名奇妙的返回,则出现此代码,
                          如果RtlUnwind时没有Exception Record参数也同样会填入这个代码
              80000003h----调试时因代码中int3中断
              80000004h----处于被单步调试状态

              注:也可以自己定义异常代码,遵循如下规则:
              _____________________________________________________________________+

              位:      31~30            29~28          27~16          15~0
              _____________________________________________________________________+
              含义:    严重程度          29位            功能代码        异常代码
                        0==成功        0==Mcrosoft    MICROSOFT定义  用户定义
                        1==通知        1==客户
                        2==警告          28位
                        3==错误        被保留必须为0
ExceptionFlags 异常标志
              0----可修复异常
              1----不可修复异常
              2----正在展开,不要试图修复什么,需要的话,释放必要的资源
pExceptionRecord 如果程序本身导致异常,指向那个异常结构
ExceptionAddress 发生异常的eip地址
ExceptionInformation 附加消息,在调用RaiseException可指定或者在异常号为C0000005h即内存异常时含义如下
              第一个dword 0==读冲突 1==写冲突
              第二个dword 读写冲突地址

        CONTEXT STRUCT                    ; _
          ContextFlags  DWORD      ?      ;  |            +0
          iDr0          DWORD      ?      ;  |            +4
          iDr1          DWORD      ?      ;  |            +8
          iDr2          DWORD      ?      ;  >调试寄存器  +C
          iDr3          DWORD      ?      ;  |            +10
          iDr6          DWORD      ?      ;  |            +14
          iDr7          DWORD      ?      ; _|            +18
          FloatSave    FLOATING_SAVE_AREA <>  ;浮点寄存器区 +1C~~~88h
          regGs        DWORD      ?      ;--|            +8C
          regFs        DWORD      ?      ;  |\段寄存器    +90 
          regEs        DWORD      ?      ;  |/            +94           
          regDs        DWORD      ?      ;--|            +98
          regEdi        DWORD      ?      ;____________    +9C
          regEsi        DWORD      ?      ;      |  通用  +A0
          regEbx        DWORD      ?      ;      |  寄    +A4
          regEdx        DWORD      ?      ;      |  存    +A8
          regEcx        DWORD      ?      ;      |  器    +AC
          regEax        DWORD      ?      ;_______|___组_  +B0     
          regEbp        DWORD      ?      ;++++++++++++++++ +B4
          regEip        DWORD      ?      ;    |控制        +B8
          regCs        DWORD      ?      ;    |寄存        +BC
          regFlag      DWORD      ?      ;    |器组        +C0
          regEsp        DWORD      ?      ;    |            +C4
          regSs        DWORD      ?      ;++++++++++++++++ +C8
          ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?)
        CONTEXT ENDS 

 I、传递给final句柄的参数,只有两个可描述为EXCEPTION_POINTERS结构,定义如下:
            EXCEPTION_POINTERS STRUCT
              pExceptionRecord  DWORD      ?             
              ContextRecord    DWORD      ?
            EXCEPTION_POINTERS ENDS

            在call xHandler之前,堆栈结构如下:
            esp    -> *EXCEPTION_RECORD
            esp+4  -> *CONTEXT record  ;//具体结构见下面

                然后执行call _Final_Handler,这样在程序里要调用什么不轻而易举了吗?

II、 传递给per_thread句柄的参数,如下:
    在call xHandler之前,在堆栈中形成如下结构
            esp    -> *EXCEPTION_RECORD
            esp+4  -> *ERR                    ;//注意这也就是fs:[0]的指向
            esp    -> *CONTEXT record          ;//point to registers
            esp    -> *Param                    ;//呵呵,没有啥意义

          然后执行 call _Per_Thread_Handler

调用handler的原型是这样
        invoke HANDLER,*EXCEPTION_RECORD,*_EXCEPTION_REGISTRATION,*CONTEXT record,*Param
        即编译代码如下:
        PUSH *Param                    ;//通常不重要,没有什么意义
        push *CONTEXT record            ;//上面的结构
        push *ERR                      ;//the struc above
        push *EXCEPTION_RECORD          ;//see above
        CALL HANDLER
        ADD ESP,10h

Windows PE 第十章 加载配置信息 加载配置信息     加载配置信息最初最用在Windows NT操作系统中,作为文件头部的延伸部分,后来被用作异常处理。加载配置信息表中存放了基于结构化异常处理(SEH)技术的各项异常句柄。当程序运行发生异常后,操作系统会根据异常处理类别对异常进行分发处理,并依据这些句柄实施程序流程的转向,保证系统能从异常中全身而退。 10.1 何为加载配置信息     加载配置信息结构是PE中定义的一种基 阅读详情

相关推荐

(笔记)逆向工程核心原理——SEH

SEH异常机制学习记录

qq_30101409的博客 878

PE结构详解(一)

本系列主要是参考英文原本ARTeam的PE File Format Tutorial并加以注解,以最简洁的语言来阐述PE格式,帮助大家快速入门。在开始之前,请确定您懂得C语言,至少是基本数据类型、数组和结构体,以及会WinHEX的基本使用方法。任何错误都欢迎指出,感激不尽! (一)介绍PE 格式     PE格式(Portable Executable,可移植可执行文件)是原生的Win32

本博客所有内容都是转的前辈们的 1507

uniapp监听扫码枪键盘事件|无输入框式监听

一般的扫码枪通过USB或蓝牙连接手机或电脑,充当的是一个外接设备。当扫码后,扫码枪会自动识别内容,然后向连接的电脑或手机发送键盘事件keydown或keyup。

tcy9099的专栏 9750

栈溢出中利用SEH

结构化异常处理(SEH)         操作系统或程序在运行,难免会遇到各种各样的错误,如除零,非法内存访问,文件打开错误,内存不足,磁盘读写错误,外设操作失败。为了保证系统在遇到错误时不至于崩溃,仍能够健壮稳定地继续运行下去,windows会对运行在其中的程序提供一次补救的机会来处理错误这种机制就是异常处理机制。 S.E.H即异常处理结构体(Structure Exception Handl

W Blog 5672

无人机飞控传感器全解析:从IMU到光流传感器的实战应用

本文深入解析了无人机飞控系统的核心传感器,从IMU、气压计、磁力计到光流传感器,详细阐述了其工作原理、数据融合策略及常见挑战。结合ICF5、Pixhawk 6C等主流飞控硬件,提供了从传感器校准、滤波器调优到应对室内光流定位、强磁干扰等实战场景的系统性优化指南,帮助开发者突破飞控“黑箱”,实现更稳定、可靠的飞行控制。

weixin_28396629的博客 168

SAP-QM-质检操作

SAP-QM-质检操作

yanzibing99的博客 8179

反调试—利用SEH链表

内容摘要:进程在被调试的时候,进程中TEB中的PEB中的BeingDebugged成员的值为1而正常情况下为0,于是通过访问该成员可知道当前是否为调试状态,来决定是否继续执行程序,原理和TLS反调试相似,不过这里把反调试代码放到了SEH中。 在说明该技术前要先来引用相关知识块: TEB(Thread Environment Block,线程环境块)系统在此TEB中保存频繁使用的线程相关的数据。

王二娃的生活的博客 2902

QGIS:安装Deepness插件+应用

简单的描述了deepness的安装应用历程,重点是记录博主本次遇到的坑

fengyi157的博客 2336

WINDOWS+PE权威指南读书笔记(16)

加载配置信息 何谓加载配置信息: Windows 结构化异常处理: 什么是SEH: Windows 异常分类: 内核模式下的异常处理: 用户模式下的异常处理: Windows SEH 机制解析: SEH 编程实例: PE 中的加载配置信息: 加载配置信息定位: 加载配置目录 IMAGE_LOAD_CONFIG_DIRECTORY: 加载配置信息实例分析: 加载配置编程: 程序源代码分析: 为 PE 添加加载配置信息: 运行测试: 注册多个异常处理函数示例: 小

沐一 · 林 的博客 1686

手把手教你用AirSim和Unreal4在Ubuntu上创建自定义无人机仿真场景

本指南详细介绍了如何在Ubuntu系统上使用AirSim和Unreal Engine 4搭建高精度无人机仿真环境。从硬件配置、驱动安装到Unreal Engine编译和AirSim集成,手把手教你创建自定义场景,适用于算法验证和开发测试。

y7z8a的博客 257

dpdk 源码分析 rte_ring

rte_ring源码分析

qq_40843445的博客 929

从安全角度看 SEH 和 VEH

异常处理程序是处理程序中不可预见的错误的基本方法之一。

dongfangycddd的博客 2261

非球面检测

摘要:非球面镜检测主要采用干涉法,包括零位补偿和非零位补偿两种方式。零位补偿通过透镜组完全校正像差,精度达1%波长,但设计复杂成本高;非零位补偿利用部分像差补偿降低波前斜率,适用于规则非球面。折射-衍射混合补偿镜能同时校正多种像差。实际检测中可采用物理补偿镜或动态CGH加载方案,光路设计需运用坐标断点和多重结构技术实现复杂光路建模。不同方法各具优势,需根据检测需求选择合适方案。

Zhaxibamao的博客 2108

SEH

SEH是Windows操作系统的异常处理基址,在程序源代码中使用__try、__except、__finally关键字来具体实现。 SEH练习示例 #1 示例程序seh.exe,该程序故意触发了内存非法访问异常,然后通过SEH机制来处理该异常,并且使用PEB信息向程序添加简单的反调试代码,使程序在正常运行与调试运行时表现出不同的行为动作。 正常运行。 使用OllyDbg调试器打开seh.exe示例程序。 打开seh.exe程序后按F9键运行,发生非法访问异常后暂停调试。 401019地址处添.

qq_39249347的博客 1137

DFT ATPG提升coverage的方法

采用更精确的故障模型,如桥接故障模型(用于描述电路中不应连接的节点之间的短路故障)和路径延迟故障模型(考虑信号在电路路径上的传输延迟),可以使ATPG工具生成更有效的测试向量来覆盖这些特殊故障。假设电路中有一个与门的输入引脚存在固定为0的故障(stuck - at - 0),D - 算法会分析与门的其他输入以及与门输出连接的后续电路逻辑,从而确定整个电路的输入值,使得当该故障存在时,电路的输出与无故障时不同。通过增加测试向量的类型,如功能测试向量和结构测试向量的结合,可以提高故障覆盖率。

Loving_enjoy的博客 969

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

X64 SEH的展开

C++ 代码: #include "stdafx.h" #include ULONG WINAPI FilterFunc(DWORD dwExceptionCode) { return (dwExceptionCode == STATUS_INTEGER_DIVIDE_BY_ZERO) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SE

nethm的专栏 2237

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

SEH结构

首先有几点问题 1.在后文中看到的PE的节中的配置信息表Load configuration是对SEH回调函数的注册,那么Exception Table是加载的什么信息。 2.什么时候走进系统异常,什么时候走进自己注册的异常回调函数。 摘自 《加密与解密》和《Windows PE权威指南》 0x01 Windows结构化异常处理   结构化异常处理是Windows OS处理程序错误...

weixin_30768175的博客 480

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

PE Tools加载配置目录编辑器:如何修改Windows PE加载配置的终极指南

PE Tools是一款强大的便携式可执行文件(PE)操作工具包,专门用于Windows PE文件的逆向工程和分析。在PE Tools v1.9版本中,新增了一个重要功能——加载配置目录编辑器,这个功能让您能够直接查看和编辑Windows PE文件的加载配置信息,为逆向工程和安全研究提供了极大的便利。😊 ## 什么是Windows PE加载配置目录? Windows PE文件的加载配置目录(L

gitblog_00776的博客 476

SEH, SAFESEH相关

  SEH, SAFESEH相关1,触发seh异常让目标程序Read/Write无效地址,如果和栈底相邻的内存只读,尝试覆盖超出栈底2,如何找到(显示)要覆盖的SEHod语法:dd fs:[0]softice语法: dd fs:03,覆盖SEH时需要填充的跳转地址需要找一个可以跳转成功的pop ?pop ?retn 的代码地址。od语法:Ctrl+B/L 5? 5? c3softice语法:s ...

weixin_34026276的博客 185

【逆向工程核心原理:SEH】

【逆向工程核心原理:SEH】

whl0071的专栏 753

seh to fasm 研究

seh to fasm 研究作者:小鱼为啥有此文呢,可能你会说seh文章很多啊。那请你去各大搜索引擎搜索下相关fasm的seh文章以及官方搜索下更是甚少,几乎没有。所以就有了此文,希望能帮到一些和我一样喜欢fasm的朋友们。1.概念 SEH的英文全称是"Structured Exception Handling", 即"结构化异常处理",是windows提供给我们异常处理机制。 在

xyblack技术地带 1705

《黑客免杀攻防学习笔记》——PE文件结构3

文章中的图片大部分为自己做截屏,少量摘自网络,若有侵权请及时告知,我会尽快删除。转载请注明出处。 接上一篇,上一篇了解了一些常用的结构的基本结构,这一篇从异常结构开始学习。 7.异常          PE文件中的异常目录用于描述异常处理函数、SEH地址等信息,存储于.pdata 段内,数据目录项的第四项指向结构的RVA。异常处理和处理器平台有关,所以书上是用64位处理器的平台作

Traxer的学习之路 1444
上一篇: hash扫描获得api函数地址学习笔记
下一篇: 如何编写病毒代码学习笔记
ProgrammingRing
博客等级 码龄17年 76粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值