首页
社区
课程
招聘
关于手工建立PE格式文件的文章,不知有没有中文版,有就算了,没有我来译!
发表于: 2004-8-26 10:35 7968

关于手工建立PE格式文件的文章,不知有没有中文版,有就算了,没有我来译!

2004-8-26 10:35
7968

$Id: pe.txt,v 1.7 1998/07/29 17:55:13 LUEVELSMEYER Exp $

The PE file format
==================

Purpose
-------

The PE ("portable executable") file format is the format of executable
binaries (DLLs and programs) for MS windows NT, windows 95 and
win32s; in windows NT, the drivers are in this format, too.
It can also be used for object files and libraries.

The format is designed by Microsoft, apparently based on a good
knowledge of COFF, the "common object file format" used for object files
and executables on several UNIXes and on VMS.

The win32 SDK includes a header file <winnt.h> containing #defines and
typedefs for the PE-format. I will mention the struct-member-names and
#defines as we go.

You may also find the DLL "imagehelp.dll" to be helpful. It is part of
windows NT, but documentation is rare. Some of its functions are
described in the "Developer Network".

General Layout
--------------

At the start of a PE file we find an MS-DOS executable ("stub"); this
makes any PE file a valid MS-DOS executable.

After the DOS-stub there is a 32-bit-signature with the magic number
0x00004550 (IMAGE_NT_SIGNATURE).

Then there is a file header (in the COFF-format) that tells on which
machine the binary is supposed to run, how many sections are in it, the
time it was linked, whether it is an executable or a DLL and so on. (The
difference between executable and DLL in this context is: a DLL can not
be started but only be used by another binary, and a binary cannot link
to an executable).

After that, we have an optional header (it is always there but still
called "optional" - COFF uses an "optional header" for libraries but not
for objects, that's why it is called "optional"). This tells us more
about how the binary should be loaded: The starting address, the amount
of stack to reserve, the size of the data segment etc..

An interesting part of the optional header is the trailing array of
'data directories'; these directories contain pointers to data in the
'sections'. If, for example, the binary has an export directory, you
will find a pointer to that directory in the array member
IMAGE_DIRECTORY_ENTRY_EXPORT, and it will point into one of the
sections.

Following the headers we find the 'sections', introduced by the 'section
headers'. Essentially, the sections' contents is what you really need to
execute a program, and all the header and directory stuff is just there
to help you find it.
Each section has some flags about alignment, what kind of data it
contains ("initialized data" and so on), whether it can be shared etc.,
and the data itself. Most, but not all, sections contain one or more
directories referenced through the entries of the optional header's
"data directory" array, like the directory of exported functions or the
directory of base relocations. Directoryless types of contents are, for
example, "executable code" or "initialized data".

    +-------------------+
    | DOS-stub          |
    +-------------------+
    | file-header       |
    +-------------------+
    | optional header   |
    |- - - - - - - - - -|
    |                   |
    | data directories  |
    |                   |
    +-------------------+
    |                   |
    | section headers   |
    |                   |
    +-------------------+
    |                   |
    | section 1         |
    |                   |
    +-------------------+
    |                   |
    | section 2         |
    |                   |
    +-------------------+
    |                   |
    | ...               |
    |                   |
    +-------------------+
    |                   |
    | section n         |
    |                   |
    +-------------------+

DOS-stub and Signature
----------------------

The concept of a DOS-stub is well-known from the 16-bit-windows-
executables (which were in the "NE" format). The stub is used for
OS/2-executables, self-extracting archives and other applications, too.
For PE-files, almost always this MS-DOS-executable consists of about 100
bytes that output an error message such as "this program needs windows
NT".
You recognize a DOS-stub by the validating the DOS-header, being a
struct IMAGE_DOS_HEADER. The first 2 bytes should be the sequence "MZ"
(there is a #define IMAGE_DOS_SIGNATURE for this WORD).
You distinguish a PE binary from other stubbed binaries by the trailing
signature, which you find at the offset given by the header member
'e_lfanew' (which is 32 bits long beginning at byte offset 60). For OS/2
and windows binaries, the signature is a 16-bit-word; for PE files, it
is a 32-bit-longword with the value IMAGE_NT_SIGNATURE #defined to be
0x00004550.

File Header
-----------

To get to the IMAGE_FILE_HEADER, validate the "MZ" of the DOS-header
(1st 2 bytes), then find the 'e_lfanew' member of the DOS-stub's header
and skip that many bytes from the beginning of the file. Verify the
signature you will find there. The file header, a struct
IMAGE_FILE_HEADER, begins immediatly after it; the members are described
top to bottom.

The first member is the 'Machine', a 16-bit-value indicating the system
the binary is intended to run on. Known legal values are

    IMAGE_FILE_MACHINE_I386 (0x14c)
        for Intel 80386 processor or better

    0x014d
        for Intel 80486 processor or better

    0x014e
        for Intel Pentium processor or better

    0x0160
        for R3000 (MIPS) processor, big endian

    IMAGE_FILE_MACHINE_R3000 (0x162)
        for R3000 (MIPS) processor, little endian

    IMAGE_FILE_MACHINE_R4000 (0x166)
        for R4000 (MIPS) processor, little endian

    IMAGE_FILE_MACHINE_R10000 (0x168)
        for R10000 (MIPS) processor, little endian

    IMAGE_FILE_MACHINE_ALPHA (0x184)
        for DEC Alpha AXP processor

    IMAGE_FILE_MACHINE_POWERPC (0x1F0)
        for IBM Power PC, little endian

Then we have the 'NumberOfSections', a 16-bit-value. It is the number of
sections that follow the headers. We will discuss the sections later.

Next is a timestamp 'TimeDateStamp' (32 bit), giving the time the file
was created. You can distinguish several versions of the same file by
this value, even if the "official" version number was not altered. (The
format of the timestamp is not documented except that it should be
somewhat unique among versions of the same file, but apparently it is
'seconds since January 1 1970 00:00:00' in UTC - the format used by most
C compilers for the time_t.)
This timestamp is used for the binding of import directories, which will
be discussed later.
Warning: some compilers tend to set this timestamp to absurd values.

The members 'PointerToSymbolTable' and 'NumberOfSymbols' (both 32 bit)
are used for debugging information. I don't know how to decipher them,
and I've found the pointer to be always 0.

'SizeOfOptionalHeader' (16 bit) is simply sizeof(IMAGE_OPTIONAL_HEADER).
You can use it to validate the correctness of the PE file's structure.

'Characteristics' is 16 bits and consists of a collection of flags, most
of them being valid only for object files and libraries:

    Bit 0 (IMAGE_FILE_RELOCS_STRIPPED) is set if there is no relocation
    information in the file. This refers to relocation information per
    section in the sections themselves; it is not used for executables,
    which have relocation information in the 'base relocation' directory
    described below.

    Bit 1 (IMAGE_FILE_EXECUTABLE_IMAGE) is set if the file is executable
    (i.e. it is not an object file or a library).

    Bit 2 (IMAGE_FILE_LINE_NUMS_STRIPPED) is set if the line number
    information is stripped; this is not used for executable files.

    Bit 3 (IMAGE_FILE_LOCAL_SYMS_STRIPPED) is set if there is no
    information about local symbols in the file (this is not used
    for executable files).

    Bit 4 (IMAGE_FILE_AGGRESIVE_WS_TRIM) is set if the operating system
    is supposed to trim the working set of the running process (the
    amount of RAM the process uses) aggressivly by paging it out. This
    should be set if it is a demon-like application that waits most of
    the time and only wakes up once a day, or the like.

    Bits 7 (IMAGE_FILE_BYTES_REVERSED_LO) and 15
    (IMAGE_FILE_BYTES_REVERSED_HI) are set if the endianess of the file is
    not what the machine would expect, so it must swap bytes before
    reading. This is unreliable for executable files (the OS expects
    executables to be correctly byte-ordered).

    Bit 8 (IMAGE_FILE_32BIT_MACHINE) is set if the machine is expected
    to be a 32 bit machine. This is always set.

    Bit 9 (IMAGE_FILE_DEBUG_STRIPPED) is set if there is no debugging
    information in the file. This is unused for executable files.

    Bit 10 (IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) is set if the application
    may not run from a removable medium such as a floppy or a CD-ROM. In
    this case, the operating system is advised to copy the file to the
    swapfile and execute it from there.

    Bit 11 (IMAGE_FILE_NET_RUN_FROM_SWAP) is set if the application may
    not run from the network. In this case, the operating system is
    advised to copy the file to the swapfile and execute it from there.

    Bit 12 (IMAGE_FILE_SYSTEM) is set if the file is a system file such
    as a driver. This is unused for executable files.

    Bit 13 (IMAGE_FILE_DLL) is set if the file is a DLL.

    Bit 14 (IMAGE_FILE_UP_SYSTEM_ONLY) is set if the file is not
    designed to run on multiprocessor systems (that is, it will crash
    there because it relies in some way on exactly one processor).

Relative Virtual Addresses
--------------------------

The PE format makes heavy use of so-called RVAs. An RVA, aka "relative
virtual address", is used to describe a memory address if you don't know
the base address. It is the value you need to add to the base address to
get the linear address.
The base address is the address the PE image is loaded to, and may vary
from one invocation to the next.

Example: suppose an executable file is loaded to address 0x400000 and
execution starts at RVA 0x1560. The effective execution start will then
be at the address 0x401560. If the executable were loaded to 0x100000,
the execution start would be 0x101560.

Things become complicated because the parts of the PE-file (the
sections) are not necessarily aligned the same way the loaded image is.
For example, the sections of the file are often aligned to
512-byte-borders, but the loaded image is perhaps aligned to
4096-byte-borders. See 'SectionAlignment' and 'FileAlignment' below.

So to find a piece of information in a PE-file for a specific RVA,
you must calculate the offsets as if the file were loaded, but skip
according to the file-offsets.
As an example, suppose you knew the execution starts at RVA 0x1560, and
want to diassemble the code starting there. To find the address in the
file, you will have to find out that sections in RAM are aligned to 4096
bytes and the ".code"-section starts at RVA 0x1000 in RAM and is 16384
bytes long; then you know that RVA 0x1560 is at offset 0x560 in that
section. Find out that the sections are aligned to 512-byte-borders in
the file and that ".code" begins at offset 0x800 in the file, and you
know that the code execution start is at byte 0x800+0x560=0xd60 in the
file.

Easy if you know how it works :-)

Optional Header
---------------

Immediatly following the file header is the IMAGE_OPTIONAL_HEADER
(which, in spite of the name, is always there). It contains
information about how to treat the PE-file exactly. We'll also have the
members from top to bottom.

The first 16-bit-word is 'Magic' and has, as far as I looked into
PE-files, always the value 0x010b.

The next 2 bytes are the version of the linker ('MajorLinkerVersion' and
'MinorLinkerVersion') that produced the file. These values, again, are
unreliable and do not always reflect the linker version properly.
(Several linkers simply don't set this field.)
And, coming to think about it, what good is the version if you have got
no idea *which* linker was used?

The next 3 longwords (32 bit each) are intended to be the size of the
executable code ('SizeOfCode'), the size of the initialized data
('SizeOfInitializedData', the so-called "data segment"), and the size of
the uninitialized data ('SizeOfUninitializedData', the so-called "bss
segment"). These values are, again, unreliable (e.g. the data segment
may actually be split into several segments by the compiler or linker),
and you get better sizes by inspecting the 'sections' that follow the
optional header.

Next is a 32-bit-value that is a RVA. This RVA is the offset to the
codes's entry point ('AddressOfEntryPoint'). Execution starts here; it
is e.g. the address of a DLL's LibMain() or a program's startup code
(which will in turn call main()) or a driver's DriverEntry().
If you dare to load the image "by hand", you call this address to start
the process after you have done all the fixups and relocations.

The next 2 32-bit-values are the offsets to the executable code
('BaseOfCode') and the initialized data ('BaseOfData'), both of them
RVAs again, and both of them being of little interest because you get
more reliable information by inspecting the 'sections' that follow the
headers.
There is no offset to the uninitialized data because, being
uninitialized, there is little point in providing this data in the
image.

The next entry is a 32-bit-value that is a RVA giving the preferred load
address ('ImageBase'). This is the address the file has been relocated
to by the linker; if the binary can in fact be loaded to that address,
the loader doesn't need to relocate the file again, which is a big win
in loading time.
The preferred load address can not be used if another image has already
been loaded to that address (an "address clash", which happens quite
often if you load several DLLs that are all relocated to the linker's
default), or the memory in question has been used for other purposes
(stack, malloc(), uninitialized data, whatever). In these cases, the
image must be loaded to some other address and it needs to be relocated
(see 'relocation directory' below). This has further consequences if the
image is a DLL, because then the "bound imports" are no longer valid,
and fixups have to be made to the binary that uses the DLL - see 'import
directory' below.

The next 2 32-bit-values are the alignments of the PE-file's sections in
RAM ('SectionAlignment', when the image has been loaded) and in the file
('FileAlignment'). Usually both values are 32, or FileAlignment is 512
and SectionAlignment is 4096. Sections will be discussed later.

The next 2 16-bit-words are the expected operating system version
('MajorOperatingSystemVersion' and 'MinorOperatingSystemVersion' [they
_do_ like self-documenting names at MS]). This version information is
intended to be the operating system's (e.g. NT or Win95) version, as
opposed to the subsystem's version (e.g. Win32); it is often not
supplied, or wrong supplied. The loader doesn't use it, apparently.

The next 2 16-bit-words are the binary's version, ('MajorImageVersion' and
'MinorImageVersion'). Many linkers don't set this information correctly
and many programmers don't bother to supply it, so it is better to rely
on the version-resource if one exists.

The next 2 16-bit-words are the expected subsystem version
('MajorSubsystemVersion' and 'MinorSubsystemVersion'). This should be
the Win32 version or the POSIX version, because 16-bit-programs or
OS/2-programs won't be in PE-format, obviously.
This subsystem version should be supplied correctly, because it *is*
checked and used:
If the application is a Win32-GUI-application and runs on NT, and the
subsystem version is *not* 4.0, the dialogs won't be 3D-style and
certain other features will also work "old-style" because the
application expects to run on NT 3.51, which had the program manager
instead of explorer and so on, and NT 4.0 will mimic that behaviour as
faithfully as possible.

Then we have a 'Win32VersionValue' of 32 bits. I don't know what it is
good for. It has been 0 in all the PE files that I inspected.

Next is a 32-bits-value giving the amount of memory the image will need,
in bytes ('SizeOfImage'). It is the sum of all sections' lengths. It is
a hint to the loader how many pages it will need in order to load the image.

The next thing is a 32-bit-value giving the total length of all headers
including the data directories and the section headers
('SizeOfHeaders'). It is at the same time the offset from the beginning
of the file to the first section's raw data.

Then we have got a 32-bit-checksum ('CheckSum'). This checksum is, for
current versions of NT, only checked if the image is a NT-driver (the
driver will fail to load if the checksum isn't correct).
The algorithm to compute the checksum is property of Microsoft, and they
won't tell you. However, several tools of the Win32 SDK will compute a
valid checksum, and the function CheckSumMappedFile() in the
imagehelp.dll will do so too.
The checksum is supposed to prevent loading of damaged binaries that
would crash anyway - and a crashing driver would result in a BSOD, so
it is better not to load it at all.

Then there is a 16-bit-word 'Subsystem' that tells in which of the
NT-subsystems the image runs:

    IMAGE_SUBSYSTEM_NATIVE (1)
        The binary doesn't need a subsystem. This is used for drivers.
   
    IMAGE_SUBSYSTEM_WINDOWS_GUI (2)
        The image is a Win32 graphical binary. (It can still open a
        console with AllocConsole() but won't get one automatically at
        startup.)
   
    IMAGE_SUBSYSTEM_WINDOWS_CUI (3)
        The binary is a Win32 console binary. (It will get a console
        per default at startup.)
   
    IMAGE_SUBSYSTEM_OS2_CUI (5)
        The binary is a OS/2 console binary. (OS/2 binaries will be in
        OS/2 format, so this value will seldom be used in a PE
        file.)
   
    IMAGE_SUBSYSTEM_POSIX_CUI (7)
        The binary uses the POSIX console subsystem.

Windows 95 binaries will always use the Win32 subsystem, so the only
legal values for these binaries are 2 and 3; I don't know if "native"
binaries on windows 95 are possible.

The next thing is a 16-bit-value that tells, if the image is a DLL, when
to call the DLL's entry point ('DllCharacteristics'). This seems not to
be used; apparently, the DLL is always notified about everything.
    If bit 0 is set, the DLL is notified about process attachment (i.e.
    DLL load).
    If bit 1 is set, the DLL is notified about thread detachments (i.e.
    thread terminations).
    If bit 2 is set, the DLL is notified about thread attachments (i.e.
    thread creations).
    If bit 3 is set, the DLL is notified about process detachment (i.e.
    DLL unload).

The next 4 32-bit-values are the size of reserved stack
('SizeOfStackReserve'), the size of initially committed stack
('SizeOfStackCommit'), the size of the reserved heap
('SizeOfHeapReserve') and the size of the committed heap
('SizeOfHeapCommit').
The 'reserved' amounts are address space (not real RAM) that is reserved
for the specific purpose; at program startup, the 'committed' amount is
actually allocated in RAM. The 'committed' value is also the amount by
which the committed stack or heap grows if necessary.
So, as an example, if a program has a reserved heap of 1 MB and a
committed heap of 64 KB, the heap will start out at 64 KB and is
guaranteed to be enlargeable up to 1 MB. The heap will grow in
64-KB-chunks.
The 'heap' in this context is the primary (default) heap.
DLLs don't have a stack or heap of their own, so the values are ignored
for their images.

After these stack- and heap-descriptions, we find 32 bits of
'LoaderFlags', which I didn't find a useful description of. I only found
that you may set bits that automatically invoke a breakpoint or a
debugger after loading the image; however, this doesn't seem to work.

Then we find 32 bits of 'NumberOfRvaAndSizes', which is the number of
valid entries in the directories that follow immediatly. I've found this
value to be unreliable; you might wish use the constant
IMAGE_NUMBEROF_DIRECTORY_ENTRIES instead, or the lesser of both.

After the 'NumberOfRvaAndSizes' there is an array of
IMAGE_NUMBEROF_DIRECTORY_ENTRIES (16) IMAGE_DATA_DIRECTORYs.
Each of these directories describes the location (32 bits RVA called
'VirtualAddress') and size (also 32 bit, called 'Size') of a particular
piece of information, which is located in one of the sections that
follow the directory entries.
For example, the security directory is found at the RVA and has the size
that are given at index 4.
The directories that I know the structure of will be discussed later.
Defined directory indexes are:

    IMAGE_DIRECTORY_ENTRY_EXPORT (0)
        The directory of exported symbols; mostly used for DLLs.
        Described below.
   
    IMAGE_DIRECTORY_ENTRY_IMPORT (1)
        The directory of imported symbols; see below.
   
    IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
        Directory of resources. Described below.
   
    IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
        Exception directory - structure and purpose unknown.
   
    IMAGE_DIRECTORY_ENTRY_SECURITY (4)
        Security directory - structure and purpose unknown.
   
    IMAGE_DIRECTORY_ENTRY_BASERELOC (5)
        Base relocation table - see below.
   
    IMAGE_DIRECTORY_ENTRY_DEBUG (6)
        Debug directory - contents is compiler dependent. Moreover, many
        compilers stuff the debug information into the code section and
        don't create a separate section for it.
   
    IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)
        Description string - some arbitrary copyright note or the like.
   
    IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)
        Machine Value (MIPS GP) - structure and purpose unknown.
   
    IMAGE_DIRECTORY_ENTRY_TLS (9)
        Thread local storage directory - structure unknown; contains
        variables that are declared "declspec(thread)".
   
    IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)
        Load configuration directory - structure and purpose unknown.
   
    IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)
        Bound import directory - see description of import directory.
   
    IMAGE_DIRECTORY_ENTRY_IAT (12)
        Import Address Table - structure and purpose unknown
   
As an example, if we find at index 7 the 2 longwords 0x12000 and 33, and
the load address is 0x10000, we know that the copyright data is at
address 0x10000+0x12000 (in whatever section there may be), and the
copyright note is 33 bytes long.
If a directory of a particular type is not used in a binary, the Size
and VirtualAddress are both 0.

Section directories
-------------------

The sections consist of two major parts: first, a section description
(of type IMAGE_SECTION_HEADER) and then the raw section data. So after
the data directories we find an array of 'NumberOfSections' section
headers.

A section header is actually a very *small* struct. It contains:

An array of IMAGE_SIZEOF_SHORT_NAME (8) bytes that make up the name
(ASCII) of the section. If all of the 8 bytes are used there is no 0-
terminator for the string! The name is typically something like ".data"
or ".text" or ".bss". There need not be a leading '.', the names may
also be "CODE" or "IAT" or the like.
Please note that the names are not at all related to the contents of the
section. A section named ".code" may or may not contain the executable
code; it may just as well contain the import address table; it may also
contain the code *and* the address table *and* the initialized data.
To find information in the sections, you will have to look it up via the
data directories of the optional header. Do not rely on the names, and
do not assume that the section's raw data starts at the beginning of a
section.

The next member of the IMAGE_SECTION_HEADER is a 32-bit-union of
'PhysicalAddress' and 'VirtualSize'. In an object file, this is the
address the contents is relocated to; in an executable, it is the size of
the contents. In fact, the field seems to be unused; There are linkers
that enter the size, and there are linkers that enter the address, and
I've also found a linker that enters a 0, and all the executables run
like the gentle wind.

The next member is 'VirtualAddress', a 32-bit-value holding the RVA to
the section's data when it is loaded in RAM.

Then we have got 32 bits of 'SizeOfRawData', which is the size of the
secion's data rounded up to the next multiple of 'FileAlignment'.

Next is 'PointerToRawData' (32 bits), which is incredibly usefull
because it is the offset from the file's beginning to the section's data.
If it is 0, the section's data are not contained in the file and will be
arbitrary.

Then we have got 'PointerToRelocations' (32 bits) and
'PointerToLinenumbers' (also 32 bits), 'NumberOfRelocations' (16 bits)
and 'NumberOfLinenumbers' (also 16 bits). All of these are information
that's only used for object files. Executables have a special base
relocation directory, and the line number information, if present at
all, is usually contained in a special purpose debugging segment or
elsewhere.

The last member of a section header is the 32 bits 'Characteristics',
which is a bunch of flags describing how the section's memory should be
treated:

    If bit 5 (IMAGE_SCN_CNT_CODE) is set, the section contains
    executable code.
   
    If bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA) is set, the section
    contains data that gets a defined value before execution starts. In
    other words: the section's data in the file is meaningful.
   
    If bit 7 (IMAGE_SCN_CNT_UNINITIALIZED_DATA) is set, this section's
    data is uninitialized data and will be initialized to all-0-bytes
    before execution starts. This is normally the BSS.

    If bit 9 (IMAGE_SCN_LNK_INFO) is set, the section doesn't contain
    image data but comments, description or other documentation.

    If bit 11 (IMAGE_SCN_LNK_REMOVE) is set, the data is part of an
    object file's section that is supposed to be left out when the
    executable file is linked.

    If bit 12 (IMAGE_SCN_LNK_COMDAT) is set, the section contains
    "common block data", which are packaged functions of some sort.

    If bit 15 (IMAGE_SCN_MEM_FARDATA) is set, we have far data -
    whatever that means. This bit's meaning is unsure.

    If bit 17 (IMAGE_SCN_MEM_PURGEABLE) is set, the section's data
    is purgeable - but I don't think that this is the same as
    "discardable", which has a bit of its own, see below.
    The same bit is apparently used to indicate 16-bit-information as
    there is also a define IMAGE_SCN_MEM_16BIT for it.
    This bit's meaning is unsure.

    If bit 18 (IMAGE_SCN_MEM_LOCKED) is set, the section should not be
    moved in memory? This bit's meaning is unsure.

    If bit 19 (IMAGE_SCN_MEM_PRELOAD) is set, the section should be
    paged in before execution starts? This bit's meaning is unsure.

    Bits 20 to 23 specify an alignment that I have no information
    about. There are #defines IMAGE_SCN_ALIGN_16BYTES and the like. The
    only value I've ever seen used is 0, for the default 16-byte-
    alignment. I suspect that this is the alignment of objects in a
    library file or the like.

    If bit 24 (IMAGE_SCN_LNK_NRELOC_OVFL) is set, the section contains
    some extended relocations that I don't know about.

    If bit 25 (IMAGE_SCN_MEM_DISCARDABLE) is set, the section's data is
    not needed after the process has started. This is the case,
    for example, with the relocation information. I've seen it also for
    startup routines of drivers and services that are only executed
    once.

    If bit 26 (IMAGE_SCN_MEM_NOT_CACHED) is set, the section's data
    should not be cached. Don't ask my why not. Does this mean to switch
    off the 2nd-level-cache?

    If bit 27 (IMAGE_SCN_MEM_NOT_PAGED) is set, the section's data
    should not be paged out. This may be interesting for drivers.

    If bit 28 (IMAGE_SCN_MEM_SHARED) is set, the section's data is
    shared among all running instances of the image. If it is e.g. the
    initialized data of a DLL, all running instances of the DLL will at
    any time have the same variable contents.
    Note that only the first instance's section is initialized.
    Sections containing code are always shared.

    If bit 29 (IMAGE_SCN_MEM_EXECUTE) is set, the process gets
    'execute'-access to the section's memory.
   
    If bit 30 (IMAGE_SCN_MEM_READ) is set, the process gets
    'read'-access to the section's memory.
   
    If bit 31 (IMAGE_SCN_MEM_WRITE) is set, the process gets
    'write'-access to the section's memory.

After the section headers we find the sections themselves. They are, in
the file, aligned to 'FileAlignment' bytes (that is, after the optional
header and after each section's data there will be padding bytes). When
loaded (in RAM), the sections are aligned to 'SectionAlignment' bytes.

As an example, if the optional header ends at file offset 981 and
'FileAlignment' is 512, the first section will start at byte 1024. Note
that you can find the sections via the 'PointerToRawData' or the
'VirtualAddress', so there is hardly any need to actually fuss around
with the alignments.

I will try to make an image of it all:

    +-------------------+
    | DOS-stub          |
    +-------------------+
    | file-header       |
    +-------------------+
    | optional header   |
    |- - - - - - - - - -|
    |                   |----------------+
    | data directories  |                |
    |                   |                |
    |(RVAs to direc-    |-------------+  |
    |tories in sections)|             |  |
    |                   |---------+   |  |
    |                   |         |   |  |
    +-------------------+         |   |  |
    |                   |-----+   |   |  |
    | section headers   |     |   |   |  |
    | (RVAs to section  |--+  |   |   |  |
    |  borders)         |  |  |   |   |  |
    +-------------------+<-+  |   |   |  |
    |                   |     | <-+   |  |
    | section data 1    |     |       |  |
    |                   |     | <-----+  |
    +-------------------+<----+          |
    |                   |                |
    | section data 2    |                |
    |                   | <--------------+
    +-------------------+

There is one section header for each section, and each data directory
will point to one of the sections (several data directories may point to
the same section, and there may be sections without data directory
pointing to them).

Sections' raw data
------------------

general
-------
All sections are aligned to 'SectionAlignment' when loaded in RAM, and
'FileAlignment' in the file. The sections are described by entries in
the section headers: You find the sections in the file via
'PointerToRawData' and in memory via 'VirtualAddress'; the length is in
'SizeOfRawData'.

There are several kinds of sections, depending on what's contained in
them. In most cases (but not in all) there will be at least one
data directory in a section, with a pointer to it in the optional
header's data directory array.

code section
------------
First, I will mention the code section. The section will have, at least,
the bits 'IMAGE_SCN_CNT_CODE', 'IMAGE_SCN_MEM_EXECUTE' and
'IMAGE_SCN_MEM_READ' set, and 'AddressOfEntryPoint' will point somewhere
into the section, to the start of the function that the developer wants
to execute first.
'BaseOfCode' will normally point to the start of this section, but may
point to somewhere later in the section if some non-code-bytes are
placed before the code in the section.
Normally, there will be nothing but executable code in this section, and
there will be only one code section, but don't rely on this.
Typical section names are ".text", ".code", "AUTO" and the like.

data section
-------------
The next thing we'll discuss is the initialized variables; this section
contains initialized static variables (like "static int i = 5;"). It will
have, at least, the bits 'IMAGE_SCN_CNT_INITIALIZED_DATA',
'IMAGE_SCN_MEM_READ' and 'IMAGE_SCN_MEM_WRITE' set. Some linkers may
place constant data into a section of their own that doesn't have the
writeable-bit. If part of the data is shareable, or there are other
peculiarities, there may be more sections with the apropriate section-
bits set.
The section, or sections, will be in the range 'BaseOfData' up to
'BaseOfData'+'SizeOfInitializedData'.
Typical section names are '.data', '.idata', 'DATA' and so on.

bss section
-----------
Then there is the uninitialized data (for static variables like "static
int k;"); this section is quite like the initialized data, but will have
a file offset ('PointerToRawData') of 0 to indicate its contents is not
stored in the file, and 'IMAGE_SCN_CNT_UNINITIALIZED_DATA' is set
instead of 'IMAGE_SCN_CNT_INITIALIZED_DATA' to indicate that the
contents should be set to 0-bytes at load-time.
The length will be 'SizeOfUninitializedData'.
Typical names are '.bss', 'BSS' and the like.

These were the section data that are *not* pointed to by data
directories. Their contents and structure is supplied by the compiler,
not by the linker.
(The stack-segment and heap-segment are not sections in the binary but
created by the loader from the stacksize- and heapsize-entries in the
optional header.)

copyright
---------
To begin with a simple directory-section, let's look at the data
directory 'IMAGE_DIRECTORY_ENTRY_COPYRIGHT'. The contents is a
copyright- or description string in ASCII (not 0-terminated), like
"Gonkulator control application, copyright (c) 1848 Hugendubel & Cie".
This string is, normally, supplied to the linker with the command line.
This string is not needed at runtime and may be discarded. It is not
writeable; in fact, the application doesn't need access at all.
So the linker will find out if there is a discardable non-writeable
section already and if not, create one (named '.descr' or the like). It
will then stuff the string into the section and let the
copyright-directory-pointer point to the string. The
'IMAGE_SCN_CNT_INITIALIZED_DATA' bit should be set.

exported symbols
----------------
The next-simplest thing is the export directory,
'IMAGE_DIRECTORY_ENTRY_EXPORT'. This is a directory typically found
in DLLs; it contains the entry points of exported functions (and the
addresses of exported objects etc.). Executables may of course also have
exported symbols but usually they don't.
The containing section should be "initialized data" and "readable". It
can not be "discardable" because the process might call "GetProcAddress()"
to find a function's entry point at runtime.
The section is normally called '.edata' if it is a separate thing; often
enough, it is merged into some other section like "initialized data".

The structure of the export table ('IMAGE_EXPORT_DIRECTORY') comprises a
header and the export data, that is: the symbol names, their ordinals
and the offsets to their entry points.

First, we have 32 bits of 'Characteristics' that are unused and normally
0. Then there is a 32-bit-'TimeDateStamp', which presumably should give
the time the table was created in the time_t-format; alas, it is not
always valid (some linkers set it to 0). Then we have 2 16-bit-words of
version-info ('MajorVersion' and 'MinorVersion'), and these, too, are
often enough set to 0.

The next thing is 32 bits of 'Name'; this is an RVA to the DLL name as a
0-terminated ASCII string. (The name is necessary in case the DLL file is
renamed - see "binding" at the import directory.)
Then, we have got a 32-bit-'Base'. We'll come to that in a moment.

Now we have got sort of a problem, because the next two 32-bit-values are
the number of exported functions ('NumberOfFunctions') and the number of
exported names ('NumberOfNames'). These values are *not* always equal,
and weird things happen if you try to decipher the export directory past
the minimum of both. In *most* export-directories the values are equal
and we're fine. It seems to be always safe to decipher up to the minimum
of both values.
I've got a suspicion that it is somehow possible do export symbols
without a name (just by ordinal), but I didn't find anything about this
topic.

The next 3 32-bit-values are RVAs to 3 arrays. The arrays run parallel:
the array of entry points 'AddressOfFunctions' (given as 32-bit-RVAs),
the array of 32-bit-RVAs to symbol names 'AddressOfNames', and the array
of 16-bit-ordinals 'AddressOfNameOrdinals'.

To get information about the i-th exported symbol, find the export
directory, follow the 3 RVAs, skip to index i in the 3 arrays, and
you have got a) the RVA of the function's entry point; b) the RVA to the
function's name (a 0-terminated ASCII-string) and c) the function's
ordinal (you have to add the 'Base' to the value to get the real
ordinal).

For functions, the entry-point-RVA will normally point into the code
section; for objects, it will almost always point into the data- or
bss-section.

imported symbols
----------------
This is one for the tough.

When the compiler finds a call to a function that is in a different
executable (mostly in a DLL), it will, in the most simplistic case, not
know anything about the circumstances and simply output a normal
call-instruction to that symbol, the address of which the linker will
have to fix. The linker uses an import library to supply the addresses,
and that library has stubs for all the exported symbols, each of which
consists of a jump-instruction; the stubs are the actual call-targets.
These jump-instructions will actually jump to an address that's fetched
from the import address table we're discussing here.
In more sophisticated applications (when "declspec(dllimport)" is used),
the compiler knows the function is imported, and outputs a call to the
address that's in the import address table, bypassing the jump.

Hm. So far for that matter. You are kindly referred to [1] and [2].

However, the address of the function in the DLL is always necessary and
will be supplied by the loader when the application is loaded. The
loader knows which symbols in what libraries have to be looked up and
their addresses fixed by searching the import directory.

I will better give you an example. The calls with or without
__declspec(dllimport) look like this:

    source:
        int symbol(char *);
        __declspec(dllimport) int symbol2(char*);
        void foo(void)
        {
            int i=symbol("bar");
            int j=symbol2("baz");
        }
   
    assembly:
        ...
        call _symbol                 ; without declspec(dllimport)
        ...
        call ptr cs:0xdeadbeef       ; with declspec(dllimport)
        ...

    the stub provided by the import library (not used with
                __declspec(dllimport)) is located in the code section
                and is the above mentioned jump-instruction:
        _symbol: jmp dword ptr [0xdeadbeef]

So however you do it, there is a call or a jump to a location the
address of which is at 0xdeadbeef (in our example). This 0xdeadbeef is
right in the middle of the import directory; it is in the entry for the
function "symbol" in the DLL "foo.dll".

Now we'll look how an import directory is made up.

The import directory should reside in a section that's "initialized
data" and "readable".
The import directory is an array of IMAGE_IMPORT_DESCRIPTORs, one for
each used DLL. The list is terminated by a IMAGE_IMPORT_DESCRIPTOR
that's entirely filled with 0-bytes.
An IMAGE_IMPORT_DESCRIPTOR is a struct with these members:

    OriginalFirstThunk
        An RVA (32 bit) to a 0-terminated array of RVAs to
        IMAGE_THUNK_DATAs, each describing one imported function. The
        array will never change.

    TimeDateStamp
        A 32-bit-timestamp that has several purposes. Let's pretent that
        the timestamp is 0, and handle the advanced cases later.

    ForwarderChain
        The 32-bit-index of the first forwarder in the list of imported
        functions. Forwarders are also advanced stuff.
        
    Name
        A 32-bit-RVA to the Name (a 0-terminated ASCII string) of the
        DLL.
        
    FirstThunk
        An RVA (32 bit) to a 0-terminated array of RVAs to
        IMAGE_THUNK_DATAs, each describing one imported function. The
        array will change.

Each IMAGE_IMPORT_DESCRIPTOR in the array gives you the name of the
exporting DLL and, apart from the forwarder and timestamp, it gives you
2 RVAs to arrays of IMAGE_THUNK_DATAs, using 32 bits. (The last member
of each array is entirely filled with 0-bytes.)
Each IMAGE_THUNK_DATA is, for now, an RVA to a IMAGE_IMPORT_BY_NAME
which describes the imported function.
The interesting point is now, the arrays run parallel, i.e.: they point
to the same IMAGE_IMPORT_BY_NAMEs.

No need to be desparate, I will draw another picture. This is the
essential contents of one IMAGE_IMPORT_DESCRIPTOR:

     OriginalFirstThunk      FirstThunk
            |                    |
            |                    |
            |                    |
            V                    V

            0-->    func1     <--0
            1-->    func2     <--1
            2-->    func3     <--2
            3-->    foo       <--3
            4-->    mumpitz   <--4
            5-->    knuff     <--5
            6-->0            0<--6      /* the last RVA is 0! */

where the names in the center are the yet to discuss
IMAGE_IMPORT_BY_NAMEs. Each of them is a 16-bit-number (the ordinal of
the imported symbol) followed by an unspecified amount of bytes, being
the 0-terminated ASCII name of the imported symbol.
Mind you, the ordinal may be wrong. It is only a hint where to start
looking for the symbol name in the export table of the exporting DLL;
you may not compare the ordinals and take the matching entry, but you
must find the matching symbol name.

To summarize, if you want to look up information about the imported
function "foo" from DLL "knurr", you first find the entry
IMAGE_DIRECTORY_ENTRY_IMPORT in the data directories, get an RVA, find
that address in the raw section data and now have an array of
IMAGE_IMPORT_DESCRIPTORs. Get the member of this array that relates to
the DLL "knurr" by inspecting the strings pointed to by the 'Name's.
When you have found the right IMAGE_IMPORT_DESCRIPTOR, follow its
'OriginalFirstThunk' and get hold of the pointed-to array of
IMAGE_THUNK_DATAs; inspect the RVAs and find the function "foo".

This was the basic structure, for simple cases. Now we'll learn about
tweaks in the import directories.

First, the bit IMAGE_ORDINAL_FLAG (that is: the MSB) of the
IMAGE_THUNK_DATA in the arrays can be set, in which case there is no
symbol-name-information in the list and the symbol is imported purely by
ordinal. You get the ordinal by inspecting the lower word of the
IMAGE_THUNK_DATA.
Obviously, in this case the warning about unreliable ordinals doesn't
apply because all you have *is* the ordinal.

Ok, now, why do we have *two* lists of pointers to the
IMAGE_IMPORT_BY_NAMEs? Because at runtime the application doesn't need
the imported functions' names but the addresses. The loader will look up
each imported symbol in the export-directory of the DLL in question and
replace the IMAGE_THUNK_DATA in the 'FirstThunk'-list with the linear
address of the DLL's entry point. Remember the stubs "jmp dword ptr
[0xdeadbeef]"; this address 0xdeadbeef is exactly the address of the
IMAGE_THUNK_DATA in the 'FirstThunk'-list.
The 'OriginalFirstThunk' remains untouched, so you can always look up
the original list of imported names via the 'OriginalFirstThunk'-list.

Now we get the so-called "bound imports".
Think about the loader's task: when a binary that it wants to execute
needs a function from a DLL, the loader loads the DLL, finds its export
directory, looks up the function's RVA and calculates the function's
entry point. Then it patches the so-found address into the 'FirstThunk'-
list.
Given that the programmer was clever and supplied unique preferred load
addresses for the DLLs that don't clash, we can assume that the
functions' entry points will always be the same. They can be computed
and patched into the 'FirstThunk'-list at link-time, and that's what
happens with the "bound imports". (The utility "bind" does this.)

Of course, one must be cautious: The user's DLL may have a different
version, or it may be necessary to relocate the DLL, thus invalidating
the pre-patched 'FirstThunk'-list; in this case, the loader will still
be able to walk the 'OriginalFirstThunk'-list, find the imported symbols
and re-patch the 'FirstThunk'-list. The loader knows that this is
necessary if a) the versions of the exporting DLL don't match or b) the
exporting DLL had to be relocated.

To decide whether there were relocations is no problem for the loader,
but how to find out if the versions differ? This is where the
'TimeDateStamp' of the IMAGE_IMPORT_DESCRIPTOR comes in. If it is 0, the
import-list has not been bound, and the loader must fix the entry points
always. Otherwise, the imports are bound, and 'TimeDateStamp' must match
the 'TimeDateStamp' of the exporting DLL's 'FileHeader'; if it doesn't
match, the loader assumes that the binary is bound to a "wrong" DLL and
will re-patch the import list.

This was the so-called "old-style" binding.

There is an additional quirk about "forwarders" in the import-list. A DLL
can export a symbol that's not defined in the DLL but imported from
another DLL; such a symbol is said to be forwarded. Now, obviously you
can't tell if the symbol's entry point is valid by looking into the
timestamp of a DLL that doesn't actually contain the entry point. So the
forwarded symbols' entry points must always be fixed up, for safety
reasons. In the import list of a binary, imports of forwarded symbols
need to be found so the loader can patch them.

This is done via the 'ForwarderChain'. It is an index in the thunk-
lists; the import at the indexed position is a forwarded export, and the
contents of the 'FirstThunk'-list at this position is the index of the
*next* forwarded import, and so on, until the index is "-1" which
indicates there are no more forwards.

At this point, we should sum up what we have had so far :-)

Ok, I will assume you have found the IMAGE_DIRECTORY_ENTRY_IMPORT and you have
followed it to find the import-directory, which will be in one of the
sections. Now you're at the beginning of an array of
IMAGE_IMPORT_DESCRIPTORs the last of which will be entirely 0-bytes-
filled.
To decipher one of the IMAGE_IMPORT_DESCRIPTORs, you first look into the
'Name'-field, follow the RVA and thusly find the name of the exporting
DLL. Next you decide whether the imports are bound or not;
'TimeDateStamp' will be non-zero if the imports are bound. If they are
bound, now is a good time to check if the DLL version matches yours by
comparing the 'TimeDateStamp's.
Now you follow the 'OriginalFirstThunk'-RVA to go to the
IMAGE_THUNK_DATA-array; walk down this array (it is be 0-terminated),
and each member will be the RVA of a IMAGE_IMPORT_BY_NAME (unless the
hi-bit is set in which case you don't have a name but are left with a
mere ordinal). Follow the RVA, and skip 2 bytes (the ordinal), and now
you have got a pointer to a 0-terminated ASCII-string that's the name of
the imported function.
To find the supplied entry point addresses in case it is a bound import,
follow the 'FirstThunk' and walk it parallel to the
'OriginalFirstThunk'-array; the array-members are the linear addresses
of the entry points.

There is one thing I didn't mention until now: Apparently there are
linkers that exhibit a bug when they build the import directory (I've
found this bug being in use by a Borland C linker). These linkers set
the 'OriginalFirstThunk' in the IMAGE_IMPORT_DESCRIPTOR to 0 and create
only the 'FirstThunk'-array. Obviously, such import directories cannot
be bound (else the necessary information to re-fix the imports were
lost). In this case, you will have to follow the 'FirstThunk'-array to get
the imported symbol names, and you will never have pre-patched entry point
addresses.

The last tweak about the import directories is the so-called "new style"
binding (it is described in [3]), which can also be done with the
"bind"-utility. When this is used, the 'TimeDateStamp' is set to
all-bits-1 and there is no forwarderchain; all imported symbols get their
address patched, whether they are forwarded or not. Still, you need to
know the DLLs' version, and you need to distinguish forwarded symbols
from ordinary ones. For this purpose, the
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT directory is created. This will, as
far as I could find out, *not* be in a section but in the header, after
the section headers and before the first section. (Hey, I didn't invent
this, I'm only describing it!)
This directory tells you, for each used DLL, from which other DLLs there
are forwarded exports.
The structure is an IMAGE_BOUND_IMPORT_DESCRIPTOR, comprising (in this
order):
A 32-bit number, giving you the 'TimeDateStamp' of the DLL;
a 16-bit-number 'OffsetModuleName', being the offset from the beginning
of the directory to the 0-terminated name of the forwarded-to DLL;
a 16-bit-number 'NumberOfModuleForwarderRefs' giving you the number of
DLLs that this DLL uses for its forwarders.

Immediatly following this struct you find 'NumberOfModuleForwarderRefs'
structs that tell you the names and versions of the DLLs that this DLL
forwards from. These structs are 'IMAGE_BOUND_FORWARDER_REF's:
A 32-bit-number 'TimeDateStamp';
a 16-bit-number 'OffsetModuleName', being the offset from the beginning
of the directory to the 0-terminated name of the forwarded-from DLL;
16 unused bits.

Following the 'IMAGE_BOUND_FORWARDER_REF's is the next
'IMAGE_BOUND_IMPORT_DESCRIPTOR' and so on; the list is terminated by an
all-0-bits-IMAGE_BOUND_IMPORT_DESCRIPTOR.

Sorry for the inconvenience, but that's what it looks like :-)

Now, if you have a new-bound import directory, you load all the DLLs,
use the directory pointer IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT to find the
IMAGE_BOUND_IMPORT_DESCRIPTOR, scan through it and check if the
'TimeDateStamp's of the loaded DLLs match the ones given in this
directory. If not, fix them in the 'FirstThunk'-array of the import
directory.

It is easy if you know how it works :-)

resources
---------
The resources, like dialog boxes, menus, icons and so on, are stored in
the data directory pointed to by IMAGE_DIRECTORY_ENTRY_RESOURCE. It is in
a section that has, at least, the bits 'IMAGE_SCN_CNT_INITIALIZED_DATA'
and 'IMAGE_SCN_MEM_READ' set.

A resource base is a 'IMAGE_RESOURCE_DIRECTORY's; it contains several
'IMAGE_RESOURCE_DIRECTORY_ENTRY's which in turn may point to a
'IMAGE_RESOURCE_DIRECTORY'. This way, you get a tree of
'IMAGE_RESOURCE_DIRECTORY's with 'IMAGE_RESOURCE_DIRECTORY_ENTRY's as
leafs; these leafs point to the actual resource data.

In real life, the situation is somewhat relaxed. Normally you won't find
convoluted trees you can't possibly sort out.
The hierarchy is, normally, like this: one directory is the root. It
points to directories, one for each resource type. These directories
point to subdirectories, each of which will have a name or an ID and
point to a directory of the languages provided for this resource; for
each language you will find one resource entry, which will finally point
to the data.

The tree, without the pointer to the data, may look like this:

                           (root)
                              |
             +----------------+------------------+
             |                |                  |
            menu            dialog             icon
             |                |                  |
       +-----+-----+        +-+----+           +-+----+----+
       |           |        |      |           |      |    |
    "main"      "popup"   0x10   "maindlg"    0x100 0x110 0x120
       |            |       |      |           |      |    |
   +---+-+          |       |      |           |      |    |
   |     |     default   english   default    def.   def.  def.
german english

A IMAGE_RESOURCE_DIRECTORY comprises:
32 bits of unused flags called 'Characteristics';
32 bits 'TimeDateStamp' (again in the common time_t representation),
giving you the time the resource was created (if the entry is set);
16 bits 'MajorVersion' and 16 bits 'MinorVersion', thusly allowing you
to maintain several versions of the resource;
16 bits 'NumberOfNamedEntries' and another 16 bits 'NumberOfIdEntries'.

Immediatly following such a structure are
'NumberOfNamedEntries'+'NumberOfIdEntries' structs which are of the
format 'IMAGE_RESOURCE_DIRECTORY_ENTRY', those with the names coming first.
They may point to further 'IMAGE_RESOURCE_DIRECTORY's or they point to
the actual resource data.
A IMAGE_RESOURCE_DIRECTORY_ENTRY consists of:
32 bits giving you the id of the resource or the directory
it describes;
32 bits offset to the data or offset to the next sub-directory.

The meaning of the id depends on the level in the tree; the id may be a
number (if the hi-bit is clear) or a name (if the hi-bit is set). If it
is a name, the lower 31 bits are the offset from the beginning of the
resource section's raw data to the name (the name consists of 16 bits
length and trailing wide characters, in unicode, not 0-terminated).

If you are in the root-directory, the id, if it is a number, is the
resource-type:
    1: cursor
    2: bitmap
    3: icon
    4: menu
    5: dialog
    6: string table
    7: font directory
    8: font
    9: accelerators
    10: unformatted resource data
    11: message table
    12: group cursor
    14: group icon
    16: version information
Any other number is user-defined. Any resource-type with a type-name is
always user-defined.

If you are one level deeper, the id is the resource-id (or resource-
name).

If you are another level deeper, the id must be a number, and it is the
language-id of the specific instance of the resource; for example, you
can have the same dialog in english, french and german localized forms,
and they all share the same resource-id. The system will choose the
dialog to load based on the process's locale, which in turn will usually
reflect the user's "regional setting".
(If the resource cannot be found for the process locale, the system will
first try to find a resource for the locale using a neutral sublanguage;
if it still can't be found, the instance with the smallest language id will
be used.)
To decipher the language id, split it into the primary language id and
the sublanguage id using the macros PRIMARYLANGID() and SUBLANGID(),
giving you the bits 0 to 9 or 10 to 15, respectivly. The values are
defined in the file "winresrc.h".
Language-resources are only supported for accelerators, dialogs, menus,
rcdata or stringtables; other resource-types should be
LANG_NEUTRAL/SUBLANG_NEUTRAL.

To find out whether the next level below a resource directory is another
directory, you inspect the hi-bit of the offset. If it is set, the
remaining 31 bits are the offset from the beginning of the resource
section's raw data to the next directory, again in the format
IMAGE_RESOURCE_DIRECTORY with trailing IMAGE_RESOURCE_DIRECTORY_ENTRYs.

If the bit is clear, the offset is the distance from the beginning of
the resource section's raw data to the resource's raw data description,
a IMAGE_RESOURCE_DATA_ENTRY. It consists of 32 bits 'OffsetToData' (the
offset to the raw data, counting from the beginning of the resource
section's raw data), 32 bits of 'Size' of the data, 32 bits 'CodePage'
and 32 unused bits.
(The use of codepages is discouraged, you should use the 'language'-
feature to support multiple locales.)

The raw data format depends on the resource type; descriptions can be
found in the MS SDK documentation. Note that any string in resources is
always in UNICODE.

relocations
-----------
The last data directory I will describe is the base relocation
directory. It is pointed to by the IMAGE_DIRECTORY_ENTRY_BASERELOC entry
in the data directories of the optional header. It is typically
contained in a section if its own, with a name like ".reloc" and the
bits IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE and
IMAGE_SCN_MEM_READ set.

The relocation data is needed by the loader if the image cannot be
loaded to the preferred load address 'ImageBase' mentioned in the
optional header. In this case, the fixed addresses supplied by the
linker are no longer valid, and the loader has to apply fixups for
absolute addresses used for locations of static variables, string
literals and so on.

The relocation directory is a sequence of chunks. Each chunk contains
the relocation information for 4 KB of the image. A chunk starts with a
'IMAGE_BASE_RELOCATION' struct. It consists of 32 bits 'VirtualAddress'
and 32 bits 'SizeOfBlock'. It is followed by the chunk's actual
relocation data, being 16 bits each.
The 'VirtualAddress' is the base RVA that the relocations of this chunk
need to be applied to; the 'SizeOfBlock' is the size of the entire chunk
in bytes.
The number of trailing relocations is
('SizeOfBlock'-sizeof(IMAGE_BASE_RELOCATION))/2
The relocation information ends when you encounter a
IMAGE_BASE_RELOCATION struct with a 'VirtualAddress' of 0.

Each 16-bit-relocation information consists of the actual relocation
position in the lower 12 bits and a relocation type in the high 4 bits.
To get the relocation RVA, you need to add the IMAGE_BASE_RELOCATION's
'VirtualAddress' to the 12-bit-position. The type is one of:
    IMAGE_REL_BASED_ABSOLUTE (0)
        This is a no-op; it is used to align the chunk to a 32-bits-
        border.
    IMAGE_REL_BASED_HIGH (1)
        The relocation must be applied to the high 16 bits of the DWORD
        in question.
    IMAGE_REL_BASED_LOW (2)
        The relocation must be applied to the low 16 bits of the DWORD
        in question.
    IMAGE_REL_BASED_HIGHLOW (3)
        The relocation must be applied to the entire 32 bits in question.
    IMAGE_REL_BASED_HIGHADJ (4)
        Unknown
    IMAGE_REL_BASED_MIPS_JMPADDR (5)
        Unknown
    IMAGE_REL_BASED_SECTION (6)
        Unknown
    IMAGE_REL_BASED_REL32 (7)
        Unknown

As an example, if you find the relocation information to be
    0x00004000      (32 bits, starting RVA)
    0x00000010      (32 bits, size of chunk)
    0x3012          (16 bits reloc data)
    0x3080          (16 bits reloc data)
    0x30f6          (16 bits reloc data)
    0x0000          (16 bits reloc data)
    0x00000000      (next chunk's RVA)
    0xff341234
you know the first chunk describes relocations starting at RVA 0x4000 and
is 16 bytes long. Because the header uses 8 bytes and one relocation
uses 2 bytes, there are (16-8)/2=4 relocations in the chunk.
The first relocation is to be applied to the DWORD at 0x4012, the next
to the DWORD at 0x4080, and the third to the DWORD at 0x40f6. The last
relocation is a no-op.
The next chunk has a RVA of 0 and finishes the list.

Now, how do you do a relocation?
You know that the image *is* relocated to the preferred load address
'ImageBase' in the optional header; you also know the address you did
load the image to. If they match, you don't need to do anything.
If they don't match, you calculate the difference
actual_base-preferred_base
and add that value to the relocation positions, which you will find with
the method described above.

Acknowledgments
---------------
Thanks go to David Binette for his debugging and proof-reading.
(The remaining errors are entirely mine.)

Copyright
---------
This text is copyright 1998 by B. Luevelsmeyer. It is freeware, and you
may use it for any purpose but on you own risk. It contains errors and
it is incomplete. You have been warned.

Bug reports
-----------
Send any bug reports (or other comments) to
[email]bernd.luevelsmeyer@iplan.heitec.net[/email]

Literature
----------

[1]
"Peering Inside the PE: A Tour of the Win32 Portable Executable File
Format" (M. Pietrek), in: Microsoft Systems Journal 3/1994

[2]
"Why to Use _declspec(dllimport) & _declspec(dllexport) In Code", MS
Knowledge Base Q132044

[3]
"Windows Q&A" (M. Pietrek), in: Microsoft Systems Journal 8/1995

[4]
"Writing Multiple-Language Resources", MS Knowledge Base Q89866

[5]
"The Portable Executable File Format from Top to Bottom" (Randy Kath),
in: Microsoft Developer Network

Appendix: hello world
---------------------
In this appendix I will show how to make programs by hand. The example
will use Intel-assembly, because I don't speak DEC Alpha.

The program will be the equivalent of

    #include <stdio.h>
    int main(void)
    {
        puts(hello,world);
        return 0;
    }

First, I translate it to use Win32 functions instead of the C runtime:

    #define STD_OUTPUT_HANDLE -11UL
    #define hello "hello, world\n"

    __declspec(dllimport) unsigned long __stdcall
    GetStdHandle(unsigned long hdl);

    __declspec(dllimport) unsigned long __stdcall
    WriteConsoleA(unsigned long hConsoleOutput,
                    const void *buffer,
                    unsigned long chrs,
                    unsigned long *written,
                    unsigned long unused
                    );

    static unsigned long written;

    void startup(void)
    {
        WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE),hello,sizeof(hello)-1,&written,0);
        return;
    }

Now I will fumble out the assembly:
    startup:
                ; parameters for WriteConsole(), backwards
    6A 00                     push      0x00000000
    68 ?? ?? ?? ??            push      offset _written
    6A 0D                     push      0x0000000d
    68 ?? ?? ?? ??            push      offset hello
                ; parameter for GetStdHandle()
    6A F5                     push      0xfffffff5
    2E FF 15 ?? ?? ?? ??      call      dword ptr cs:__imp__GetStdHandle@4
                ; result is last parameter for WriteConsole()
    50                        push      eax
    2E FF 15 ?? ?? ?? ??      call      dword ptr cs:__imp__WriteConsoleA@20
    C3                        ret      

    hello:
    68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A   "hello, world\n"
    _written:
    00 00 00 00

I need to find the functions WriteConsoleA() and GetStdHandle(). They
happen to be in "kernel32.dll".

Now I can start to make the executable. Question marks will take the
place of yet-to-find-out values; they will be patched afterwards.

First the DOS-header, starting at 0x0 and being 0x40 bytes long:
    00 | 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    10 | 00 00 00 00 00 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
    30 | 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00
As you can see, this isn't really a MS-DOS program. It's just the header
with the signature "MZ" at the beginning and the e_lfanew pointing
immediatly after the header, without any code. That's because it isn't
intended to run on MS-DOS.

Then the signature, starting at 0x40 and being 0x4 bytes long:
        50 45 00 00

Now the file-header, which will start at byte 0x44 and is 0x14 bytes long:
    Machine                     4c 01       ; i386
    NumberOfSections            02 00       ; code and data
    TimeDateStamp               00 00 00 00 ; who cares?
    PointerToSymbolTable        00 00 00 00 ; unused
    NumberOfSymbols             00 00 00 00 ; unused
    SizeOfOptionalHeader        e0 00       ; constant
    Characteristics             02 01       ; executable on 32-bit-machine

And the optional header, which will start at byte 0x58 and is 0x60 bytes long:
    Magic                       0b 01       ; constant
    MajorLinkerVersion          00          ; I'm version 0.0 :-)
    MinorLinkerVersion          00          ;
    SizeOfCode                  20 00 00 00 ; 32 bytes of code
    SizeOfInitializedData       ?? ?? ?? ?? ; yet to find out
    SizeOfUninitializedData     00 00 00 00 ; we don't have a BSS
    AddressOfEntryPoint         ?? ?? ?? ?? ; yet to find out
    BaseOfCode                  ?? ?? ?? ?? ; yet to find out
    BaseOfData                  ?? ?? ?? ?? ; yet to find out
    ImageBase                   00 00 10 00 ; 1 MB, chosen arbitrarily
    SectionAlignment            20 00 00 00 ; 32-bytes-alignment
    FileAlignment               20 00 00 00 ; 32-bytes-alignment
    MajorOperatingSystemVersion  04 00      ; NT 4.0
    MinorOperatingSystemVersion  00 00      ;
    MajorImageVersion           00 00       ; version 0.0
    MinorImageVersion           00 00       ;
    MajorSubsystemVersion       04 00       ; Win32 4.0
    MinorSubsystemVersion       00 00       ;
    Win32VersionValue           00 00 00 00 ; unused?
    SizeOfImage                 ?? ?? ?? ?? ; yet to find out
    SizeOfHeaders               ?? ?? ?? ?? ; yet to find out
    CheckSum                    00 00 00 00 ; not used for non-drivers
    Subsystem                   03 00       ; Win32 console
    DllCharacteristics          00 00       ; unused (not a DLL)
    SizeOfStackReserve          00 00 10 00 ; 1 MB stack
    SizeOfStackCommit           00 10 00 00 ; 4 KB to start with
    SizeOfHeapReserve           00 00 10 00 ; 1 MB heap
    SizeOfHeapCommit            00 10 00 00 ; 4 KB to start with
    LoaderFlags                 00 00 00 00 ; unknown
    NumberOfRvaAndSizes         10 00 00 00 ; constant

As you can see, I plan to have only 2 sections, one for code and one for
all the rest (data, constants and import directory). There will be no
relocations and no other stuff like resources.
The section alignment is the same in the file and in RAM (32 bytes);
this helps to keep the task easy.

Now we set up the data directories, beginning at byte 0xb8 and being 0x80 bytes long:
    Address        Size
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_EXPORT (0)
    ?? ?? ?? ??    ?? ?? ?? ??         ; IMAGE_DIRECTORY_ENTRY_IMPORT (1)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_SECURITY (4)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_BASERELOC (5)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_DEBUG (6)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_TLS (9)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_IAT (12)
    00 00 00 00    00 00 00 00         ; 13
    00 00 00 00    00 00 00 00         ; 14
    00 00 00 00    00 00 00 00         ; 15
Only the import directory is in use.

Next are the section headers. First we make the code section, which will
contain the above mentioned assembly. It is 32 bytes long, and so will
be the code section. The header begins at 0x138 and is 0x28 bytes long:

    Name            2e 63 6f 64 65 00 00 00     ; ".code"
    VirtualSize         00 00 00 00             ; unused
    VirtualAddress      ?? ?? ?? ??             ; yet to find out
    SizeOfRawData       20 00 00 00             ; size of code
    PointerToRawData    ?? ?? ?? ??             ; yet to find out
    PointerToRelocations 00 00 00 00            ; unused
    PointerToLinenumbers 00 00 00 00            ; unused
    NumberOfRelocations  00 00                  ; unused
    NumberOfLinenumbers  00 00                  ; unused
    Characteristics     20 00 00 60             ; code, executable, readable

The second section will contain the data. The header begins at 0x160 and
is 0x28 bytes long:

    Name            2e 64 61 74 61 00 00 00     ; ".data"
    VirtualSize         00 00 00 00             ; unused
    VirtualAddress      ?? ?? ?? ??             ; yet to find out
    SizeOfRawData       ?? ?? ?? ??             ; yet to find out
    PointerToRawData    ?? ?? ?? ??             ; yet to find out
    PointerToRelocations 00 00 00 00            ; unused
    PointerToLinenumbers 00 00 00 00            ; unused
    NumberOfRelocations  00 00                  ; unused
    NumberOfLinenumbers  00 00                  ; unused
    Characteristics     40 00 00 c0             ; initialized, readable, writeable

The next byte is 0x188, but the sections need to be aligned to 32 bytes
(because I chose so), so we need padding bytes up to 0x1a0:

    00 00 00 00 00 00       ; padding
    00 00 00 00 00 00
    00 00 00 00 00 00
    00 00 00 00 00 00

Now the first section, being the code section with the above mentioned
assembly, *does* come. It begins at byte 0x1a0 and is 0x20 bytes long:
    6A 00                    ; push      0x00000000
    68 ?? ?? ?? ??           ; push      offset _written
    6A 0D                    ; push      0x0000000d
    68 ?? ?? ?? ??           ; push      offset hello_string
    6A F5                    ; push      0xfffffff5
    2E FF 15 ?? ?? ?? ??     ; call      dword ptr cs:__imp__GetStdHandle@4
    50                       ; push      eax
    2E FF 15 ?? ?? ?? ??     ; call      dword ptr cs:__imp__WriteConsoleA@20
    C3                       ; ret      

Because of the previous section's length we don't need any padding
before the next section (data), and here it comes, beginning at 0x1c0:

    68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A  ; "hello, world\n"
    00 00 00                                ; padding to align _written
    00 00 00 00                             ; _written

Now all that's left is the import directory. It will import 2 functions
from "kernel32.dll", and it's immediatly following the variables in the
same section. First we will align it to 32 bytes:

    00 00 00 00 00 00 00 00 00 00 00 00     ; padding

It begins at 0x1e0 with the IMAGE_IMPORT_DESCRIPTOR:
    OriginalFirstThunk      ?? ?? ?? ??     ; yet to find out
    TimeDateStamp           00 00 00 00     ; unbound
    ForwarderChain          ff ff ff ff     ; no forwarders
    Name                    ?? ?? ?? ??     ; yet to find out
    FirstThunk              ?? ?? ?? ??     ; yet to find out

We need to terminate the import-directory with a 0-bytes-entry (we are at 0x1f4):
    OriginalFirstThunk      00 00 00 00     ; terminator
    TimeDateStamp           00 00 00 00     ;
    ForwarderChain          00 00 00 00     ;
    Name                    00 00 00 00     ;
    FirstThunk              00 00 00 00     ;

Now there's the DLL name left, and the 2 thunks, and the thunk-data, and
the function names. But we will be finished real soon now!

The DLL name, 0-terminated, beginning at 0x208:
    6b 65 72 6e 65 6c 33 32 2e 64 6c 6c 00  ; "kernel32.dll"
    00 00 00                                ; padding to 32-bit-boundary

The original first thunk, starting at 0x218:
    AddressOfData   ?? ?? ?? ??             ; RVA to function name "WriteConsoleA"
    AddressOfData   ?? ?? ?? ??             ; RVA to function name "GetStdHandle"
                    00 00 00 00             ; terminator

The first thunk is exactly the same list and starts at 0x224:
(__imp__WriteConsoleA@20, at 0x224)
    AddressOfData   ?? ?? ?? ??             ; RVA to function name "WriteConsoleA"
(__imp__GetStdHandle@4, at 0x228)
    AddressOfData   ?? ?? ?? ??             ; RVA to function name "GetStdHandle"
                    00 00 00 00             ; terminator

Now what's left is the two function names in the shape of an
IMAGE_IMPORT_BY_NAME. We are at byte 0x230.
    01 00                                      ; ordinal, need not be correct
    57 72 69 74 65 43 6f 6e 73 6f 6c 65 41 00  ; "WriteConsoleA"
    02 00                                      ; ordinal, need not be correct
    47 65 74 53 74 64 48 61 6e 64 6c 65 00     ; "GetStdHandle"

Ok, that's about all. The next byte, which we don't really need, is
0x24f. We need to fill the section with padding up to 0x260:
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; padding
    00

------------

We are done. Now that we know all the byte-offsets, we can apply fixups
to all those addresses and sizes that were indicated as "unknown" with
'??'-marks.
I won't force you to read that step-by-step (it's quite
straightforward), and simply present the result:

------------

DOS-header, starting at 0x0:
    00 | 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    10 | 00 00 00 00 00 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
    30 | 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00

signature, starting at 0x40:
        50 45 00 00

file-header, starting at 0x44:
    Machine                     4c 01       ; i386
    NumberOfSections            02 00       ; code and data
    TimeDateStamp               00 00 00 00 ; who cares?
    PointerToSymbolTable        00 00 00 00 ; unused
    NumberOfSymbols             00 00 00 00 ; unused
    SizeOfOptionalHeader        e0 00       ; constant
    Characteristics             02 01       ; executable on 32-bit-machine

optional header, starting at 0x58:
    Magic                       0b 01       ; constant
    MajorLinkerVersion          00          ; I'm version 0.0 :-)
    MinorLinkerVersion          00          ;
    SizeOfCode                  20 00 00 00 ; 32 bytes of code
    SizeOfInitializedData       a0 00 00 00 ; data section size
    SizeOfUninitializedData     00 00 00 00 ; we don't have a BSS
    AddressOfEntryPoint         a0 01 00 00 ; beginning of code section
    BaseOfCode                  a0 01 00 00 ; RVA to code section
    BaseOfData                  c0 01 00 00 ; RVA to data section
    ImageBase                   00 00 10 00 ; 1 MB, chosen arbitrarily
    SectionAlignment            20 00 00 00 ; 32-bytes-alignment
    FileAlignment               20 00 00 00 ; 32-bytes-alignment
    MajorOperatingSystemVersion  04 00      ; NT 4.0
    MinorOperatingSystemVersion  00 00      ;
    MajorImageVersion           00 00       ; version 0.0
    MinorImageVersion           00 00       ;
    MajorSubsystemVersion       04 00       ; Win32 4.0
    MinorSubsystemVersion       00 00       ;
    Win32VersionValue           00 00 00 00 ; unused?
    SizeOfImage                 c0 00 00 00 ; sum of all section sizes
    SizeOfHeaders               a0 01 00 00 ; offset to 1st section
    CheckSum                    00 00 00 00 ; not used for non-drivers
    Subsystem                   03 00       ; Win32 console
    DllCharacteristics          00 00       ; unused (not a DLL)
    SizeOfStackReserve          00 00 10 00 ; 1 MB stack
    SizeOfStackCommit           00 10 00 00 ; 4 KB to start with
    SizeOfHeapReserve           00 00 10 00 ; 1 MB heap
    SizeOfHeapCommit            00 10 00 00 ; 4 KB to start with
    LoaderFlags                 00 00 00 00 ; unknown
    NumberOfRvaAndSizes         10 00 00 00 ; constant

data directories, starting at 0xb8:
    Address        Size
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_EXPORT (0)
    e0 01 00 00    6f 00 00 00         ; IMAGE_DIRECTORY_ENTRY_IMPORT (1)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_SECURITY (4)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_BASERELOC (5)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_DEBUG (6)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_TLS (9)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)
    00 00 00 00    00 00 00 00         ; IMAGE_DIRECTORY_ENTRY_IAT (12)
    00 00 00 00    00 00 00 00         ; 13
    00 00 00 00    00 00 00 00         ; 14
    00 00 00 00    00 00 00 00         ; 15

section header (code), starting at 0x138:
    Name            2e 63 6f 64 65 00 00 00     ; ".code"
    VirtualSize         00 00 00 00             ; unused
    VirtualAddress      a0 01 00 00             ; RVA to code section
    SizeOfRawData       20 00 00 00             ; size of code
    PointerToRawData    a0 01 00 00             ; file offset to code section
    PointerToRelocations 00 00 00 00            ; unused
    PointerToLinenumbers 00 00 00 00            ; unused
    NumberOfRelocations  00 00                  ; unused
    NumberOfLinenumbers  00 00                  ; unused
    Characteristics     20 00 00 60             ; code, executable, readable

section header (data), starting at 0x160:
    Name            2e 64 61 74 61 00 00 00     ; ".data"
    VirtualSize         00 00 00 00             ; unused
    VirtualAddress      c0 01 00 00             ; RVA to data section
    SizeOfRawData       a0 00 00 00             ; size of data section
    PointerToRawData    c0 01 00 00             ; file offset to data section
    PointerToRelocations 00 00 00 00            ; unused
    PointerToLinenumbers 00 00 00 00            ; unused
    NumberOfRelocations  00 00                  ; unused
    NumberOfLinenumbers  00 00                  ; unused
    Characteristics     40 00 00 c0             ; initialized, readable, writeable

(padding)
    00 00 00 00 00 00       ; padding
    00 00 00 00 00 00
    00 00 00 00 00 00
    00 00 00 00 00 00

code section, starting at 0x1a0:
    6A 00                    ; push      0x00000000
    68 d0 01 10 00           ; push      offset _written
    6A 0D                    ; push      0x0000000d
    68 c0 01 10 00           ; push      offset hello_string
    6A F5                    ; push      0xfffffff5
    2E FF 15 28 02 10 00     ; call      dword ptr cs:__imp__GetStdHandle@4
    50                       ; push      eax
    2E FF 15 24 02 10 00     ; call      dword ptr cs:__imp__WriteConsoleA@20
    C3                       ; ret      

data section, beginning at 0x1c0:
    68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A  ; "hello, world\n"
    00 00 00                                ; padding to align _written
    00 00 00 00                             ; _written
padding:
    00 00 00 00 00 00 00 00 00 00 00 00     ; padding
IMAGE_IMPORT_DESCRIPTOR, starting at 0x1e0:
    OriginalFirstThunk      18 02 00 00     ; RVA to orig. 1st thunk
    TimeDateStamp           00 00 00 00     ; unbound
    ForwarderChain          ff ff ff ff     ; no forwarders
    Name                    08 02 00 00     ; RVA to DLL name
    FirstThunk              24 02 00 00     ; RVA to 1st thunk
terminator (0x1f4):
    OriginalFirstThunk      00 00 00 00     ; terminator
    TimeDateStamp           00 00 00 00     ;
    ForwarderChain          00 00 00 00     ;
    Name                    00 00 00 00     ;
    FirstThunk              00 00 00 00     ;
The DLL name, at 0x208:
    6b 65 72 6e 65 6c 33 32 2e 64 6c 6c 00  ; "kernel32.dll"
    00 00 00                                ; padding to 32-bit-boundary
original first thunk, starting at 0x218:
    AddressOfData   30 02 00 00             ; RVA to function name "WriteConsoleA"
    AddressOfData   40 02 00 00             ; RVA to function name "GetStdHandle"
                    00 00 00 00             ; terminator
first thunk, starting at 0x224:
    AddressOfData   30 02 00 00             ; RVA to function name "WriteConsoleA"
    AddressOfData   40 02 00 00             ; RVA to function name "GetStdHandle"
                    00 00 00 00             ; terminator
IMAGE_IMPORT_BY_NAME, at byte 0x230:
    01 00                                      ; ordinal, need not be correct
    57 72 69 74 65 43 6f 6e 73 6f 6c 65 41 00  ; "WriteConsoleA"
IMAGE_IMPORT_BY_NAME, at byte 0x240:
    02 00                                      ; ordinal, need not be correct
    47 65 74 53 74 64 48 61 6e 64 6c 65 00     ; "GetStdHandle"
(padding)
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; padding
    00
First unused byte: 0x260

--------------

Alas, this works on NT but didn't on windows 95. windows95 can't run
applications with a section alignment of 32 bytes, it needs an
alignment of 4 KB and, apparently, a file alignment of 512 bytes. So for
windows95 you'll have to insert a large number of 0-bytes (for padding)
and adjust the RVAs. Thanks go to D. Binette for testing on windows95.

        -- end of text --


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (5)
雪    币: 241
活跃值: (145)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
没有发现中文的。。。
2004-8-26 18:08
0
雪    币: 323
活跃值: (589)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
3
只好辛苦自己了:(
2004-8-27 14:44
0
雪    币: 323
活跃值: (589)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
4
PE文件格式
==========

目的
-----

PE(“portable executable")文件格式是微软windows NT,windows95和 win32系列操作系统上使用的的二进制可执行文件格式(包括DLL和程序)。在windowsNT中,驱动程序也是使用这种格式。PE也可用在对象文件和库文件中。

PE格式是由微软设计的,主要基于对COFF(common object file format 通用对象文件格式)的良好理解。后者用于多种UNIXes 和 VMS 上的对象文件和可执行文件。

win32 SDK 包括 <winnt.h>头文件,里面有对PE格式的预定义(#define)和自定义类型(typedef)。我在讲解的时候会介绍struct-member-names和#define。

你将发现imagehelp.dll会有用。它是winNT的一部分,但有关文档相当少。它的一些功能在”Developer Network"里有描述。

总体布局
--------

在PE文件的起始处我们会发现MS-DOS 可执行文件"stub"(MS-DOS executable "stub");这使得任何PE 文件都是有效的MS-DOS可执行文件。

在DOS-stub之后是32-bit-signature,其magic number是0x00004550(IMAGE_NT_SIGNATURE).

之后有一文件头(在COFF格式里)告诉二进制文件应运行在哪种机器上,二进制文件里有多少区块(sections),链接时间,是否是一个可执行文件(executable)或DLL文件等等。(在本文中,exe文件和DLL文件的区别在于:DLL不能直接运行,只能被别的二进制文件使用。而二进制文件不能链接到一个exe文件。)

此后,我们会看到一个可选头(optional header)(它始终存在,但仍被称为"optional"----COFF将“optional header"用于库文件(libraries)而不是对象文件(objects)--这也是为什么被称为optional)。它告诉我们更多有关二进制文件如何被加载的信息:起始地址,保留栈的数量,数据段的大小等等。

可选头的一个有趣部分是数据目录(data directories)的踪迹数组(trailing array);这些目录将指针包含到区块(sections)的数据中。例如,如果二进制文件有一个输出目录(export directory),你会在数组成员(array member)IMAGE_DIRECTORY_ENTRY_EXPORT该目录中发现一个指针,它指向某个区块。

紧跟可选头(optional headers)的是区块(sections),他们由区块头部(section headers)所引入。基本上讲,区块的内容是真正执行文件所需要的,而所有的头部和目录只是帮助你找到这些内容。

每个区块都有一些关于对齐、它包含哪种数据(如“初始化数据”等等)、是否共享等以及数据本身的一些标记。大多数(并非所的)的区块包含了一个或多个目录,这些目录引用了可选头部(optional headers)的数据目录数组("data directory" array),如输出函数目录、基址重定向目录(directory of base relocations)。举个例子,可执行代码或初始化数据就是无目录类型的内容。

    +-------------------+
    | DOS-stub          |
    +-------------------+
    | file-header       |
    |     文件头        |
    +-------------------+
    | optional header   |
    |  可选头部         |
    |- - - - - - - - - -|
    |                   |
    | data directories  |
    |   数据目录        |
    +-------------------+
    |                   |
    | section headers   |
    | (区块头部)        |
    +-------------------+
    |                   |
    | section 1         |
    | 区块1             |
    +-------------------+
    |                   |
    | section 2         |
    |  区块2            |
    +-------------------+
    |                   |
    | ...               |
    |                   |
    +-------------------+
    |                   |
    | section n         |
    |   区块N           |
    +-------------------+

DOS--stub and Signature
------------------------

DOS--stub的概念从16位windows可执行文件(它们是"NE"格式)开始就众所周知了。stub用于OS/2可执行文件,也用于自解压文件和其他应用程序。对PE文件来说,MS-DOS可执行文件(MS-DOS-executable)由100bytes字节组成,用于输出一个诸如“本程序需运行在windows NT”的错误信息。

通过一个结构为IMAGE_DOS_HEADER的有效的DOS-header,你可以认识DOS-stub。前2个字节应该是“MZ”(IMAGE_DOS_SIGNATURE对此有#define ).你可以通过踪迹签名(trailing signature)将一个PE二进制文件同另一个区分开来。踪迹签名可在头部成员’e_lfanew'(32 bits 长,起始于60 byte offset处)所给的偏移量处找到。对OS/2和windows的二进制文件,签名(signature)大小是16 bit word;对PE文件,它是32 bit longword,其值由IMAGE_NT_SIGNATURE #defined 为0x00004550。

文件头(File Header)
-------------------

要找到IMAGE_FILE_HEADER,先验证DOS-header的”MZ“(前两个字节),然后找到DOS-stub's header的‘e_lfanew’数值,再忽略文件开始处的许多字节。检测你找到的签名,文件头--结构体IMAGE_FILE_HEADER--就在其后。文件头的成员从头自尾描述。

第一个成员是16bit值(16-bit-value)‘machine’,它说明二进制文件所将运行的系统。已知的有效值是:
               IMAGE_FILE_MACHINE_I386 (0x14c)
                for Intel 80386 processor or better    //Intel 80386及以上处理器

            0x014d
                for Intel 80486 processor or better    //Intel 80486及以上处理器

            0x014e
                for Intel Pentium processor or better  //Intel 奔腾以上处理器

            0x0160
                for R3000 (MIPS) processor, big endian  

            IMAGE_FILE_MACHINE_R3000 (0x162)
                for R3000 (MIPS) processor, little endian

            IMAGE_FILE_MACHINE_R4000 (0x166)
                for R4000 (MIPS) processor, little endian

            IMAGE_FILE_MACHINE_R10000 (0x168)
                for R10000 (MIPS) processor, little endian

            IMAGE_FILE_MACHINE_ALPHA (0x184)
                for DEC Alpha AXP processor

            IMAGE_FILE_MACHINE_POWERPC (0x1F0)
                for IBM Power PC, little endian

第二个是16bit值(16-bit-value)的“NumberOfSections"。其值就是紧跟头部的区块数。我们后面会讨论这个区块。

第三个是时间印戳(timestamp)"TimeDateStamp",说明文件创建的时间。通过它我们可以区分同一文件的不同版本,即使所谓”官方”版本号没有改变。(时间印戳的格式没有写入文档,除非同一文件的不同版本有某种统一。很显然,“自UTC 1970年1月1日0时0分0秒以来多少秒”为一格式被多数C编译器用作时间印戳。)
时间印戳用作绑定我们稍后会讨论的输入目录。
警告:一些编译器试图设置时间印戳为无意义值。

成员‘PointerToSymbolTable' 和 'NumberOfSymbols'(二者都是32bit)用作调试信息。我不知道如何描述他们,但我已经发现它们的指针总是为0。

'SizeOfOptionalHeader' (16 bit)只是简单的sizeof(IMAGE_OPTIONAL_HEADER)。你可以用它来验证PE 文件结构的正确性。

'Characteristics'是16bits,由一套标记组成,大部分标记只对对象文件和库文件有效。
    Bit 0 (IMAGE_FILE_RELOCS_STRIPPED):如果文件中没有重定向信息,就设置。它引用区块本身的每一区块的重定向信息。它不用于可执行文件,因为下面描述的基址重定向目录已经有重定向信息。
    Bit 1 (IMAGE_FILE_EXECUTABLE_IMAGE):如果文件是可执行文件,就设置。(也就是说,该文件不是对象文件或库文件。)
    Bit 2 (IMAGE_FILE_LINE_NUMS_STRIPPED):如果行数信息被除去,就设置。它不用于可执行文件。
    Bit 3 (IMAGE_FILE_LOCAL_SYMS_STRIPPED):如果文件中没有本地符号信息,就设置(不用于可执行文件)。
    Bit 4 (IMAGE_FILE_AGGRESIVE_WS_TRIM) : 如果操作系统应该整理正在运行的进程的工作环境(进程所用的内存大小),并积极在屏幕上输出(aggressivly by paging it out),就设置。如果是个很烂的、大部分时间在等待,一天只醒来一次的程序,或类似的程序,也应该设置。
    Bits 7 (IMAGE_FILE_BYTES_REVERSED_LO) 和 15(IMAGE_FILE_BYTES_REVERSED_HI): 如果文件的endianess不是机器所期望的,就设置,所以在机器读取之前就应交换字节(swap bytes)。这对可执行文件来说是不可靠的(操作系统期望可执行文件是正确按字节排序的)。
    Bit 8 (IMAGE_FILE_32BIT_MACHINE) :如果机器预期是32bit 机器,就设置。该标记总是设置。
    Bit 9 (IMAGE_FILE_DEBUG_STRIPPED): 如果文件没有调试信息,就设置。对可执行文件没用。
    Bit 10 (IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) :如果应用程序不会从一个可移除的媒介如软驱或光驱运行,就设置。在这种情况下,建议操作系统将文件拷到交换文件区执行。
    Bit 11 (IMAGE_FILE_NET_RUN_FROM_SWAP):如果应用程序不会从网络运行,就设置。在这种情况下,建议操作系统将文件拷到交换文件区执行。
    Bit 12 (IMAGE_FILE_SYSTEM):如果文件是系统文件,比如驱动程序,就设置。对可执行文件无用。
    Bit 13 (IMAGE_FILE_DLL):如果文件是DLL文件,就设置。
    Bit 14 (IMAGE_FILE_UP_SYSTEM_ONLY) :如果文件并非设计在多处理器上运行,就设置(也就是说,文件如果在某种方式上只能依赖于一个处理器上运行,它就会蹦溃。)。

相对虚拟地址(Relative Virtual Addresses)
----------------------------------------

PE格式大量使用所谓的RVAs(相对虚拟地址)。一个RVA用于描述内存地址(如果你不知道基地址)。其值加上基地址就会得到你想要的线性地址。
基地址是PE映像加载的地址,可能不同的机器上会发生变化(may vary from one invocation to the next)。

例如:假设一EXE文件加载到 0x400000 地址(address)处,从RVA 0x1560开始执行。有效的执行将从 0x401560地址(address)开始。如果EXE文件加载到0x100000,执行将从0x101560开始。

PE文件的一些部分(区块)并不一定要对齐到映像文件加载的同一方式,从而使事情变得复杂了。比如:文件的区块通常对齐到512-byte-borders,而加载的映像文件可能对齐到4096-byte-borders。参见下面的 'SectionAlignment' 和 'FileAlignment'。

所以为在PE文件中寻找特定RVA的一点信息,你必须计算出偏移量(offsets),假定文件已加载,但根据文件偏移量来忽略。(as if the file were loaded, but skip according to the file-offsets)

举个例子,假定你知道文件从RVA 0x1560开始执行,并想反汇编从那里开始的代码。为找到在文件中的地址,你必须要得知内存中的区块对齐到4096 bytes,并且'.code'区块在内存中从RVA 0x1000开始,长度为16384 bytes;然后你要知道RVA 0x1560在该区块中是偏移量0x560。你得知区块在文件中是对齐到 512-byte-borders,并且'.code'在文件中开始于偏移量0x800处,你就会知道文件中的执行代码开始于0x800+0x560=0xd60 byte处。

如果知道它是如何工作的就很容易了。:-)

可选头(Optional Header)
-----------------------

紧跟文件头的是IMAGE_OPTIONAL_HEADER(尽管名字是可选,可它总是存在)。它包含有关如何准确区分PE文件的信息。其成员也是从头到尾描述。

第一个16位字(16-bit-word)是'magic',就我对PE文件的了解,其值总是0x010b。

随后2个字节是生成文件的链接器的版本号('MajorLinkerVersion' and 'MinorLinkerVersion')。同样,这些值是不可靠的,并不总是正确地反映了链接器的版本号。(为简单,一些链接器不设置这个区域)
让我们想想看,如果你对使用了哪种链接器一无所知,这个版本号又有什么用呢?

再往后面3个长字(longwords)(每个32bit)用来表示可执行代码的大小('SizeOfCode')、初始化数据的大小('SizeOfInitializedData', 所谓的 "data segment")、和未初始化数据的大小('SizeOfUninitializedData', 所谓的"bss segment")。同样,这些值是不可靠的(例如:数据段可能实际上被编译器或链接器分为几个段)。通过检查可选头后面的区块,你可以获得更准确的大小。

后面是一个32位值(32-bit-value)的RVA。该RVA是代码入口点('AddressOfEntryPoint')处的偏移量.文件从那里执行。例如它是DLL文件的LibMain()函数、程序的起始代码(也叫Main())或驱动程序的DriverEntry()。如果你去手工加载映像文件,在处理完所有的修复和重定向后,调用这一地址来开始该程序。

后面2个是32位值的可执行代码('BaseOfCode')和初始化数据('BaseOfData')的偏移量。他们两个同样都是RVAs,而且都没有什么意义,因为你可以通过检查可选头后面的区块获得更可靠的信息。
未初始化数据没有偏移量是因为由于未初始化,映像文件中提供这一数据没有意义。

下一入口是一个32位值的RVA,它给出优先加载地址preferred load address('ImageBase').这一地址是文件已经被链接器重定向的地址;事实上,如果二进制文件能被加载到该地址,加载器就不必再次重定向文件了,从而节省了大量的加载时间。
如果其他的映像文件已经加载到这一地址(“地址冲突”,如果你加载了几个DLL文件,它们都重定向到链接器默认的地址,就会经常发生“地址冲突”),或者有争议(in question)的内存被用作其他目的(stack,malloc(),未初始化数据等什么的),优先加载地址就不能再用了。在这些情况下,映像必须加载到其他某个地址并重定向(参见下面的'重定向目录').如果映像是个DLL文件,这还要进一步处理,因为“绑定输入表”(bound imports)不再有效,必须对调用DLL文件的二进制文件进行修复(参见下面的“输入目录”)。

后面2个32位值是PE文件区块在内存中的对齐('SectionAlignment',当映像被加载)和在文件中的对齐('FileAlignment')。通常二个值都是32,或者FileAlignment是512 而SectionAlignment是4096。区块在后面会讨论。   

后面的2个16-bit-words是操作系统版本('MajorOperatingSystemVersion' 和 'MinorOperatingSystemVersion')版本信息应是操作系统(如NT,win95)的版本,对应于子系统的版本(如Win32);它经常没有提供,或提供错误。加载器显然并不用它。  (待续)P355
2004-8-30 08:57
0
雪    币: 241
活跃值: (145)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
辛苦了.
2004-8-30 13:41
0
雪    币: 250
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zdd
6
very good!
2004-8-30 21:05
0
游客
登录 | 注册 方可回帖
返回
//