4.3一些关于构建新的PE文件的说明
用一下片断来排列虚拟地址和每个节区的虚拟大小
Code:
image_section_header[i]->VirtualAddress= PEAlign(image_section_header[i]->VirtualAddress, image_nt_headers->OptionalHeader.SectionAlignment);
image_section_header[i]->Misc.VirtualSize= PEAlign(image_section_header[i]->Misc.VirtualSize, image_nt_headers->OptionalHeader.SectionAlignment);
|
Align the PointerToRawData and the SizeOfRawData of each section by FileAlignment
Code:
image_section_header[i]->PointerToRawData = PEAlign(image_section_header[i]->PointerToRawData, image_nt_headers->OptionalHeader.FileAlignment);
image_section_header[i]->SizeOfRawData = PEAlign(image_section_header[i]->SizeOfRawData, image_nt_headers->OptionalHeader.FileAlignment);
|
Correct the SizeofImage by the virtual size and the virtual address of the last section
Code:
image_nt_headers->OptionalHeader.SizeOfImage = image_section_header[LastSection]->VirtualAddress + image_section_header[LastSection]->Misc.VirtualSize;
|
Set the Bound Import Directory header to zero, as this directory is not very important to execute a PE file:
Code:
image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]. VirtualAddress = 0; image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
|
4.4一些关于连接这个VC程序的解释
Set Linker->General->Enable Incremental Linking to No (/INCREMENTAL:NO).
你能了解增加连接和不增加连接之间的不同
为了获得DynLoader(), 的虚拟地址,再增加LINK中我们获得JMP pemaker.DynLoader 的虚拟地址。但是不使
用LINK时用以下代码获得地址
Code:
DWORD dwVA= (DWORD) DynLoader;
|
This setting is more critical in the incremental link when you try to find the beginning and ending of the Loader,
DynLoader(), by CPECryptor::ReturnToBytePtr():(抱歉,这句话偶不知道如何才能翻译通顺

)
Code:
void* CPECryptor::ReturnToBytePtr(void* FuncName, DWORD findstr) { void* tmpd; __asm { mov eax, FuncName jmp df hjg:inc eax df:mov ebx, [eax] cmp ebx, findstr jnz hjg mov tmpd, eax } return tmpd; }
|
5.保存重要的数据和延伸原入口点
现在,我们已经保存了原入口点,并映射了基地址以便于到达虚拟地址入口点。在DynLoader()结尾处我已
经保存一个空白区来储存这些重要的数据。DynLoader Step 2.
PE Maker - Step 2 Download source files - 58.3 Kb Code:
DynLoaderStep2>DynLoader Step 2
__stdcall void DynLoader() { _asm { //---------------------------------- DWORD_TYPE(DYN_LOADER_START_MAGIC) //---------------------------------- Main_0: PUSHAD // get base ebp CALL Main_1 Main_1: POP EBP SUB EBP,OFFSET Main_1 MOV EAX,DWORD PTR [EBP+_RO_dwImageBase] ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint] PUSH EAX RETN // >> JMP to Original OEP //---------------------------------- DWORD_TYPE(DYN_LOADER_START_DATA1) //---------------------------------- _RO_dwImageBase:DWORD_TYPE(0xCCCCCCCC) _RO_dwOrgEntryPoint:DWORD_TYPE(0xCCCCCCCC)FONT> //---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC) //---------------------------------- } }
|
5.1恢复开始时寄存器间的关系
恢复他们间的关系是重要的,但在DynLoader Step 2的源代码中我们还没有做这件事。我们可以修改
DynLoader() 函数来重建开始的关系
Code:
__stdcall void DynLoader() { _asm { //---------------------------------- DWORD_TYPE(DYN_LOADER_START_MAGIC) //---------------------------------- Main_0: <FONT color=red>PUSHAD// Save the registers context in stack CALL Main_1 Main_1: POP EBP// Get Base EBP SUB EBP,OFFSET Main_1 MOV EAX,DWORD PTR [EBP+_RO_dwImageBase] ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint] MOV DWORD PTR [ESP+1Ch],EAX // pStack.Eax <- EAX <FONT color=red>POPAD // Restore the first registers context from stack PUSH EAX XOREAX, EAX RETN // >> JMP to Original OEP //---------------------------------- DWORD_TYPE(DYN_LOADER_START_DATA1) //---------------------------------- _RO_dwImageBase:DWORD_TYPE(0xCCCCCCCC) _RO_dwOrgEntryPoint:DWORD_TYPE(0xCCCCCCCC) //---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC) //---------------------------------- } }
|
5.2恢复最初的堆栈
我们能利用设置在堆栈开始处的值加0x34到原入口点,来恢复原始的堆栈,但这不是很重要。然而在下
面的代码中我用了一个简单的技巧来到达OEP以便于修改堆栈,你能利用OD来跟踪执行过程。
Code:
__stdcall void DynLoader() { _asm { //---------------------------------- DWORD_TYPE(DYN_LOADER_START_MAGIC) //---------------------------------- Main_0: PUSHAD // Save the registers context in stack CALL Main_1 Main_1: POP EBP SUB EBP,OFFSET Main_1 MOV EAX,DWORD PTR [EBP+_RO_dwImageBase] ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint] MOV DWORD PTR [ESP+54h],EAX // pStack.Eip <- EAX POPAD // Restore the first registers context from stack CALL _OEP_Jump DWORD_TYPE(0xCCCCCCCC) _OEP_Jump: PUSH EBP MOV EBP,ESP MOV EAX,DWORD PTR [ESP+3Ch] // EAX <- pStack.Eip MOV DWORD PTR [ESP+4h],EAX// _OEP_Jump RETURN pointer <- EAX XOR EAX,EAX LEAVE RETN //---------------------------------- DWORD_TYPE(DYN_LOADER_START_DATA1) //---------------------------------- _RO_dwImageBase:DWORD_TYPE(0xCCCCCCCC) _RO_dwOrgEntryPoint:DWORD_TYPE(0xCCCCCCCC) //---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC) //---------------------------------- } }
|
5.3Approach OEP by Structured Exception Handling(借助于构造异常处理来接近OEP)
当程序执行了错误的代码就会抛出一个异常,在这种条件下,程序迅速跳转到一个叫做异常处理的功能
中。
下面的文件包含了异常处理的调用,异常的抛出,以及其功能。
Code:
#include "stdafx.h" #include "windows.h"
void RAISE_AN_EXCEPTION() { _asm { INT 3 INT 3 INT 3 INT 3 } }
int _tmain(int argc, _TCHAR* argv[]) { __try { __try{ printf("1: Raise an Exception\n"); RAISE_AN_EXCEPTION(); } __finally { printf("2: In Finally\n"); } } __except( printf("3: In Filter\n"), EXCEPTION_EXECUTE_HANDLER ) { printf("4: In Exception Handler\n"); } return 0; }
|
;Code:
main() 00401000: PUSH EBP 00401001: MOV EBP,ESP 00401003: PUSH -1 00401005: PUSH 00407160 ; __try { ; the structured exception handler (SEH) installation 0040100A: PUSH _except_handler3 0040100F: MOV EAX,DWORD PTR FS:[0] 00401015: PUSH EAX 00401016: MOV DWORD PTR FS:[0],ESP 0040101D: SUB ESP,8 00401020: PUSH EBX 00401021: PUSH ESI 00401022: PUSH EDI 00401023: MOV DWORD PTR SS:[EBP-18],ESP ;__try { 00401026: XOR ESI,ESI 00401028: MOV DWORD PTR SS:[EBP-4],ESI 0040102B: MOV DWORD PTR SS:[EBP-4],1 00401032: PUSH OFFSET "1: Raise an Exception" 00401037: CALL printf 0040103C: ADD ESP,4 ; the raise a exception, INT 3 exception ; RAISE_AN_EXCEPTION() 0040103F: INT3 00401040: INT3 00401041: INT3 00401042: INT3 ;} __finally { 00401043: MOV DWORD PTR SS:[EBP-4],ESI 00401046: CALL 0040104D 0040104B: JMP 00401080 0040104D: PUSH OFFSET "2: In Finally" 00401052: CALL printf 00401057: ADD ESP,4 0040105A: RETN <FONT color=black>;} <FONT color=black>; } <FONT color=black>; __except( 0040105B: JMP 00401080 0040105D: PUSH OFFSET "3: In Filter" 00401062: CALL printf 00401067: ADD ESP,4 0040106A: MOV EAX,1 ; EXCEPTION_EXECUTE_HANDLER = 1 0040106F: RETN ;, EXCEPTION_EXECUTE_HANDLER ) ; } ; the exception handler funtion 00401070: MOV ESP,DWORD PTR SS:[EBP-18] 00401073: PUSH OFFSET "4: In Exception Handler" 00401078: CALL printf 0040107D: ADD ESP,4 ; } 00401080: MOV DWORD PTR SS:[EBP-4],-1 0040108C: XOR EAX,EAX ; restore previous SEH 0040108E: MOV ECX,DWORD PTR SS:[EBP-10] 00401091: MOV DWORD PTR FS:[0],ECX 00401098: POP EDI 00401099: POP ESI 0040109A: POP EBX 0040109B: MOV ESP,EBP 0040109D: POP EBP 0040109E: RETN
|
建立一个控制台工程,连接并执行先前的文件,观察结果。
Code:
1: Raise an Exception 3: In Filter 2: In Finally 4: In Exception Handler _
|
程序执行了一个异常的表达式printf("3: In Filter\n");,当异常发生,(在这个例子INT 3 异常,你也能用另外
的异常),在OD中Debugging options->Exceptions你能看到不同类型异常的清单
5.3.1 Implement Exception Handler
我们希望建立一个异常处理来到达程序的OEP,借助前满的代码,你现在已经能区别SEH安装,异常抛
出,异常表达式过滤,为了建立我们的异常接近(OEP),我们需要下面的代码
SEH installation
Code:
LEA EAX,[EBP+_except_handler1_OEP_Jump] PUSH EAX PUSH DWORD PTR FS:[0] MOV DWORD PTR FS:[0],ESP
|
异常抛出
Code:
异常表达式过滤
Code:
except_handler1_OEP_Jump: PUSH EBP MOV EBP,ESP ... MOV EAX, EXCEPTION_CONTINUE_SEARCH // EXCEPTION_CONTINUE_SEARCH = 0 LEAVE RETN
|
所以我们利用ASM嵌套来到达OEP
Code:
__try // SEH installation { __asm { INT 3 // An Exception Raise } } __except( ..., EXCEPTION_CONTINUE_SEARCH ){} // Exception handler expression filter
|
ASM代码
Code:
;---------------------------------------------------- ; the structured exception handler (SEH) installation ; __try { LEA EAX,[EBP+_except_handler1_OEP_Jump] PUSH EAX PUSH DWORD PTR FS:[0] MOV DWORD PTR FS:[0],ESP <FONT color=green>; ---------------------------------------------------- ; the raise a INT 3 exception INT 3 INT 3 INT 3 INT 3 ; } ; __except( ... ; ---------------------------------------------------- ; exception handler expression filter _except_handler1_OEP_Jump: PUSH EBP MOV EBP,ESP ... MOV EAX, EXCEPTION_CONTINUE_SEARCH ; EXCEPTION_CONTINUE_SEARCH = 0 LEAVE RETN ;, EXCEPTION_CONTINUE_SEARCH ) { }
|
异常值__except(..., Value), 定义了这个异常如何被处理,他能由三个值分别是1,0,-1。为了理解他们
请在MSDN Library中寻找有关
try-except 声明的描述。当我们把他设置为0时,就不会执行异常处理的功能,
因此,借助于这个值,异常处理就会被忽略,并且线程继续执行其他的代码。
如何安装SEH 就像你从前面的代码中看到的,SEH是借助于FS段寄存器还完成安装的。WIN32使用FS段寄存器作为主线
程中数据块的指针。开始的0X1C字节包含Thread Information Block (TIB)的信息,此外,FS:[00h] 在与主线程
的ExceptionList 有关(表三),在我们的代码中,我们已经将_except_handler1_OEP_Jump指针压入堆栈,
并改变ExceptionList, FS:[00h]的值为堆栈起始的值,ESP
线程信息块(TIB)
Code:
typedef struct _NT_TIB32 { DWORD ExceptionList; DWORD StackBase; DWORD StackLimit; DWORD SubSystemTib; union { DWORD FiberData; DWORD Version; }; DWORD ArbitraryUserPointer; DWORD Self; } NT_TIB32, *PNT_TIB32;
|
表三 FS段寄存器和线程信息块
5.3.2借助于调整线程间的关系获得OEP 在这部分,我们将完成OEP的接近,并改变线程间的关系,忽略每个简单的异常处理。让线程在先前的
OEP中继续执行代码。当产生异常时,进程间的关系就被保存在堆栈中,借助EXCEPTION_POINTERS,我
们能访问ContextRecord的指针。ContextRecord拥有CONTEXT的数据结构(表四),这便是在执行时间中
的线程间的关系。当我们使用 EXCEPTION_CONTINUE_SEARCH (0)来忽略异常时,指令指针间的关系也
被设置到ContextRecord,以便程序可以返回先前的环境中去。因此,如果我们改变 Win32 Thread Context
中的EIP,使其为开始的OEP,这便可以进入OEP。
表四


Code:
MOV EAX, ContextRecord MOV EDI, dwOEP; EAX <- dwOEP MOV DWORD PTR DS:[EAX+0B8h], EDI ; pContext.Eip <- EAX
|
WIN32线程间关系的数据结构 Code:
#define MAXIMUM_SUPPORTED_EXTENSION512
typedef struct _CONTEXT { //----------------------------------------- DWORD ContextFlags; //----------------------------------------- DWORDDr0; DWORDDr1; DWORDDr2; DWORDDr3; DWORDDr6; DWORDDr7; //----------------------------------------- FLOATING_SAVE_AREA FloatSave; //----------------------------------------- DWORDSegGs; DWORDSegFs; DWORDSegEs; DWORDSegDs; //----------------------------------------- DWORDEdi; DWORDEsi; DWORDEbx; DWORDEdx; DWORDEcx; DWORDEax; //----------------------------------------- DWORDEbp; DWORDEip; DWORDSegCs; DWORDEFlags; DWORDEsp; DWORDSegSs; //----------------------------------------- BYTEExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; //---------------------------------------- } CONTEXT, *LPCONTEXT;
|
用下面的代码,我们就能使用异常处理程序来到达OEP
Code:
__stdcall void DynLoader() { _asm { //---------------------------------- DWORD_TYPE(DYN_LOADER_START_MAGIC) //---------------------------------- Main_0: PUSHAD// Save the registers context in stack CALL Main_1 Main_1: POP EBP SUB EBP,OFFSET Main_1 // Get Base EBP MOV EAX,DWORD PTR [EBP+_RO_dwImageBase] ADD EAX,DWORD PTR [EBP+_RO_dwOrgEntryPoint] MOV DWORD PTR [ESP+10h],EAX// pStack.Ebx <- EAX LEA EAX,[EBP+_except_handler1_OEP_Jump] MOV DWORD PTR [ESP+1Ch],EAX// pStack.Eax <- EAX POPAD// Restore the first registers context from stack //---------------------------------------------------- // the structured exception handler (SEH) installation PUSH EAX XOREAX, EAX PUSH DWORD PTR FS:[0]// NT_TIB32.ExceptionList MOV DWORD PTR FS:[0],ESP// NT_TIB32.ExceptionList <-ESP //---------------------------------------------------- // the raise a INT 3 exception DWORD_TYPE(0xCCCCCCCC) //-------------------------------------------------------- // -------- exception handler expression filter ---------- _except_handler1_OEP_Jump: PUSH EBP MOV EBP,ESP //------------------------------ MOV EAX,DWORD PTR SS:[EBP+010h]// PCONTEXT: pContext <- EAX //============================== PUSH EDI // restore original SEH MOV EDI,DWORD PTR DS:[EAX+0C4h]// pContext.Esp PUSH DWORD PTR DS:[EDI] POP DWORD PTR FS:[0] ADD DWORD PTR DS:[EAX+0C4h],8// pContext.Esp //------------------------------ // set the Eip to the OEP MOV EDI,DWORD PTR DS:[EAX+0A4h] // EAX <- pContext.Ebx MOV DWORD PTR DS:[EAX+0B8h],EDI // pContext.Eip <- EAX //------------------------------ POP EDI //============================== MOV EAX, EXCEPTION_CONTINUE_SEARCH LEAVE RETN //---------------------------------- DWORD_TYPE(DYN_LOADER_START_DATA1) //---------------------------------- _RO_dwImageBase:DWORD_TYPE(0xCCCCCCCC) _RO_dwOrgEntryPoint:DWORD_TYPE(0xCCCCCCCC) //---------------------------------- DWORD_TYPE(DYN_LOADER_END_MAGIC) //---------------------------------- } }
|