Search code examples
cexe

Determine where MZ exe ends, and LE/LX/PE starts


I wonder what's the best way to determine where the MZ part of an EXE file ends, and the attached extended executable starts (can be PE/LE/LX/NE/COFF etc ...).

I found this website: http://www.delorie.com/djgpp/doc/exe/ which tries to explain it, but I never get the expected result. I always end up with an offset way beyond the actual PE or LX start offset.

// LXInfo.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

struct EXE {
  unsigned short signature; /* == 0x5a4D */
  unsigned short bytes_in_last_block;
  unsigned short blocks_in_file;
  unsigned short num_relocs;
  unsigned short header_paragraphs;
  unsigned short min_extra_paragraphs;
  unsigned short max_extra_paragraphs;
  unsigned short ss;
  unsigned short sp;
  unsigned short checksum;
  unsigned short ip;
  unsigned short cs;
  unsigned short reloc_table_offset;
  unsigned short overlay_number;
};

struct EXE_RELOC {
  unsigned short offset;
  unsigned short segment;
};


int _tmain(int argc, _TCHAR* argv[])
{
    struct EXE header1;
    char sFile[]="c:\\register.dll";
    unsigned int extra_data_start;
    char test;
    FILE *fp;
    fp = fopen(sFile, "rb");
    fread(&header1,sizeof(struct EXE),1,fp);

    //read the header
    printf("EXE Signature: %x \n", header1.signature);
    printf("Bytes in last block: %08x \n", header1.bytes_in_last_block);
    printf("Blocks in file: %08x \n", header1.blocks_in_file);
    printf("Number of relocations: %08x \n", header1.num_relocs);
    printf("Header paragraphs: %08x \n", header1.header_paragraphs);
    printf("Min. extra paragraphs: %08x \n", header1.min_extra_paragraphs);
    printf("Max. extra paragraphs: %08x \n", header1.max_extra_paragraphs);
    printf("Initial SS value: %08x \n", header1.ss);
    printf("Initial SP value: %08x \n", header1.sp);
    printf("Checksum value: %08x \n", header1.checksum);
    printf("Initial CS value: %08x \n", header1.cs);
    printf("Initial IP value: %08x \n", header1.ip);
    printf("Relocation table offset: %08x \n", header1.reloc_table_offset);
    printf("Overlay number: %x \n", header1.overlay_number);
    printf("\n");
    printf("Start of EXE data: %08x \n", header1.header_paragraphs * 16L);

    //calculate end of MZ EXE, according to Delorie
    extra_data_start = header1.blocks_in_file * 512L;
    if (header1.bytes_in_last_block)
        extra_data_start -= (512 - header1.bytes_in_last_block);

    printf("End of EXE data: %08x \n", extra_data_start);

    // let's read the first two bytes after the MZ EXE data. This should give us a P and E on windows, or L and X on OS/2...
    fseek(fp,extra_data_start,SEEK_SET);
    fread(&test,1,1,fp);
    printf("test char: %c \n", test);
    fread(&test,1,1,fp);
    printf("test char: %c \n", test);

    fclose(fp);
    getch();
    return 0;
}

Solution

  • Read the following article: An In-Depth Look into the Win32 Portable Executable File Format.
    You should also read up on the The Portable Executable File Format

    For example, to get the offset of the IMAGE_NT_HEADERS you would do like this:

    IMAGE_DOS_HEADER* pdos = (IMAGE_DOS_HEADER*)peBuffer; 
    IMAGE_NT_HEADERS* pnt = (IMAGE_NT_HEADERS*)((DWORD)pdos + pdos->e_lfanew);