Inject Your Code To A Portable Executable File - CodeProject®
Inject Your Code To A Portable Executable File - CodeProject®
doicanhden
422
Sign out
Home
Articles
Quick Answers
Discussions
Learning Zones
Features
Help!
The Lounge
Contents
0. Preface 1. Prerequisite 2. Portable Executable file format 2.1 The MS-DOS data 2.2 The Windows NT data 2.3 The Section Headers and Sections 3. Debugger, Disassembler and some Useful Tools 3.1 Debuggers 3.1.1 SoftICE 3.1.2 OllyDbg 3.1.3 Which parts are important in a debugger interface? 3.2 Disassembler 3.2.1 Proview disassembler 3.2.2 W32Dasm 3.2.3 IDA Pro 3.3 Some Useful Tools 3.3.1 LordPE 3.3.2 PEiD 3.3.3 Resource Hacker 3.3.4 WinHex 3.3.5 CFF Explorer 4. Add new section and Change OEP 4.1 Retrieve and Rebuild PE file 4.2 Create Data for new Section 4.3 Some notes regarding creating a new PE file 4.4 Some notes regarding linking this VC Project
1 of 40
5. Store Important Data and Reach Original OEP 5.1 Restore the first Registers Context 5.2 Restore the Original Stack 5.3 Approach OEP by Structured Exception Handling 5.3.1 Implement Exception Handler 5.3.2 Attain OEP by adjusting the Thread Context 6. Build an Import Table and Reconstruct the Original Import Table 6.1 Construct the Client Import Table 6.2 Using other API functions in run-time 6.3 Fix up the Original Import Table 7. Support DLL and OCX 7.1 Twice OEP approach 7.2 Implement Relocation Table 7.3 Build a Special Import table 8. Preserve the Thread Local Storage 9. Inject your code 10. Conclusion
0 Preface
It might be, you demand to comprehend the ways a virus program injects its procedure in to the interior of a portable executable file and corrupts it, or you are interested in implementing a packer or a protector for your specific intention to encrypt the data of your portable executable (PE) file. This article is committed to represent a brief intuition to realize the performance which is accomplished by EXE tools or some kind of mal-wares. You can employ the source code of this article to create your custom EXE builder. It could be used to make an EXE protector in the right way, or with a wrong intention, to pullulate a virus. However, my purpose of writing this article has been to gaze on the first application, so I will not be responsible for the immoral usage of these methods.
1 Prerequisite
There are no specific mandatory prerequisites to follow the topics in this article. If you are familiar with debugger and also the portable file format, I suggest you to drop the sections 2 and 3, the whole of these sections have been made for people who dont have any knowledge regarding the EXE file format and also debuggers.
2 of 40
e_lfanew is the offset which refers to the position of the Windows NT data. I have provided a program
to obtain the header information from an EXE file and to display it to you. To use the program, just try: PE Viewer Download source files - 132 Kb
3 of 40
This sample is useful for the whole of this article. Table 1 - Portable Executable file format structure DOS EXE Signature
4 of 40
0000000A 0000000C 0000000E 00000010 00000012 00000014 00000016 00000018 0000001A 0000001C 0000003B 0000003C
DW DW DW DW DW DW DW DW DW DB
DB 00 DD 000000F0
Signature
PE signature (PE)
000000F0
ASCII "PE"
Machine NumberOfSections TimeDateStamp IMAGE_ PointerToSymbolTable FILE_HEADER NumberOfSymbols SizeOfOptionalHeader Characteristics MagicNumber MajorLinkerVersion MinorLinkerVersion SizeOfCode SizeOfInitializedData SizeOfUninitializedData AddressOfEntryPoint BaseOfCode BaseOfData Windows NT ImageBase information SectionAlignment IMAGE_ FileAlignment NT_HEADERS MajorOSVersion MinorOSVersion MajorImageVersion IMAGE_ MinorImageVersion OPTIONAL_ MajorSubsystemVersion HEADER32 MinorSubsystemVersion Reserved SizeOfImage SizeOfHeaders CheckSum Subsystem DLLCharacteristics SizeOfStackReserve SizeOfStackCommit SizeOfHeapReserve SizeOfHeapCommit LoaderFlags NumberOfRvaAndSizes IMAGE_ DATA_DIRECTORY[16]
DW DW DD DD DD DW DW
00000108 0000010A 0000010B 0000010C 00000110 00000114 00000118 0000011C 00000120 00000124 00000128 0000012C 00000130 00000132 00000134 00000136 00000138 0000013A 0000013C 00000140 00000144 00000148 0000014C 0000014E 00000150 00000154 00000158 0000015C 00000160 00000164
DW DB DB DD DD DD DD DD DD DD DD DD DW DW DW DW DW DW DD DD DD DD DW DW DD DD DD DD DD DD
010B 07 00 00012800 00009C00 00000000 00012475 00001000 00014000 01000000 00001000 00000200 0005 0001 0005 0001 0004 0000 00000000 0001F000 00000400 0001D7FC 0002 8000 00040000 00001000 00100000 00001000 00000000 00000010
5 of 40
Resource Table Exception Table Certificate File Relocation Table Debug Data Architecture Data Global Ptr TLS Table Load Config Table Bound Import Table Import Address Table Delay Import Descriptor COM+ Runtime Header Reserved
Name[8] VirtualSize VirtualAddress SizeOfRawData PointerToRawData PointerToRelocations PointerToLineNumbers NumberOfRelocations NumberOfLineNumbers Characteristics
00000210 00000218 0000021C 00000220 00000224 00000228 0000022C 00000230 00000232 00000234 00000238 00000240 00000244 00000248 0000024C 00000250 00000254 00000258 0000025A 0000025C ASCII".data"; DD 0000101C ; DD 00014000 ; DD 00000A00 ; DD 00012C00 ; DD 00000000 ; DD 00000000 ; DW 0000 ; DW 0000 ; DD C0000040 ; ASCII".rsrc"; DD 00008960 ; DD 00016000 ; DD 00008A00 ; DD 00013600 ; DD 00000000 ; DD 00000000 ; DW 0000 ; DW 0000 ; DD 40000040 ;
000001E8 ASCII".text" 000001F0 DD 000126B0 000001F4 DD 00001000 000001F8 DD 00012800 000001FC DD 00000400 00000200 DD 00000000 00000204 DD 00000000 00000208 DW 0000 0000020A DW 0000 0000020C DD 60000020 CODE|EXECUTE|READ
SECTION VirtualSize = 0x101C VirtualAddress = 0x14000 SizeOfRawData = 0xA00 PointerToRawData = 0x12C00 PointerToRelocations = 0x0 PointerToLineNumbers = 0x0 NumberOfRelocations = 0x0 NumberOfLineNumbers = 0x0 Characteristics = INITIALIZED_DATA|READ|WRITE SECTION VirtualSize = 0x8960 VirtualAddress = 0x16000 SizeOfRawData = 0x8A00 PointerToRawData = 0x13600 PointerToRelocations = 0x0 PointerToLineNumbers = 0x0 NumberOfRelocations = 0x0 NumberOfLineNumbers = 0x0 Characteristics = INITIALIZED_DATA|READ
SECTION[0]
00000400 00000408 00000410 00000418 00000420 00000428 00000430 00000438 00000440 00000448 00000450
EA 9A 2E FF 93 FD A3 41 E6 7A 14
22 18 1E 1E 9F A5 36 E3 1B 17 1B
DD DD C7 C7 E7 E7 E7 E6 E6 E6 E7
77 77 77 77 77 77 77 77 77 77 77
D7 00 83 00 D8 AD 03 60 2B 79 C1
23 00 1D 00 05 A9 38 8D 2A C8 30
DD 00 C7 00 E8 E9 E7 E7 E7 E6 E7
77 00 77 00 77 77 77 77 77 77 77
"w#w w.... .ww w.... w w w­w 6w>8w Aw`w w+*w z wyw w0w
6 of 40
SECTION[n]
0001BF00 0001BF08 0001BF10 0001BF18 0001BF20 0001BF28 0001BF30 0001BF38 0001BF40 0001BF48 0001BF50 0001BF58 0001BF60 0001BF68 0001BF70 0001BF78
63 6D 6C 61 11 20 75 4D 72 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2E 0A 63 74 4E 45 67 65 79 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
63 43 75 6F 6F 6E 68 6D 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
68 61 6C 72 74 6F 20 6F 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c...c.h. m...C.a. l.c.u.l. a.t.o.r. .N.o.t. .E.n.o. u.g.h. . M.e.m.o. r.y..... ........ ........ ........ ........ ........ ........ ........
It seems to be very simple, the retrieval of the headers information. I recommend inspecting the MSDN library regarding the IMAGE_NT_HEADERS structure definition. It makes comprehensible to grasp what the image NT header maintains to execute a code inside the Windows NT OS. Now, you are conversant with the Windows NT structure, it consists of the "PE" Signature, the File Header, and the Optional Header. Do not forget to take a glimpse at their comments in the MSDN Library and besides in Table 1. One the whole, I consider merely, on the most circumstances, the following cells of the IMAGE_NT_HEADERS structure:
FileHeader->NumberOfSections OptionalHeader->AddressOfEntryPoint OptionalHeader->ImageBase OptionalHeader->SectionAlignment OptionalHeader->FileAlignment OptionalHeader->SizeOfImage OptionalHeader-> DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]->VirtualAddress OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]->Size
You can observe clearly, the main purpose of these values, and their role when the internal virtual memory space allocated for an EXE file by the Windows OS is fully allocated, if you pay attention to their explanations in MSDN library, so I am not going to repeat the MSDN annotations here. I should mention a brief comment regarding the PE data directories, or OptionalHeader-> DataDirectory[], as I think there are a few aspects of interest concerning them. When you come to survey the Optional header through the Windows NT information, you will find that there are 16 directories at the end of the Optional Header, where you can find the consecutive directories, including their Relative Virtual Address and Size. I just mention here, the notes from <winnt.h> to clarify these information:
7 of 40
#define IMAGE_DIRECTORY_ENTRY_EXPORT #define IMAGE_DIRECTORY_ENTRY_IMPORT #define IMAGE_DIRECTORY_ENTRY_RESOURCE #define IMAGE_DIRECTORY_ENTRY_EXCEPTION #define IMAGE_DIRECTORY_ENTRY_SECURITY #define IMAGE_DIRECTORY_ENTRY_BASERELOC #define IMAGE_DIRECTORY_ENTRY_DEBUG #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR #define IMAGE_DIRECTORY_ENTRY_TLS #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT #define IMAGE_DIRECTORY_ENTRY_IAT #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
0 1 2 3 4 5 6 7 8 9 10 11 12 13
// Export Directory // Import Directory // Resource Directory // Exception Directory // Security Directory // Base Relocation Table // Debug Directory // Architecture Specific Data // RVA of GP // TLS Directory // Load Configuration Directory // Bound Import Directory in headers // Import Address Table // Delay Load Import Descriptors // COM Runtime descriptor
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
The last one (15) was reserved for use in future; I have not yet seen any purpose to use it even in PE64. For instance, if you desire to perceive the relative virtual address (RVA) and the size of the resource data, it is enough to retrieve them by:
DWORD dwRVA = image_nt_headers.OptionalHeader-> DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]->VirtualAddress; DWORD dwSize = image_nt_headers.OptionalHeader-> DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]->Size;
To comprehend more regarding the significance of data directories, I forward you to section 3.4.3, Microsoft Portable Executable and the Common Object File Format Specification document by Microsoft, and furthermore section 6 of this document, where you discern the various types of sections and their applications. We will discuss the section's advantage subsequently.
8 of 40
File Format Specification documents. I believe, it represents the totality of sections by their names, Table 2. Table 2 - Section names ".text" Code Section "CODE" Code Section of file linked by Borland Delphi or Borland Pascal ".data" Data Section "DATA" Data Section of file linked by Borland Delphi or Borland Pascal ".rdata" Section for Constant Data ".idata" Import Table ".edata" Export Table ".tls" TLS Table ".reloc" Relocation Information ".rsrc" Resource Information To comprehend the section headers and also the sections, you can run the sample PE viewer. By this PE viewer, you only can realize the application of the section headers in a file image, so to observe the main significance in the Virtual Memory, you should try to load a PE file by a debugger, and the next section represents the main idea of using the virtual address and size in the virtual memory by using a debugger. The last note is about IMAGE_NT_HEADERS-> FileHeader-><CODE>NumberOfSections, that provides a number of sections in a PE file, do not forget to adjust it whenever you remove or add some sections to a PE file, I am talking about section injection!
3.1 Debuggers
The first essential prerequisite, to become a PE tools developer, is to have enough experience with bug tracer tools. Furthermore, you should know most of the assembly instructions. To me, the Intel documents are the best references. You can obtain them from the Intel site for IA-32, and on top of that IA-64; the future belongs to IA-64 CPUs, Windows XP 64-bit, and also PE64! IA-32 Intel Architecture Software Developers Manuals. Intel Itanium Architecture Assembly Language Reference Guide. The Intel Itanium Processor Developer Resource Guide. To trace a PE file, SoftICE by Compuware Corporation, I knew it also as named NuMega when I was at high school, is the best debugger in the world. It implements process tracing by using kernel mode method debugging without applying Windows debugging application programming interface (API) functions. In addition, I am going to introduce one perfect debugger in user mode level. It utilizes the Windows debugging API to trace a PE file and also attaches itself to an active process. These API functions have been provided by Microsoft teams, inside the Windows Kernel32 library, to trace a specific process, by using Microsoft tools, or perhaps, to make your own debugger! Some of those API functions inlude: CreateThread(), CreateProcess(), OpenProcess(), DebugActiveProcess(), GetThreadContext(), SetThreadContext(), ContinueDebugEvent(), DebugBreak(), ReadProcessMemory(), WriteProcessMemory(), SuspendThread(), and ResumeThread().
3.1.1 SoftICE
It was in 1987; Frank Grossman and Jim Moskun decided to establish a company called NuMega Technologies in Nashua, NH, in order to develop some equipments to trace and test the reliability of Microsoft Windows software programs. Now, it is a part of Compuware Corporation and its product has participated to accelerate the reliability in Windows software, and additionally in Windows driver developments. Currently, everyone knows the Compuware DriverStudio which is used to establish an environment for implementing the elaboration of a kernel driver or a system file by aiding the Windows Driver Development Kit (DDK). It bypasses the involvement of DDK to implement a portable executable file of kernel level for a Windows system software developer. For us, only one instrument of
9 of 40
DriverStudio is important, SoftICE, this debugger can be used to trace every portable executable file, a PE file for user mode level or a PE file for kernel mode level. Figure 1 - SoftICE Window EAX=00000000 EBX=7FFDD000 ECX=0007FFB0 EDX=7C90EB94 ESI=FFFFFFFF EDI=7C919738 EBP=0007FFF0 ESP=0007FFC4 EIP=010119E0 o d i s z a p c CS=0008 DS=0023 SS=0010 ES=0023 FS=0030 GS=0000 SS:0007FFC4=87C816D4F 0023:01013000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0023:01013010 01 00 00 00 20 00 00 00-0A 00 00 00 0A 00 00 00 ................ 0023:01013020 20 00 00 00 00 00 00 00-53 63 69 43 61 6C 63 00 ........SciCalc. 0023:01013030 00 00 00 00 00 00 00 00-62 61 63 6B 67 72 6F 75 ........backgrou 0023:01013040 6E 64 00 00 00 00 00 00-2E 00 00 00 00 00 00 00 nd.............. 0010:0007FFC4 4F 6D 81 7C 38 07 91 7C-FF FF FF FF 00 90 FD 7F Om |8 | . 0010:0007FFD4 ED A6 54 80 C8 FF 07 00-E8 B4 F5 81 FF FF FF FF T . 0010:0007FFE4 F3 99 83 7C 58 6D 81 7C-00 00 00 00 00 00 00 00 Xm |........ 0010:0007FFF4 00 00 00 00 E0 19 01 01-00 00 00 00 00 00 00 00 .... .... 010119E0 PUSH EBP 010119E1 MOV EBP,ESP 010119E3 PUSH -1 010119E5 PUSH 01001570 010119EA PUSH 01011D60 010119EF MOV EAX,DWORD PTR FS:[0] 010119F5 PUSH EAX 010119F6 MOV DWORD PTR FS:[0],ESP 010119FD ADD ESP,-68 01011A00 PUSH EBX 01011A01 PUSH ESI 01011A02 PUSH EDI 01011A03 MOV DWORD PTR SS:[EBP-18],ESP 01011A06 MOV DWORD PTR SS:[EBP-4],0 :_
3.1.2 OllyDbg
It was about 4 years ago, that I first saw this debugger by chance. For me, it was the best choice, I was not so wealthy to purchase SoftICE, and at that time, SoftICE only had good functions for DOS, Windows 98, and Windows 2000. I found that this debugger supported all kinds of Windows versions. Therefore, I started to learn it very fast, and now it is my favorite debugger for the Windows OS. It is a debugger that can be used to trace all kinds of portable executable files except a Common Language Infrastructure (CLI) file format in user mode level, by using the Windows debugging API. Oleh Yuschuk, the author, is one of worthiest software developers I have seen in my life. He is a Ukrainian who now lives in Germany. I should mention here that his debugger is the best choice for hacker and cracker parties around the world! It is a freeware! You can try it from OllyDbg Homepage. Figure 2 - OllyDbg CPU Window
10 of 40
11 of 40
010119E1 010119E3 010119E5 010119EA 010119EF 010119F5 010119F6 010119FD 01011A00 01011A01 01011A02 01011A03 01011A06
MOV EBP,ESP PUSH -1 PUSH 01001570 PUSH 01011D60 MOV EAX,DWORD PUSH EAX MOV DWORD PTR ADD ESP,-68 PUSH EBX PUSH ESI PUSH EDI MOV DWORD PTR MOV DWORD PTR
SS:[EBP-18],ESP SS:[EBP-4],0
3. Memory watcher. 0023:01013000 0023:01013010 0023:01013020 ........SciCalc. 0023:01013030 ........backgrou 0023:01013040 4. Stack viewer. 0010:0007FFC4 4F 6D 81 7C 38 07 91 7C-FF FF FF FF 00 90 FD 7F Om |8 | . 0010:0007FFD4 ED A6 54 80 C8 FF 07 00-E8 B4 F5 81 FF FF FF FF T . 0010:0007FFE4 F3 99 83 7C 58 6D 81 7C-00 00 00 00 00 00 00 00 Xm |........ 0010:0007FFF4 00 00 00 00 E0 19 01 01-00 00 00 00 00 00 00 00 .... .... 5. Command line, command buttons, or shortcut keys to follow the debugging process. Command Run Step Into Step Over Set Break Point SoftICE F5 F11 F10 F8 OllyDbg F9 F7 F8 F2 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 01 00 00 00 20 00 00 00-0A 00 00 00 0A 00 00 00 ................ 20 00 00 00 00 00 00 00-53 63 69 43 61 6C 63 00 00 00 00 00 00 00 00 00-62 61 63 6B 67 72 6F 75 6E 64 00 00 00 00 00 00-2E 00 00 00 00 00 00 00 nd..............
You can compare Figure 1 and Figure 2 to distinguish the difference between SoftICE and OllyDbg. When you want to trace a PE file, you should mostly consider these five subdivisions. Furthermore, every debugger comprises of some other useful parts; you should discover them by yourself.
3.2 Disassembler
We can consider OllyDbg and SoftICE as excellent disassemblers, but I also want to introduce another disassembler tool which is famous in the reverse engineering world.
3.2.2 W32Dasm
W32DASM can disassemble both 16 and 32 bit executable file formats. In addition to its disassembling ability, you can employ it to analyze import, export and resource data directories data.
12 of 40
for any newbie in this area. Furthermore, it has the capability to trace an executable file inside the user mode level in the same way as OllyDbg.
3.3.1 LordPE
LordPE by y0da is still the first choice to retrieve PE file information with the possibility to modify them.
3.3.2 PEiD
PE iDentifier is valuable to identify the type of compilers, packers, and cryptors of PE files. As of now, it can detect more than 500 different signature types of PE files.
3.3.4 WinHex
WinHex, it is clear what you can do with this tool.
13 of 40
00000000 00000002 00000004 00000006 00000008 0000000A 0000000C 0000000E 00000010 00000012 00000014 00000016 00000018 0000001A 0000001C 0000001D 0000001E
4D 5A 9000 0300 0000 0400 0000 FFFF 0000 B800 0000 0000 0000 4000 0000 00 00 00
ASCII "MZ" DW 0090 DW 0003 DW 0000 DW 0004 DW 0000 DW FFFF DW 0000 DW 00B8 DW 0000 DW 0000 DW 0000 DW 0040 DW 0000 DB 00 DB 00 DB 00
DOS EXE Signature DOS_PartPag = 90 (144.) DOS_PageCnt = 3 DOS_ReloCnt = 0 DOS_HdrSize = 4 DOS_MinMem = 0 DOS_MaxMem = FFFF (65535.) DOS_ReloSS = 0 DOS_ExeSP = B8 DOS_ChkSum = 0 DOS_ExeIP = 0 DOS_ReloCS = 0 DOS_TablOff = 40 DOS_Overlay = 0
14 of 40
0000001F 00000020 00000021 00000022 00000023 00000024 00000025 00000026 00000027 00000028 00000029 0000002A 0000002B 0000002C 0000002D 0000002E 0000002F 00000030 00000031 00000032 00000033 00000034 00000035 00000036 00000037 00000038 00000039 0000003A 0000003B 0000003C
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0000000
DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB DD
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000000F0
Offset to PE signature
I want to explain how we can plainly change the Offset of Entry Point (OEP) in our sample file, CALC.EXE of Windows XP. First, by using a PE Tool, and also using our PE Viewer, we find OEP, 0x00012475, and Image Base, 0x01000000. This value of OEP is the Relative Virtual Address, so the Image Base value is used to convert it to the Virtual Address. Virtual_Address = Image_Base + Relative_Virtual_Address
DWORD OEP_RVA = image_nt_headers->OptionalHeader.AddressOfEntryPoint ; // OEP_RVA = 0x00012475 DWORD OEP_VA = image_nt_headers->OptionalHeader.ImageBase + OEP_RVA ; // OEP_VA = 0x01000000 + 0x00012475 = 0x01012475
PE Maker - Step 1 Download source files - 54.7 Kb CALC.EXE - test file Download test PE file - 49.5 Kb
DynLoader(), in loader.cpp, is reserved for the data of the new section, in other words, the Loader.
DynLoader Step 1
__stdcall void DynLoader() { _asm { //---------------------------------DWORD_TYPE(DYN_LOADER_START_MAGIC) //---------------------------------MOV EAX,01012475h // << Original OEP
15 of 40
Unfortunately, this source can only be applied for the sample test file. We should complete it by saving the value of the original OEP in the new section, and use it to reach the real OEP. I have accomplished it in Step 2 (Section 5).
16 of 40
By Table 1, the usage of image_dos_header, pDosStub, image_nt_headers, image_section_header [MAX_SECTION_NUM], and image_section[MAX_SECTION_NUM] is clear. We use OpenFile() and SaveFile() to retrieve and rebuild a PE file. Furthermore, AddNewSection() is employed to create the new section, the important step.
Correct the SizeofImage by the virtual size and the virtual address of the last section:
image_nt_headers->OptionalHeader.SizeOfImage = image_section_header[LastSection]->VirtualAddress + image_section_header[LastSection]->Misc.VirtualSize;
17 of 40
Set the Bound Import Directory header to zero, as this directory is not very important to execute a PE file:
image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]. VirtualAddress = 0; image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
You can comprehend the difference between incremental link and no-incremental link by looking at the following picture:
To acquire the virtual address of DynLoader(), we obtain the virtual address of JMP pemaker.DynLoader in the incremental link, but by no-incremental link, the real virtual address is gained by the following 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():
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; }
18 of 40
The new function, CPECryptor::CopyData1(), will implement the copy of the Image Base value and the Offset of Entry Point value into 8 bytes of free space in the loader.
19 of 40
PUSH EAX XOR EAX, 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) //---------------------------------} }
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) //---------------------------------} }
20 of 40
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; } ; 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
21 of 40
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 ; } ; } ; __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
Make a Win32 console project, and link and run the preceding C++ code, to perceive the result: 1: 3: 2: 4: _ Raise an Exception In Filter In Finally In Exception Handler
This program runs the exception expression, printf("3: In Filter\n");, when an exception happens, in this example the INT 3 exception. You can employ other kinds of exception too. In OllyDbg, Debugging options->Exceptions, you can see a short list of different types of exceptions.
22 of 40
We desire to construct a structured exception handler in order to reach OEP. Now, I think you have distinguished the SEH installation, the exception raise, and the exception expression filter, by foregoing the assembly code. To establish our exception handler approach, we need to comprise the following codes: SEH installation:
LEA EAX,[EBP+_except_handler1_OEP_Jump] PUSH EAX PUSH DWORD PTR FS:[0] MOV DWORD PTR FS:[0],ESP
An Exception Raise:
INT 3
So we yearn for making the ensuing C++ code in assembly language to inaugurate our engine to approach the Offset of Entry Point by SEH.
__try // SEH installation { __asm { INT 3 // An Exception Raise } } __except( ..., EXCEPTION_CONTINUE_SEARCH ){} // Exception handler expression filter
In assembly 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 ; ---------------------------------------------------; the raise a INT 3 exception INT INT INT INT ; } 3 3 3 3
23 of 40
PUSH EBP MOV EBP,ESP ... MOV EAX, EXCEPTION_CONTINUE_SEARCH ; EXCEPTION_CONTINUE_SEARCH = 0 LEAVE RETN ; , EXCEPTION_CONTINUE_SEARCH ) { }
The exception value, __except(..., Value), determines how the exception is handled, it can have three values, 1, 0, -1. To understand them, refer to the try-except statement description in the MSDN library. We set it to EXCEPTION_CONTINUE_SEARCH (0), not to run the exception handler function, therefore by this value, the exception is not recognized, is simply ignored, and the thread continues its code-execution. How the SEH installation is implemented As you perceived from the illustrated code, the SEH installation is done by the FS segment register. Microsoft Windows 32 bit uses the FS segment register as a pointer to the data block of the main thread. The first 0x1C bytes comprise the information of the Thread Information Block (TIB). Therefore, FS:[00h] refers to ExceptionList of the main thread, Table 3. In our code, we have pushed the pointer to _except_handler1_OEP_Jump in the stack and changed the value of ExceptionList, FS:[00h], to the beginning of the stack, ESP. Thread Information Block (TIB)
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;
Table 3 - FS segment register and Thread Information Block DWORD DWORD DWORD DWORD DWORD DWORD DWORD PTR PTR PTR PTR PTR PTR PTR FS:[00h] ExceptionList FS:[04h] StackBase FS:[08h] StackLimit FS:[0Ch] SubSystemTib FS:[10h] FiberData / Version FS:[14h] ArbitraryUserPointer FS:[18h] Self
24 of 40
typedef struct _CONTEXT { //----------------------------------------DWORD ContextFlags; //----------------------------------------DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; //----------------------------------------FLOATING_SAVE_AREA FloatSave; //----------------------------------------DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; //----------------------------------------DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; //----------------------------------------DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; //----------------------------------------BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; //---------------------------------------} CONTEXT, *LPCONTEXT;
Table 4 - CONTEXT Context ContextFlags 0x00000000 Flags Dr0 0x00000004 Dr1 0x00000008 Context Dr2 0x0000000C Debug Dr3 0x00000010 Registers Dr6 0x00000014 Dr7 0x00000018 StatusWord 0x0000001C Context 0x00000020 StatusWord Floating 0x00000024 FloatSave TagWord Point ErrorOffset 0x00000028
25 of 40
0x0000002C 0x00000030 0x00000034 0x00000038 ... 0x00000087 0x00000088 0x0000008C Context 0x00000090 Segments 0x00000094 0x00000098 0x0000009C 0x000000A0 Context 0x000000A4 Integer 0x000000A8 0x000000AC 0x000000B0 0x000000B4 0x000000B8 Context 0x000000BC Control 0x000000C0 0x000000C4 0x000000C8
Cr0NpxState SegGs SegFs SegEs SegDs Edi Esi Ebx Edx Ecx Eax Ebp Eip SegCs EFlags Esp SegSs
Context 0x000000CC ExtendedRegisters[0x200] Extended ... Registers 0x000002CB By the following code, we have accomplished the main purpose of coming to OEP by the structured exception handler:
__stdcall void DynLoader() { _asm { //---------------------------------DWORD_TYPE(DYN_LOADER_START_MAGIC) //---------------------------------Main_0: PUSHAD
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 XOR EAX, EAX PUSH DWORD PTR FS:[0] MOV DWORD PTR FS:[0],ESP
26 of 40
//---------------------------------------------------// 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] //============================== PUSH EDI // restore original SEH MOV EDI,DWORD PTR DS:[EAX+0C4h] PUSH DWORD PTR DS:[EDI] POP DWORD PTR FS:[0] ADD DWORD PTR DS:[EAX+0C4h],8 //-----------------------------// 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) //---------------------------------} } // pContext.Esp // PCONTEXT: pContext <- EAX
// pContext.Esp
27 of 40
When you make a Windows application project, the linker includes at least kernel32.dll in the base dependencies of your project. Without LoadLibrary() and GetProcAddress() of Kernel32.dll, we can not load a DLL in run-time. The dependencies information is stored in the import table section. By Dependency Walker, it is not so difficult to observe the DLL module and the functions which are imported into a PE file.
28 of 40
We attempt to establish our custom import table to conduct our project. Furthermore, we have to fix up the original import table at the end in order to run the real code of the program. PE Maker - Step 3 Download source files - 65.4 Kb
The VirtualAddress refers to structures by IMAGE_IMPORT_DESCRIPTOR. This structure contains the pointer to the imported DLL name and the relative virtual address of the first thunk.
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; // the imported DLL name DWORD FirstThunk; // the relative virtual address of the first thunk
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
When a program is running, the Windows task manager sets the thunks by the virtual address of the function. The virtual address is found by the name of the function. At first, the thunks hold the relative virtual address of the function name, Table 5; during execution, they are fixed up by the virtual address of the functions, Table 6.
29 of 40
OriginalFirstThunk TimeDateStamp ForwarderChain ------>"kernel32.dll",0 IMAGE_IMPORT_ Name_RVA DESCRIPTOR[0] FirstThunk_RVA ------> proc_1_name_RVA ------> 0,0,"LoadLibraryA",0 proc_2_name_RVA ------> 0,0,"GetProcAddress",0 proc_3_name_RVA ------> 0,0,"GetModuleHandleA",0
...
OriginalFirstThunk TimeDateStamp ForwarderChain Name_RVA ------> "kernel32.dll",0 IMAGE_IMPORT_DESCRIPTOR[0] FirstThunk_RVA ------>proc_1_VA proc_2_VA proc_3_VA ... IMAGE_IMPORT_DESCRIPTOR[1] ... IMAGE_IMPORT_DESCRIPTOR[n]
We want to make a simple import table to import LoadLibrary(), and GetProcAddress() from Kernel32.dll. We need these two essential API functions to cover other API functions in run-time. The following assembly code shows how easily we can reach our solution:
0101F000: 00000000 ; OriginalFirstThunk 0101F004: 00000000 ; TimeDateStamp 0101F008: 00000000 ; ForwarderChain 0101F00C: 0001F034 ; Name; ImageBase + 0001F034 -> 0101F034 -> "Kernel32.dll",0 0101F010: 0001F028 ; FirstThunk; ImageBase + 0001F028 -> 0101F028 0101F014: 00000000 0101F018: 00000000 0101F01C: 00000000 0101F020: 00000000 0101F024: 00000000 0101F028: 0001F041 ; ImageBase + 0001F041 -> 0101F041 -> 0,0,"LoadLibraryA",0 0101F02C: 0001F050 ; ImageBase + 0001F050 -> 0101F050 -> 0,0,"GetProcAddress",0 0101F030: 00000000 0101F034: 'K' 'e' 'r' 'n' 'e' 'l' '3' '2' '.' 'd' 'l' 'l' 00 0001F041: 00 00 'L' 'o' 'a' 'd' 'L' 'i' 'b' 'r' 'a' 'r' 'y' 'A' 00 0001F050: 00 00 'G' 'e' 't' 'P' 'r' 'o' 'c' 'A' 'd' 'd' 'r' 'e' 's' 's' 00
After running...
0101F000: 00000000 ; OriginalFirstThunk 0101F004: 00000000 ; TimeDateStamp 0101F008: 00000000 ; ForwarderChain 0101F00C: 0001F034 ; Name; ImageBase + 0001F034 -> 0101F034 -> "Kernel32.dll",0 0101F010: 0001F028 ; FirstThunk; ImageBase + 0001F028 -> 0101F028 0101F014: 00000000 0101F018: 00000000 0101F01C: 00000000
30 of 40
00000000 00000000 7C801D77 ; -> Kernel32.LoadLibrary() 7C80AC28 ; -> Kernel32.GetProcAddress() 00000000 'K' 'e' 'r' 'n' 'e' 'l' '3' '2' '.' 'd' 'l' 'l' 00 00 'L' 'o' 'a' 'd' 'L' 'i' 'b' 'r' 'a' 'r' 'y' 'A' 00 00 'G' 'e' 't' 'P' 'r' 'o' 'c' 'A' 'd' 'd' 'r' 'e' 's' 's'
I have prepared a class library to make every import table by using a client string table. The CITMaker class library in itmaker.h, it will build an import table by sz_IT_EXE_strings and also the relative virtual address of the import table.
static const char *sz_IT_EXE_strings[]= { "Kernel32.dll", "LoadLibraryA", "GetProcAddress", 0,, 0, };
We subsequently employ this class library to establish an import table to support DLLs and OCXs, so this is a general library to present all possible import tables easily. The next step is clarified in the following code.
CITMaker *ImportTableMaker = new CITMaker( IMPORT_TABLE_EXE ); ... pimage_section_header=AddNewSection( ".xxx", dwNewSectionSize ); // build import table by the current virtual address ImportTableMaker->Build ( pimage_section_header->VirtualAddress ); memcpy( pNewSection, ImportTableMaker->pMem, ImportTableMaker->dwSize ); ... memcpy( image_section[image_nt_headers->FileHeader.NumberOfSections-1], pNewSection, dwNewSectionSize ); ... image_nt_headers->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = pimage_section_header->VirtualAddress; image_nt_headers->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = ImportTableMaker->dwSize; ... delete ImportTableMaker;
The import table is copied at the beginning of the new section, and the relevant data directory is adjusted to the relative virtual address of the new section and the size of the new import table
31 of 40
push esi mov eax,offset _p_GetProcAddress call [ebp+eax] //GetModuleHandle=GetProcAddress(hModule, lpProcName); //--------------------
I want to have a complete imported function table similar in performance done in a real EXE file. If you look inside a PE file, you will discover that an API call is done by an indirection jump through the virtual address of the API function: JMP DWORD PTR [XXXXXXXX]
... 0101F028: 7C801D77 ; Virtual Address of kernel32.LoadLibrary() ... 0101F120: JMP DWORD PTR [0101F028] ... 0101F230: CALL 0101F120 ; JMP to kernel32.LoadLibrary ...
It makes it easy to expand the other part of our project by this performance, so we construct two data tables: first for API virtual addresses, and the second for the JMP [XXXXXXXX].
#define __jmp_api byte_type(0xFF) byte_type(0x25) __asm { ... //---------------------------------------------------------------_p_GetModuleHandle: dword_type(0xCCCCCCCC) _p_VirtualProtect: dword_type(0xCCCCCCCC) _p_GetModuleFileName: dword_type(0xCCCCCCCC) _p_CreateFile: dword_type(0xCCCCCCCC) _p_GlobalAlloc: dword_type(0xCCCCCCCC) //---------------------------------------------------------------_jmp_GetModuleHandle: __jmp_api dword_type(0xCCCCCCCC) _jmp_VirtualProtect: __jmp_api dword_type(0xCCCCCCCC) _jmp_GetModuleFileName: __jmp_api dword_type(0xCCCCCCCC) _jmp_CreateFile: __jmp_api dword_type(0xCCCCCCCC) _jmp_GlobalAlloc: __jmp_api dword_type(0xCCCCCCCC) //---------------------------------------------------------------... }
In the succeeding code, we has concluded our ambition to install a custom internal import table! (We can not call it import table.)
... lea edi,[ebp+_p_szKernel32] lea ebx,[ebp+_p_GetModuleHandle] lea ecx,[ebp+_jmp_GetModuleHandle] add ecx,02h _api_get_lib_address_loop: push ecx push edi mov eax,offset _p_LoadLibrary call [ebp+eax] //LoadLibrary(lpLibFileName); pop ecx mov esi,eax // esi -> hModule push edi call __strlen add esp,04h add edi,eax _api_get_proc_address_loop: push ecx push edi push esi mov eax,offset _p_GetProcAddress call [ebp+eax]//GetModuleHandle=GetProcAddress(hModule, lpProcName); pop ecx
32 of 40
mov [ebx],eax mov [ecx],ebx // JMP DWORD PTR [XXXXXXXX] add ebx,04h add ecx,06h push edi call __strlen add esp,04h add edi,eax mov al,byte ptr [edi] test al,al jnz _api_get_proc_address_loop inc edi mov al,byte ptr [edi] test al,al jnz _api_get_lib_address_loop ...
33 of 40
add dword ptr [ebp+_p_dwThunk], 004h // dwHintName => next dwHintName add dword ptr [ebp+_p_dwHintName],004h jmp _it_fixup_get_proc_address_loop _it_fixup_next_module: add ebx,014h // sizeof(IMAGE_IMPORT_DESCRIPTOR) jmp _it_fixup_get_lib_address_loop _it_fixup_end: ...
Destructor: When the main program frees the library usage by FreeLibrary(), the second OEP arrival happens.
FreeLibrary( hinstDLL ); FreeLibrary( hinstOCX );
To perform this, I have employed a trick, that causes in the second time again, the instruction pointer (EIP) traveling towards the original OEP by the structured exception handler.
_main_0: pushad // save the registers context in stack call _main_1 _main_1: pop ebp sub ebp,offset _main_1 // get base ebp //---------------- support dll, ocx ----------------_support_dll_0: jmp _support_dll_1 // nop; nop; // << trick // in the second time OEP jmp _support_dll_2 _support_dll_1: //---------------------------------------------------... //---------------- support dll, ocx 1 --------------mov edi,[ebp+_p_dwImageBase] add edi,[edi+03Ch]// edi -> IMAGE_NT_HEADERS mov ax,word ptr [edi+016h]// edi -> image_nt_headers->FileHeader.Characteristics
34 of 40
test ax,IMAGE_FILE_DLL jz _support_dll_2 mov ax, 9090h // << trick mov word ptr [ebp+_support_dll_0],ax _support_dll_2: //---------------------------------------------------... into OEP by SEH ...
I hope you have caught the trick in the preceding code, but this is not all of it, we have problem in ImageBase, when the library has been loaded in different image bases by the main program. We should write some code to find the real image base and store it to use forward.
mov eax,[esp+24h] // the real imagebase mov ebx,[esp+30h] // oep cmp eax,ebx ja _no_dll_pe_file_0 cmp word ptr [eax],IMAGE_DOS_SIGNATURE jne _no_dll_pe_file_0 mov [ebp+_p_dwImageBase],eax _no_dll_pe_file_0:
This code finds the real image base by investigating the stack information. By using the real image base and the formal image base, we should correct all memory calls inside the image program!! Don't be afraid, it will be done simply by the relocating the table information.
Table 7 - The Relocation Table VirtualAddress SizeOfBlock type:4 offset:12 type:4 offset:12 Block[1] type:4 offset:12 type:4 offset:12 type:4 offset:12 type:4 offset:12 ... ... ... ... type:4 offset:12 00 00 VirtualAddress SizeOfBlock type:4 offset:12 type:4 offset:12 Block[2] type:4 offset:12 type:4 offset:12 type:4 offset:12 type:4 offset:12 ... ... ... ... type:4 offset:12 00 00 ... ...
VirtualAddress SizeOfBlock Block[n] type:4 offset:12 type:4 offset:12 type:4 offset:12 type:4 offset:12 type:4 offset:12 type:4 offset:12
35 of 40
... 00
... 00
Table 7 illustrates the main idea of the relocation table. Furthermore, you can upload a DLL or an OCX file in OllyDbg to observe the relocation table, the ".reloc" section through Memory map window. By the way, we find the position of the relocation table by using the following code in our project:
DWORD dwVirtualAddress = image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]. VirtualAddress; DWORD dwSize = image_nt_headers-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
By OllyDbg, we have the same as the following for the ".reloc" section, by using the Long Hex viewer mode. In this example, the base virtual address is 0x1000 and the size of the block is 0x184.
008E1000 008E1010 008E1020 008E1030 008E1040 008E1050 008E1060 : : : : : : : 00001000 30683054 30E030DC 3120310D 31D031CC 320C3208 3260325C 00000184 308C3080 30E830E4 315F3150 31F431EC 32143210 32683264 30163000 30AC309C 30F030EC 31A431A0 31FC31F8 324C322C 3270326C 30403028 30D830CC 310030F4 31C031A8 32043200 32583254 32B03274
Each package performs the relocation by using consecutive 4 bytes form its internal information. The first byte refers to the type of relocation and the next three bytes are the offset which must be used with the base virtual address and the image base to correct the image information. type offset 03 00 00 00 What is the type The type can be one of the following values:
IMAGE_REL_BASED_ABSOLUTE (0): no effect. IMAGE_REL_BASED_HIGH (1): relocate by the high 16 bytes of the base virtual address and the
offset.
IMAGE_REL_BASED_LOW (2): relocate by the low 16 bytes of the base virtual address and the
offset.
IMAGE_REL_BASED_HIGHLOW (3): relocate by the base virtual address and the offset.
What is done in the relocation? By relocation, some values inside the virtual memory are corrected according to the current image base by the ".reloc" section packages. delta_ImageBase = current_ImageBase - image_nt_headers->OptionalHeader.ImageBase
mem[ current_ImageBase + 0x1000 ] = mem[ current_ImageBase + 0x1000 ] + delta_ImageBase ; mem[ current_ImageBase + 0x1016 ] = mem[ current_ImageBase + 0x1016 ] + delta_ImageBase ; mem[ current_ImageBase + 0x1028 ] = mem[ current_ImageBase + 0x1028 ] + delta_ImageBase ;
36 of 40
mem[ current_ImageBase + 0x1040 ] = mem[ current_ImageBase + 0x1040 ] + delta_ImageBase ; mem[ current_ImageBase + 0x1054 ] = mem[ current_ImageBase + 0x1054 ] + delta_ImageBase ; ...
I have employed the following code from Morphine packer to implement the relocation.
... _reloc_fixup: mov eax,[ebp+_p_dwImageBase] mov edx,eax mov ebx,eax add ebx,[ebx+3Ch] // edi -> IMAGE_NT_HEADERS mov ebx,[ebx+034h]// edx ->image_nt_headers->OptionalHeader.ImageBase sub edx,ebx // edx -> reloc_correction // delta_ImageBase je _reloc_fixup_end mov ebx,[ebp+_p_dwRelocationVirtualAddress] test ebx,ebx jz _reloc_fixup_end add ebx,eax _reloc_fixup_block: mov eax,[ebx+004h] //ImageBaseRelocation.SizeOfBlock test eax,eax jz _reloc_fixup_end lea ecx,[eax-008h] shr ecx,001h lea edi,[ebx+008h] _reloc_fixup_do_entry: movzx eax,word ptr [edi]//Entry push edx mov edx,eax shr eax,00Ch //Type = Entry >> 12 mov esi,[ebp+_p_dwImageBase]//ImageBase and dx,00FFFh add esi,[ebx] add esi,edx pop edx _reloc_fixup_HIGH: // IMAGE_REL_BASED_HIGH dec eax jnz _reloc_fixup_LOW mov eax,edx shr eax,010h //HIWORD(Delta) jmp _reloc_fixup_LOW_fixup _reloc_fixup_LOW: // IMAGE_REL_BASED_LOW dec eax jnz _reloc_fixup_HIGHLOW movzx eax,dx //LOWORD(Delta) _reloc_fixup_LOW_fixup: add word ptr [esi],ax// mem[x] = mem[x] + delta_ImageBase jmp _reloc_fixup_next_entry _reloc_fixup_HIGHLOW: // IMAGE_REL_BASED_HIGHLOW dec eax jnz _reloc_fixup_next_entry add [esi],edx // mem[x] = mem[x] + delta_ImageBase _reloc_fixup_next_entry: inc edi inc edi //Entry++ loop _reloc_fixup_do_entry _reloc_fixup_next_base: add ebx,[ebx+004h] jmp _reloc_fixup_block _reloc_fixup_end: ...
37 of 40
{ "Kernel32.dll", "LoadLibraryA", "GetProcAddress", "GetModuleHandleA", 0, "User32.dll", "GetKeyboardType", "WindowFromPoint", 0, "AdvApi32.dll", "RegQueryValueExA", "RegSetValueExA", "StartServiceA", 0, "Oleaut32.dll", "SysFreeString", "CreateErrorInfo", "SafeArrayPtrOfIndex", 0, "Gdi32.dll", "UnrealizeObject", 0, "Ole32.dll", "CreateStreamOnHGlobal", "IsEqualGUID", 0, "ComCtl32.dll", "ImageList_SetIconSize", 0, 0, };
Without these API functions, the library can not be loaded, and moreover the DllregisterServer() and DllUregisterServer() will not operate. In CPECryptor::CryptFile, I have distinguished between EXE files and DLL files in the initialization of the new import table object during creation:
if(( image_nt_headers->FileHeader.Characteristics & IMAGE_FILE_DLL ) == IMAGE_FILE_DLL ) { ImportTableMaker = new CITMaker( IMPORT_TABLE_OCX ); } else { ImportTableMaker = new CITMaker( IMPORT_TABLE_EXE ); }
To keep safe the TLS directory, I have copied it in a special place inside the loader:
... _tls_dwStartAddressOfRawData: _tls_dwEndAddressOfRawData:
dword_type(0xCCCCCCCC) dword_type(0xCCCCCCCC)
38 of 40
10 Conclusion
By this article, you have perceived how easily we can inject code to a portable executable file. You can complete the code by using the source of other packers, create a packer in the same way as Yoda's Protector, and make your packer undetectable by mixing up with Morphine source code. I hope that you have enjoyed this brief discussion of one part of the reverse engineering field. See you again in the next discussion!
Update
39 of 40
ANTIVIRUS - false positive kuso666 My vote of 4 yakazem My vote of 5 Heaven2020 pemaker5 does not compile TelmoM Re: pemaker5 does not compile panda1987 Re: pemaker5 does not compile TelmoM Re: pemaker5 does not compile panda1987 Re: pemaker5 does not compile TelmoM Few questions... tafdasfasf Re: Few questions... tafdasfasf How does AddNewSection work? adhesive Re: How does AddNewSection work? tafdasfasf anyone explain why? minkun Injected EXE can't run John Kenedy S.Kom Re: Injected EXE can't run John Kenedy S.Kom Re: Injected EXE can't run Member 4626918 Maybe the wrong with Offset in Data viewer wk19830318 Re: Maybe the wrong with Offset in Data viewer wk19830318 well Nguyen Sy Bang Excellent eg_Anubhava great! newine Great article JoeJiao Problem related to virtual directory Nazneen.Insignia nice article Li Shu nice article! whitehat.c Last Visit: 14:13 30 Dec '11 Last Update: 11:26 31 Dec '11
11:07 30 Sep '11 4:15 20 Sep '11 8:57 11 Nov '10 10:26 26 Oct '10 21:20 26 Oct '10 6:07 27 Oct '10 5:43 28 Oct '10 12:08 28 Oct '10 10:15 23 Oct '10 8:58 1 Nov '10 4:28 30 Jul '10 9:31 23 Oct '10 1:27 26 May '10 5:30 13 May '10 5:58 13 May '10 17:15 19 Nov '10 9:03 31 Jan '10 2:20 2 Feb '10 3:27 19 Jan '10 4:00 16 Dec '09 1:53 11 Sep '09 12:31 4 Aug '09 6:31 29 Jul '09 4:40 15 Apr '09 1:32 6 Mar '09 1 2 3 4 5 Next
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
Last Updated 27 Dec 2005 | Advertise | Privacy | Terms of Use | Copyright CodeProject, 1999-2011
40 of 40