|
.EXE / .DLL PE Explorer - Felix John COLIBRI.
|
- abstract : presents and analyzes the content of .EXE and .DLL files
- key words : Windows - Portable Executable - System Programming - tTreeView
- Memory Mapped files
- software used : Windows XP, Delphi 6
- hardware used : Pentium 2.800Mhz, 512 M memory, 140 G hard disc
- scope : Delphi 1 to 2006, Turbo Delphi for Windows
- level : Windows developer
- plan :
1 - Analyzing the PE format
In order to perform some logging on an database application, we had to monitor
each call to the Sql Engine. To do so, we chose to capture calls to the Client
DLL, and this required some .DLL Injection technique. So we took our old .EXE
analyzer and added the required code.
Spelunking into the .EXE was not that easy, since the original code dated back
to Windows 3.1. And since it turns out that the format is still used (event for
64 bits and for .Net compiled files), we decided to publish the project.
This paper presents the overall organization, and the project allows to present
the structure and content of those files.
2 - The Portable Executable Format
2.1 - General PE properties
Programs and DLLs compiled for Windows are saved to disc in the PE (Portable
Executable) format.
The main properties of the format are:
2.2 - Overall format
The overall structure contains
- a DOS stub
- a NT header
- a Section header list
- the list of Sections
Here is the overall picture:
2.3 - The Dos stub
The historical part allowed to check the nature of the file at the time when
DOS was the base OS, and Windows (3.1) was loaded from DOS.
This $100 byte part mainly contains
- a signature ($544D, which is MZ, Mark Zbikowski, a Dos architect)
- m_nt_header_file_address field: he address of the main table which is the
NT header
2.4 - The NT header
The header contains
- the "PE" signature
- a file header
- the "optional" header (which is not optional at all)
- the section directory
Here is the definition
t_nt_header= Record
m_nt_signature: dWord;
m_nt_file_header: t_nt_file_header;
m_nt_optional_header: t_nt_optional_header;
m_nt_section_directory: t_nt_section_directory;
end; // t_nt_header
|
with the following figure:
2.4.1 - The File Header
Here is the definition
t_nt_file_header= packed record
Machine: Word;
m_number_of_sections: Word;
TimeDateStamp: dWord;
PointerToSymbolTable: dWord;
NumberOfSymbols: dWord;
SizeOfOptionalHeader: Word;
Characteristics: Word;
end; // t_nt_file_header
|
and
- SizeOfOptionalHeader is the size of the t_nt_optional_header
2.5 - The optional header
This header is defined by
t_nt_optional_header= packed record
// -- Standard fields.
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: dWord;
SizeOfInitializedData: dWord;
SizeOfUninitializedData: dWord;
AddressOfEntryPoint: dWord;
BaseOfCode: dWord;
BaseOfData: dWord;
// --Nt additional fields
ImageBase: dWord;
SectionAlignment: dWord;
FileAlignment: dWord;
MajorOperatingSystemVersion: Word;
MinorOperatingSystemVersion: Word;
MajorImageVersion: Word;
MinorImageVersion: Word;
MajorSubsystemVersion: Word;
MinorSubsystemVersion: Word;
Win32VersionValue: dWord;
SizeOfImage: dWord;
SizeOfHeaders: dWord;
CheckSum: dWord;
Subsystem: Word;
DllCharacteristics: Word;
SizeOfStackReserve: dWord;
SizeOfStackCommit: dWord;
SizeOfHeapReserve: dWord;
SizeOfHeapCommit: dWord;
LoaderFlags: dWord;
m_section_entry_count: dWord;
end; // t_nt_optional_header
|
2.5.1 - The Section Directory (alias DataDirectory)
This is an array of 16 structures telling where the Sections are located.
Each section entry is defined by
t_section_entry= record
m_section_relative_virtual_address: dWord;
m_section_size: dWord;
end; // t_section_entry
|
where:
- m_section_relative_virtual_address is the address of the section start
(relative to the base load address)
- m_section_size is the section size
and the directory is an array of those entries:
const k_section_directory_count= 16;
type t_nt_section_directory=
packed array[0..k_section_directory_count- 1] of t_section_entry;
|
Note that
- the WINNT.H names this structure DataDirectory, and it is included in the
ImageOptionalHeader structure
2.6 - The Section Headers
This structure is located just after the t_optional_header, and is an array or
t_section_header records:
Each section header is defined by:
const k_section_name_max= 8;
type t_section_address_and_size= packed record
case Integer of
0: (m_section_physical_address: dWord);
1: (m_section_virtual_size: dWord);
end; // t_section_address_and_size
t_section_header= packed record
m_name: packed array[0..k_section_name_max- 1] of Byte;
m_address_and_size: t_section_address_and_size;
m_virtual_address: dWord;
m_size_of_raw_data: dWord;
m_pointer_to_raw_data: dWord;
m_pointer_to_relocations: dWord;
PointerToLinenumbers: dWord;
NumberOfRelocations: Word;
NumberOfLinenumbers: Word;
Characteristics: dWord;
end; // t_section_header
|
The header count is given by t_nt_file_header.m_number_of_sections
2.7 - The Sections
2.7.1 - The different sections
Section can contain executable code, data (globals), resources etc.
- the imported and exported symbols (functions, variables)
- the Windows resources (bitmaps, cursors etc)
2.7.2 - Function Import and Export
When a DLL exports a function, it can either use an index or an identifier
name.
Since using names involves a search in a name list, using function indexes is
quicker. However developer might prefer the usage of names for maintenance or
readability considerations.
In any case, this explains why both import and export structures contain both
name lists and index lists.
2.7.3 - The Import Section
This section contains an list of import descriptors. At the end of the list
there is an 0 filled descriptor.
Each import descriptor is defined by
t_imported_module= record
// -- INT: Import Name table
m_pt_first_function_original: t_pt_imported_function;
m_import_timestamp: dWord;
m_import_forwarder_chain: dWord;
m_imported_module_name_rva: dWord;
// -- IAT: Import Address Table
m_pt_first_function: t_pt_imported_function;
end; // t_imported_module
|
where
- m_imported_module_name_rva is the relative address of the .DLL name
- m_import_forwarder_chain allows to forwad the imported name from one .DLL
to another one
- m_pt_first_function_original and m_pt_first_function are pointers to 2
tables containing information about each imported function.
Each function is described by a variant record:
t_imported_by_name_function=
record
m_import_index_hint: Word;
m_import_name: Byte;
end; // t_imported_by_name_function
t_pt_imported_by_name_function= ^t_imported_by_name_function;
t_imported_function=
record
case Integer of
1: (m_pt_forwarder_string: ^Byte);
2: (m_function_address: pdWord);
3: (m_function_index: dWord);
4: (m_pt_imported_by_name_function: t_pt_imported_by_name_function);
end; // t_imported_function
t_pt_imported_function= ^t_imported_function;
|
Let's recap the import structure:
- the t_nt_header allows to get the t_section_header
- having the section base address, we compute the individual .DLL imported
module record
- this record contains the module name and pointers to 2 function lists with
identical structure and slightly different content. The end of the function
list is indicated by a zero record
The overall picture is
or with more detail
For the INT, the content for each imported function is a dWord. And the dWord
contains
- if the high bit of this value is 1, the 31 low bit part is a function index
- if the high bit is 0, the 31 low bit part are a relative value address
pointing to a t_imported_by_name_function record
For the IAT, the content on disc is the same, as the INT, but once loaded into
memory, the dWord is overwritten with the actual address of the code of the
imported function
The presence of two structure allows binding: if the loader overwrites the
dWord value with the true code address, there must be a way to still keep the
original information. The INT was long ignored by Borland compilers
2.7.4 - The Export Section
The export section is defined by:
t_exported_functions= record
Characteristics: dWord;
TimeDateStamp: dWord;
MajorVersion: Word;
MinorVersion: Word;
m_dll_name_rva: dWord;
m_export_start_index: dWord;
m_exported_function_count: dWord;
m_exported_name_count: dWord;
// -- export Address Table (EAT)
m_exported_function_address: Pdword;
// -- export name table (ENT)
m_exported_function_name_address: pdWord;
m_exported_function_index_address: pdWord;
end; // t_exported_functions
|
and:
- m_dll_name_rva: this .DLL name
- for the exported name list:
- exported_name_count: how many functions are in the table name
- m_exported_function_name_address: a table of the the export name
addresses
- for each of those names, there is a corresponding entry in the
m_exported_function_index_address table of function indexes. To the
indexes, you have to add the m_export_start_index (usually the index starts
at 1)
- the index plus the starting index point to the m_exported_function_address
table, which contains the code address of the function
This looks like this:
2.7.5 - The Resource Section
The resource section is organized like a file directory: it contains folders
and entries.
The base directory (folder) structure is:
t_resource_directory= packed record
Characteristics: DWORD;
TimeDateStamp: DWORD;
MajorVersion: WORD;
MinorVersion: WORD;
m_name_entry_count: WORD;
m_id_entry_count: WORD;
end; // t_resource_directory
|
and:
- m_name_entry_count is the number of name entries
- m_id_entry_count is the name of id entries
Each (nested) directory is followed by a table of entries (the count is the sum
of both counts). The entries are defined by:
t_resource_entry= packed record
m_name_or_id: DWORD;
m_offset_to_data: DWORD;
end; // t_resource_entry
|
where:
- m_offset_to_data:
- if the high bit is set, the entry is a nested directory, and if not, it
is a data entry
- the low bits are the offset RELATIVE TO THE RESOURCE SECTION (not a rva)
- m_name_or_id:
- for the first level, and if the high bit is 0, the value is a resource
type (1 for Cursor, 2 for Bitmap and so on)
- in the other cases, this field is a resource id (a number or an address
to a WideString name)
And each data entry looks like this
t_resource_data_entry= packed record
m_data_offset_to_data: DWORD;
m_data_size: DWORD;
m_data_code_page: DWORD;
Reserved: DWORD;
end; // t_resource_data_entry
|
This traditional recursive structure can then be displayed like this
3 - The Delphi PE Explorer source code
3.1 - Basic organization
The code include 3 parts
- a PE structure definition UNIT (which was used in the previous
presentation)
- a CLASS allowing to compute and display the different parts
- the main tForm
3.2 - The definitions
This UNIT is the straight Pascal version of the WinNT.H, with simple
renaming applied.
Notice that many of the definitions are already in the WINDOWS.PAS Delphi file
3.3 - The c_pe CLASS
This CLASS has the ability to access and display the different parts of a PE
structure. The definition is the following:
c_pe= Class(tComponent)
private
public
m_file_name: String;
m_is_file_exe: Boolean;
m_file_handle: tHandle;
m_mapping_handle: tHandle;
m_base_address: dWord;
m_c_display_strings: tStrings;
constructor Create(p_file_name: String; p_c_strings: tStrings);
procedure display_pe(p_text: String);
function f_display_address(p_pt: Pointer): String;
procedure display_address_and_text(p_pt: Pointer; p_text: String);
function f_pt_nt_header: t_pt_nt_header;
function f_pt_section_header(p_section: dWord): t_pt_section_header;
function f_pt_section_by_name_header(p_pt_section_name_z: pChar):
t_pt_section_header;
function f_pt_enclosing_header_section(p_enclosed_address: dWord):
t_pt_section_header;
function f_pt_imported_module(p_dll_index: dWord;
var pv_delta_mapping: dWord): t_pt_imported_module;
function f_pt_exports_directory(var pv_delta_mapping,
pv_start_address, pv_end_address: dWord): t_pt_exported_functions;
function f_pt_buffer(p_address: Integer): t_pt_bytes;
function f_pt_word(p_address: Integer): t_pt_word;
procedure analyze_dos_header;
procedure analyze_nt_header;
procedure analyze_nt_file_header;
procedure analyze_nt_optional_header;
procedure analyze_section_directory;
procedure analyze_section_header_table;
procedure analyze_imported_dlls(p_c_dll_name_stringlist: tStrings);
procedure analyze_imported_functions(p_dll_name: String; p_c_strings: tStrings;
p_borland: String);
function f_pt_imported_function_address(p_dll_name, p_function_name: String): pdWord;
procedure analyze_exports;
procedure analyze_resources;
end; // c_pe
|
The original code was written using Memory Mapped Files, which is a Windows
technique allowing to access with pointer a disc file. We also wanted to be
able to explore an in-memory module, so we included a flag allowing to compute
addresses from file or memory. Here is the constructor:
constructor c_pe.Create(p_file_name: String; p_c_strings: tStrings);
begin
Inherited Create(Nil);
m_file_name:= p_file_name;
m_c_visited_list:= c_address_range_list.create_address_range_list('visited_list');
if p_file_name= ''
then begin
m_is_file_exe:= False;
m_base_address:= hInstance;
end
else begin
m_file_handle:= CreateFile(pChar(p_file_name),
Generic_Read, File_Share_Read, k_security_nil,
Open_Existing, File_Attribute_Normal, k_template_0);
// -- create a mapping
m_mapping_handle:= CreateFileMapping(m_file_handle,
k_security_nil, Page_ReadOnly, k_high_max_0, k_low_max_0,
k_name_nil);
// -- create a view
m_base_address:= dWord(MapViewOfFile(m_mapping_handle, File_Map_Read,
k_high_offset_0, k_low_offset_0,
k_all_offsets_0));
m_is_file_exe:= True;
end;
m_c_display_strings:= p_c_strings;
end; // Create
|
There are several function which compute the addresses or return pointers to
the different parts of a PE. For instance, here is the NT header access
function:
function c_pe.f_pt_nt_header: t_pt_nt_header;
begin
Result:= t_pt_nt_header(m_base_address
+ t_pt_dos_header(m_base_address)^.m_nt_header_file_address);
end; // f_pt_nt_header
|
Using those access functions, it is then easy to display each PE element
content.
The TreeView explorer
To load the PE information into a tTreeView, we first started to directly
create the nodes by using the c_pe routines. This was quite akward, since we
had to handle both our access routines and the recursive tree structure. In
addition, no object was associated with each tTreeNode.
So we finally created a c_pe_element tree, with specific nodes for each part
of the PE, and those nodes were added to each tTreeNode.
The structure of our pe elements follow the classic composite pattern, with a
separate element for each PE record
Once the structure was filled, initializing the tTreeNode was a ten minute
job.
3.4 - The Main Form
This Form allows
- to select the target .EXE or .DLL
- to display the content of some part in a tMemo
- to build a tTreeView displaying the PE content
3.5 - sample DLL
To illustrate the analysis of the PE structure, we will use a simple Delphi
.DLL, with the following code:
library d_min_max;
var g_integer: Integer;
function f_min(p_one, p_two: Integer): Integer; stdcall;
var l_local: Integer;
begin
l_local:= $AAAA;
g_integer:= l_local+ $7777;
if p_one < p_two
then Result := p_one
else Result:= p_two;
end; // f_min
function f_max(p_one, p_two: Integer): Integer; stdcall;
var l_local: Integer;
begin
l_local:= $BBBB;
if p_one > p_two
then Result:= p_one
else Result:= p_two;
end; // f_max
exports
f_min,
f_max;
begin
end.
|
The compiled .DLL is a 8 K file, d_min_max.dll. A simplified hex dump of this
file is:
Dos Header
0000: 4D 5A 50 00 02 00 00 00 04 00 0F 00 FF FF 00 00 : MZP..... ......
0010: B8 00 00 00 00 00 00 00 40 00 1A 00 00 00 00 00 : ....... @.......
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
0030: 00 00 00 00 00 00 00 00 00 00 00 00
m_nt_header_file_address
00 01 00 00 : ........ ........
...ooo...
NT header
nt_signature
0100: 50 45 00 00
nt_file_header
4C 01 07 00 19 5E 42 2A 00 00 00 00 : PE..L... .^B*....
0110: 00 00 00 00 E0 00 8E A1
nt_optional_header
0B 01 02 19 00 10 00 00 : ..... ........
0120: 00 0C 00 00 00 00 00 00 DC 1F 00 00 00 10 00 00 : ........ .......
0130: 00 20 00 00 00 00 40 00 00 10 00 00 00 02 00 00 : . ....@. ........
0140: 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 : ........ ........
0150: 00 80 00 00 00 04 00 00 00 00 00 00 02 00 01 00 : ....... ........
0160: 00 00 00 00 00 00 00 00 00 00 10 00 00 10 00 00 : ........ ........
0170: 00 00 00 00 10 00 00 00
nt_section_directory
00 50 00 00 56 00 00 00 : ........ .P..V...
0180: 00 40 00 00 BE 02 00 00 00 70 00 00 00 02 00 00 : .@..... .p......
0190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
01A0: 00 60 00 00 AC 01 00 00 00 00 00 00 00 00 00 00 : .`..... ........
01B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
01C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
01D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
01E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
01F0: 00 00 00 00 00 00 00 00
section headers
CODE section header
43 4F 44 45 00 00 00 00 : ........ CODE....
0200: F4 0F 00 00 00 10 00 00 00 10 00 00 00 04 00 00 : ....... ........
0210: 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 : ........ .... ..`
0220: 44 41 54 41 00 00 00 00 A0 00 00 00 00 20 00 00 : DATA.... .... ..
0230: 00 02 00 00 00 14 00 00 00 00 00 00 00 00 00 00 : ........ ........
0240: 00 00 00 00 40 00 00 C0
42 53 53 00 00 00 00 00 : ....@.. BSS.....
0250: ED 06 00 00 00 30 00 00 00 00 00 00 00 16 00 00 : ....0.. ........
0260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 : ........ .......
0270: 2E 69 64 61 74 61 00 00 BE 02 00 00 00 40 00 00 : .idata.. ....@..
0280: 00 04 00 00 00 16 00 00 00 00 00 00 00 00 00 00 : ........ ........
0290: 00 00 00 00 40 00 00 C0
2E 65 64 61 74 61 00 00 : ....@.. .edata..
02A0: 56 00 00 00 00 50 00 00 00 02 00 00 00 1A 00 00 : V....P.. ........
02B0: 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 50 : ........ ....@..P
02C0: 2E 72 65 6C 6F 63 00 00 AC 01 00 00 00 60 00 00 : .reloc.. ....`..
02D0: 00 02 00 00 00 1C 00 00 00 00 00 00 00 00 00 00 : ........ ........
02E0: 00 00 00 00 40 00 00 50
2E 72 73 72 63 00 00 00 : ....@..P .rsrc...
02F0: 00 02 00 00 00 70 00 00 00 02 00 00 00 1E 00 00 : .....p.. ........
0300: 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 50 : ........ ....@..P
0310: 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 : ........ .......
0320: 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 : ..... .. ........
0330: 00 00 00 00 40 00 00 50
...ooo...
Sections
CODE
...ooo...
1310: 64 89 10 68 20 1F 40 00 C3 E9 82 F6 FF FF EB F8 : d.h .@.
1320: 5D C3 8B C0 83 2D E0 36 40 00 01 C3
f_min ( $AAAA and $7777 markers )
55 8B EC 83 : ]Ë-6 @..U
1330: C4 F8 C7 45 F8 AA AA 00 00 8B 45 F8 05 77 77 00 : E. .E.ww.
1340: 00 A3 E8 36 40 00 8B 45 08 3B 45 0C 7D 08 8B 45 : .6@.E .;E.}.E
1350: 08 89 45 FC EB 06 8B 45 0C 89 45 FC 8B 45 FC 59 : .E.E .EEY
1360: 59 5D C2 08 00 8D 40 00
f_max ( $BBBB marker )
55 8B EC 83 C4 F8 C7 45 : Y]..@. UE
1370: F8 BB BB 00 00 8B 45 08 3B 45 0C 7E 08 8B 45 08 : ..E. ;E.~.E.
1380: 89 45 FC EB 06 8B 45 0C 89 45 FC 8B 45 FC 59 59 : E.E. EEYY
1390: 5D C2 08 00 55 8B EC 33 C0 55 68 B3 1F 40 00
...ooo...
DATA
1400: 00 00 00 00 00 00 00 00 02 8D 40 00 32 13 8B C0 : ........ .@.2.
1410: 00 8D 40 00 00 8D 40 00 00 8D 40 00 00 00 00 00 : .@..@. .@.....
1420: 00 00 00 00 00 CB CC C8 C9 D7 CF C8 CD CE DB D8 : .....
1430: CA D9 DA DC DD DE DF E0 E1 E3 00 E4 E5 8D 40 00 : .@.
1440: 52 75 6E 74 69 6D 65 20 65 72 72 6F 72 20 20 20 : Runtime error
1450: 20 20 61 74 20 30 30 30 30 30 30 30 30 00 8B C0 : at 000 00000.
1460: 45 72 72 6F 72 00 8B C0 30 31 32 33 34 35 36 37 : Error. 01234567
1470: 38 39 41 42 43 44 45 46 00 00 00 00 00 00 00 00 : 89ABCDEF ........
1480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
1490: 3C 1E 40 00 E8 1D 40 00 A4 1D 40 00 14 1E 40 00 :.@..@. .@...@.
...ooo...
.iData (imported symbols)
1600: 00 00 00 00 00 00 00 00 00 00 00 00 DC 40 00 00 : ........ ....@..
1610: 64 40 00 00
00 00 00 00 00 00 00 00 00 00 00 00 : d@...... ........
1620: F8 41 00 00 A4 40 00 00
00 00 00 00 00 00 00 00 : A..@.. ........
1630: 00 00 00 00 24 42 00 00 B0 40 00 00 00 00 00 00 : ....$B.. @......
1640: 00 00 00 00 00 00 00 00 64 42 00 00 C0 40 00 00 : ........ dB..@..
1650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
1660: 00 00 00 00 EA 40 00 00 02 41 00 00 1A 41 00 00 : ....@.. .A...A..
1670: 32 41 00 00 40 41 00 00 4C 41 00 00 62 41 00 00 : 2A..@A.. LA..bA..
1680: 74 41 00 00 86 41 00 00 94 41 00 00 A2 41 00 00 : tA..A.. A..A..
1690: AE 41 00 00 CA 41 00 00 D6 41 00 00 E8 41 00 00 : A..A.. A..A..
16A0: 00 00 00 00 04 42 00 00 16 42 00 00 00 00 00 00 : .....B.. .B......
16B0: 32 42 00 00 46 42 00 00 56 42 00 00 00 00 00 00 : 2B..FB.. VB......
16C0: 72 42 00 00 80 42 00 00 8E 42 00 00 98 42 00 00 : rB..B.. B..B..
16D0: A4 42 00 00 B0 42 00 00 00 00 00 00
Kernel32.Dll imported functions
6B 65 72 6E : B..B.. ....kern
16E0: 65 6C 33 32 2E 64 6C 6C 00 00 00 00 44 65 6C 65 : el32.dll ....Dele
16F0: 74 65 43 72 69 74 69 63 61 6C 53 65 63 74 69 6F : teCritic alSectio
1700: 6E 00 00 00 4C 65 61 76 65 43 72 69 74 69 63 61 : n...Leav eCritica
1710: 6C 53 65 63 74 69 6F 6E 00 00 00 00 45 6E 74 65 : lSection ....Ente
1720: 72 43 72 69 74 69 63 61 6C 53 65 63 74 69 6F 6E : rCritica lSection
1730: 00 00 00 00 56 69 72 74 75 61 6C 46 72 65 65 00 : ....Virt ualFree.
1740: 00 00 4C 6F 63 61 6C 46 72 65 65 00 00 00 47 65 : ..LocalF ree...Ge
1750: 74 43 75 72 72 65 6E 74 54 68 72 65 61 64 49 64 : tCurrent ThreadId
1760: 00 00 00 00 47 65 74 53 74 61 72 74 75 70 49 6E : ....GetS tartupIn
1770: 66 6F 41 00 00 00 47 65 74 43 6F 6D 6D 61 6E 64 : foA...Ge tCommand
1780: 4C 69 6E 65 41 00 00 00 46 72 65 65 4C 69 62 72 : LineA... FreeLibr
1790: 61 72 79 00 00 00 45 78 69 74 50 72 6F 63 65 73 : ary...Ex itProces
17A0: 73 00 00 00 57 72 69 74 65 46 69 6C 65 00 00 00 : s...Writ eFile...
17B0: 55 6E 68 61 6E 64 6C 65 64 45 78 63 65 70 74 69 : Unhandle dExcepti
17C0: 6F 6E 46 69 6C 74 65 72 00 00 00 00 52 74 6C 55 : onFilter ....RtlU
17D0: 6E 77 69 6E 64 00 00 00 52 61 69 73 65 45 78 63 : nwind... RaiseExc
17E0: 65 70 74 69 6F 6E 00 00 00 00 47 65 74 53 74 64 : eption.. ..GetStd
17F0: 48 61 6E 64 6C 65 00 00
75 73 65 72 33 32 2E 64 : Handle.. user32.d
1800: 6C 6C 00 00 00 00 47 65 74 4B 65 79 62 6F 61 72 : ll....Ge tKeyboar
1810: 64 54 79 70 65 00 00 00 4D 65 73 73 61 67 65 42 : dType... MessageB
1820: 6F 78 41 00
61 64 76 61 70 69 33 32 2E 64 6C 6C : oxA.adva pi32.dll
1830: 00 00 00 00 52 65 67 51 75 65 72 79 56 61 6C 75 : ....RegQ ueryValu
1840: 65 45 78 41 00 00 00 00 52 65 67 4F 70 65 6E 4B : eExA.... RegOpenK
1850: 65 79 45 78 41 00 00 00 52 65 67 43 6C 6F 73 65 : eyExA... RegClose
1860: 4B 65 79 00 6B 65 72 6E 65 6C 33 32 2E 64 6C 6C : Key.kern el32.dll
1870: 00 00 00 00 54 6C 73 53 65 74 56 61 6C 75 65 00 : ....TlsS etValue.
1880: 00 00 54 6C 73 47 65 74 56 61 6C 75 65 00 00 00 : ..TlsGet Value...
1890: 54 6C 73 46 72 65 65 00 00 00 54 6C 73 41 6C 6C : TlsFree. ..TlsAll
18A0: 6F 63 00 00 00 00 4C 6F 63 61 6C 46 72 65 65 00 : oc....Lo calFree.
18B0: 00 00 4C 6F 63 61 6C 41 6C 6C 6F 63 00 00 00 00 : ..LocalA lloc....
...ooo...
.eData (exported function section)
1A00: 00 00 00 00 00 00 00 00 00 00 00 00 3C 50 00 00 : ........ .....P..
1A10: 01 00 00 00 02 00 00 00 02 00 00 00 28 50 00 00 : ........ ....(P..
1A20: 30 50 00 00 38 50 00 00
m_exported_address (f_min at $132C, f_max at $1368)
68 1F 00 00 2C 1F 00 00 : 0P..8P.. h...,...
m_exported_name
1A30: 4A 50 00 00 50 50 00 00 : JP..PP..
m_exported_index
00 00 01 00 ....
m_dll_name
64 5F 6D 69 d_mi
1A40: 6E 5F 6D 61 78 2E 64 6C 6C 00 : n_max.dl l.
m_exported_name_table
66 5F 6D 61 78 00 : f_max.
1A50: 66 5F 6D 69 6E 00 : f_min.
00 00 00 00 00 00 00 00 00 00 : .. ........
1A60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
...ooo...
1C00: 00 10 00 00 98 01 00 00 02 30 0A 30 12 30 1A 30 : ....... .0.0.0.0
1C10: 22 30 2A 30 32 30 3A 30 42 30 4A 30 52 30 5A 30 : "0*020:0 B0J0R0Z0
1C20: 62 30 6A 30 96 30 9E 30 A6 30 AE 30 B6 30 CA 30 : b0j000 0000
...ooo...
.rsrc (Resource Section
1E00: 00 00 00 00 13 41 39 36 00 00 00 00 00 00 01 00 : .....A96 ........
1E10: 0A 00 00 00 18 00 00 80
00 00 00 00 13 41 39 36 : ....... .....A96
1E20: 00 00 00 00 02 00 00 00 88 00 00 80 38 00 00 80 : ........ ..8..
1E30: 96 00 00 80 50 00 00 80
00 00 00 00 13 41 39 36 : ..P.. .....A96
1E40: 00 00 00 00 00 00 01 00 00 00 00 00 68 00 00 00 : ........ ....h...
1E50: 00 00 00 00 13 41 39 36 00 00 00 00 00 00 01 00 : .....A96 ........
1E60: 00 00 00 00 78 00 00 00
B0 70 00 00 10 00 00 00 : ....x... p......
1E70: 00 00 00 00 00 00 00 00
C0 70 00 00 2C 00 00 00 : ........ p..,...
1E80: 00 00 00 00 00 00 00 00
06 00 44 00 56 00 43 00 : ........ ..D.V.C.
1E90: 4C 00 41 00 4C 00 0B 00 50 00 41 00 43 00 4B 00 : L.A.L... P.A.C.K.
1EA0: 41 00 47 00 45 00 49 00 4E 00 46 00 4F 00 00 00 : A.G.E.I. N.F.O...
1EB0: 26 3D 4F 38 C2 82 37 B8 F3 24 42 03 17 9B 3A 83 : &=O87 $B..:
1EC0: 01 00 00 8C 00 00 00 00 03 00 00 00 01 F9 64 5F : ....... .....d_
1ED0: 6D 69 6E 5F 6D 61 78 00 00 81 53 79 73 49 6E 69 : min_max. .SysIni
1EE0: 74 00 00 C7 53 79 73 74 65 6D 00 00 00 00 00 00 : t..Syst em......
...ooo...
1FF0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ........ ........
|
The source, as well as the .DLL are
downloadable
3.6 - Mini Howto Manual
We will illustrate the use of the PE Explorer using our previous .DLL:
4 - Download the EXE DLL Analyzer Sources
Here are the source code files:
The .ZIP file(s) contain:
- the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any
other auxiliary form
- any .TXT for parameters, samples, test data
- all units (.PAS) for units
Those .ZIP
- are self-contained: you will not need any other product (unless expressly
mentioned).
- for Delphi 6 projects, can be used from any folder (the pathes are RELATIVE)
- will not modify your PC in any way beyond the path where you placed the .ZIP
(no registry changes, no path creation etc).
To use the .ZIP:
- create or select any folder of your choice
- unzip the downloaded file
- using Delphi, compile and execute
To remove the .ZIP simply delete the folder.
The Pascal code uses the Alsacian notation, which prefixes identifier by
program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre,
F_unction, C_lass etc. This notation is presented in the
Alsacian
Notation paper.
As usual:
- please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs or had
some problem downloading the file. Resulting corrections will be helpful
for other readers
- we welcome any comment, criticism, enhancement, other sources or reference
suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
- or more simply, enter your (anonymous or with your e-mail if you want an
answer) comments below and clic the "send" button
- and if you liked this article, talk about this site to your fellow
developpers, add a link to your links page ou mention our articles in
your newsgroup posts when relevant. That's the way we operate: the more
traffic and Google references we get, the more articles we will write.
5 - References
- Matt PIETRECK:
- "Windows 95 System Programming Secrets"
IDG Books - Isbn 1 56884 318 6
The best book on this topic
- An In-Depth look into the Win32 Portable Executable File Format
MSJ - Feb
2002
- An In-Depth look into the Win32 Portable Executable File Format, Part 2
MSJ - March 2002
- Jeffrey RICHTER
6 - The author
Felix John COLIBRI works at the Pascal
Institute. He programs in Pascal since 1979, and is mainly active in the area
of custom software
development and training, and is a frequent speaker at Borland
Developer Conferences. His web site features
tutorials, technical papers about programming with full downloadable source
code, and the description and calendar of forthcoming Delphi,
Interbase, Asp.Net, Ado.Net and OOP / UML training sessions.
|