/* From http://www.hanshq.net/making-executables.html */

#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static const uint8_t rot13_program[] = {
0x6a,0xf6,                     /*   pushl  $-10                  */
0xff,0x15,0x00,0x30,0x40,0x00, /*   call   *iat_slot0            */
0x89,0xc6,                     /*   movl   %eax,%esi             */
0x6a,0xf5,                     /*   pushl  $-11                  */
0xff,0x15,0x00,0x30,0x40,0x00, /*   call   *iat_slot0            */
0x89,0xc7,                     /*   movl   %eax,%edi             */
                               /* read:                          */
0x6a,0x00,                     /*   pushl  $0                    */
0x89,0xe0,                     /*   movl   %esp,%eax             */
0x6a,0x00,                     /*   pushl  $0                    */
0x50,                          /*   pushl  %eax                  */
0x68,0x00,0x10,0x00,0x00,      /*   pushl  $4096                 */
0x68,0x00,0x40,0x40,0x00,      /*   pushl  $buffer               */
0x56,                          /*   pushl  %esi                  */
0xff,0x15,0x04,0x30,0x40,0x00, /*   call   *iat_slot1            */
0x58,                          /*   popl   %eax                  */
0x85,0xc0,                     /*   testl  %eax,%eax             */
0x7e,0x26,                     /*   jle    end                   */
0xb9,0x00,0x40,0x40,0x00,      /*   movl   $buffer,%ecx          */
0x89,0xc2,                     /*   movl   %eax,%edx             */
                               /* rot13:                         */
0x0f,0xb6,0x19,                /*   movzbl (%ecx),%ebx           */
0x8a,0x9b,0x00,0x20,0x40,0x00, /*   movb   rot13_table(%ebx),%bl */
0x88,0x19,                     /*   movb   %bl,(%ecx)            */
0x41,                          /*   incl   %ecx                  */
0x48,                          /*   decl   %eax                  */
0x75,0xf1,                     /*   jnz    rot13                 */
0x29,0xd1,                     /*   subl   %edx,%ecx             */
0x6a,0x00,                     /*   pushl  $0                    */
0x54,                          /*   pushl  %esp                  */
0x52,                          /*   pushl  %edx                  */
0x51,                          /*   pushl  %ecx                  */
0x57,                          /*   pushl  %edi                  */
0xff,0x15,0x08,0x30,0x40,0x00, /*   call   *iat_slot2            */
0xeb,0xbd,                     /*   jmp    read                  */
                               /* end:                           */
0x6a,0x00,                     /*   pushl  $0                    */
0xff,0x15,0x0c,0x30,0x40,0x00, /*   call   *iat_slot3            */
};

static const uint8_t rot13_table[] = {   0,   1,   2,   3,   4,   5,   6,
  7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,
 23,  24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
 39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,
 55,  56,  57,  58,  59,  60,  61,  62,  63,  64, 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
'J', 'K', 'L', 'M',  91,  92,  93,  94,  95,  96, 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
247, 248, 249, 250, 251, 252, 253, 254, 255 };

static const uint8_t dos_program[] = {
0x8c, 0xc8,                    /* mov    %cs,%ax        */
0x8e, 0xd8,                    /* mov    %ax,%ds        */
0xba, 0x10, 0x00,              /* mov    $16,%dx        */
0xb4, 0x09,                    /* mov    $0x9,%ah       */
0xcd, 0x21,                    /* int    $0x21          */
0xb8, 0x01, 0x4c,              /* mov    $0x4c01,%ax    */
0xcd, 0x21,                    /* int    $0x21          */
'H', 'e', 'l', 'l', 'o', ',',  /* ($-terminated string) */
' ', 'D', 'O', 'S', ' ', 'w',
'o', 'r', 'l', 'd', '!', '$' };


/* Write an 8-bit value to file. */
static void write8(FILE *file, uint8_t value)
{
        if (fputc(value, file) == EOF) {
                perror("fputc");
                exit(1);
        }
}

/* Write a 16-bit value in little-endian to file. */
static void write16(FILE *file, uint16_t value)
{
        write8(file, (value >> 0) & 0xff);
        write8(file, (value >> 8) & 0xff);
}

/* Write a 32-bit value in little-endian to file. */
static void write32(FILE *file, uint32_t value)
{
        write8(file, (value >> 0)  & 0xff);
        write8(file, (value >> 8)  & 0xff);
        write8(file, (value >> 16) & 0xff);
        write8(file, (value >> 24) & 0xff);
}

/* Write a string to file, padded to 8 bytes. */
static void writestr8(FILE *file, const char *str)
{
        size_t len, i;

        len = strlen(str);
        assert(len <= 8 && "The string must fit in 8 bytes.");

        for (i = 0; i < 8; i++) {
                write8(file, i < len ? str[i] : 0);
        }
}

/* Write a string to file, null-terminated and padded to 16 bytes. */
static void writestr16(FILE *file, const char *str)
{
        size_t len, i;

        len = strlen(str);
        assert(len <= 15 && "The string and terminator must fit in 16 bytes.");

        for (i = 0; i < 16; i++) {
                write8(file, i < len ? str[i] : 0);
        }
}

/* Seek to offset in file. */
static void seek(FILE *file, long offset)
{
        if (fseek(file, offset, SEEK_SET) == -1) {
                perror("fseek");
                exit(1);
        }
}

/* Round value up to alignment. */
static uint32_t align_to(uint32_t value, uint32_t alignment)
{
        uint32_t remainder = value % alignment;

        if (remainder == 0) {
                return value;
        }

        return value + (alignment - remainder);
}

#define DOS_HDR_SZ 0x40
#define PE_SIG_SZ   0x4
#define COFF_HDR_SZ 0x14
#define OPT_HDR_SZ  0xe0
#define PE_HDR_SZ   (PE_SIG_SZ + COFF_HDR_SZ + OPT_HDR_SZ)
#define SEC_HDR_SZ  0x28

#define IAT_ENTRY_SZ               0x4
#define IMPORT_DIR_ENTRY_SZ        0x14
#define IMPORT_LOOKUP_TBL_ENTRY_SZ IAT_ENTRY_SZ
#define NAME_TABLE_ENTRY_SZ        0x12

#define IMAGE_BASE 0x00400000
#define SEC_ALIGN  0x1000
#define FILE_ALIGN 0x200

int main(int argc, char **argv)
{
        FILE *f;
        size_t i;

        if (argc != 2) {
                fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
                exit(1);
        }

        f = fopen(argv[1], "wb");
        if (f == NULL) {
                perror("fopen");
                exit(1);
        }

        uint32_t dos_stub_sz = DOS_HDR_SZ + sizeof(dos_program);
        uint32_t pe_offset = align_to(dos_stub_sz, 8);

        write8(f, 'M');                /* Magic number (2 bytes) */
        write8(f, 'Z');
        write16(f, dos_stub_sz % 512); /* Last page size */
        write16(f, align_to(dos_stub_sz, 512) / 512); /* Pages in file */
        write16(f, 0);                 /* Relocations */
        write16(f, DOS_HDR_SZ / 16);   /* Size of header in paragraphs */
        write16(f, 0);                 /* Minimum extra paragraphs needed */
        write16(f, 1);                 /* Maximum extra paragraphs needed */
        write16(f, 0);                 /* Initial (relative) SS value */
        write16(f, 0);                 /* Initial SP value */
        write16(f, 0);                 /* Checksum */
        write16(f, 0);                 /* Initial IP value */
        write16(f, 0);                 /* Initial (relative) CS value */
        write16(f, DOS_HDR_SZ);        /* File address of relocation table */
        write16(f, 0);                 /* Overlay number */
        write16(f, 0);                 /* Reserved0 */
        write16(f, 0);                 /* Reserved1 */
        write16(f, 0);                 /* Reserved2 */
        write16(f, 0);                 /* Reserved3 */
        write16(f, 0);                 /* OEM id */
        write16(f, 0);                 /* OEM info */
        for (i = 0; i < 10; i++)
                write16(f, 0);         /* Reserved (10 words) */
        write32(f, pe_offset);         /* File offset of PE header. */

        /* DOS Program. */
        for (i = 0; i < sizeof(dos_program); i++) {
                write8(f, dos_program[i]);
        }

        /* PE signature. */
        seek(f, pe_offset);
        write8(f, 'P');
        write8(f, 'E');
        write8(f, 0);
        write8(f, 0);

        uint32_t num_sections = 4;

        /* COFF header. */
        write16(f, 0x14c); /* Machine: IMAGE_FILE_MACHINE_I386 */
        write16(f, num_sections); /* NumberOfSections */
        write32(f, 0); /* TimeDateStamp */
        write32(f, 0); /* PointerToSymbolTable */
        write32(f, 0); /* NumberOfSymbols */
        write16(f, OPT_HDR_SZ); /* SizeOfOptionalHeader */
        write16(f, 0x103); /* Characteristics: no relocations, exec, 32-bit */


        uint32_t headers_sz = pe_offset + PE_HDR_SZ + num_sections * SEC_HDR_SZ;

        uint32_t text_rva = align_to(headers_sz, SEC_ALIGN);
        uint32_t text_offset = align_to(headers_sz, FILE_ALIGN);
        uint32_t text_sz = sizeof(rot13_program);

        uint32_t rdata_rva = align_to(text_rva + text_sz, SEC_ALIGN);
        uint32_t rdata_offset = align_to(text_offset + text_sz, FILE_ALIGN);
        uint32_t rdata_sz = sizeof(rot13_table);

        uint32_t idata_rva = align_to(rdata_rva + rdata_sz, SEC_ALIGN);
        uint32_t idata_offset = align_to(rdata_offset + rdata_sz, FILE_ALIGN);

        uint32_t num_imports = 4;
        uint32_t iat_rva = idata_rva;
        uint32_t iat_sz = (num_imports + 1) * IAT_ENTRY_SZ;
        uint32_t import_dir_table_rva = iat_rva + iat_sz;
        uint32_t import_dir_table_sz = 2 * IMPORT_DIR_ENTRY_SZ;
        uint32_t import_lookup_table_rva = import_dir_table_rva +
                                           import_dir_table_sz;
        uint32_t name_table_rva = import_lookup_table_rva +
                        (num_imports + 1) * IMPORT_LOOKUP_TBL_ENTRY_SZ;
        uint32_t dll_name_rva = name_table_rva +
                                num_imports * NAME_TABLE_ENTRY_SZ;
        uint32_t name_table_sz = num_imports * NAME_TABLE_ENTRY_SZ + 16;
        uint32_t idata_sz = name_table_rva + name_table_sz - idata_rva;

        uint32_t bss_rva = align_to(idata_rva + idata_sz, SEC_ALIGN);
        uint32_t bss_sz = 4096;

        /* Optional header, part 1: standard fields */
        write16(f, 0x10b); /* Magic: PE32 */
        write8(f, 0); /* MajorLinkerVersion */
        write8(f, 0); /* MinorLinkerVersion */
        write32(f, text_sz); /* SizeOfCode */
        write32(f, rdata_sz + idata_sz); /* SizeOfInitializedData */
        write32(f, bss_sz); /* SizeOfUninitializedData */
        write32(f, text_rva); /* AddressOfEntryPoint */
        write32(f, text_rva); /* BaseOfCode */
        write32(f, rdata_rva); /* BaseOfData */

        /* Optional header, part 2: Windows-specific fields */
        write32(f, IMAGE_BASE); /* ImageBase */
        write32(f, SEC_ALIGN); /* SectionAlignment */
        write32(f, FILE_ALIGN); /* FileAlignment */
        write16(f, 3); /* MajorOperatingSystemVersion */
        write16(f, 10); /* MinorOperatingSystemVersion*/
        write16(f, 0); /* MajorImageVersion */
        write16(f, 0); /* MinorImageVersion */
        write16(f, 3); /* MajorSubsystemVersion */
        write16(f, 10); /* MinorSubsystemVersion */
        write32(f, 0); /* Win32VersionValue*/
        write32(f, align_to(bss_rva + bss_sz, SEC_ALIGN)); /* SizeOfImage */
        write32(f, align_to(headers_sz, FILE_ALIGN)); /* SizeOfHeaders */
        write32(f, 0); /* Checksum */
        write16(f, 3); /* Subsystem: Console */
        write16(f, 0); /* DllCharacteristics */
        write32(f, 0x100000); /* SizeOfStackReserve */
        write32(f, 0x1000); /* SizeOfStackCommit */
        write32(f, 0x100000); /* SizeOfHeapReserve */
        write32(f, 0x1000); /* SizeOfHeapCommit */
        write32(f, 0); /* LoadFlags */
        write32(f, 16); /* NumberOfRvaAndSizes */

        /* Optional header, part 3: data directories. */
        write32(f, 0); write32(f, 0); /* Export Table. */
        write32(f, import_dir_table_rva); /* Import Table. */
        write32(f, import_dir_table_sz);
        write32(f, 0); write32(f, 0); /* Resource Table. */
        write32(f, 0); write32(f, 0); /* Exception Table. */
        write32(f, 0); write32(f, 0); /* Certificate Table. */
        write32(f, 0); write32(f, 0); /* Base Relocation Table. */
        write32(f, 0); write32(f, 0); /* Debug. */
        write32(f, 0); write32(f, 0); /* Architecture. */
        write32(f, 0); write32(f, 0); /* Global Ptr. */
        write32(f, 0); write32(f, 0); /* TLS Table. */
        write32(f, 0); write32(f, 0); /* Load Config Table. */
        write32(f, 0); write32(f, 0); /* Bound Import. */
        write32(f, iat_rva); /* Import Address Table. */
        write32(f, iat_sz);
        write32(f, 0); write32(f, 0); /* Delay Import Descriptor. */
        write32(f, 0); write32(f, 0); /* CLR Runtime Header. */
        write32(f, 0); write32(f, 0); /* (Reserved). */

        /* Section header #1: .text */
        writestr8(f, ".text"); /* Name */
        write32(f, text_sz); /* VirtualSize */
        write32(f, text_rva); /* VirtualAddress */
        write32(f, align_to(text_sz, FILE_ALIGN)); /* SizeOfRawData */
        write32(f, text_offset); /* PointerToRawData */
        write32(f, 0); /* PointerToRelocations */
        write32(f, 0); /* PointerToLinenumbers */
        write16(f, 0); /* NumberOfRelocations */
        write16(f, 0); /* NumberOfLinenumbers */
        write32(f, 0x60000020); /* Characteristics: code, read, execute */

        /* Section header #2: .rdata */
        writestr8(f, ".rdata"); /* Name */
        write32(f, rdata_sz); /* VirtualSize */
        write32(f, rdata_rva); /* VirtualAddress */
        write32(f, align_to(rdata_sz, FILE_ALIGN)); /* SizeOfRawData */
        write32(f, rdata_offset); /* PointerToRawData */
        write32(f, 0); /* PointerToRelocations */
        write32(f, 0); /* PointerToLinenumbers */
        write16(f, 0); /* NumberOfRelocations */
        write16(f, 0); /* NumberOfLinenumbers */
        write32(f, 0x40000040); /* Characteristics: data, read */

        /* Section header #3: .idata */
        writestr8(f, ".idata"); /* Name */
        write32(f, idata_sz); /* VirtualSize */
        write32(f, idata_rva); /* VirtualAddress */
        write32(f, align_to(idata_sz, FILE_ALIGN)); /* SizeOfRawData */
        write32(f, idata_offset); /* PointerToRawData */
        write32(f, 0); /* PointerToRelocations */
        write32(f, 0); /* PointerToLinenumbers */
        write16(f, 0); /* NumberOfRelocations */
        write16(f, 0); /* NumberOfLinenumbers */
        write32(f, 0xc0000040); /* Characteristics: data, read, write */

        /* Section header #4: .bss */
        writestr8(f, ".bss"); /* Name */
        write32(f, bss_sz); /* VirtualSize */
        write32(f, bss_rva); /* VirtualAddress */
        write32(f, 0); /* SizeOfRawData */
        write32(f, 0); /* PointerToRawData */
        write32(f, 0); /* PointerToRelocations */
        write32(f, 0); /* PointerToLinenumbers */
        write16(f, 0); /* NumberOfRelocations */
        write16(f, 0); /* NumberOfLinenumbers */
        write32(f, 0xc0000080); /* Characteristics: uninit. data, read, write */

        /* Write .text segment. */
        seek(f, text_offset);
        for (i = 0; i < sizeof(rot13_program); i++) {
                write8(f, rot13_program[i]);
        }

        /* Write .rdata segment. */
        seek(f, rdata_offset);
        for (i = 0; i < sizeof(rot13_table); i++) {
                write8(f, rot13_table[i]);
        }
        
        /* Write .idata segment. */
        seek(f, idata_offset);

        /* Import Address Table (IAT):
           (Same as the Import Lookup Table) */
        write32(f, name_table_rva + 0 * NAME_TABLE_ENTRY_SZ);
        write32(f, name_table_rva + 1 * NAME_TABLE_ENTRY_SZ);
        write32(f, name_table_rva + 2 * NAME_TABLE_ENTRY_SZ);
        write32(f, name_table_rva + 3 * NAME_TABLE_ENTRY_SZ);
        write32(f, 0);

        /* Import Directory Table */
        /* kernel32.dll */
        write32(f, import_lookup_table_rva); /* ILT RVA */
        write32(f, 0); /* Time/Data Stamp */
        write32(f, 0); /* Forwarder Chain */
        write32(f, dll_name_rva); /* Name RVA */
        write32(f, iat_rva); /* Import Address Table RVA */
        /* Null terminator */
        write32(f, 0);
        write32(f, 0);
        write32(f, 0);
        write32(f, 0);
        write32(f, 0);

        /* Import Lookup Table */
        write32(f, name_table_rva + 0 * NAME_TABLE_ENTRY_SZ); /* GetStdHandle */
        write32(f, name_table_rva + 1 * NAME_TABLE_ENTRY_SZ); /* ReadFile */
        write32(f, name_table_rva + 2 * NAME_TABLE_ENTRY_SZ); /* WriteFile */
        write32(f, name_table_rva + 3 * NAME_TABLE_ENTRY_SZ); /* ExitProcess */
        write32(f, 0); /* Null terminator */

        /* Hint/Name Table */
        write16(f, 0); /* Hint */
        writestr16(f, "GetStdHandle"); /* Name */
        write16(f, 0); /* Hint */
        writestr16(f, "ReadFile"); /* Name */
        write16(f, 0); /* Hint */
        writestr16(f, "WriteFile"); /* Name */
        write16(f, 0); /* Hint */
        writestr16(f, "ExitProcess"); /* Name */
        /* Put the dll name here too; we've got to write it somewhere. */
        writestr16(f, "kernel32.dll");

 
        if (fclose(f) == EOF) {
                perror("fclose");
                exit(1);
        }

        return 0;
}
