"""Build tiny-x86_64.exe: a PE32+ console image that calls puts and exit through its import address table. Every field follows the Microsoft PE Format page: MS-DOS stub with e_lfanew, PE signature, COFF file header, optional header, data directories, section table, import directory, hint/name table, export directory, and a .reloc base relocation block. The file is not run on this machine. llvm-objdump reads it.""" from __future__ import annotations import struct from pathlib import Path HERE = Path(__file__).resolve().parent OUT = HERE / "tiny-x86_64.exe" IMAGE_BASE = 0x140000000 FILE_ALIGN = 0x200 SECTION_ALIGN = 0x1000 DLL = b"msvcrt.dll\0" IMPORTS = ["puts", "exit"] MESSAGE = b"linked by hand\0" EXPORT_NAME = b"tiny_entry\0" IMAGE_NAME = b"tiny-x86_64.exe\0" IMAGE_FILE_MACHINE_AMD64 = 0x8664 IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 PE32_PLUS = 0x20B SUBSYSTEM_CUI = 3 DLL_HIGH_ENTROPY_VA, DLL_DYNAMIC_BASE, DLL_NX_COMPAT, DLL_TERMINAL_SERVER_AWARE = 0x0020, 0x0040, 0x0100, 0x8000 SCN_CODE, SCN_INITIALIZED_DATA = 0x00000020, 0x00000040 SCN_MEM_DISCARDABLE, SCN_MEM_EXECUTE, SCN_MEM_READ, SCN_MEM_WRITE = 0x02000000, 0x20000000, 0x40000000, 0x80000000 IMAGE_REL_BASED_DIR64 = 10 DIR_EXPORT, DIR_IMPORT, DIR_BASERELOC, DIR_IAT = 0, 1, 5, 12 def rel32(target_rva: int, next_rva: int) -> bytes: return struct.pack(" bytes: return blob + b"\0" * (size - len(blob)) def build() -> bytes: text_rva, rdata_rva, data_rva, reloc_rva = 0x1000, 0x2000, 0x3000, 0x4000 text_off, rdata_off, data_off, reloc_off = 0x200, 0x400, 0x600, 0x800 descriptor_rva = rdata_rva ilt_rva = rdata_rva + 0x28 iat_rva = rdata_rva + 0x40 hint_rva = {IMPORTS[0]: rdata_rva + 0x58, IMPORTS[1]: rdata_rva + 0x60} dll_rva = rdata_rva + 0x68 export_dir_rva = rdata_rva + 0x80 eat_rva = export_dir_rva + 40 enpt_rva = eat_rva + 4 eot_rva = enpt_rva + 4 image_name_rva = eot_rva + 2 export_name_rva = image_name_rva + len(IMAGE_NAME) message_rva = export_name_rva + len(EXPORT_NAME) export_size = message_rva - export_dir_rva code = ( b"\x48\x83\xec\x28" + b"\x48\x8d\x0d" + rel32(message_rva, text_rva + 11) + b"\xff\x15" + rel32(iat_rva, text_rva + 17) + b"\x31\xc9" + b"\xff\x15" + rel32(iat_rva + 8, text_rva + 25) + b"\xcc" ) rdata = bytearray(0x200) struct.pack_into("