ARTICLE AD BOX
Dear Fellow Developers,
I am interested in learning how the Portable Executable format works. So far, I have written files in the com-format, which are executable in DosBox. However, I would like to generate code executable directly in Power Shell or Command Prompt.
I have looked into Patrick Webster’s (NoxWizard's) code below for generating an exe-file for Windows (https://gist.github.com/Noxwizard/17d823a49729bfe63b0643136591c98f). The file is made up by the headers and the text, rdata, and data sections. The program works when executed in Windows.
To understand how the sections work I have written the hex code (line 136-142) for a very simple program that uses Dos interrupt calls to print the letter 'X' and exit the execution. It works as a com-file (new.com) in DosBox (line 144-149). However, I have also inserted the program at the beginning of the text section of the exe-file (line 155). But when I execute the resulting exe-file (new.exe), nothing happens. The program just ends without writing anything.
Obviously, there is something I have missed. I wonder if someone can explain how it works, or point me some suitable information.
1: #include <Windows.h> 2: #include <iostream> 3: 4: int main(int argc, char** argv) 5: { 6: IMAGE_DOS_HEADER dos; 7: memset(&dos, 0, sizeof(IMAGE_DOS_HEADER)); 8: // Only these two fields are actually parsed 9: dos.e_magic = 0x5A4D; // "MZ" 10: dos.e_lfanew = sizeof(IMAGE_DOS_HEADER); // File address of new exe header (NT) 11: 12: IMAGE_NT_HEADERS32 nt_header; 13: nt_header.Signature = 0x4550; // "PE" 14: 15: nt_header.FileHeader.Machine = IMAGE_FILE_MACHINE_I386; 16: nt_header.FileHeader.NumberOfSections = 3; // text, data, rdata 17: nt_header.FileHeader.TimeDateStamp = 0x1234; 18: nt_header.FileHeader.PointerToSymbolTable = 0; 19: nt_header.FileHeader.NumberOfSymbols = 0; 20: nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32); 21: nt_header.FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE; 22: 23: nt_header.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC; 24: nt_header.OptionalHeader.MajorLinkerVersion = 0; 25: nt_header.OptionalHeader.MinorLinkerVersion = 0; 26: nt_header.OptionalHeader.SizeOfCode = 66560; // Size of .text section 27: nt_header.OptionalHeader.SizeOfInitializedData = 93696; // Size of .text + data sections 28: nt_header.OptionalHeader.SizeOfUninitializedData = 0; 29: nt_header.OptionalHeader.AddressOfEntryPoint = 0x1CBD; 30: nt_header.OptionalHeader.BaseOfCode = 0x1000; //FIX if VA of .text is not 0x1000 31: nt_header.OptionalHeader.BaseOfData = 0x012000; // Start of .rdata section (RVA) 32: nt_header.OptionalHeader.ImageBase = 0x00400000; 33: nt_header.OptionalHeader.SectionAlignment = 0x1000; 34: nt_header.OptionalHeader.FileAlignment = 0x200; 35: nt_header.OptionalHeader.MajorOperatingSystemVersion = 6; 36: nt_header.OptionalHeader.MinorOperatingSystemVersion = 0; 37: nt_header.OptionalHeader.MajorImageVersion = 0; 38: nt_header.OptionalHeader.MinorImageVersion = 0; 39: nt_header.OptionalHeader.MajorSubsystemVersion = 6; 40: nt_header.OptionalHeader.MinorSubsystemVersion = 0; 41: nt_header.OptionalHeader.Win32VersionValue = 0; 42: nt_header.OptionalHeader.SizeOfImage = 0x27000; 43: nt_header.OptionalHeader.SizeOfHeaders = 0x400; 44: nt_header.OptionalHeader.CheckSum = 0; 45: nt_header.OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; 46: nt_header.OptionalHeader.DllCharacteristics = 0; // IMAGE_DLLCHARACTERISTICS_NO_SEH; // | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | IMAGE_DLLCHARACTERISTICS_NX_COMPAT; 47: nt_header.OptionalHeader.SizeOfStackReserve = 0x100000; 48: nt_header.OptionalHeader.SizeOfStackCommit = 0x1000; 49: nt_header.OptionalHeader.SizeOfHeapReserve = 0x100000; 50: nt_header.OptionalHeader.SizeOfHeapCommit = 0x1000; 51: nt_header.OptionalHeader.LoaderFlags = 0; 52: nt_header.OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; 53: 54: memset(nt_header.OptionalHeader.DataDirectory, 0, sizeof(IMAGE_DATA_DIRECTORY) * IMAGE_NUMBEROF_DIRECTORY_ENTRIES); 55: 56: // Import directory 57: nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0x017004; 58: nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = 0x28; 59: 60: // Import Address Table (IAT) 61: nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0x012000; 62: nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0x144; 63: 64: IMAGE_SECTION_HEADER text_section; 65: memset(&text_section, 0, sizeof(IMAGE_SECTION_HEADER)); 66: const char* text_name = ".text"; 67: memcpy(text_section.Name, text_name, 5); 68: text_section.VirtualAddress = 0x1000; 69: text_section.SizeOfRawData = 0x10400; 70: text_section.Misc.VirtualSize = text_section.SizeOfRawData; 71: text_section.PointerToRawData = 0x400; 72: text_section.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ; 73: 74: IMAGE_SECTION_HEADER rdata_section; 75: memset(&rdata_section, 0, sizeof(IMAGE_SECTION_HEADER)); 76: const char* rdata_name = ".rdata"; 77: memcpy(rdata_section.Name, rdata_name, 6); 78: rdata_section.VirtualAddress = 0x012000; 79: rdata_section.SizeOfRawData = 0x5800; 80: rdata_section.Misc.VirtualSize = rdata_section.SizeOfRawData; 81: rdata_section.PointerToRawData = 0x10800; 82: rdata_section.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; 83: 84: IMAGE_SECTION_HEADER data_section; 85: memset(&data_section, 0, sizeof(IMAGE_SECTION_HEADER)); 86: const char* data_name = ".data"; 87: memcpy(data_section.Name, data_name, 5); 88: data_section.VirtualAddress = 0x18000; 89: data_section.SizeOfRawData = 0x1200; 90: data_section.Misc.VirtualSize = data_section.SizeOfRawData + 0x1E00; // Add a pseudo BSS section at the end of this segment 91: data_section.PointerToRawData = 0x16000; 92: data_section.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; 93: 94: // DOS header 95: // DOS Stub (optional) 96: // NT header 97: // Optional header (not optional) 98: // Section table 99: // .text 100: // .rdata 101: // .data 102: // Binary data 103: // .text @ 0x400 - size: 0x10400 - VA: 0x401000-4113FF 104: // .rdata @ 0x10800 - size: 0x5800 - VA: 0x412000-4177FF 105: // .data @ 0x16000 - size: 0x1200 - VA: 0x418000-4191FF 106: 107: size_t header_size = sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) * 3; 108: size_t padding_size = 0x400 - header_size; 109: size_t total_size = header_size + padding_size + text_section.SizeOfRawData + data_section.SizeOfRawData + 110: rdata_section.SizeOfRawData; 111: std::cout << total_size << std::endl; 112: 113: unsigned char* buf = (unsigned char*) VirtualAlloc(NULL, total_size, MEM_COMMIT, PAGE_READWRITE); 114: if (buf) { 115: unsigned char* start = buf; 116: 117: // Headers 118: memcpy(buf, &dos, sizeof(IMAGE_DOS_HEADER)); 119: buf += sizeof(IMAGE_DOS_HEADER); 120: 121: memcpy(buf, &nt_header, sizeof(IMAGE_NT_HEADERS32)); 122: buf += sizeof(IMAGE_NT_HEADERS32); 123: 124: memcpy(buf, &text_section, sizeof(IMAGE_SECTION_HEADER)); 125: buf += sizeof(IMAGE_SECTION_HEADER); 126: 127: memcpy(buf, &rdata_section, sizeof(IMAGE_SECTION_HEADER)); 128: buf += sizeof(IMAGE_SECTION_HEADER); 129: 130: memcpy(buf, &data_section, sizeof(IMAGE_SECTION_HEADER)); 131: buf += sizeof(IMAGE_SECTION_HEADER); 132: 133: // Padding 134: buf += padding_size; 135: 136: unsigned char print_x_and_exit[] = 137: { 0xB4, 0x02, // mov ah, 0x02 138: 0xB2, 0x58, // mov dl, 'X' 139: 0xCD, 0x21, // int 0x21 ; print 'X' 140: 0xB4, 0x4C, // mov ah, 0x4C 141: 0xB0, 0x00, // mov al, 0x00 142: 0xCD, 0x21 }; // int 0x21 ; exit with 0 143: 144: FILE* fp; 145: fopen_s(&fp, "new.com", "wb"); 146: if (fp) { 147: fwrite(print_x_and_exit, 1, sizeof(print_x_and_exit), fp); 148: fclose(fp); 149: } 150: 151: // Binary data 152: fopen_s(&fp, ".text", "rb"); 153: if (fp) { 154: fread(buf, 1, text_section.SizeOfRawData, fp); 155: memcpy(buf, print_x_and_exit, sizeof(print_x_and_exit)); 156: buf += text_section.SizeOfRawData; 157: fclose(fp); 158: } 159: 160: fopen_s(&fp, ".rdata", "rb"); 161: if (fp) { 162: fread(buf, 1, rdata_section.SizeOfRawData, fp); 163: buf += rdata_section.SizeOfRawData; 164: fclose(fp); 165: } 166: 167: fopen_s(&fp, ".data", "rb"); 168: if (fp) { 169: fread(buf, 1, data_section.SizeOfRawData, fp); 170: buf += data_section.SizeOfRawData; 171: fclose(fp); 172: } 173: 174: fopen_s(&fp, "new.exe", "w"); 175: if (fp) { 176: fwrite(start, 1, total_size, fp); 177: fclose(fp); 178: } 179: 180: VirtualFree(buf, 0, MEM_RELEASE); 181: } 182: 183: return 0; 184: }