Inject Your Code To A Portable Executable File
Inject Your Code To A Portable Executable File
Description: This article demonstrates five steps to inject your code in a portable executable (EXE, DLL, OCX,...) file without recompiling source code.
THE CODE PROJECT Prize winner: December, 2005
Downloads
PE Viewer
Contents
0. Preface
1. Prerequisite
3.1 Debuggers
3.1.1 SoftICE
3.1.2 OllyDbg
3.2 Disassembler
3.2.2 W32Dasm
3.3.1 LordPE
3.3.2 PEiD
3.3.4 WinHex
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.
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
IMAGE_DOS_
HEADER
DOS_PartPag
DOS_PageCnt
DOS_ReloCnt
DOS_HdrSize
DOS_MinMem
DOS_MaxMem
DOS_ReloSS
DOS_ExeSP
00000000
00000002
00000004
00000006
00000008
0000000A
0000000C
0000000E
00000010
00000012
00000014
ASCII "MZ"
DW 0090
DW 0003
DW 0000
DW 0004
DW 0000
DW FFFF
DW 0000
DW 00B8
DW 0000
DW 0000
DOS_ChkSum
DOS_ExeIPP
DOS_ReloCS
DOS_TablOff
DOS_Overlay
MS-DOS Stub
Program
Signature
IMAGE_
FILE_HEADER
Windows NT
information
IMAGE_
NT_HEADERS
IMAGE_
OPTIONAL_
HEADER32
00000016 DW 0000
00000018 DW 0040
0000001A DW 0000
Reserved words
0000001C DB 00
Offset to PE signature
canno
00000040 ..!\L!This program
00000060 t be run in DOS mode....$.......
000000F0 ASCII "PE"
PE signature (PE)
Machine
000000F4 DW 014C
NumberOfSections
000000F6 DW 0003
TimeDateStamp
000000F8 DD 3B7D8410
PointerToSymbolTable
000000FC DD 00000000
00000100 DD 00000000
NumberOfSymbols
00000104 DW 00E0
SizeOfOptionalHeader
00000106 DW 010F
Characteristics
MagicNumber
MajorLinkerVersion
00000108 DW 010B
MinorLinkerVersion
0000010A DB 07
SizeOfCode
0000010B DB 00
SizeOfInitializedData
0000010C DD 00012800
00000110 DD 00009C00
SizeOfUninitializedData
00000114 DD 00000000
AddressOfEntryPoint
00000118 DD 00012475
BaseOfCode
0000011C DD 00001000
00000120 DD 00014000
BaseOfData
00000124 DD 01000000
ImageBase
00000128 DD 00001000
SectionAlignment
0000012C DD 00000200
FileAlignment
00000130 DW 0005
00000132 DW 0001
MajorOSVersion
00000134 DW 0005
MinorOSVersion
00000136 DW 0001
MajorImageVersion
00000138 DW 0004
0000013A DW 0000
MinorImageVersion
0000013C DD 00000000
MajorSubsystemVersion
00000140 DD 0001F000
MinorSubsystemVersion
00000144 DD 00000400
00000148 DD 0001D7FC
Reserved
0000014C DW 0002
SizeOfImage
0000014E DW 8000
SizeOfHeaders
00000150 DD 00040000
CheckSum
00000154 DD 00001000
00000158 DD 00100000
Subsystem
0000015C DD 00001000
DLLCharacteristics
00000160 DD 00000000
SizeOfStackReserve
00000164 DD 00000010
SizeOfStackCommit
SizeOfHeapReserve
SizeOfHeapCommit
LoaderFlags
NumberOfRvaAndSizes
Export Table
Import Table
Resource Table
Exception Table
Certificate File
Relocation Table
Debug Data
IMAGE_
DATA_DIRECTORY[16]
Architecture Data
Global Ptr
TLS Table
Load Config Table
Bound Import Table
Import Address Table
Delay Import Descriptor
COM+ Runtime Header
Reserved
Sections
information
IMAGE_
SECTION_
HEADER[0]
IMAGE_
SECTION_
HEADER[n]
Name[8]
VirtualSize
VirtualAddress
SizeOfRawData
PointerToRawData
PointerToRelocations
PointerToLineNumbers
NumberOfRelocations
NumberOfLineNumbers
Characteristics
00000210 ASCII".data";
00000218 DD 0000101C ;
0000021C DD 00014000 ;
00000220 DD 00000A00 ;
00000224 DD 00012C00 ;
00000228 DD 00000000 ;
0000022C DD 00000000 ;
00000230 DW 0000
;
00000232 DW 0000
;
00000234 DD C0000040 ;
00000238
00000240
00000244
00000248
0000024C
ASCII".rsrc";
DD 00008960 ;
DD 00016000 ;
DD 00008A00 ;
DD 00013600 ;
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
SECTION[0]
SECTION[n]
00000250
00000254
00000258
0000025A
0000025C
DD
DD
DW
DW
DD
00000000
00000000
0000
0000
40000040
;
;
;
;
;
00000400
00000408
00000410
00000418
00000420
00000428
00000430
00000438
00000440
00000448
00000450
0001BF00
0001BF08
0001BF10
0001BF18
0001BF20
0001BF28
0001BF30
0001BF38
0001BF40
0001BF48
0001BF50
0001BF58
0001BF60
0001BF68
0001BF70
0001BF78
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
PointerToRelocations = 0x0
PointerToLineNumbers = 0x0
NumberOfRelocations = 0x0
NumberOfLineNumbers = 0x0
Characteristics =
INITIALIZED_DATA|READ
D7 23 DD 77 "w#w
00 00 00 00 w....
83 1D C7 77 .-ww
00 00 00 00 -w....
D8 05 E8 77 ww
AD A9 E9 77 ww
03 38 E7 77 6w8w
60 8D E7 77 Aw`w
2B 2A E7 77 w+*w
79 C8 E6 77 zwyw
C1 30 E7 77 w0w
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.....
........
........
........
........
........
........
........
IMAGE_DOS_HEADER
image_dos_header;
IMAGE_NT_HEADERS
image_nt_headers;
PCHAR pMem;
memcpy(&image_dos_header, pMem,
sizeof(IMAGE_DOS_HEADER));
memcpy(&image_nt_headers,
pMem+image_dos_header.e_lfanew,
sizeof(IMAGE_NT_HEADERS));
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 task manager 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:
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
IMAGE_DIRECTORY_ENTRY_EXPORT
IMAGE_DIRECTORY_ENTRY_IMPORT
IMAGE_DIRECTORY_ENTRY_RESOURCE
IMAGE_DIRECTORY_ENTRY_EXCEPTION
IMAGE_DIRECTORY_ENTRY_SECURITY
IMAGE_DIRECTORY_ENTRY_BASERELOC
IMAGE_DIRECTORY_ENTRY_DEBUG
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE
IMAGE_DIRECTORY_ENTRY_GLOBALPTR
IMAGE_DIRECTORY_ENTRY_TLS
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
IMAGE_DIRECTORY_ENTRY_IAT
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
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
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.
have significant rules. While developing an EXE packer, you should be clever enough to play with them. There are somethings to be noted while you modify them; you should take
care to align the VirtualSize and VirtualAddress according to OptionalHeader->SectionAlignment, as well
SizeOfRawData and PointerToRawData in line with OptionalHeader->FileAlignment. Otherwise, you will corrupt your target EXE file and it will
never run. Regarding Characteristics, I pay attention mostly to establish a section by IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE |
IMAGE_SCN_CNT_INITIALIZED_DATA, I prefer my new section has ability to initialize such data during running process; such as import table; besides, I need it to be
as
able to modify itself by the loader with my settings in the section characteristics to read- and writeable.
Moreover, you should pay attention to the section names, you can know the purpose of each section by its name. I will just forward you to section 6: Microsoft Portable
Executable and the Common Object File Format Specification documents. I believe, it represents the totality of sections by their names, Table 2.
Table 2 - Section names
".text"
Code Section
TLS Table
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->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!
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(
00
01
20
00
6E
00
00
00
00
64
00
00
00
00
00
00
00
00
00
00
00
20
00
00
00
00
00
00
00
00
00
00
00
00
00
00-00
00-0A
00-53
00-62
00-2E
00 00 00 00 00 00 00 ................
00 00 00 0A 00 00 00 ................
63 69 43 61 6C 63 00 ........SciCalc.
61 63 6B 67 72 6F 75 ........backgrou
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
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
Registers viewer.
EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI
EIP
odtszapc
2.
010119E1
010119E3
010119E5
010119EA
010119EF
010119F5
010119F6
010119FD
01011A00
01011A01
01011A02
01011A03
01011A06
3.
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
FS:[0],ESP
SS:[EBP-18],ESP
SS:[EBP-4],0
Memory watcher.
0023:01013000
0023:01013010
0023:01013020
0023:01013030
0023:01013040
4.
PTR FS:[0]
00
01
20
00
6E
00
00
00
00
64
00
00
00
00
00
00
00
00
00
00
00
20
00
00
00
00
00
00
00
00
00
00
00
00
00
00-00 00
00-0A 00
00-53 63
00-62 61
00-2E 00
00 00 00 00 00 00 ................
00 00 0A 00 00 00 ................
69 43 61 6C 63 00 ........SciCalc.
63 6B 67 72 6F75 ........backgrou
00 00 00 00 00 00 nd..............
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
SoftICE
OllyDbg
Run
F5
F9
Step Into
F11
F7
Step Over
F10
F8
F8
F2
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.
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.
00000000
00000002
00000004
00000006
00000008
0000000A
0000000C
0000000E
00000010
00000012
00000014
00000016
00000018
0000001A
0000001C
0000001D
0000001E
0000001F
00000020
4D 5A
9000
0300
0000
0400
0000
FFFF
0000
B800
0000
0000
0000
4000
0000
00
00
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
DB 00
DB 00
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
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
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
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.
DynLoader(), in loader.cpp, is reserved for the data of the new section, in other words, the Loader.
__stdcall __cdecl __stdcall
(Google/Programming/Reverse Engineering/Assembly/_cdecl,_pascal,_stdcall )
DynLoader Step 1
__stdcall void DynLoader()
{
_asm
{
//---------------------------------DWORD_TYPE(DYN_LOADER_START_MAGIC)
//---------------------------------MOV EAX,01012475h // << Original OEP
JMP EAX
//---------------------------------DWORD_TYPE(DYN_LOADER_END_MAGIC)
//---------------------------------}
}
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).
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:
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:
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:
DynLoader(),
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
DynLoader() to
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
DynLoader() to
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
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
other kinds of exception too. In OllyDbg,Debugging options->Exceptions, you can see a short list of different types of exceptions.
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
_except_handler1_OEP_Jump:
PUSH EBP
MOV EBP,ESP
...
MOV EAX, EXCEPTION_CONTINUE_SEARCH // EXCEPTION_CONTINUE_SEARCH = 0
LEAVE
RETN
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 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), determines how the exception is handled, it can have three values, 1, 0, -1. To understand them, refer to the tryexcept statement description in the MSDN library. We set it to EXCEPTION_CONTINUE_SEARCH (0), not to run the exception handler function, therefore by this
The exception value,
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:
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
ExceptionList
StackBase
StackLimit
SubSystemTib
Self
512
Context
0x00000000
Flags
ContextFlags
0x00000004
0x00000008
Context 0x0000000C
Debug
Registers 0x00000010
0x00000014
0x00000018
0x0000001C FloatSave
Context 0x00000020
Floating
Point 0x00000024
0x00000028
0x0000002C
0x00000030
Dr0
Dr1
Dr2
Dr3
Dr6
Dr7
StatusWord
StatusWord
TagWord
ErrorOffset
ErrorSelector
DataOffset
0x00000034
DataSelector
0x00000038
...
0x00000087
RegisterArea[0x50]
0x00000088
0x0000008C
Context 0x00000090
Segment
0x00000094
s
0x00000098
0x0000009C
0x000000A0
Context 0x000000A4
Integer 0x000000A8
0x000000AC
0x000000B0
0x000000B4
0x000000B8
Context 0x000000BC
Control 0x000000C0
0x000000C4
0x000000C8
Context
Extended
Registers
0x000000C
C
...
0x000002CB
Cr0NpxState
SegGs
SegFs
SegEs
SegDs
Edi
Esi
Ebx
Edx
Ecx
Eax
Ebp
Eip
SegCs
EFlags
Esp
SegSs
ExtendedRegisters[0x200]
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 // 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
XOR EAX, 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)
//---------------------------------}
}
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.
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
IMAGE_IMPORT_DESCRIPTOR. This structure contains the pointer to the imported DLL name and the relative virtual
OriginalFirstThu
nk
TimeDateStamp
ForwarderChain
Name_RVA
IMAGE_IMPOR FirstThunk_RVA
T_
DESCRIPTOR[
0]
- "kernel32.dll",0
>
- proc_1_name_R
VA
>
- 0,0,"LoadLibraryA",0
>
proc_2_name_R
VA
- 0,0,"GetProcAddress",0
>
proc_3_name_R
VA
0,0,"GetModuleHandleA
",0
>
...
IMAGE_IMPOR
T_
DESCRIPTOR[
1]
...
IMAGE_IMPOR
T_
DESCRIPTOR[
n]
Table 6 - The Import Table in virtual memory
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:
0101F004:
0101F008:
0101F00C:
0101F010:
0101F014:
0101F018:
0101F01C:
0101F020:
0101F024:
00000000
00000000
00000000
0001F034
0001F028
00000000
00000000
00000000
00000000
00000000
;
;
;
;
;
OriginalFirstThunk
TimeDateStamp
ForwarderChain
Name;
ImageBase + 0001F034 -> 0101F034 -> "Kernel32.dll",0
FirstThunk; ImageBase + 0001F028 -> 0101F028
0101F028:
0101F02C:
0101F030:
0101F034:
0001F041:
0001F050:
After running...
0101F000:
0101F004:
0101F008:
0101F00C:
0101F010:
0101F014:
0101F018:
0101F01C:
0101F020:
0101F024:
0101F028:
0101F02C:
0101F030:
0101F034:
0001F041:
0001F050:
00000000 ; OriginalFirstThunk
00000000 ; TimeDateStamp
00000000 ; ForwarderChain
0001F034 ; Name;
ImageBase + 0001F034 -> 0101F034 -> "Kernel32.dll",0
0001F028 ; FirstThunk; ImageBase + 0001F028 -> 0101F028
00000000
00000000
00000000
00000000
00000000
7C801D77 ; -> Kernel32.LoadLibrary()
7C80AC28 ; -> Kernel32.GetProcAddress()
00000000
'K' 'e' 'r' 'n' 'e' 'l' '3' '2' '.' 'd' 'l' 'l' 00
00 00 'L' 'o' 'a' 'd' 'L' 'i' 'b' 'r' 'a' 'r' 'y' 'A' 00
00 00 'G' 'e' 't' 'P' 'r' 'o' 'c' 'A' 'd' 'd' 'r' 'e' 's' 's' 00
I have prepared a class library to make every import table by using a client string table. The
by
sz_IT_EXE_strings and also the relative virtual address of the import table.
[XXXXXXXX].
#define __jmp_api
__asm
{
...
byte_type(0xFF) byte_type(0x25)
JMP
//---------------------------------------------------------------_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
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
...
Constructor:
Destructor:
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
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.
VirtualAddress
SizeOfBlock
offset:1
type:4
type:4 offset:12
2
Block[1 type:4
]
type:4
offset:1
type:4 offset:12
2
offset:1
type:4 offset:12
2
...
...
...
...
type:4
offset:1
2
00
00
VirtualAddress
SizeOfBlock
type:4
Block[2 type:4
]
type:4
offset:1
type:4 offset:12
2
offset:1
type:4 offset:12
2
offset:1
type:4 offset:12
2
...
...
offset:1
type:4
2
...
...
...
00
00
...
VirtualAddress
SizeOfBlock
Block[n]
type:4
offset:1
type:4 offset:12
2
type:4
offset:1
type:4 offset:12
2
type:4
offset:1
type:4 offset:12
2
...
...
...
...
type:4
offset:1
2
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
+
+
+
+
+
0x0000
0x0016
0x0028
0x0040
0x0054
=
=
=
=
=
0x1000
0x1016
0x1028
0x1040
0x1054
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_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.
By relocation, some values inside the virtual memory are corrected according to the current image base by the ".reloc" section packages.
"ImageList_SetIconSize",
0,
0,
};
Without these API functions, the library can not be loaded, and moreover the
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 );
}
dword_type(0xCCCCCCCC)
dword_type(0xCCCCCCCC)
dword_type(0xCCCCCCCC)
dword_type(0xCCCCCCCC)
dword_type(0xCCCCCCCC)
dword_type(0xCCCCCCCC)
...
push MB_OK | MB_ICONINFORMATION
lea eax,[ebp+_p_szCaption]
push eax
lea eax,[ebp+_p_szText]
push eax
push NULL
call _jmp_MessageBox
// MessageBox(NULL, szText, szCaption, MB_OK | MB_ICONINFORMATION) ;
...
PE Maker - Step 5
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!
Ashkbiz Danehkar