diff --git a/src/linker/lnk_export_table.c b/src/linker/lnk_export_table.c index efe896a8..02bdba68 100644 --- a/src/linker/lnk_export_table.c +++ b/src/linker/lnk_export_table.c @@ -198,7 +198,7 @@ lnk_build_edata(LNK_ExportTable *exptab, LNK_SectionTable *st, LNK_SymbolTable * LNK_Section *edata = lnk_section_table_search(st, str8_lit(".edata")); // push header - PE_ExportTable *header = push_array(edata->arena, PE_ExportTable, 1); + PE_ExportTableHeader *header = push_array(edata->arena, PE_ExportTableHeader, 1); header->ordinal_base = safe_cast_u16(ordinal_low + 1); header->export_address_table_count = safe_cast_u32(exptab->name_export_ht->count + exptab->noname_export_ht->count); header->name_pointer_table_count = safe_cast_u32(exptab->name_export_ht->count); @@ -226,10 +226,10 @@ lnk_build_edata(LNK_ExportTable *exptab, LNK_SectionTable *st, LNK_SymbolTable * LNK_Symbol *ordinal_table_symbol = lnk_symbol_table_push_defined_chunk(symtab, str8_lit("export_table.ordinal_table_voff"), LNK_DefinedSymbolVisibility_Internal, 0, ordinal_table_chunk, 0, 0, 0); // patch header fields - lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTable, name_voff), image_name_symbol); - lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTable, export_address_table_voff), address_table_symbol); - lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTable, name_pointer_table_voff), name_table_symbol); - lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTable, ordinal_table_voff), ordinal_table_symbol); + lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTableHeader, name_voff), image_name_symbol); + lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTableHeader, export_address_table_voff), address_table_symbol); + lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTableHeader, name_pointer_table_voff), name_table_symbol); + lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTableHeader, ordinal_table_voff), ordinal_table_symbol); // reserve virtual offset chunks LNK_Chunk **ordinal_voff_map = push_array(scratch.arena, LNK_Chunk *, exptab->max_ordinal); diff --git a/src/pe/pe.c b/src/pe/pe.c index 88695abf..d0c919de 100644 --- a/src/pe/pe.c +++ b/src/pe/pe.c @@ -58,6 +58,340 @@ pe_string_from_subsystem(PE_WindowsSubsystem subsystem) return str8(0,0); } +internal String8 +pe_string_from_unwind_gpr_x64(PE_UnwindGprRegX64 x) +{ + switch (x) { + case PE_UnwindGprRegX64_RAX: return str8_lit("RCX"); + case PE_UnwindGprRegX64_RCX: return str8_lit("RCX"); + case PE_UnwindGprRegX64_RDX: return str8_lit("RDX"); + case PE_UnwindGprRegX64_RBX: return str8_lit("RBX"); + case PE_UnwindGprRegX64_RSP: return str8_lit("RSP"); + case PE_UnwindGprRegX64_RBP: return str8_lit("RBP"); + case PE_UnwindGprRegX64_RSI: return str8_lit("RSI"); + case PE_UnwindGprRegX64_RDI: return str8_lit("RDI"); + case PE_UnwindGprRegX64_R8: return str8_lit("R8"); + case PE_UnwindGprRegX64_R9: return str8_lit("R9"); + case PE_UnwindGprRegX64_R10: return str8_lit("R10"); + case PE_UnwindGprRegX64_R11: return str8_lit("R11"); + case PE_UnwindGprRegX64_R12: return str8_lit("R12"); + case PE_UnwindGprRegX64_R13: return str8_lit("R13"); + case PE_UnwindGprRegX64_R14: return str8_lit("R14"); + case PE_UnwindGprRegX64_R15: return str8_lit("R15"); + } + return str8_zero(); +} + +internal String8 +pe_string_from_data_directory_index(PE_DataDirectoryIndex x) +{ + switch (x) { + case PE_DataDirectoryIndex_EXPORT: return str8_lit("Export"); + case PE_DataDirectoryIndex_IMPORT: return str8_lit("Import"); + case PE_DataDirectoryIndex_RESOURCES: return str8_lit("Resources"); + case PE_DataDirectoryIndex_EXCEPTIONS: return str8_lit("Exceptions"); + case PE_DataDirectoryIndex_CERT: return str8_lit("Cert"); + case PE_DataDirectoryIndex_BASE_RELOC: return str8_lit("BaseReloc"); + case PE_DataDirectoryIndex_DEBUG: return str8_lit("Debug"); + case PE_DataDirectoryIndex_ARCH: return str8_lit("Arch"); + case PE_DataDirectoryIndex_GLOBAL_PTR: return str8_lit("GlobalPtr"); + case PE_DataDirectoryIndex_TLS: return str8_lit("TLS"); + case PE_DataDirectoryIndex_LOAD_CONFIG: return str8_lit("LoadConfig"); + case PE_DataDirectoryIndex_BOUND_IMPORT: return str8_lit("BoundImport"); + case PE_DataDirectoryIndex_IMPORT_ADDR: return str8_lit("ImportAddr"); + case PE_DataDirectoryIndex_DELAY_IMPORT: return str8_lit("DelayImport"); + case PE_DataDirectoryIndex_COM_DESCRIPTOR: return str8_lit("COM Descriptor"); + case PE_DataDirectoryIndex_RESERVED: return str8_lit("Reserved"); + } + return str8_zero(); +} + +internal String8 +pe_string_from_debug_directory_type(PE_DebugDirectoryType x) +{ + switch (x) { + case PE_DebugDirectoryType_UNKNOWN: return str8_lit("UNKNOWN"); + case PE_DebugDirectoryType_COFF: return str8_lit("COFF"); + case PE_DebugDirectoryType_CODEVIEW: return str8_lit("CODEVIEW"); + case PE_DebugDirectoryType_FPO: return str8_lit("FPO"); + case PE_DebugDirectoryType_MISC: return str8_lit("MISC"); + case PE_DebugDirectoryType_EXCEPTION: return str8_lit("EXCEPTION"); + case PE_DebugDirectoryType_FIXUP: return str8_lit("FIXUP"); + case PE_DebugDirectoryType_OMAP_TO_SRC: return str8_lit("OMAP_TO_SRC"); + case PE_DebugDirectoryType_OMAP_FROM_SRC: return str8_lit("OMAP_FROM_SRC"); + case PE_DebugDirectoryType_BORLAND: return str8_lit("BORLAND"); + case PE_DebugDirectoryType_RESERVED10: return str8_lit("RESERVED10"); + case PE_DebugDirectoryType_CLSID: return str8_lit("CLSID"); + case PE_DebugDirectoryType_VC_FEATURE: return str8_lit("VC_FEATURE"); + case PE_DebugDirectoryType_POGO: return str8_lit("POGO"); + case PE_DebugDirectoryType_ILTCG: return str8_lit("ILTCG"); + case PE_DebugDirectoryType_MPX: return str8_lit("MPX"); + case PE_DebugDirectoryType_REPRO: return str8_lit("REPRO"); + case PE_DebugDirectoryType_EX_DLLCHARACTERISTICS: return str8_lit("EX_DLLCHARACTERISTICS"); + case PE_DebugDirectoryType_COUNT: return str8_lit("COUNT"); + } + return str8_zero(); +} + +internal String8 +pe_string_from_fpo_type(PE_FPOType x) +{ + switch (x) { + case PE_FPOType_FPO: return str8_lit("FPO"); + case PE_FPOType_TRAP: return str8_lit("TRAP"); + case PE_FPOType_TSS: return str8_lit("TSS"); + case PE_FPOType_NOFPO: return str8_lit("NOFPO"); + } + return str8_zero(); +} + +internal String8 +pe_string_from_misc_type(PE_DebugMiscType x) +{ + switch (x) { + case PE_DebugMiscType_NULL: break; + case PE_DebugMiscType_EXE_NAME: return str8_lit("EXE_NAME"); + } + return str8_zero(); +} + +internal String8 +pe_resource_kind_to_string(PE_ResourceKind x) +{ + String8 result = str8_zero(); + switch (x) { + case PE_ResourceKind_ACCELERATOR: result = str8_lit("Accelerator"); break; + case PE_ResourceKind_ANICURSOR: result = str8_lit("Animated Cursor"); break; + case PE_ResourceKind_ANIICON: result = str8_lit("Animated Icon"); break; + case PE_ResourceKind_BITMAP: result = str8_lit("Bitmap"); break; + case PE_ResourceKind_CURSOR: result = str8_lit("Cursor"); break; + case PE_ResourceKind_DIALOG: result = str8_lit("Dialog"); break; + case PE_ResourceKind_FONT: result = str8_lit("Font"); break; + case PE_ResourceKind_FONTDIR: result = str8_lit("Font Directory"); break; + case PE_ResourceKind_GROUP_CURSOR: result = str8_lit("Cursor Group"); break; + case PE_ResourceKind_GROUP_ICON: result = str8_lit("Icon Group"); break; + case PE_ResourceKind_HTML: result = str8_lit("HTML"); break; + case PE_ResourceKind_ICON: result = str8_lit("Icon"); break; + case PE_ResourceKind_MANIFEST: result = str8_lit("Manifest"); break; + case PE_ResourceKind_MENU: result = str8_lit("Menu"); break; + case PE_ResourceKind_MESSAGETABLE: result = str8_lit("Message Table"); break; + case PE_ResourceKind_PLUGPLAY: result = str8_lit("Plug Play"); break; + case PE_ResourceKind_RCDATA: result = str8_lit("RC Data"); break; + case PE_ResourceKind_STRING: result = str8_lit("String"); break; + case PE_ResourceKind_VERSION: result = str8_lit("Version Info"); break; + case PE_ResourceKind_VXD: result = str8_lit("VXD"); break; + } + return result; +} + +internal String8 +pe_string_from_fpo_flags(Arena *arena, PE_FPOFlags flags) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List l = {0}; + if (flags & PE_FPOFlags_HAS_SEH) { + str8_list_pushf(scratch.arena, &l, "HAS_SEH"); + } + if (flags & PE_FPOFlags_USE_BP_REG) { + str8_list_pushf(scratch.arena, &l, "USE_BP_REG"); + } + if (flags & PE_FPOFlags_RESERVED) { + str8_list_pushf(scratch.arena, &l, "RESERVED"); + } + String8 result = str8_list_join(arena, &l, &(StringJoin){.sep=str8_lit(" ")}); + scratch_end(scratch); + return result; +} + +internal String8 +pe_string_from_global_flags(Arena *arena, PE_GlobalFlags flags) +{ + Temp scratch = scratch_begin(&arena, 1); + + String8List l = {0}; + if (flags & PE_GlobalFlags_STOP_ON_EXCEPTION) { + str8_list_pushf(scratch.arena, &l, "STOP_ON_EXCEPTION"); + } + if (flags & PE_GlobalFlags_SHOW_LDR_SNAPS) { + str8_list_pushf(scratch.arena, &l, "SHOW_LDR_SNAPS"); + } + if (flags & PE_GlobalFlags_DEBUG_INITIAL_COMMAND) { + str8_list_pushf(scratch.arena, &l, "DEBUG_INITIAL_COMMAND"); + } + if (flags & PE_GlobalFlags_STOP_ON_HUNG_GUI) { + str8_list_pushf(scratch.arena, &l, "STOP_ON_HUNG_GUI"); + } + if (flags & PE_GlobalFlags_HEAP_ENABLE_TAIL_CHECK) { + str8_list_pushf(scratch.arena, &l, "HEAP_ENABLE_TAIL_CHECK"); + } + if (flags & PE_GlobalFlags_HEAP_ENABLE_FREE_CHECK) { + str8_list_pushf(scratch.arena, &l, "HEAP_ENABLE_FREE_CHECK"); + } + if (flags & PE_GlobalFlags_HEAP_VALIDATE_PARAMETERS) { + str8_list_pushf(scratch.arena, &l, "HEAP_VALIDATE_PARAMETERS"); + } + if (flags & PE_GlobalFlags_HEAP_VALIDATE_ALL) { + str8_list_pushf(scratch.arena, &l, "HEAP_VALIDATE_ALL"); + } + if (flags & PE_GlobalFlags_APPLICATION_VERIFIER) { + str8_list_pushf(scratch.arena, &l, "APPLICATION_VERIFIER"); + } + if (flags & PE_GlobalFlags_POOL_ENABLE_TAGGING) { + str8_list_pushf(scratch.arena, &l, "POOL_ENABLE_TAGGING"); + } + if (flags & PE_GlobalFlags_HEAP_ENABLE_TAGGING) { + str8_list_pushf(scratch.arena, &l, "HEAP_ENABLE_TAGGING"); + } + if (flags & PE_GlobalFlags_STACK_TRACE_DB) { + str8_list_pushf(scratch.arena, &l, "STACK_TRACE_DB"); + } + if (flags & PE_GlobalFlags_KERNEL_STACK_TRACE_DB) { + str8_list_pushf(scratch.arena, &l, "KERNEL_STACK_TRACE_DB"); + } + if (flags & PE_GlobalFlags_MAINTAIN_OBJECT_TYPELIST) { + str8_list_pushf(scratch.arena, &l, "MAINTAIN_OBJECT_TYPELIST"); + } + if (flags & PE_GlobalFlags_HEAP_ENABLE_TAG_BY_DLL) { + str8_list_pushf(scratch.arena, &l, "HEAP_ENABLE_TAG_BY_DLL"); + } + if (flags & PE_GlobalFlags_DISABLE_STACK_EXTENSION) { + str8_list_pushf(scratch.arena, &l, "DISABLE_STACK_EXTENSION"); + } + if (flags & PE_GlobalFlags_ENABLE_CSRDEBUG) { + str8_list_pushf(scratch.arena, &l, "ENABLE_CSRDEBUG"); + } + if (flags & PE_GlobalFlags_ENABLE_KDEBUG_SYMBOL_LOAD) { + str8_list_pushf(scratch.arena, &l, "ENABLE_KDEBUG_SYMBOL_LOAD"); + } + if (flags & PE_GlobalFlags_DISABLE_PAGE_KERNEL_STACKS) { + str8_list_pushf(scratch.arena, &l, "DISABLE_PAGE_KERNEL_STACKS"); + } + if (flags & PE_GlobalFlags_ENABLE_SYSTEM_CRIT_BREAKS) { + str8_list_pushf(scratch.arena, &l, "ENABLE_SYSTEM_CRIT_BREAKS"); + } + if (flags & PE_GlobalFlags_HEAP_DISABLE_COALESCING) { + str8_list_pushf(scratch.arena, &l, "HEAP_DISABLE_COALESCING"); + } + if (flags & PE_GlobalFlags_ENABLE_CLOSE_EXCEPTIONS) { + str8_list_pushf(scratch.arena, &l, "ENABLE_CLOSE_EXCEPTIONS"); + } + if (flags & PE_GlobalFlags_ENABLE_EXCEPTION_LOGGING) { + str8_list_pushf(scratch.arena, &l, "ENABLE_EXCEPTION_LOGGING"); + } + if (flags & PE_GlobalFlags_ENABLE_HANDLE_TYPE_TAGGING) { + str8_list_pushf(scratch.arena, &l, "ENABLE_HANDLE_TYPE_TAGGING"); + } + if (flags & PE_GlobalFlags_HEAP_PAGE_ALLOCS) { + str8_list_pushf(scratch.arena, &l, "HEAP_PAGE_ALLOCS"); + } + if (flags & PE_GlobalFlags_DEBUG_INITIAL_COMMAND_EX) { + str8_list_pushf(scratch.arena, &l, "DEBUG_INITIAL_COMMAND_EX"); + } + if (flags & PE_GlobalFlags_DISABLE_DBGPRINT) { + str8_list_pushf(scratch.arena, &l, "DISABLE_DBGPRINT"); + } + if (flags & PE_GlobalFlags_CRITSEC_EVENT_CREATION) { + str8_list_pushf(scratch.arena, &l, "CRITSEC_EVENT_CREATION"); + } + if (flags & PE_GlobalFlags_LDR_TOP_DOWN) { + str8_list_pushf(scratch.arena, &l, "LDR_TOP_DOWN"); + } + if (flags & PE_GlobalFlags_ENABLE_HANDLE_EXCEPTIONS) { + str8_list_pushf(scratch.arena, &l, "ENABLE_HANDLE_EXCEPTIONS"); + } + if (flags & PE_GlobalFlags_DISABLE_PROTDLLS) { + str8_list_pushf(scratch.arena, &l, "DISABLE_PROTDLLS"); + } + + String8 result = str8_list_join(arena, &l, &(StringJoin){.sep=str8_lit(" ")}); + return result; +} + +internal String8 +pe_string_from_load_config_guard_flags(Arena *arena, PE_LoadConfigGuardFlags flags) +{ + Temp scratch = scratch_begin(&arena, 1); + + String8List l = {0}; + if (flags & PE_LoadConfigGuardFlags_CF_INSTRUMENTED) { + str8_list_pushf(scratch.arena, &l, "CF_INSTRUMENTED"); + } + if (flags & PE_LoadConfigGuardFlags_CFW_INSTRUMENTED) { + str8_list_pushf(scratch.arena, &l, "CFW_INSTRUMENTED"); + } + if (flags & PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_PRESENT) { + str8_list_pushf(scratch.arena, &l, "CF_FUNCTION_TABLE_PRESENT"); + } + if (flags & PE_LoadConfigGuardFlags_SECURITY_COOKIE_UNUSED) { + str8_list_pushf(scratch.arena, &l, "SECURITY_COOKIE_UNUSED"); + } + if (flags & PE_LoadConfigGuardFlags_PROTECT_DELAYLOAD_IAT) { + str8_list_pushf(scratch.arena, &l, "PROTECT_DELAYLOAD_IAT"); + } + if (flags & PE_LoadConfigGuardFlags_DELAYLOAD_IAT_IN_ITS_OWN_SECTION) { + str8_list_pushf(scratch.arena, &l, "DELAYLOAD_IAT_IN_ITS_OWN_SECTION"); + } + if (flags & PE_LoadConfigGuardFlags_CF_EXPORT_SUPPRESSION_INFO_PRESENT) { + str8_list_pushf(scratch.arena, &l, "CF_EXPORT_SUPPRESSION_INFO_PRESENT"); + } + if (flags & PE_LoadConfigGuardFlags_CF_ENABLE_EXPORT_SUPPRESSION) { + str8_list_pushf(scratch.arena, &l, "CF_ENABLE_EXPORT_SUPPRESSION"); + } + if (flags & PE_LoadConfigGuardFlags_CF_LONGJUMP_TABLE_PRESENT) { + str8_list_pushf(scratch.arena, &l, "CF_LONGJUMP_TABLE_PRESENT"); + } + if (flags & PE_LoadConfigGuardFlags_EH_CONTINUATION_TABLE_PRESENT) { + str8_list_pushf(scratch.arena, &l, "EH_CONTINUATION_TABLE_PRESENT"); + } + + String8 result = str8_list_join(arena, &l, &(StringJoin){.sep = str8_lit(" ")}); + scratch_end(scratch); + return result; +} + +internal String8 +pe_string_from_dll_characteristics(Arena *arena, PE_DllCharacteristics dll_chars) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List l = {0}; + if (dll_chars & PE_DllCharacteristic_HIGH_ENTROPY_VA) { + str8_list_pushf(scratch.arena, &l, "High Entropy Virtual Address"); + } + if (dll_chars & PE_DllCharacteristic_DYNAMIC_BASE) { + str8_list_pushf(scratch.arena, &l, "Dynamic Base"); + } + if (dll_chars & PE_DllCharacteristic_FORCE_INTEGRITY) { + str8_list_pushf(scratch.arena, &l, "Force Integrity"); + } + if (dll_chars & PE_DllCharacteristic_NX_COMPAT) { + str8_list_pushf(scratch.arena, &l, "NX Compatible"); + } + if (dll_chars & PE_DllCharacteristic_NO_ISOLATION) { + str8_list_pushf(scratch.arena, &l, "No Isolation"); + } + if (dll_chars & PE_DllCharacteristic_NO_SEH) { + str8_list_pushf(scratch.arena, &l, "No SEH"); + } + if (dll_chars & PE_DllCharacteristic_NO_BIND) { + str8_list_pushf(scratch.arena, &l, "No Bind"); + } + if (dll_chars & PE_DllCharacteristic_APPCONTAINER) { + str8_list_pushf(scratch.arena, &l, "App Container"); + } + if (dll_chars & PE_DllCharacteristic_WDM_DRIVER) { + str8_list_pushf(scratch.arena, &l, "WDM Driver"); + } + if (dll_chars & PE_DllCharacteristic_GUARD_CF) { + str8_list_pushf(scratch.arena, &l, "GuardCF"); + } + if (dll_chars & PE_DllCharacteristic_TERMINAL_SERVER_AWARE) { + str8_list_pushf(scratch.arena, &l, "Terminal Server Aware"); + } + String8 result = str8_list_join(arena, &l, &(StringJoin){.sep=str8_lit(", ")}); + scratch_end(scratch); + return result; +} + internal PE_WindowsSubsystem pe_subsystem_from_string(String8 string) { @@ -217,7 +551,7 @@ pe_bin_info_from_data(Arena *arena, String8 data) { PE_CvHeaderPDB20 cv = {0}; str8_deserial_read_struct(data, cv_offset, &cv); - dbg_time = cv.time; + dbg_time = cv.time_stamp; dbg_age = cv.age; dbg_path_off = cv_offset + sizeof(cv); }break; @@ -301,63 +635,58 @@ pe_bin_info_from_data(Arena *arena, String8 data) //~ rjf: Helpers internal U64 -pe_intel_pdata_off_from_voff__binary_search(String8 data, PE_BinInfo *bin, U64 voff) +pe_pdata_off_from_voff__binary_search_x8664(String8 raw_pdata, U64 voff) { U64 result = 0; - if(bin->arch != Arch_Null && PE_DataDirectoryIndex_EXCEPTIONS < bin->data_dir_count) + + if(raw_pdata.size >= sizeof(PE_IntelPdata)) { - Rng1U64 range = bin->data_dir_franges[PE_DataDirectoryIndex_EXCEPTIONS]; - U64 pdata_off = range.min; - U64 pdata_count = (range.max - range.min)/sizeof(PE_IntelPdata); - - // check if this bin includes a pdata array - if(pdata_count > 0 && 0 <= pdata_off && pdata_off < data.size) + U64 pdata_count = raw_pdata.size/sizeof(PE_IntelPdata); + PE_IntelPdata *pdata_array = (PE_IntelPdata*)raw_pdata.str; + if(voff >= pdata_array[0].voff_first) { - PE_IntelPdata *pdata_array = (PE_IntelPdata*)(data.str + pdata_off); - if(voff >= pdata_array[0].voff_first) + // binary search: + // find max index s.t. pdata_array[index].voff_first <= voff + // we assume (i < j) -> (pdata_array[i].voff_first < pdata_array[j].voff_first) + U64 index = pdata_count; + U64 min = 0; + U64 opl = pdata_count; + for(;;) { - // binary search: - // find max index s.t. pdata_array[index].voff_first <= voff - // we assume (i < j) -> (pdata_array[i].voff_first < pdata_array[j].voff_first) - U64 index = pdata_count; - U64 min = 0; - U64 opl = pdata_count; - for(;;) + U64 mid = (min + opl)/2; + PE_IntelPdata *pdata = pdata_array + mid; + if(voff < pdata->voff_first) { - U64 mid = (min + opl)/2; - PE_IntelPdata *pdata = pdata_array + mid; - if(voff < pdata->voff_first) - { - opl = mid; - } - else if(pdata->voff_first < voff) - { - min = mid; - } - else - { - index = mid; - break; - } - if(min + 1 >= opl) - { - index = min; - break; - } + opl = mid; } - - // if we are in range fill result + else if(pdata->voff_first < voff) { - PE_IntelPdata *pdata = pdata_array + index; - if(pdata->voff_first <= voff && voff < pdata->voff_one_past_last) - { - result = pdata_off + index*sizeof(PE_IntelPdata); - } + min = mid; + } + else + { + index = mid; + break; + } + if(min + 1 >= opl) + { + index = min; + break; + } + } + + // if we are in range fill result + { + PE_IntelPdata *pdata = pdata_array + index; + if(pdata->voff_first <= voff && voff < pdata->voff_one_past_last) + { + result = index*sizeof(PE_IntelPdata); } } } } - return(result); + + return result; } internal void * @@ -637,6 +966,447 @@ pe_get_entry_point_names(COFF_MachineType machine, //////////////////////////////// +internal PE_ParsedImport * +pe_parsed_imports_from_data(Arena *arena, + B32 is_pe32, + U64 section_count, + COFF_SectionHeader *sections, + String8 raw_data, + U64 name_table_voff, + U64 *import_count_out) +{ + PE_ParsedImport *imports = 0; + U64 import_count = 0; + + U64 name_table_off = coff_foff_from_voff(sections, section_count, name_table_voff); + + if (is_pe32) { + for (;; ++import_count) { + U32 raw_entry = 0; + str8_deserial_read_struct(raw_data, name_table_off + import_count*sizeof(raw_entry), &raw_entry); + if (raw_entry == 0) { + break; + } + } + + imports = push_array(arena, PE_ParsedImport, import_count); + + for (U64 imp_idx = 0; imp_idx < import_count; ++imp_idx) { + U32 raw_entry = 0; + str8_deserial_read_struct(raw_data, name_table_off + imp_idx*sizeof(raw_entry), &raw_entry); + + B32 is_ordinal = raw_entry & (1 << 31); + + PE_ParsedImport *imp = imports+imp_idx; + ++imp_idx; + + if (is_ordinal) { + // fill out ordinal import + imp->type = PE_ParsedImport_Ordinal; + imp->u.ordinal = raw_entry & max_U16; + } else { + // map voff -> foff + U64 off = coff_foff_from_voff(sections, section_count, raw_entry); + + // read hint & name + U16 hint = 0; + String8 name = str8_zero(); + str8_deserial_read_struct(raw_data, off, &hint); + str8_deserial_read_cstr(raw_data, off+sizeof(hint), &name); + + // fill out named import + imp->type = PE_ParsedImport_Name; + imp->u.name.hint = hint; + imp->u.name.string = name; + } + } + } else { + for (;; ++import_count) { + U64 raw_entry = 0; + str8_deserial_read_struct(raw_data, name_table_off + import_count*sizeof(raw_entry), &raw_entry); + if (raw_entry == 0) { + break; + } + } + + imports = push_array(arena, PE_ParsedImport, import_count); + + for (U64 imp_idx = 0; imp_idx < import_count; ++imp_idx) { + U64 raw_entry = 0; + str8_deserial_read_struct(raw_data, name_table_off + imp_idx*sizeof(raw_entry), &raw_entry); + + B32 is_ordinal = raw_entry & (1ull << 63); + + PE_ParsedImport *imp = imports+imp_idx; + ++imp_idx; + + if (is_ordinal) { + // fill out ordinal import + imp->type = PE_ParsedImport_Ordinal; + imp->u.ordinal = raw_entry & max_U16; + } else { + // map voff -> foff + U64 off = coff_foff_from_voff(sections, section_count, raw_entry); + + // read hint & name + U16 hint = 0; + String8 name = str8_zero(); + str8_deserial_read_struct(raw_data, off, &hint); + str8_deserial_read_cstr(raw_data, off + sizeof(hint), &name); + + // fill out named import + imp->type = PE_ParsedImport_Name; + imp->u.name.hint = hint; + imp->u.name.string = name; + } + } + } + + *import_count_out = import_count; + return imports; +} + +internal U64 * +pe_array_from_null_term_addr(Arena *arena, B32 is_pe32, String8 raw_data, Rng1U64 range, U64 *count_out) +{ + U64 *result = 0; + *count_out = 0; + + if (is_pe32) { + U32 *src = (U32 *)(raw_data.str + range.min); + U32 *opl = (U32 *)(raw_data.str + AlignDownPow2(range.max, sizeof(*opl))); + + // count items + U32 *ptr; + for (ptr = src; ptr < opl && *ptr != 0; ++ptr); + + // push output array + *count_out = (U64)(ptr - src); + result = push_array(arena, U64, *count_out); + + // convert & copy + for (U64 i = 0; i < *count_out; ++i) { + result[i] = (U64)src[i]; + } + } else { + U64 *src = (U64 *)(raw_data.str + range.min); + U64 *opl = (U64 *)(raw_data.str + AlignDownPow2(range.max, sizeof(*opl))); + + // count items + U64 *ptr; + for (ptr = src; ptr < opl && *ptr != 0; ++ptr); + + // push output array + *count_out = (U64)(ptr - src); + result = push_array(arena, U64, *count_out); + + // copy + MemoryCopyTyped(result, src, *count_out); + } + + return result; +} + +internal PE_ParsedStaticImportTable +pe_static_imports_from_data(Arena *arena, + B32 is_pe32, + U64 section_count, + COFF_SectionHeader *sections, + String8 raw_data, + Rng1U64 dir_file_range) +{ + // count imports + U64 max_dll_count = dim_1u64(dir_file_range) / sizeof (PE_ImportEntry); + U64 dll_count = max_dll_count; + for (U64 i = 0; i < max_dll_count; ++i) { + PE_ImportEntry *imp = str8_deserial_get_raw_ptr(raw_data, dir_file_range.min+(i*sizeof(*imp)), sizeof(*imp)); + if (memory_is_zero(imp, sizeof(*imp))) { + dll_count = i; + break; + } + } + + PE_ParsedStaticDLLImport *dlls = push_array(arena, PE_ParsedStaticDLLImport, dll_count); + + for (U64 dll_idx = 0; dll_idx < dll_count; ++dll_idx) { + PE_ImportEntry *raw_dll = str8_deserial_get_raw_ptr(raw_data, dir_file_range.min+(dll_idx*sizeof(*raw_dll)), sizeof(*raw_dll)); + + // get name + U64 name_off = coff_foff_from_voff(sections, section_count, raw_dll->name_voff); + String8 name = str8_zero(); + str8_deserial_read_cstr(raw_data, name_off, &name); + + U64 import_count = 0; + PE_ParsedImport *imports = pe_parsed_imports_from_data(arena, + is_pe32, + section_count, + sections, + raw_data, + raw_dll->lookup_table_voff, + &import_count); + + PE_ParsedStaticDLLImport *dll = dlls+dll_idx; + dll->name = name; + dll->import_address_table_voff = raw_dll->import_addr_table_voff; + dll->import_name_table_voff = raw_dll->lookup_table_voff; + dll->time_stamp = raw_dll->time_stamp; + dll->forwarder_chain = raw_dll->forwarder_chain; + dll->import_count = import_count; + dll->imports = imports; + } + + PE_ParsedStaticImportTable imptab = {0}; + imptab.count = dll_count; + imptab.v = dlls; + + return imptab; +} + +internal PE_ParsedDelayImportTable +pe_delay_imports_from_data(Arena *arena, + B32 is_pe32, + U64 section_count, + COFF_SectionHeader *sections, + String8 raw_data, + Rng1U64 dir_file_range) +{ + // count imports + U64 max_dll_count = dim_1u64(dir_file_range) / sizeof(PE_DelayedImportEntry); + U64 dll_count = 0; + for (; dll_count < max_dll_count; ++dll_count) { + PE_DelayedImportEntry *raw_dll = str8_deserial_get_raw_ptr(raw_data, dir_file_range.min+(dll_count*sizeof(*raw_dll)), sizeof(*raw_dll)); + if (memory_is_zero(raw_dll, sizeof(*raw_dll))) { + break; + } + } + + // parse dll imports + PE_ParsedDelayDLLImport *dlls = push_array(arena, PE_ParsedDelayDLLImport, dll_count); + for (U64 dll_idx = 0; dll_idx < dll_count; ++dll_idx) { + PE_DelayedImportEntry *raw_dll = str8_deserial_get_raw_ptr(raw_data, dir_file_range.min+(dll_idx*sizeof(*raw_dll)), sizeof(*raw_dll)); + + U64 name_off = coff_foff_from_voff(sections, section_count, raw_dll->name_voff); + String8 name = str8_zero(); + str8_deserial_read_cstr(raw_data, name_off, &name); + + // parse import table + U64 import_count = 0; + PE_ParsedImport *imports = pe_parsed_imports_from_data(arena, + is_pe32, + section_count, + sections, + raw_data, + raw_dll->name_table_voff, + &import_count); + + // parse bound table + U64 bound_table_foff = coff_foff_from_voff(sections, section_count, raw_dll->bound_table_voff); + Rng1U64 bound_table_range = rng_1u64(bound_table_foff, raw_data.size); + U64 bound_table_count; + U64 * bound_table = pe_array_from_null_term_addr(arena, is_pe32, raw_data, bound_table_range, &bound_table_count); + + // parse unload table + U64 unload_table_foff = coff_foff_from_voff(sections, section_count, raw_dll->unload_table_voff); + Rng1U64 unload_table_range = rng_1u64(unload_table_foff, raw_data.size); + U64 unload_table_count; + U64 * unload_table = pe_array_from_null_term_addr(arena, is_pe32, raw_data, unload_table_range, &unload_table_count); + + // fill out DLL + PE_ParsedDelayDLLImport *dll = dlls+dll_idx; + dll->attributes = raw_dll->attributes; + dll->name = name; + dll->module_handle_voff = raw_dll->module_handle_voff; + dll->iat_voff = raw_dll->iat_voff; + dll->name_table_voff = raw_dll->name_table_voff; + dll->bound_table_voff = raw_dll->bound_table_voff; + dll->unload_table_voff = raw_dll->unload_table_voff; + dll->time_stamp = raw_dll->time_stamp; + dll->bound_table_count = bound_table_count; + dll->bound_table = bound_table; + dll->unload_table_count = unload_table_count; + dll->unload_table = unload_table; + dll->import_count = import_count; + dll->imports = imports; + } + + // fill out result + PE_ParsedDelayImportTable imptab = {0}; + imptab.count = dll_count; + imptab.v = dlls; + + return imptab; +} + +internal PE_ParsedExportTable +pe_exports_from_data(Arena *arena, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 dir_file_range, Rng1U64 dir_virt_range) +{ + Temp scratch = scratch_begin(&arena, 1); + + String8 raw_dir = str8_substr(raw_data, dir_file_range); + + PE_ExportTableHeader *header = str8_deserial_get_raw_ptr(raw_dir, 0, sizeof(*header)); + + U64 name_table_off = coff_foff_from_voff(sections, section_count, header->name_pointer_table_voff); + U64 export_table_off = coff_foff_from_voff(sections, section_count, header->export_address_table_voff); + U64 ordinal_table_off = coff_foff_from_voff(sections, section_count, header->ordinal_table_voff); + + U32 *name_table = str8_deserial_get_raw_ptr(raw_data, name_table_off, sizeof(*name_table)*header->name_pointer_table_count); + U32 *export_table = str8_deserial_get_raw_ptr(raw_data, export_table_off, sizeof(*export_table)*header->export_address_table_count); + U16 *ordinal_table = str8_deserial_get_raw_ptr(raw_data, ordinal_table_off, sizeof(*ordinal_table)*header->name_pointer_table_count); + + // Scan export address table to get accruate count of ordinals. + // We can't rely on "name_pointer_table_count" becuase it is possible + // to define an export without a name through NONAME attribute in DEF file + U64 ordinal_count = 0; + for (U64 voff_idx = 0; voff_idx < header->export_address_table_count; ++voff_idx) { + if (export_table[voff_idx] != 0) { + ++ordinal_count; + } + } + + U64 ordinal_max = header->export_address_table_count; + B32 *is_ordinal_used = push_array(scratch.arena, B32, ordinal_max); + + PE_ParsedExport *exports = push_array(arena, PE_ParsedExport, ordinal_count); + PE_ParsedExport *curr_exp = exports; + + // parse exports with name + for (U64 i = 0; i < header->name_pointer_table_count; ++i) { + // get name + U32 name_voff = name_table[i]; + U64 name_foff = coff_foff_from_voff(sections, section_count, name_voff); + String8 name = str8_cstring_capped(raw_data.str+name_foff, raw_data.str+raw_data.size); + + // get ordinal + U16 ordinal_nb = ordinal_table[i]; + + // mark ordinal + Assert(ordinal_nb < ordinal_max); + is_ordinal_used[ordinal_nb] = 1; + + // get voff + U32 export_voff = 0; + if (ordinal_nb < header->export_address_table_count) { + export_voff = export_table[ordinal_nb]; + } + + // make ordinal + U16 ordinal = header->ordinal_base + ordinal_nb; + + String8 forwarder = str8_zero(); + { + B32 is_forwarder = dir_virt_range.min <= export_voff && export_voff < dir_virt_range.max; + if (is_forwarder) { + U64 fwd_name_off = coff_foff_from_voff(sections, section_count, name_voff); + str8_deserial_read_cstr(raw_data, fwd_name_off, &forwarder); + } + } + + curr_exp->forwarder = forwarder; + curr_exp->name = name; + curr_exp->voff = export_voff; + curr_exp->ordinal = ordinal; + ++curr_exp; + } + + // parse exports with ordinal + for (U64 ordinal_nb = 0; ordinal_nb < header->export_address_table_count; ++ordinal_nb) { + U32 voff = export_table[ordinal_nb]; + B32 is_voff_taken = (voff != 0); + B32 is_ordinal_free = !is_ordinal_used[ordinal_nb]; + if (is_voff_taken && is_ordinal_free) { + curr_exp->name = str8_zero(); + curr_exp->voff = voff; + curr_exp->ordinal = header->ordinal_base; + ++curr_exp; + } + } + + // fill out result + PE_ParsedExportTable exptab = {0}; + exptab.flags = header->flags; + exptab.time_stamp = header->time_stamp; + exptab.major_ver = header->major_ver; + exptab.minor_ver = header->minor_ver; + exptab.ordinal_base = header->ordinal_base; + exptab.export_count = ordinal_count; + exptab.exports = exports; + + scratch_end(scratch); + return exptab; +} + + +internal PE_ParsedTLS +pe_tls_from_data(Arena *arena, + COFF_MachineType machine, + U64 image_base, + U64 section_count, + COFF_SectionHeader *sections, + String8 raw_data, + Rng1U64 tls_frange) +{ + String8 raw_tls = str8_substr(raw_data, tls_frange); + + PE_TLSHeader64 header64 = {0}; + U64 callback_count = 0; + U64 *callback_addrs = 0; + + switch (machine) { + case COFF_MachineType_UNKNOWN: break; + case COFF_MachineType_X86: { + PE_TLSHeader32 header32 = {0}; + str8_deserial_read_struct(raw_tls, 0, &header32); + + header64.raw_data_start = header32.raw_data_start; + header64.raw_data_end = header32.raw_data_end; + header64.index_address = header32.index_address; + header64.callbacks_address = header32.callbacks_address; + header64.zero_fill_size = header32.zero_fill_size; + header64.characteristics = header32.characteristics; + + U64 callbacks_voff = header32.callbacks_address - image_base; + U64 callbacks_foff = coff_foff_from_voff(sections, section_count, callbacks_voff); + + U32 *src = (U32 *)(raw_data.str + callbacks_foff); + U32 *opl = (U32 *)(raw_data.str + raw_data.size); + U32 *ptr = src; + for (; ptr < opl && *ptr != 0; ++ptr); + + callback_count = (U64)(ptr-src); + callback_addrs = push_array(arena, U64, callback_count); + for (U64 i = 0; i < callback_count; ++i) { + callback_addrs[i] = (U64)src[i]; + } + } break; + case COFF_MachineType_X64: { + str8_deserial_read_struct(raw_tls, 0, &header64); + + U64 callbacks_voff = header64.callbacks_address - image_base; + U64 callbacks_foff = coff_foff_from_voff(sections, section_count, callbacks_voff); + + U64 *src = (U64 *)(raw_data.str + callbacks_foff); + U64 *opl = (U64 *)(raw_data.str + raw_data.size); + U64 *ptr = src; + for (; ptr < opl && *ptr != 0; ++ptr); + + callback_count = (U64)(ptr-src); + callback_addrs = push_array(arena, U64, callback_count); + MemoryCopyTyped(callback_addrs, src, callback_count); + } break; + default: NotImplemented; + } + + PE_ParsedTLS result = {0}; + result.header = header64; + result.callback_count = callback_count; + result.callback_addrs = callback_addrs; + + return result; +} + +//////////////////////////////// + internal B32 pe_is_res(String8 data) { diff --git a/src/pe/pe.h b/src/pe/pe.h index f1669c5b..e102ea5c 100644 --- a/src/pe/pe.h +++ b/src/pe/pe.h @@ -56,22 +56,22 @@ enum typedef U16 PE_ImageFileCharacteristics; enum { - PE_ImageFileCharacteristic_STRIPPED = (1 << 0), - PE_ImageFileCharacteristic_EXE = (1 << 1), - PE_ImageFileCharacteristic_NUMS_STRIPPED = (1 << 2), - PE_ImageFileCharacteristic_PE_STRIPPED = (1 << 3), - PE_ImageFileCharacteristic_AGGRESIVE_WS_TRIM = (1 << 4), - PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE = (1 << 5), - PE_ImageFileCharacteristic_UNUSED1 = (1 << 6), - PE_ImageFileCharacteristic_BYTES_RESERVED_LO = (1 << 7), - PE_ImageFileCharacteristic_32BIT_MACHINE = (1 << 8), - PE_ImageFileCharacteristic_DEBUG_STRIPPED = (1 << 9), - PE_ImageFileCharacteristic_FILE_REMOVABLE_RUN_FROM_SWAP = (1 << 10), - PE_ImageFileCharacteristic_NET_RUN_FROM_SWAP = (1 << 11), - PE_ImageFileCharacteristic_FILE_SYSTEM = (1 << 12), - PE_ImageFileCharacteristic_FILE_DLL = (1 << 13), - PE_ImageFileCharacteristic_FILE_UP_SYSTEM_ONLY = (1 << 14), - PE_ImageFileCharacteristic_BYTES_RESERVED_HI = (1 << 15), + PE_ImageFileCharacteristic_STRIPPED = (1 << 0), + PE_ImageFileCharacteristic_EXE = (1 << 1), + PE_ImageFileCharacteristic_NUMS_STRIPPED = (1 << 2), + PE_ImageFileCharacteristic_PE_STRIPPED = (1 << 3), + PE_ImageFileCharacteristic_AGGRESIVE_WS_TRIM = (1 << 4), + PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE = (1 << 5), + PE_ImageFileCharacteristic_UNUSED1 = (1 << 6), + PE_ImageFileCharacteristic_BYTES_RESERVED_LO = (1 << 7), + PE_ImageFileCharacteristic_32BIT_MACHINE = (1 << 8), + PE_ImageFileCharacteristic_DEBUG_STRIPPED = (1 << 9), + PE_ImageFileCharacteristic_FILE_REMOVABLE_RUN_FROM_SWAP = (1 << 10), + PE_ImageFileCharacteristic_NET_RUN_FROM_SWAP = (1 << 11), + PE_ImageFileCharacteristic_FILE_SYSTEM = (1 << 12), + PE_ImageFileCharacteristic_FILE_DLL = (1 << 13), + PE_ImageFileCharacteristic_FILE_UP_SYSTEM_ONLY = (1 << 14), + PE_ImageFileCharacteristic_BYTES_RESERVED_HI = (1 << 15), }; typedef U16 PE_DllCharacteristics; @@ -93,70 +93,70 @@ enum typedef struct PE_OptionalHeader32 PE_OptionalHeader32; struct PE_OptionalHeader32 { - U16 magic; - U8 major_linker_version; - U8 minor_linker_version; - U32 sizeof_code; - U32 sizeof_inited_data; - U32 sizeof_uninited_data; - U32 entry_point_va; - U32 code_base; - U32 data_base; - U32 image_base; - U32 section_alignment; - U32 file_alignment; - U16 major_os_ver; - U16 minor_os_ver; - U16 major_img_ver; - U16 minor_img_ver; - U16 major_subsystem_ver; - U16 minor_subsystem_ver; - U32 win32_version_value; - U32 sizeof_image; - U32 sizeof_headers; - U32 check_sum; - PE_WindowsSubsystem subsystem; + U16 magic; + U8 major_linker_version; + U8 minor_linker_version; + U32 sizeof_code; + U32 sizeof_inited_data; + U32 sizeof_uninited_data; + U32 entry_point_va; + U32 code_base; + U32 data_base; + U32 image_base; + U32 section_alignment; + U32 file_alignment; + U16 major_os_ver; + U16 minor_os_ver; + U16 major_img_ver; + U16 minor_img_ver; + U16 major_subsystem_ver; + U16 minor_subsystem_ver; + U32 win32_version_value; + U32 sizeof_image; + U32 sizeof_headers; + U32 check_sum; + PE_WindowsSubsystem subsystem; PE_DllCharacteristics dll_characteristics; - U32 sizeof_stack_reserve; - U32 sizeof_stack_commit; - U32 sizeof_heap_reserve; - U32 sizeof_heap_commit; - U32 loader_flags; - U32 data_dir_count; + U32 sizeof_stack_reserve; + U32 sizeof_stack_commit; + U32 sizeof_heap_reserve; + U32 sizeof_heap_commit; + U32 loader_flags; + U32 data_dir_count; }; typedef struct PE_OptionalHeader32Plus PE_OptionalHeader32Plus; struct PE_OptionalHeader32Plus { - U16 magic; - U8 major_linker_version; - U8 minor_linker_version; - U32 sizeof_code; - U32 sizeof_inited_data; - U32 sizeof_uninited_data; - U32 entry_point_va; - U32 code_base; - U64 image_base; - U32 section_alignment; - U32 file_alignment; - U16 major_os_ver; - U16 minor_os_ver; - U16 major_img_ver; - U16 minor_img_ver; - U16 major_subsystem_ver; - U16 minor_subsystem_ver; - U32 win32_version_value; - U32 sizeof_image; - U32 sizeof_headers; - U32 check_sum; - PE_WindowsSubsystem subsystem; + U16 magic; + U8 major_linker_version; + U8 minor_linker_version; + U32 sizeof_code; + U32 sizeof_inited_data; + U32 sizeof_uninited_data; + U32 entry_point_va; + U32 code_base; + U64 image_base; + U32 section_alignment; + U32 file_alignment; + U16 major_os_ver; + U16 minor_os_ver; + U16 major_img_ver; + U16 minor_img_ver; + U16 major_subsystem_ver; + U16 minor_subsystem_ver; + U32 win32_version_value; + U32 sizeof_image; + U32 sizeof_headers; + U32 check_sum; + PE_WindowsSubsystem subsystem; PE_DllCharacteristics dll_characteristics; - U64 sizeof_stack_reserve; - U64 sizeof_stack_commit; - U64 sizeof_heap_reserve; - U64 sizeof_heap_commit; - U32 loader_flags; - U32 data_dir_count; + U64 sizeof_stack_reserve; + U64 sizeof_stack_commit; + U64 sizeof_heap_reserve; + U64 sizeof_heap_commit; + U32 loader_flags; + U32 data_dir_count; }; typedef enum PE_DataDirectoryIndex @@ -229,21 +229,32 @@ enum PE_FPOEncoded_FLAGS_SHIFT = 11, PE_FPOEncoded_FLAGS_MASK = 0x7, PE_FPOEncoded_FRAME_TYPE_SHIFT = 14, PE_FPOEncoded_FRAME_TYPE_MASK = 0x3, }; -#define PE_FPOEncoded_Extract_PROLOG_SIZE(f) (U8)(((f) >> PE_FPOEncoded_PROLOG_SIZE_SHIFT) & PE_FPOEncoded_PROLOG_SIZE_MASK) +#define PE_FPOEncoded_Extract_PROLOG_SIZE(f) (U8)(((f) >> PE_FPOEncoded_PROLOG_SIZE_SHIFT) & PE_FPOEncoded_PROLOG_SIZE_MASK) #define PE_FPOEncoded_Extract_SAVED_REGS_SIZE(f) (U8)(((f) >> PE_FPOEncoded_SAVED_REGS_SIZE_SHIFT) & PE_FPOEncoded_SAVED_REGS_SIZE_MASK) -#define PE_FPOEncoded_Extract_FLAGS(f) (U8)(((f) >> PE_FPOEncoded_FLAGS_SHIFT) & PE_FPOEncoded_FLAGS_MASK) -#define PE_FPOEncoded_Extract_FRAME_TYPE(f) (U8)(((f) >> PE_FPOEncoded_FRAME_TYPE_SHIFT) & PE_FPOEncoded_FRAME_TYPE_MASK) +#define PE_FPOEncoded_Extract_FLAGS(f) (U8)(((f) >> PE_FPOEncoded_FLAGS_SHIFT) & PE_FPOEncoded_FLAGS_MASK) +#define PE_FPOEncoded_Extract_FRAME_TYPE(f) (U8)(((f) >> PE_FPOEncoded_FRAME_TYPE_SHIFT) & PE_FPOEncoded_FRAME_TYPE_MASK) typedef U8 PE_FPOType; enum { - PE_FPOType_FPO = 0, - PE_FPOType_TRAP = 1, - PE_FPOType_TSS = 2, + PE_FPOType_FPO = 0, + PE_FPOType_TRAP = 1, + PE_FPOType_TSS = 2, PE_FPOType_NOFPO = 3, PE_FPOType_COUNT = 4 }; +// winnt.h: FPO_DATA +typedef struct PE_DebugFPO PE_DebugFPO; +struct PE_DebugFPO +{ + U32 func_code_off; + U32 func_size; + U32 locals_size; + U16 params_size; + U16 flags; +}; + typedef U32 PE_DebugMiscType; enum { @@ -252,17 +263,42 @@ enum PE_DebugMiscType_COUNT = 2 }; +// winnt.h: IMAGE_DEBUG_MISC +typedef struct PE_DebugMisc PE_DebugMisc; +struct PE_DebugMisc +{ + PE_DebugMiscType data_type; + U32 size; + U8 unicode; + U8 pad[3]; + //char name[]; +}; + +// winnt.h: IMAGE_COFF_SYMBOLS_HEADER +typedef struct PE_DebugCoff PE_DebugCoff; +struct PE_DebugCoff +{ + U32 symbol_count; + U32 lva_to_first_symbol; + U32 line_number_count; + U32 lva_to_first_line_number; + U32 virt_off_to_first_byte_of_code; + U32 virt_off_to_last_byte_of_code; + U32 virt_off_to_first_byte_of_data; + U32 virt_off_to_last_byte_of_data; +}; + typedef struct PE_DebugDirectory PE_DebugDirectory; struct PE_DebugDirectory { - U32 characteristics; - COFF_TimeStamp time_stamp; - U16 major_ver; - U16 minor_ver; + U32 characteristics; + COFF_TimeStamp time_stamp; + U16 major_ver; + U16 minor_ver; PE_DebugDirectoryType type; - U32 size; - U32 voff; - U32 foff; + U32 size; + U32 voff; + U32 foff; }; typedef U32 PE_GlobalFlags; @@ -318,6 +354,133 @@ enum }; #define PE_LoadConfigGuardFlags_Extract_CF_FUNCTION_TABLE_SIZE(f) (U32)(((f) >> PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_SHIFT) & PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_MASK) +typedef struct PE_LoadConfigCodeIntegrity PE_LoadConfigCodeIntegrity; +struct PE_LoadConfigCodeIntegrity +{ + U16 flags; + U16 catalog; + U32 catalog_offset; + U32 reserved; +}; + +typedef struct PE_LoadConfig32 PE_LoadConfig32; +struct PE_LoadConfig32 +{ + U32 size; + COFF_TimeStamp time_stamp; + U16 major_version; + U16 minor_version; + U32 global_flag_clear; + U32 global_flag_set; + U32 critical_section_timeout; + U32 decommit_free_block_threshold; + U32 decommit_total_free_threshold; + U32 lock_prefix_table; + U32 maximum_allocation_size; + U32 virtual_memory_threshold; + U32 process_affinity_mask; + U32 process_heap_flags; + U16 csd_version; + U16 reserved; + U32 edit_list; + U32 security_cookie; + U32 seh_handler_table; + U32 seh_handler_count; + + // msvc 2015 + U32 guard_cf_check_func_ptr; + U32 guard_cf_dispatch_func_ptr; + U32 guard_cf_func_table; + U32 guard_cf_func_count; + U32 guard_flags; + + // msvc 2017 + PE_LoadConfigCodeIntegrity code_integrity; + U32 guard_address_taken_iat_entry_table; + U32 guard_address_taken_iat_entry_count; + U32 guard_long_jump_target_table; + U32 guard_long_jump_target_count; + U32 dynamic_value_reloc_table; + U32 chpe_metadata_ptr; + U32 guard_rf_failure_routine; + U32 guard_rf_failure_routine_func_ptr; + U32 dynamic_value_reloc_table_offset; + U16 dynamic_value_reloc_table_section; + U16 reserved2; + U32 guard_rf_verify_stack_pointer_func_ptr; + U32 hot_patch_table_offset; + + // msvc 2019 + U32 reserved3; + U32 enclave_config_ptr; + U32 volatile_metadata_ptr; + U32 guard_eh_continue_table; + U32 guard_eh_continue_count; + U32 guard_xfg_check_func_ptr; + U32 guard_xfg_dispatch_func_ptr; + U32 guard_xfg_table_dispatch_func_ptr; + U32 cast_guard_os_determined_failure_mode; +}; + +typedef struct PE_LoadConfig64 PE_LoadConfig64; +struct PE_LoadConfig64 +{ + U32 size; + COFF_TimeStamp time_stamp; + U16 major_version; + U16 minor_version; + U32 global_flag_clear; + U32 global_flag_set; + U32 critical_section_timeout; + U64 decommit_free_block_threshold; + U64 decommit_total_free_threshold; + U64 lock_prefix_table; + U64 maximum_allocation_size; + U64 virtual_memory_threshold; + U64 process_affinity_mask; + U32 process_heap_flags; + U16 csd_version; + U16 reserved; + U64 edit_list; + U64 security_cookie; + U64 seh_handler_table; + U64 seh_handler_count; + + // msvc 2015 + U64 guard_cf_check_func_ptr; + U64 guard_cf_dispatch_func_ptr; + U64 guard_cf_func_table; + U64 guard_cf_func_count; + U32 guard_flags; + + // msvc 2017 + PE_LoadConfigCodeIntegrity code_integrity; + U64 guard_address_taken_iat_entry_table; + U64 guard_address_taken_iat_entry_count; + U64 guard_long_jump_target_table; + U64 guard_long_jump_target_count; + U64 dynamic_value_reloc_table; + U64 chpe_metadata_ptr; + U64 guard_rf_failure_routine; + U64 guard_rf_failure_routine_func_ptr; + U32 dynamic_value_reloc_table_offset; + U16 dynamic_value_reloc_table_section; + U16 reserved2; + U64 guard_rf_verify_stack_pointer_func_ptr; + U32 hot_patch_table_offset; + + // msvc 2019 + U32 reserved3; + U64 enclave_config_ptr; + U64 volatile_metadata_ptr; + U64 guard_eh_continue_table; + U64 guard_eh_continue_count; + U64 guard_xfg_check_func_ptr; + U64 guard_xfg_dispatch_func_ptr; + U64 guard_xfg_table_dispatch_func_ptr; + U64 cast_guard_os_determined_failure_mode; +}; + // this is the "MZ" as a 16-bit short #define PE_DOS_MAGIC 0x5a4d #define PE_MAGIC 0x00004550u @@ -362,10 +525,10 @@ struct PE_IntelPdata typedef struct PE_CvHeaderPDB20 PE_CvHeaderPDB20; struct PE_CvHeaderPDB20 { - U32 magic; - U32 offset; - U32 time; - U32 age; + U32 magic; + U32 offset; + COFF_TimeStamp time_stamp; + U32 age; // file name packed after struct }; @@ -389,11 +552,11 @@ struct PE_CvHeaderRDI typedef struct PE_ImportEntry PE_ImportEntry; struct PE_ImportEntry { - U32 lookup_table_voff; + U32 lookup_table_voff; COFF_TimeStamp time_stamp; - U32 forwarder_chain; - U32 name_voff; - U32 import_addr_table_voff; + U32 forwarder_chain; + U32 name_voff; + U32 import_addr_table_voff; }; typedef struct PE_DelayedImportEntry PE_DelayedImportEntry; @@ -401,55 +564,55 @@ struct PE_DelayedImportEntry { // According to COFF/PE spec this field is unused and should be set zero, // but when I compile mule with MSVC 2019 this is set to 1. - U32 attributes; - U32 name_voff; // Name of the DLL - U32 module_handle_voff; // Place where module handle from LoadLibrary is stored - U32 iat_voff; - U32 name_table_voff; // Array of hint/name or oridnals - U32 bound_table_voff; // (Optional) Points to an array of PE_ThunkData - U32 unload_table_voff; // (Optional) Copy of iat_voff + U32 attributes; + U32 name_voff; // Name of the DLL + U32 module_handle_voff; // Place where module handle from LoadLibrary is stored + U32 iat_voff; + U32 name_table_voff; // Array of hint/name or oridnals + U32 bound_table_voff; // (Optional) Points to an array of PE_ThunkData + U32 unload_table_voff; // (Optional) Copy of iat_voff // 0 not bound // -1 if bound and real timestamp located in bounded import directory // Otherwise time when dll was bound - COFF_TimeStamp time_stamp; + COFF_TimeStamp time_stamp; }; -typedef struct PE_ExportTable PE_ExportTable; -struct PE_ExportTable +typedef struct PE_ExportTableHeader PE_ExportTableHeader; +struct PE_ExportTableHeader { - U32 flags; // must be zero - COFF_TimeStamp time_stamp; // time and date when export table was created - U16 major_ver; // table version, user can change major and minor version - U16 minor_ver; - U32 name_voff; // ASCII name of the dll - U32 ordinal_base; // Starting oridnal number - U32 export_address_table_count; - U32 name_pointer_table_count; - U32 export_address_table_voff; - U32 name_pointer_table_voff; - U32 ordinal_table_voff; + U32 flags; // must be zero + COFF_TimeStamp time_stamp; // time and date when export table was created + U16 major_ver; // table version, user can change major and minor version + U16 minor_ver; + U32 name_voff; // ASCII name of the dll + U32 ordinal_base; // Starting oridnal number + U32 export_address_table_count; + U32 name_pointer_table_count; + U32 export_address_table_voff; + U32 name_pointer_table_voff; + U32 ordinal_table_voff; }; typedef struct PE_TLSHeader32 PE_TLSHeader32; struct PE_TLSHeader32 { - U32 raw_data_start; // Range of initialized data that is copied for each thread from the image. - U32 raw_data_end; // (Typically points to .tls section) - U32 index_address; // Address where image loader places TLS slot index. - U32 callbacks_address; // Zero terminated list of callbacks used for initializing data with constructors. - U32 zero_fill_size; // Amount of memory to fill with zeroes in TLS. - U32 characteristics; // COFF_SectionFlags but only align flags are used. + U32 raw_data_start; // Range of initialized data that is copied for each thread from the image. + U32 raw_data_end; // (Typically points to .tls section) + U32 index_address; // Address where image loader places TLS slot index. + U32 callbacks_address; // Zero terminated list of callbacks used for initializing data with constructors. + U32 zero_fill_size; // Amount of memory to fill with zeroes in TLS. + COFF_SectionFlags characteristics; // COFF_SectionFlags but only align flags are used. }; typedef struct PE_TLSHeader64 PE_TLSHeader64; struct PE_TLSHeader64 { - U64 raw_data_start; // Range of initialized data that is copied for each thread from the image. - U64 raw_data_end; // (Typically points to .tls section) - U64 index_address; // Address where image loader places TLS slot index. - U64 callbacks_address; // Zero terminated list of callbacks used for initializing data with constructors. - U32 zero_fill_size; // Amount of memory to fill with zeroes in TLS. - U32 characteristics; // COFF_SectionFlags but only align flags are used. + U64 raw_data_start; // Range of initialized data that is copied for each thread from the image. + U64 raw_data_end; // (Typically points to .tls section) + U64 index_address; // Address where image loader places TLS slot index. + U64 callbacks_address; // Zero terminated list of callbacks used for initializing data with constructors. + U32 zero_fill_size; // Amount of memory to fill with zeroes in TLS. + COFF_SectionFlags characteristics; // COFF_SectionFlags but only align flags are used. }; global read_only U8 PE_RES_MAGIC[] = @@ -506,15 +669,15 @@ typedef struct PE_ResourceHeader PE_ResourceHeader; struct PE_ResourceHeader { COFF_ResourceHeaderPrefix prefix; - U16 type; - U16 pad0; - U16 name; - U16 pad1; - U32 data_version; - COFF_ResourceMemoryFlags memory_flags; - U16 language_id; - U32 version; - U32 characteristics; + U16 type; + U16 pad0; + U16 name; + U16 pad1; + U32 data_version; + COFF_ResourceMemoryFlags memory_flags; + U16 language_id; + U32 version; + U32 characteristics; }; typedef U16 PE_BaseRelocKind; @@ -605,6 +768,7 @@ union PE_UnwindCode #define PE_UNWIND_INFO_FLAGS_FROM_HDR(x) (((x) >> 3)&0x1F) #define PE_UNWIND_INFO_REG_FROM_FRAME(x) ((x)&0xF) #define PE_UNWIND_INFO_OFF_FROM_FRAME(x) (((x) >> 4)&0xF) +#define PE_UNWIND_INFO_GET_CODE_COUNT(x) (((x)+1) & ~1) typedef struct PE_UnwindInfo PE_UnwindInfo; struct PE_UnwindInfo @@ -638,8 +802,8 @@ read_only global String8 pe_dos_program = {pe_dos_program_data, sizeof(pe_dos_pr typedef struct PE_BaseRelocBlock PE_BaseRelocBlock; struct PE_BaseRelocBlock { - U64 page_virt_off; - U64 entry_count; + U64 page_virt_off; + U64 entry_count; U16 *entries; }; @@ -647,7 +811,7 @@ typedef struct PE_BaseRelocBlockNode PE_BaseRelocBlockNode; struct PE_BaseRelocBlockNode { PE_BaseRelocBlockNode *next; - PE_BaseRelocBlock v; + PE_BaseRelocBlock v; }; typedef struct PE_BaseRelocBlockList PE_BaseRelocBlockList; @@ -655,7 +819,7 @@ struct PE_BaseRelocBlockList { PE_BaseRelocBlockNode *first; PE_BaseRelocBlockNode *last; - U64 count; + U64 count; }; //- rjf: resources @@ -664,29 +828,27 @@ typedef struct PE_Resource PE_Resource; struct PE_Resource { COFF_ResourceID id; - PE_ResDataKind kind; + PE_ResDataKind kind; union { COFF_ResourceDataEntry leaf; struct PE_ResourceDir *dir; struct { - COFF_ResourceID type; - U32 data_version; - U32 version; + COFF_ResourceID type; + U32 data_version; + U32 version; COFF_ResourceMemoryFlags memory_flags; - String8 data; - } - coff_res; - } - u; + String8 data; + } coff_res; + } u; }; typedef struct PE_ResourceNode PE_ResourceNode; struct PE_ResourceNode { PE_ResourceNode *next; - PE_Resource data; + PE_Resource data; }; typedef struct PE_ResourceList PE_ResourceList; @@ -694,69 +856,197 @@ struct PE_ResourceList { PE_ResourceNode *first; PE_ResourceNode *last; - U64 count; + U64 count; }; typedef struct PE_ResourceArray PE_ResourceArray; struct PE_ResourceArray { PE_Resource *v; - U64 count; + U64 count; }; typedef struct PE_ResourceDir PE_ResourceDir; struct PE_ResourceDir { - U32 characteristics; - COFF_TimeStamp time_stamp; - U16 major_version; - U16 minor_version; + U32 characteristics; + COFF_TimeStamp time_stamp; + U16 major_version; + U16 minor_version; PE_ResourceList named_list; PE_ResourceList id_list; }; +//- exports & imports + +typedef struct PE_ParsedExport PE_ParsedExport; +struct PE_ParsedExport +{ + String8 forwarder; + String8 name; + U64 voff; + U64 ordinal; +}; + +typedef struct PE_ParsedExportTable PE_ParsedExportTable; +struct PE_ParsedExportTable +{ + U32 flags; + COFF_TimeStamp time_stamp; + U16 major_ver; + U16 minor_ver; + U64 ordinal_base; + U64 export_count; + PE_ParsedExport *exports; +}; + +typedef U32 PE_ParsedImportType; +enum PE_ParsedImportTypeEnum +{ + PE_ParsedImport_Null, + PE_ParsedImport_Ordinal, + PE_ParsedImport_Name, +}; + +typedef struct PE_ParsedImport PE_ParsedImport; +struct PE_ParsedImport +{ + PE_ParsedImportType type; + union + { + U16 ordinal; + struct + { + U64 hint; + String8 string; + } name; + } u; +}; + +typedef struct PE_ParsedStaticDLLImport PE_ParsedStaticDLLImport; +struct PE_ParsedStaticDLLImport +{ + String8 name; + U64 import_address_table_voff; + U64 import_name_table_voff; + COFF_TimeStamp time_stamp; + U64 forwarder_chain; + U64 import_count; + PE_ParsedImport *imports; +}; + +typedef struct PE_ParsedStaticImportTable PE_ParsedStaticImportTable; +struct PE_ParsedStaticImportTable +{ + U64 count; + PE_ParsedStaticDLLImport *v; +}; + +typedef struct PE_ParsedDelayDLLImport PE_ParsedDelayDLLImport; +struct PE_ParsedDelayDLLImport +{ + U32 attributes; + String8 name; + U64 module_handle_voff; + U64 iat_voff; + U64 name_table_voff; + U64 bound_table_voff; + U64 unload_table_voff; + COFF_TimeStamp time_stamp; + U64 bound_table_count; + U64 *bound_table; + U64 unload_table_count; + U64 *unload_table; + U64 import_count; + PE_ParsedImport *imports; +}; + +typedef struct PE_ParsedDelayImportTable PE_ParsedDelayImportTable; +struct PE_ParsedDelayImportTable +{ + U64 count; + PE_ParsedDelayDLLImport *v; +}; + +typedef struct PE_ParsedTLS PE_ParsedTLS; +struct PE_ParsedTLS +{ + PE_TLSHeader64 header; + U64 callback_count; + U64 *callback_addrs; +}; + +//////////////////////////////// +// SEH Scope Table + +typedef struct PE_HandlerScope PE_HandlerScope; +struct PE_HandlerScope +{ + U32 begin; + U32 end; + U32 handler; + U32 target; +}; + //- rjf: bundle typedef struct PE_BinInfo PE_BinInfo; struct PE_BinInfo { - U64 image_base; - U64 entry_point; - B32 is_pe32; - U64 virt_section_align; - U64 file_section_align; - U64 section_array_off; - U64 section_count; - U64 symbol_array_off; - U64 symbol_count; - U64 string_table_off; - U64 dbg_path_off; - U64 dbg_path_size; - Guid dbg_guid; - U32 dbg_age; - U32 dbg_time; - Arch arch; - Rng1U64 *data_dir_franges; - U32 data_dir_count; - PE_TLSHeader64 tls_header; + U64 image_base; + U64 entry_point; + B32 is_pe32; + U64 virt_section_align; + U64 file_section_align; + U64 section_array_off; + U64 section_count; + U64 symbol_array_off; + U64 symbol_count; + U64 string_table_off; + U64 dbg_path_off; + U64 dbg_path_size; + Guid dbg_guid; + U32 dbg_age; + U32 dbg_time; + Arch arch; + Rng1U64 *data_dir_franges; + U32 data_dir_count; + PE_TLSHeader64 tls_header; }; //////////////////////////////// //~ rjf: Basic Enum Functions internal U32 pe_slot_count_from_unwind_op_code(PE_UnwindOpCode opcode); -internal String8 pe_string_from_subsystem(PE_WindowsSubsystem subsystem); internal PE_WindowsSubsystem pe_subsystem_from_string(String8 string); +internal String8 pe_string_from_subsystem(PE_WindowsSubsystem x); +internal String8 pe_string_from_unwind_gpr_x64(PE_UnwindGprRegX64 x); +internal String8 pe_string_from_data_directory_index(PE_DebugDirectoryType x); +internal String8 pe_string_from_debug_directory_type(PE_DebugDirectoryType x); +internal String8 pe_string_from_fpo_type(PE_FPOType x); +internal String8 pe_string_from_misc_type(PE_DebugMiscType x); +internal String8 pe_resource_kind_to_string(PE_ResourceKind x); + +internal String8 pe_string_from_fpo_flags(Arena *arena, PE_FPOFlags flags); +internal String8 pe_string_from_global_flags(Arena *arena, PE_GlobalFlags flags); +internal String8 pe_string_from_load_config_guard_flags(Arena *arena, PE_LoadConfigGuardFlags flags); +internal String8 pe_string_from_dll_characteristics(Arena *arena, PE_DllCharacteristics dll_chars); + //////////////////////////////// //~ rjf: Parser Functions internal PE_BinInfo pe_bin_info_from_data(Arena *arena, String8 data); +internal PE_ParsedStaticImportTable pe_static_imports_from_data(Arena *arena, B32 is_pe32, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 dir_file_range); +internal PE_ParsedDelayImportTable pe_delay_imports_from_data(Arena *arena, B32 is_pe32, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 dir_file_range); +internal PE_ParsedExportTable pe_exports_from_data(Arena *arena, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 dir_file_range, Rng1U64 dir_virt_range); +internal PE_ParsedTLS pe_tls_from_data(Arena *arena, COFF_MachineType machine, U64 image_base, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 tls_frange); + //////////////////////////////// //~ rjf: Helpers -internal U64 pe_intel_pdata_off_from_voff__binary_search(String8 data, PE_BinInfo *bin, U64 voff); +internal U64 pe_pdata_off_from_voff__binary_search_x8664(String8 raw_data, U64 voff); internal void * pe_ptr_from_voff(String8 data, PE_BinInfo *bin, U64 voff); internal U64 pe_section_num_from_voff(String8 data, PE_BinInfo *bin, U64 voff); internal void * pe_ptr_from_section_num(String8 data, PE_BinInfo *bin, U64 n);