diff --git a/programs/develop/clink/epep/example.c b/programs/develop/clink/epep/example.c new file mode 100644 index 0000000000..1506887228 --- /dev/null +++ b/programs/develop/clink/epep/example.c @@ -0,0 +1,339 @@ +#include +#include +#include + +#define EPEP_INST +#include "epep.h" + +#define ERROR(epep) (printf("Error #%u from EPEP at " __FILE__ ": %u", epep.error_code, __LINE__), 1) + +int main(int argc, char **argv) { + if (argc < 2) { + printf("Usage:\n%s \n", argv[0]); + return 0; + } + Epep epep = { 0 }; + FILE *fp = fopen(argv[1], "rb"); + if (!fp) { + printf("File not found: %s\n", argv[1]); + return 1; + } + if (!epep_init(&epep, fp)) { + printf("Not PE"); + return 1; + } + printf("COFF File Header:\n"); + printf(" Machine: %04x\n", epep.coffFileHeader.Machine); + printf(" NumberOfSections: %04x\n", epep.coffFileHeader.NumberOfSections); + printf(" TimeDateStamp: %08x\n", epep.coffFileHeader.TimeDateStamp); + printf(" PointerToSymbolTable: %08x\n", epep.coffFileHeader.PointerToSymbolTable); + printf(" NumberOfSymbols: %08x\n", epep.coffFileHeader.NumberOfSymbols); + printf(" SizeOfOptionalHeader: %04x\n", epep.coffFileHeader.SizeOfOptionalHeader); + printf(" Characteristics: %04x\n", epep.coffFileHeader.Characteristics); + printf("\n"); + if (epep.coffFileHeader.SizeOfOptionalHeader != 0) { + printf("Optional Header:\n"); + printf(" Magic: %04x\n", epep.optionalHeader.Magic); + printf(" MajorLinkerVersion: %02x\n", epep.optionalHeader.MajorLinkerVersion); + printf(" MinorLinkerVersion: %02x\n", epep.optionalHeader.MinorLinkerVersion); + printf(" SizeOfCode: %08x\n", epep.optionalHeader.SizeOfCode); + printf(" SizeOfInitializedData: %08x\n", epep.optionalHeader.SizeOfInitializedData); + printf(" SizeOfUninitializedData: %08x\n", epep.optionalHeader.SizeOfUninitializedData); + printf(" AddressOfEntryPoint: %08x\n", epep.optionalHeader.AddressOfEntryPoint); + printf(" BaseOfCode: %08x\n", epep.optionalHeader.BaseOfCode); + printf(" BaseOfData: %08x\n", epep.optionalHeader.BaseOfData); + printf(" ImageBase: %016x\n", epep.optionalHeader.ImageBase); + printf(" SectionAlignment: %08x\n", epep.optionalHeader.SectionAlignment); + printf(" FileAlignment: %08x\n", epep.optionalHeader.FileAlignment); + printf(" MajorOperatingSystemVersion: %04x\n", epep.optionalHeader.MajorOperatingSystemVersion); + printf(" MinorOperatingSystemVersion: %04x\n", epep.optionalHeader.MinorOperatingSystemVersion); + printf(" MajorImageVersion: %04x\n", epep.optionalHeader.MajorImageVersion); + printf(" MinorImageVersion: %04x\n", epep.optionalHeader.MinorImageVersion); + printf(" MajorSubsystemVersion: %04x\n", epep.optionalHeader.MajorSubsystemVersion); + printf(" MinorSubsystemVersion: %04x\n", epep.optionalHeader.MinorSubsystemVersion); + printf(" Win32VersionValue: %08x\n", epep.optionalHeader.Win32VersionValue); + printf(" SizeOfImage: %08x\n", epep.optionalHeader.SizeOfImage); + printf(" SizeOfHeaders: %08x\n", epep.optionalHeader.SizeOfHeaders); + printf(" CheckSum: %08x\n", epep.optionalHeader.CheckSum); + printf(" Subsystem: %04x\n", epep.optionalHeader.Subsystem); + printf(" DllCharacteristics: %04x\n", epep.optionalHeader.DllCharacteristics); + printf(" SizeOfStackReserve: %016x\n", epep.optionalHeader.SizeOfStackReserve); + printf(" SizeOfStackCommit: %016x\n", epep.optionalHeader.SizeOfStackCommit); + printf(" SizeOfHeapReserve: %016x\n", epep.optionalHeader.SizeOfHeapReserve); + printf(" SizeOfHeapCommit: %016x\n", epep.optionalHeader.SizeOfHeapCommit); + printf(" LoaderFlags: %08x\n", epep.optionalHeader.LoaderFlags); + printf(" NumberOfRvaAndSizes: %08x\n", epep.optionalHeader.NumberOfRvaAndSizes); + printf("\n"); + printf("Data directories:\n"); + for (size_t i = 0; i < epep.optionalHeader.NumberOfRvaAndSizes; i++) { + char *dds[] = { + "Export Table", + "Import Table", + "Resource Table", + "Exception Table", + "Certificate Table", + "Base Relocation Table", + "Debug", + "Architecture", + "Global Ptr", + "TLS Table", + "Load Config Table", + "Bound Import", + "Import Address Table", + "Delay Import Descriptor", + "CLR Runtime Header", + "Reserved, must be zero" + }; + EpepImageDataDirectory idd = { 0 }; + if (!epep_get_data_directory_by_index(&epep, &idd, i)) { + return ERROR(epep); + } + printf(" Data directory #%u:\n", i); + printf(" Type: %s\n", dds[i % 16]); + printf(" VirtualAddress: %016x\n", idd.VirtualAddress); + printf(" Size: %016x\n", idd.Size); + // Certificate table (4'th) data directory's VirtualAddress isn't a real RVA, it's a file offset + // so it's actually outside of any section, so let's skip section name printing for it + if (idd.VirtualAddress && i != 4) { + EpepSectionHeader sh = { 0 }; + if (!epep_get_section_header_by_rva(&epep, &sh, idd.VirtualAddress)) { + return ERROR(epep); + } + printf(" Section: %s\n", sh.Name); + } + } + printf("\n"); + } + + // Get string table useful to show long names of sections + size_t string_table_size = 1; + if (epep.kind == EPEP_OBJECT && !epep_get_string_table_size(&epep, &string_table_size)) { + return ERROR(epep); + } + char *string_table = malloc(string_table_size); + if (epep.kind == EPEP_OBJECT && !epep_get_string_table(&epep, string_table)) { + return ERROR(epep); + } + + printf("Section Table:\n"); + for (size_t i = 0; i < epep.coffFileHeader.NumberOfSections; i++) { + EpepSectionHeader sh = { 0 }; + if (!epep_get_section_header_by_index(&epep, &sh, i)) { + return ERROR(epep); + } + printf(" Section #%u\n", i); + if (epep.kind == EPEP_OBJECT && sh.Name[0] == '/') { + printf(" Name: %s\n", &string_table[atoi(sh.Name + 1)]); + } else { + printf(" Name: %.*s\n", 8, sh.Name); + } + printf(" VirtualSize: %08x\n", sh.VirtualSize); + printf(" VirtualAddress: %08x\n", sh.VirtualAddress); + printf(" SizeOfRawData: %08x\n", sh.SizeOfRawData); + printf(" PointerToRawData: %08x\n", sh.PointerToRawData); + printf(" PointerToRelocations: %08x\n", sh.PointerToRelocations); + printf(" PointerToLinenumbers: %08x\n", sh.PointerToLinenumbers); + printf(" NumberOfRelocations: %08x\n", sh.NumberOfRelocations); + printf(" NumberOfLinenumbers: %08x\n", sh.NumberOfLinenumbers); + printf(" Characteristics: %08x\n", sh.Characteristics); + } + printf("\n"); + if (epep.kind == EPEP_OBJECT && epep.coffFileHeader.NumberOfSymbols != 0) { + printf("Symbols:\n"); + for (size_t i = 0; i < epep.coffFileHeader.NumberOfSymbols; i++) { + EpepCoffSymbol sym = { 0 }; + if (!epep_get_symbol_by_index(&epep, &sym, i)) { + return ERROR(epep); + } + printf(" Symbol #%u\n", i); + if (sym.symbol.Zeroes == 0) { + printf(" Name: %s\n", &string_table[sym.symbol.Offset]); + } else { + printf(" Name: %.*s\n", 8, sym.symbol.ShortName); + } + printf(" Value: %08x\n", sym.symbol.Value); + printf(" SectionNumber: %04x\n", sym.symbol.SectionNumber); + printf(" Type: %04x\n", sym.symbol.Type); + printf(" StorageClass: %02x\n", sym.symbol.StorageClass); + printf(" NumberOfAuxSymbols: %02x\n", sym.symbol.NumberOfAuxSymbols); + for (size_t j = 0; j < sym.symbol.NumberOfAuxSymbols; j++) { + i++; + } + } + printf("\n"); + } + if (epep_has_import_table(&epep)) { + printf("Import Directory Table:\n"); + for (size_t i = 0; i < 1024; i++) { + EpepImportDirectory import_directory = { 0 }; + if (!epep_get_import_directory_by_index(&epep, &import_directory, i)) { + return ERROR(epep); + } + if (import_directory.NameRva == 0) { + break; + } + size_t name_max = 1024; + char name[name_max]; + if (!epep_get_import_directory_name_s(&epep, &import_directory, name, name_max)) { + return ERROR(epep); + } + printf(" Import Directory #%lu:\n", i); + printf(" Name: %s\n", name); + printf(" ImportLookupTableRva: %08x\n", import_directory.ImportLookupTableRva); + printf(" TimeDateStamp: %08x\n", import_directory.TimeDateStamp); + printf(" ForwarderChain: %08x\n", import_directory.ForwarderChain); + printf(" ImportAddressTableRva: %08x\n", import_directory.ImportAddressTableRva); + for (size_t j = 0; j < 1024 * 1024; j++) { + size_t lookup = 0; + if (!epep_get_import_directory_lookup_by_index(&epep, &import_directory, &lookup, j)) { + return ERROR(epep); + } + if (lookup == 0) { + break; + } + size_t name_max = 1024; + char name[name_max]; + if (!epep_get_lookup_name_s(&epep, lookup, name, name_max)) { + return ERROR(epep); + } + printf(" Lookup: %016x (%s)\n", lookup, name); + } + } + printf("\n"); + } + if (epep_has_export_table(&epep)) { + if (!epep_read_export_directory(&epep)) { + return ERROR(epep); + } + size_t name_max = 256; + char name[name_max]; + strcpy(name, "undefined"); + if (!epep_get_dll_name_s(&epep, name, name_max)) { + return ERROR(epep); + } + printf("Export Directory:\n"); + printf(" ExportFlags: %08x\n", epep.export_directory.ExportFlags); + printf(" TimeDateStamp: %08x\n", epep.export_directory.TimeDateStamp); + printf(" MajorVersion: %04x\n", epep.export_directory.MajorVersion); + printf(" MinorVersion: %04x\n", epep.export_directory.MinorVersion); + printf(" NameRva: %08x (%s)\n", epep.export_directory.NameRva, name); + printf(" OrdinalBase: %08x\n", epep.export_directory.OrdinalBase); + printf(" AddressTableEntries: %08x\n", epep.export_directory.AddressTableEntries); + printf(" NumberOfNamePointers: %08x\n", epep.export_directory.NumberOfNamePointers); + printf(" ExportAddressTableRva: %08x\n", epep.export_directory.ExportAddressTableRva); + printf(" NamePointerRva: %08x\n", epep.export_directory.NamePointerRva); + printf(" OrdinalTableRva: %08x\n", epep.export_directory.OrdinalTableRva); + printf(" Exports:\n"); + for (size_t i = 0; i < epep.export_directory.AddressTableEntries; i++) { + printf(" Export #%u:\n", i); + size_t name_max = 1024; + char name[name_max]; + printf(" Ordinal: %u\n", epep.export_directory.OrdinalBase + i); + if (epep_get_export_name_s_by_index(&epep, name, name_max, i)) { + printf(" Name: %s\n", name); + } + EpepExportAddress ea = { 0 }; + if (!epep_get_export_address_by_index(&epep, &ea, i)) { + return ERROR(epep); + } + if (epep_export_address_is_forwarder(&epep, &ea)) { + size_t forwarder_max = 1024; + char forwarder[forwarder_max]; + if (!epep_get_export_address_forwarder_s(&epep, &ea, forwarder, forwarder_max)) { + return ERROR(epep); + } + printf(" ForwarderRva: %08x (%s)\n", ea.ForwarderRva, forwarder); + } else { + printf(" ExportRva: %08x\n", ea.ExportRva); + } + } + printf("\n"); + } + if (epep_has_base_relocation_table(&epep)) { + printf("Base Relocations:\n"); + EpepBaseRelocationBlock brb = { 0 }; + if (!epep_get_first_base_relocation_block(&epep, &brb)) { + return ERROR(epep); + } + for (size_t i = 0; brb.offset; i++) { + printf(" Base Relocation Block #%u:\n", i); + printf(" PageRva: %08x\n", brb.PageRva); + printf(" BlockSize: %08x\n", brb.BlockSize); + printf(" Relocations:\n"); + for (size_t j = 0; j < ((brb.BlockSize - 8) / 2); j++) { + char *strs[] = { + "IMAGE_REL_BASED_ABSOLUTE", + "IMAGE_REL_BASED_HIGH", + "IMAGE_REL_BASED_LOW", + "IMAGE_REL_BASED_HIGHLOW", + "IMAGE_REL_BASED_HIGHADJ", + "IMAGE_REL_BASED_MIPS_JMPADDR | IMAGE_REL_BASED_ARM_MOV32 | IMAGE_REL_BASED_RISCV_HIGH20", + "reserved, must be zero", + "IMAGE_REL_BASED_THUMB_MOV32 | IMAGE_REL_BASED_RISCV_LOW12I", + "IMAGE_REL_BASED_RISCV_LOW12S", + "IMAGE_REL_BASED_MIPS_JMPADDR16", + "IMAGE_REL_BASED_DIR64", + }; + printf(" Relocation #%u:\n", j); + EpepBaseRelocation br = { 0 }; + if (!epep_get_base_relocation_block_base_relocation_by_index(&epep, &brb, &br, j)) { + return ERROR(epep); + } + printf(" Type: %01x (%s)\n", br.Type, strs[br.Type % (sizeof(strs) / sizeof(*strs))]); + printf(" Offset: %03x (%u)\n", br.Offset, br.Offset); + } + if (!epep_get_next_base_relocation_block(&epep, &brb)) { + return ERROR(epep); + } + } + printf("\n"); + } + if (epep.kind == EPEP_OBJECT) { + for (size_t i = 0; i < epep.coffFileHeader.NumberOfSections; i++) { + EpepSectionHeader sh = { 0 }; + if (!epep_get_section_header_by_index(&epep, &sh, i)) { + return ERROR(epep); + } + printf(" Relocations for section #%u", i); + if (epep.kind == EPEP_OBJECT && sh.Name[0] == '/') { + printf(" (%s)\n", &string_table[atoi(sh.Name + 1)]); + } else { + printf(" (%.*s)\n", 8, sh.Name); + } + for (size_t i = 0; i < sh.NumberOfRelocations; i++) { + EpepCoffRelocation rel = { 0 }; + if (!epep_get_section_relocation_by_index(&epep, &sh, &rel, i)) { + return ERROR(epep); + } + printf(" COFF Relocation #%u\n", i); + printf(" VirtualAddress: %08x\n", rel.VirtualAddress); + printf(" SymbolTableIndex: %08x\n", rel.SymbolTableIndex); + printf(" Type: %04x\n", rel.Type); + } + } + } + if (epep.kind == EPEP_OBJECT) { + for (size_t i = 0; i < epep.coffFileHeader.NumberOfSections; i++) { + EpepSectionHeader sh = { 0 }; + if (!epep_get_section_header_by_index(&epep, &sh, i)) { + return ERROR(epep); + } + printf(" Linenumbers for section #%u", i); + if (epep.kind == EPEP_OBJECT && sh.Name[0] == '/') { + printf(" (%s)\n", &string_table[atoi(sh.Name + 1)]); + } else { + printf(" (%.*s)\n", 8, sh.Name); + } + for (size_t i = 0; i < sh.NumberOfLinenumbers; i++) { + EpepCoffLinenumber ln = { 0 }; + if (!epep_get_section_line_number_by_index(&epep, &sh, &ln, i)) { + return ERROR(epep); + } + printf(" COFF Line Number #%u\n", i); + printf(" Type: %04x\n", ln.Type); + printf(" Linenumber: %08x\n", ln.Linenumber); + } + } + } + return 0; +}