Skip to content

Instantly share code, notes, and snippets.

@Noxwizard
Created May 27, 2021 15:17
Show Gist options
  • Save Noxwizard/17d823a49729bfe63b0643136591c98f to your computer and use it in GitHub Desktop.
Save Noxwizard/17d823a49729bfe63b0643136591c98f to your computer and use it in GitHub Desktop.
Building PE files from scratch
#include <Windows.h>
#include <iostream>
int main(int argc, char** argv)
{
IMAGE_DOS_HEADER dos;
memset(&dos, 0, sizeof(IMAGE_DOS_HEADER));
// Only these two fields are actually parsed
dos.e_magic = 0x5A4D; // "MZ"
dos.e_lfanew = sizeof(IMAGE_DOS_HEADER); // File address of new exe header (NT)
IMAGE_NT_HEADERS32 nt_header;
nt_header.Signature = 0x4550; // "PE"
nt_header.FileHeader.Machine = IMAGE_FILE_MACHINE_I386;
nt_header.FileHeader.NumberOfSections = 3; // text, data, rdata
nt_header.FileHeader.TimeDateStamp = 0x1234;
nt_header.FileHeader.PointerToSymbolTable = 0;
nt_header.FileHeader.NumberOfSymbols = 0;
nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
nt_header.FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE;
nt_header.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC;
nt_header.OptionalHeader.MajorLinkerVersion = 0;
nt_header.OptionalHeader.MinorLinkerVersion = 0;
nt_header.OptionalHeader.SizeOfCode = 66560; // Size of .text section
nt_header.OptionalHeader.SizeOfInitializedData = 93696; // Size of .text + data sections
nt_header.OptionalHeader.SizeOfUninitializedData = 0;
nt_header.OptionalHeader.AddressOfEntryPoint = 0x1CBD;
nt_header.OptionalHeader.BaseOfCode = 0x1000; //FIX if VA of .text is not 0x1000
nt_header.OptionalHeader.BaseOfData = 0x012000; // Start of .rdata section (RVA)
nt_header.OptionalHeader.ImageBase = 0x00400000;
nt_header.OptionalHeader.SectionAlignment = 0x1000;
nt_header.OptionalHeader.FileAlignment = 0x200;
nt_header.OptionalHeader.MajorOperatingSystemVersion = 6;
nt_header.OptionalHeader.MinorOperatingSystemVersion = 0;
nt_header.OptionalHeader.MajorImageVersion = 0;
nt_header.OptionalHeader.MinorImageVersion = 0;
nt_header.OptionalHeader.MajorSubsystemVersion = 6;
nt_header.OptionalHeader.MinorSubsystemVersion = 0;
nt_header.OptionalHeader.Win32VersionValue = 0;
nt_header.OptionalHeader.SizeOfImage = 0x27000;
nt_header.OptionalHeader.SizeOfHeaders = 0x400;
nt_header.OptionalHeader.CheckSum = 0;
nt_header.OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
nt_header.OptionalHeader.DllCharacteristics = 0; // IMAGE_DLLCHARACTERISTICS_NO_SEH; // | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | IMAGE_DLLCHARACTERISTICS_NX_COMPAT;
nt_header.OptionalHeader.SizeOfStackReserve = 0x100000;
nt_header.OptionalHeader.SizeOfStackCommit = 0x1000;
nt_header.OptionalHeader.SizeOfHeapReserve = 0x100000;
nt_header.OptionalHeader.SizeOfHeapCommit = 0x1000;
nt_header.OptionalHeader.LoaderFlags = 0;
nt_header.OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES;
memset(nt_header.OptionalHeader.DataDirectory, 0, sizeof(IMAGE_DATA_DIRECTORY) * IMAGE_NUMBEROF_DIRECTORY_ENTRIES);
// Import directory
nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0x017004;
nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = 0x28;
// Import Address Table (IAT)
nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0x012000;
nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0x144;
IMAGE_EXPORT_DIRECTORY d;
IMAGE_SECTION_HEADER text_section;
memset(&text_section, 0, sizeof(IMAGE_SECTION_HEADER));
const char* text_name = ".text";
memcpy(text_section.Name, text_name, 5);
text_section.VirtualAddress = 0x1000;
text_section.SizeOfRawData = 0x10400;
text_section.Misc.VirtualSize = text_section.SizeOfRawData;
text_section.PointerToRawData = 0x400;
text_section.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
IMAGE_SECTION_HEADER rdata_section;
memset(&rdata_section, 0, sizeof(IMAGE_SECTION_HEADER));
const char* rdata_name = ".rdata";
memcpy(rdata_section.Name, rdata_name, 6);
rdata_section.VirtualAddress = 0x012000;
rdata_section.SizeOfRawData = 0x5800;
rdata_section.Misc.VirtualSize = rdata_section.SizeOfRawData;
rdata_section.PointerToRawData = 0x10800;
rdata_section.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
IMAGE_SECTION_HEADER data_section;
memset(&data_section, 0, sizeof(IMAGE_SECTION_HEADER));
const char* data_name = ".data";
memcpy(data_section.Name, data_name, 5);
data_section.VirtualAddress = 0x18000;
data_section.SizeOfRawData = 0x1200;
data_section.Misc.VirtualSize = data_section.SizeOfRawData+0x1E00; // Add a pseudo BSS section at the end of this segment
data_section.PointerToRawData = 0x16000;
data_section.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
// DOS header
// DOS Stub (optional)
// NT header
// Optional header (not optional)
// Section table
// .text
// .rdata
// .data
// Binary data
// .text @ 0x400 - size: 0x10400 - VA: 0x401000-4113FF
// .rdata @ 0x10800 - size: 0x5800 - VA: 0x412000-4177FF
// .data @ 0x16000 - size: 0x1200 - VA: 0x418000-4191FF
size_t header_size = sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) * 3;
size_t padding_size = 0x400 - header_size;
size_t total_size = header_size + padding_size + text_section.SizeOfRawData + data_section.SizeOfRawData + rdata_section.SizeOfRawData;
std::cout << total_size << std::endl;
unsigned char* buf = (unsigned char*)VirtualAlloc(NULL, total_size, MEM_COMMIT, PAGE_READWRITE);
unsigned char* start = buf;
// Headers
memcpy(buf, &dos, sizeof(IMAGE_DOS_HEADER));
buf += sizeof(IMAGE_DOS_HEADER);
memcpy(buf, &nt_header, sizeof(IMAGE_NT_HEADERS32));
buf += sizeof(IMAGE_NT_HEADERS32);
memcpy(buf, &text_section, sizeof(IMAGE_SECTION_HEADER));
buf += sizeof(IMAGE_SECTION_HEADER);
memcpy(buf, &rdata_section, sizeof(IMAGE_SECTION_HEADER));
buf += sizeof(IMAGE_SECTION_HEADER);
memcpy(buf, &data_section, sizeof(IMAGE_SECTION_HEADER));
buf += sizeof(IMAGE_SECTION_HEADER);
// Padding
buf += padding_size;
// Binary data
FILE* fp = fopen("C:\\Users\\User\\Desktop\\eset\\.text", "rb");
fread(buf, 1, text_section.SizeOfRawData, fp);
buf += text_section.SizeOfRawData;
fclose(fp);
fp = fopen("C:\\Users\\User\\Desktop\\eset\\.rdata", "rb");
fread(buf, 1, rdata_section.SizeOfRawData, fp);
buf += rdata_section.SizeOfRawData;
fclose(fp);
fp = fopen("C:\\Users\\User\\Desktop\\eset\\.data", "rb");
fread(buf, 1, data_section.SizeOfRawData, fp);
buf += data_section.SizeOfRawData;
fclose(fp);
fp = fopen("C:\\Users\\User\\Desktop\\eset\\new.exe", "wb");
fwrite(start, 1, total_size, fp);
fclose(fp);
VirtualFree(buf, 0, MEM_RELEASE);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment