Created
July 10, 2012 14:29
-
-
Save shinaisan/3083633 to your computer and use it in GitHub Desktop.
Example generation of a PE file targeted for Windows CE/MIPS. (getchar() -> MessageBoxW() -> putchar())
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Example generation of a PE file targeted for Windows CE/MIPS. (getchar() -> MessageBoxW() -> putchar()) */ | |
/* To build w/ MSVC: cl pegen.c */ | |
/* To generate a PE: pegen <arbitrary_number> <output_file_name.exe> */ | |
#include <windows.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
typedef uint32_t inst_t; | |
/* Configuration start. */ | |
static char text[512] = {0}; | |
static char idata[512] = {0}; | |
inst_t addr_image = 0x00010000; | |
inst_t addr_text_base = 0x1000; | |
inst_t addr_idata_base = 0x2000; | |
inst_t addr_data_base = 0x3000; | |
/* Configuration end. */ | |
/* These are determined at run time. */ | |
static int idata_size = 0; | |
inst_t addr_MessageBoxW = 0; | |
inst_t addr_wsprintfW = 0; | |
inst_t addr_getchar = 0; | |
inst_t addr_putchar = 0; | |
inst_t addr_buffer = 0; | |
inst_t addr_format = 0; | |
inst_t addr_caption = 0; | |
/* A total of 32 registers. */ | |
typedef enum { | |
REG_ZERO = 0, | |
REG_AT, | |
REG_V0, | |
REG_V1, | |
REG_A0, | |
REG_A1, | |
REG_A2, | |
REG_A3, | |
REG_T0, | |
REG_T1, | |
REG_T2, | |
REG_T3, | |
REG_T4, | |
REG_T5, | |
REG_T6, | |
REG_T7, | |
REG_S0, | |
REG_S1, | |
REG_S2, | |
REG_S3, | |
REG_S4, | |
REG_S5, | |
REG_S6, | |
REG_S7, | |
REG_T8, | |
REG_T9, | |
REG_K0, | |
REG_K1, | |
REG_GP, | |
REG_SP, | |
REG_FP, | |
REG_RA, | |
} mips_reg_t; | |
#define MIPS_INST_NOP (0x00000000) | |
#define MIPS_INST_ADDIU(reg0, reg1, imm) (0x24000000 | (reg0 << 16) | (reg1 << 21) | (imm & 0xffff)) | |
#define MIPS_INST_SB(reg0, reg1, disp) (0xa0000000 | (reg0 << 16) | (reg1 << 21) | (disp & 0xffff)) | |
#define MIPS_INST_SW(reg0, reg1, disp) (0xac000000 | (reg0 << 16) | (reg1 << 21) | (disp & 0xffff)) | |
#define MIPS_INST_LB(reg0, reg1, disp) (0x80000000 | (reg0 << 16) | (reg1 << 21) | (disp & 0xffff)) | |
#define MIPS_INST_LW(reg0, reg1, disp) (0x8c000000 | (reg0 << 16) | (reg1 << 21) | (disp & 0xffff)) | |
#define MIPS_INST_ADD(reg0, reg1, reg2) (0x00000020 | (reg0 << 11) | (reg1 << 21) | (reg2 << 16)) | |
#define MIPS_INST_AND(reg0, reg1, reg2) (0x00000024 | (reg0 << 11) | (reg1 << 21) | (reg2 << 16)) | |
#define MIPS_INST_ADDU(reg0, reg1, reg2) (0x00000021 | (reg0 << 11) | (reg1 << 21) | (reg2 << 16)) | |
#define MIPS_INST_ADDI(reg0, reg1, imm) (0x20000000 | (reg0 << 16) | (reg1 << 21) | (imm & 0xffff)) | |
#define MIPS_INST_ANDI(reg0, reg1, imm) (0x30000000 | (reg0 << 16) | (reg1 << 21) | (imm & 0xffff)) | |
#define MIPS_INST_SRL(reg0, reg1, sa) (0x00000002 | (reg0 << 11) | (reg1 << 16) | (sa << 6)) | |
#define MIPS_INST_MOVE(reg0, reg1) MIPS_INST_ADDU(reg0, REG_ZERO, reg1) | |
#define MIPS_INST_LUI(reg0, imm) (0x3c000000 | (reg0 << 16) | (imm & 0xffff)) | |
#define MIPS_INST_ORI(reg0, reg1, imm) (0x34000000 | (reg0 << 16) | (reg1 << 21) | (imm & 0xffff)) | |
#define MIPS_INST_LI(reg0, imm) MIPS_INST_ORI(reg0, REG_ZERO, imm) | |
#define MIPS_INST_JR(reg0) (0x00000008 | (reg0 << 21)) | |
#define MIPS_INST_JALR(ret, dest) (0x00000009 | (ret << 11) | (dest << 21)) | |
#define MIPS_INST_BEQ(reg0, reg1, dest) (0x10000000 | (reg0 << 21) | (reg1 << 16) | (dest & 0xffff)) | |
#define MIPS_INST_BEQZ(reg0, dest) MIPS_INST_BEQ(reg0, REG_ZERO, dest) | |
#define MIPS_INST_B(dest) MIPS_INST_BEQ(REG_ZERO, REG_ZERO, dest) | |
#define MIPS_INST_BREAK (0x0000000d) | |
int make_text(inst_t *dest, int inst_max) { | |
int idx = 0; | |
if (inst_max < 32) { | |
/* The lower limit of 32 is not carefully chosen but it's enough for holding the prolog and epilog. */ | |
printf("Output buffer is too short: %d, expected more than (or equal to) 32.\n", inst_max); | |
return (-1); | |
} | |
memset((void *)dest, 0, inst_max * sizeof(inst_t)); | |
/* addiu $sp, $sp, -24 */ | |
dest[idx++] = MIPS_INST_ADDIU(REG_SP, REG_SP, -24); | |
/* sw $ra, 16($sp) */ | |
dest[idx++] = MIPS_INST_SW(REG_RA, REG_SP, 16); | |
/* sw $s3, 12($sp) */ | |
dest[idx++] = MIPS_INST_SW(REG_S3, REG_SP, 12); | |
/* sw $s2, 8($sp) */ | |
dest[idx++] = MIPS_INST_SW(REG_S2, REG_SP, 8); | |
/* sw $s1, 4($sp) */ | |
dest[idx++] = MIPS_INST_SW(REG_S1, REG_SP, 4); | |
/* sw $s0, 0($sp) */ | |
dest[idx++] = MIPS_INST_SW(REG_S0, REG_SP, 0); | |
/* move $v0, $zero */ | |
dest[idx++] = MIPS_INST_MOVE(REG_V0, REG_ZERO); | |
/* lui $v0, (getchar >> 16) */ | |
dest[idx++] = MIPS_INST_LUI(REG_V0, (addr_getchar >> 16)); | |
/* ori $v0, $v0, (getchar & 0xffff) */ | |
dest[idx++] = MIPS_INST_ORI(REG_V0, REG_V0, (addr_getchar & 0xffff)); | |
/* lw $v0, 0($v0) */ | |
dest[idx++] = MIPS_INST_LW(REG_V0, REG_V0, 0); | |
/* jalr $v0 */ | |
dest[idx++] = MIPS_INST_JALR(REG_RA, REG_V0); | |
/* nop */ | |
dest[idx++] = MIPS_INST_NOP; | |
/* lui $a0, 0x0001 */ | |
dest[idx++] = MIPS_INST_LUI(REG_A0, (addr_buffer >> 16)); | |
/* ori $a0, $a0, 0x3020 */ | |
dest[idx++] = MIPS_INST_ORI(REG_A0, REG_A0, (addr_buffer & 0xffff)); /* buffer */ | |
/* lui $a1, 0x0001 */ | |
dest[idx++] = MIPS_INST_LUI(REG_A1, (addr_format >> 16)); | |
/* ori $a1, $a1, 0x3010 */ | |
dest[idx++] = MIPS_INST_ORI(REG_A1, REG_A1, (addr_format & 0xffff)); /* %d */ | |
/* move $a2, $v0(getchar) */ | |
dest[idx++] = MIPS_INST_MOVE(REG_A2, REG_V0); | |
/* move $v0, $zero */ | |
dest[idx++] = MIPS_INST_MOVE(REG_V0, REG_ZERO); | |
/* lui $v0, (wsprintfW >> 16) */ | |
dest[idx++] = MIPS_INST_LUI(REG_V0, (addr_wsprintfW >> 16)); | |
/* ori $v0, $v0, (wsprintfW & 0xffff) */ | |
dest[idx++] = MIPS_INST_ORI(REG_V0, REG_V0, (addr_wsprintfW & 0xffff)); | |
/* lw $v0, 0($v0) */ | |
dest[idx++] = MIPS_INST_LW(REG_V0, REG_V0, 0); | |
/* jalr $v0 */ | |
dest[idx++] = MIPS_INST_JALR(REG_RA, REG_V0); | |
/* nop */ | |
dest[idx++] = MIPS_INST_NOP; | |
/* move $a0, $zero */ | |
dest[idx++] = MIPS_INST_MOVE(REG_A0, REG_ZERO); | |
/* lui $a1, 0x0001 */ | |
dest[idx++] = MIPS_INST_LUI(REG_A1, (addr_buffer >> 16)); | |
/* ori $a1, $a1, 0x3020 */ | |
dest[idx++] = MIPS_INST_ORI(REG_A1, REG_A1, (addr_buffer & 0xffff)); /* buffer */ | |
/* lui $a2, 0x0001 */ | |
dest[idx++] = MIPS_INST_LUI(REG_A2, (addr_caption >> 16)); | |
/* ori $a2, $a2, 0x3000 */ | |
dest[idx++] = MIPS_INST_ORI(REG_A2, REG_A2, (addr_caption & 0xffff)); /* Message */ | |
/* move $a3, $zero */ | |
dest[idx++] = MIPS_INST_MOVE(REG_A3, REG_ZERO); | |
/* move $v0, $zero */ | |
dest[idx++] = MIPS_INST_MOVE(REG_V0, REG_ZERO); | |
/* lui $v0, (MessageBoxW >> 16) */ | |
dest[idx++] = MIPS_INST_LUI(REG_V0, (addr_MessageBoxW >> 16)); | |
/* ori $v0, $v0, (MessageBoxW & 0xffff) */ | |
dest[idx++] = MIPS_INST_ORI(REG_V0, REG_V0, (addr_MessageBoxW & 0xffff)); | |
/* lw $v0, 0($v0) */ | |
dest[idx++] = MIPS_INST_LW(REG_V0, REG_V0, 0); | |
/* jalr $v0 */ | |
dest[idx++] = MIPS_INST_JALR(REG_RA, REG_V0); | |
/* nop */ | |
dest[idx++] = MIPS_INST_NOP; | |
/* li $a0, 0x21(!) */ | |
dest[idx++] = MIPS_INST_LI(REG_A0, 0x21); | |
/* move $v0, $zero */ | |
dest[idx++] = MIPS_INST_MOVE(REG_V0, REG_ZERO); | |
/* lui $v0, (putchar >> 16) */ | |
dest[idx++] = MIPS_INST_LUI(REG_V0, (addr_putchar >> 16)); | |
/* ori $v0, $v0, (putchar & 0xffff) */ | |
dest[idx++] = MIPS_INST_ORI(REG_V0, REG_V0, (addr_putchar & 0xffff)); | |
/* lw $v0, 0($v0) */ | |
dest[idx++] = MIPS_INST_LW(REG_V0, REG_V0, 0); | |
/* jalr $v0 */ | |
dest[idx++] = MIPS_INST_JALR(REG_RA, REG_V0); | |
/* nop */ | |
dest[idx++] = MIPS_INST_NOP; | |
/* lw $s0, 0($sp) */ | |
dest[idx++] = MIPS_INST_LW(REG_S0, REG_SP, 0); | |
/* lw $s1, 4($sp) */ | |
dest[idx++] = MIPS_INST_LW(REG_S1, REG_SP, 4); | |
/* lw $s2, 8($sp) */ | |
dest[idx++] = MIPS_INST_LW(REG_S2, REG_SP, 8); | |
/* lw $s3, 12($sp) */ | |
dest[idx++] = MIPS_INST_LW(REG_S3, REG_SP, 12); | |
/* lw $ra, 16($sp) */ | |
dest[idx++] = MIPS_INST_LW(REG_RA, REG_SP, 16); | |
/* jr $ra */ | |
dest[idx++] = MIPS_INST_JR(REG_RA); | |
/* addiu $sp, $sp, 24 */ | |
dest[idx++] = MIPS_INST_ADDIU(REG_SP, REG_SP, 24); | |
return (idx); | |
} | |
void write_padding(FILE *fp, int n) { | |
while (n-- > 0) { | |
fputc(0, fp); | |
} | |
} | |
void write_string(FILE *fp, int len, char *str) { | |
while ((*str) && (len-- > 0)) { | |
fputc(*str++, fp); | |
} | |
while (len-- > 0) { | |
fputc(0, fp); | |
} | |
} | |
void write_pe_header(FILE *fp) { | |
static unsigned char stub[] = { | |
/* 00-3b: DOS Header */ | |
'M', 'Z', 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x00, | |
0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
/* 3c-3f: Pointer to PE Header (=80) */ | |
0x80, 0x00, 0x00, 0x00, | |
/* 40-7f: DOS stub */ | |
0xba, 0x10, 0x00, 0x0e, 0x1f, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x90, 0x90, | |
'T', 'h', 'i', 's', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm', ' ', 'c', 'a', 'n', | |
'n', 'o', 't', ' ', 'b', 'e', ' ', 'r', 'u', 'n', ' ', 'i', 'n', ' ', 'D', 'O', | |
'S', ' ', 'm', 'o', 'd', 'e', '.', '\r', '\n', '$', 0, 0, 0, 0, 0, 0, | |
/* 80-83: PE Signature */ | |
'P', 'E', 0, 0 | |
}; | |
IMAGE_FILE_HEADER coff = { | |
0x0166, 3, 0, 0, 0, sizeof(IMAGE_OPTIONAL_HEADER32), 0x030f | |
}; | |
IMAGE_OPTIONAL_HEADER32 opt = { | |
0x010b, /* Magic */ | |
6, 0, /* MajorLinkerVersion, MinorLinkerVersion */ | |
sizeof(text), /* SizeOfCode */ | |
512, /* SizeOfInitializedData */ | |
0, /* SizeOfUninitializedData */ | |
addr_text_base, /* AddressOfEntryPoint */ | |
addr_text_base, /* BaseOfCode */ | |
addr_data_base, /* BaseOfData */ | |
addr_image, /* ImageBase */ | |
0x1000, /* SectionAlignment */ | |
0x0200, /* FileAlignment */ | |
4, 0, /* MajorOperatingSystemVersion, MinorOperatingSystemVersion */ | |
0, 0, /* MajorImageVersion, MinorImageVersion */ | |
2, 0, /* MajorSubsystemVersion, MinorSubsystemVersion */ | |
0, /* Win32VersionValue */ | |
0x00004000, /* SizeOfImage */ | |
0x200, /* SizeOfHeaders */ | |
0, /* CheckSum */ | |
9, /* Subsystem */ | |
0, /* DllCharacteristics */ | |
1024*1024, /* SizeOfStackReserve */ | |
8*1024, /* SizeOfStackCommit */ | |
1024*1024, /* SizeOfHeapReserve */ | |
4*1024, /* SizeOfHeapCommit */ | |
0, /* LoaderFlags */ | |
16 /* NumberOfRvaAndSizes */ | |
}; | |
IMAGE_SECTION_HEADER sects[3]; | |
fwrite(stub, sizeof(stub), 1, fp); | |
fwrite(&coff, sizeof(coff), 1, fp); | |
memset(opt.DataDirectory, 0, sizeof(opt.DataDirectory)); | |
opt.DataDirectory[1].VirtualAddress = addr_idata_base; /* import table */ | |
opt.DataDirectory[1].Size = idata_size; | |
fwrite(&opt, sizeof(opt), 1, fp); | |
memset(sects, 0, sizeof(sects)); | |
strcpy((char *)sects[0].Name, ".text"); | |
sects[0].Misc.VirtualSize = sizeof(text); | |
sects[0].VirtualAddress = addr_text_base; | |
sects[0].SizeOfRawData = (sizeof(text) + 511) / 512 * 512; | |
sects[0].PointerToRawData = 0x600; | |
sects[0].Characteristics = 0x60000020; | |
strcpy((char *)sects[1].Name, ".idata"); | |
sects[1].Misc.VirtualSize = idata_size; | |
sects[1].VirtualAddress = addr_idata_base; | |
sects[1].SizeOfRawData = 512; | |
sects[1].PointerToRawData = 0x200; | |
sects[1].Characteristics = 0xc0000040; | |
strcpy((char *)sects[2].Name, ".data"); | |
sects[2].Misc.VirtualSize = 512; | |
sects[2].VirtualAddress = addr_data_base; | |
sects[2].SizeOfRawData = 512; | |
sects[2].PointerToRawData = 0x400; | |
sects[2].Characteristics = 0xc0000040; | |
fwrite(sects, sizeof(sects), 1, fp); | |
write_padding(fp, 0x200 - ftell(fp)); | |
} | |
/* Should be called before write_pe_header(). */ | |
int make_idata(char *buf) { | |
int size = 0; | |
int cpysize = 0; | |
int idt[] = { | |
// IDT 1 | |
0x2028, 0, 0, 0x203c, 0x204c, | |
// IDT (Terminator) | |
0, 0, 0, 0, 0 | |
}; | |
int ilt_iat[] = {0x2060, 0x206e, 0x207a, 0x2084, 0}; | |
addr_MessageBoxW = addr_image + 0x204c; | |
addr_wsprintfW = addr_MessageBoxW + 4; | |
addr_getchar = addr_wsprintfW + 4; | |
addr_putchar = addr_getchar + 4; | |
// IDT | |
cpysize = sizeof(idt); | |
memcpy((void *)buf, (void *)&idt[0], cpysize); | |
buf += cpysize; | |
size += cpysize; | |
// ILT | |
cpysize = sizeof(ilt_iat); | |
memcpy((void *)buf, (void *)&ilt_iat[0], cpysize); | |
buf += cpysize; | |
size += cpysize; | |
// DLL | |
cpysize = 16; | |
memset(buf, 0, cpysize); | |
strcpy(buf, "coredll.dll"); | |
buf += cpysize; | |
size += cpysize; | |
// IAT | |
cpysize = sizeof(ilt_iat); | |
memcpy((void *)buf, (void *)&ilt_iat[0], cpysize); | |
buf += cpysize; | |
size += cpysize; | |
// MessageBoxW | |
cpysize = 14; | |
memset(buf, 0, cpysize); | |
buf += 2; | |
strcpy(buf, "MessageBoxW"); | |
buf += (cpysize - 2); | |
size += cpysize; | |
// wsprintfW | |
cpysize = 12; | |
memset(buf, 0, cpysize); | |
buf += 2; | |
strcpy(buf, "wsprintfW"); | |
buf += (cpysize - 2); | |
size += cpysize; | |
// getchar | |
cpysize = 10; | |
memset(buf, 0, cpysize); | |
buf += 2; | |
strcpy(buf, "getchar"); | |
buf += (cpysize - 2); | |
size += cpysize; | |
// putchar | |
cpysize = 10; | |
memset(buf, 0, cpysize); | |
buf += 2; | |
strcpy(buf, "putchar"); | |
buf += (cpysize - 2); | |
size += cpysize; | |
return (idata_size = size); | |
} | |
void write_data(FILE *fp) { | |
addr_caption = addr_image + addr_data_base; | |
addr_format = addr_caption + 16; | |
addr_buffer = addr_format + 16; | |
fwrite("M\0e\0s\0s\0a\0g\0e\0\0\0", 1, 16, fp); | |
fwrite("%\0d\0", 1, 4, fp); | |
write_padding(fp, 0x600 - ftell(fp)); | |
} | |
int write_pe(FILE *out) { | |
memset(idata, 0, sizeof(idata)); | |
make_idata(idata); | |
write_pe_header(out); | |
fwrite(idata, 1, sizeof(idata), out); | |
write_data(out); | |
make_text((inst_t *)(&text[0]), sizeof(text) / sizeof(inst_t)); | |
fwrite(text, 1, sizeof(text), out); | |
return (0); | |
} | |
int main(int argc, char *argv[]) { | |
char *exe_path = NULL; | |
FILE *exe_file = NULL; | |
int ret = 0; | |
if (argc < 2) { | |
printf("[USAGE] %s <output file name(exe)>\n", argv[0]); | |
return (-1); | |
} | |
exe_path = argv[1]; | |
exe_file = fopen(exe_path, "wb"); | |
if (!exe_file) { | |
printf("Cannot create file: %s\n", exe_path); | |
ret = -1; | |
goto main_end; | |
} | |
write_pe(exe_file); | |
main_end: | |
if (exe_file) { | |
fclose(exe_file); | |
} | |
return (ret); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment