/**************************** coff.cpp *********************************** * Author: Agner Fog * Date created: 2006-07-15 * Last modified: 2009-01-22 * Project: objconv * Module: coff.cpp * Description: * Module for reading PE/COFF files * * Class CCOFF is used for reading, interpreting and dumping PE/COFF files. * * Copyright 2006-2009 GNU General Public License http://www.gnu.org/licenses *****************************************************************************/ #include "stdafx.h" // Relocation type names SIntTxt COFF32RelNames[] = { {COFF32_RELOC_ABS, "Absolute"}, // Ignored {COFF32_RELOC_DIR32, "Direct32"}, // 32-bit absolute virtual address {COFF32_RELOC_IMGREL, "Image relative"}, // 32-bit image relative virtual address {COFF32_RELOC_SECTION, "Section16"}, // 16-bit section index in file {COFF32_RELOC_SECREL, "Section relative"}, // 32-bit section-relative {COFF32_RELOC_SECREL7, "7 bit section relative"}, // 7-bit section-relative {COFF32_RELOC_TOKEN, "CLR token"}, // CLR token {COFF32_RELOC_REL32, "EIP relative"} // 32-bit relative to end of address field }; SIntTxt COFF64RelNames[] = { {COFF64_RELOC_ABS, "Ignore"}, // Ignored {COFF64_RELOC_ABS64, "64 bit absolute"}, // 64 bit absolute virtual address {COFF64_RELOC_ABS32, "32 bit absolute"}, // 32 bit absolute virtual address {COFF64_RELOC_IMGREL, "Image relative"}, // 32 bit image-relative {COFF64_RELOC_REL32, "RIP relative"}, // 32 bit, RIP-relative {COFF64_RELOC_REL32_1, "RIP relative-1"}, // 32 bit, relative to RIP - 1. For instruction with immediate byte operand {COFF64_RELOC_REL32_2, "RIP relative-2"}, // 32 bit, relative to RIP - 2. For instruction with immediate word operand {COFF64_RELOC_REL32_3, "RIP relative-3"}, // 32 bit, relative to RIP - 3. Useless {COFF64_RELOC_REL32_4, "RIP relative-4"}, // 32 bit, relative to RIP - 4. For instruction with immediate dword operand {COFF64_RELOC_REL32_5, "RIP relative-5"}, // 32 bit, relative to RIP - 5. Useless {COFF32_RELOC_SECTION, "Section index"}, // 16-bit section index in file {COFF64_RELOC_SECREL, "Section relative"}, // 32-bit section-relative {COFF64_RELOC_SECREL7, "7 bit section rel"},// 7-bit section-relative {COFF64_RELOC_TOKEN, "CLR token"}, // 64 bit absolute virtual address without inline addend {COFF64_RELOC_SREL32, "32b span dependent"}, // {COFF64_RELOC_PAIR, "pair after span dependent"}, // {COFF64_RELOC_PPC_REFHI,"high 16 of 32 bit abs"}, // {COFF64_RELOC_PPC_REFLO,"low 16 of 32 bit abs"}, // {COFF64_RELOC_PPC_PAIR, "pair after high 16"}, // {COFF64_RELOC_PPC_SECRELO,"low 16 of 32 bit section relative"}, {COFF64_RELOC_PPC_GPREL, "16 bit GP relative"}, // {COFF64_RELOC_PPC_TOKEN, "CLR token"} // }; // Machine names SIntTxt COFFMachineNames[] = { {0, "Any/unknown"}, // Any machine/unknown {0x184, "Alpha"}, // Alpha AXP {0x1C0, "Arm"}, // Arm {0x284, "Alpha 64 bit"}, // Alpha AXP 64 bit {0x14C, "I386"}, // x86, 32 bit {0x200, "IA64"}, // Intel Itanium {0x268, "Motorola68000"}, // Motorola 68000 series {0x266, "MIPS16"}, {0x366, "MIPSwFPU"}, {0x466, "MIPS16wFPU"}, {0x1F0, "PowerPC"}, {0x1F1, "PowerPC"}, {0x162, "R3000"}, {0x166, "R4000MIPS"}, {0x168, "R10000"}, {0x1A2, "SH3"}, {0x1A6, "SH4"}, {0x1C2, "Thumb"}, {0x8664, "x86-64"} // x86-64 / AMD64 / Intel EM64T }; // Storage class names SIntTxt COFFStorageClassNames[] = { {COFF_CLASS_END_OF_FUNCTION, "EndOfFunc"}, {COFF_CLASS_AUTOMATIC, "AutoVariable"}, {COFF_CLASS_EXTERNAL, "External/Public"}, {COFF_CLASS_STATIC, "Static/Nonpublic"}, {COFF_CLASS_REGISTER, "Register"}, {COFF_CLASS_EXTERNAL_DEF, "ExternalDef"}, {COFF_CLASS_LABEL, "Label"}, {COFF_CLASS_UNDEFINED_LABEL, "UndefLabel"}, {COFF_CLASS_MEMBER_OF_STRUCTURE, "StructMem"}, {COFF_CLASS_ARGUMENT, "FuncArgument"}, {COFF_CLASS_STRUCTURE_TAG, "StructTag"}, {COFF_CLASS_MEMBER_OF_UNION, "UnionMember"}, {COFF_CLASS_UNION_TAG, "UnionTag"}, {COFF_CLASS_TYPE_DEFINITION, "TypeDef"}, {COFF_CLASS_UNDEFINED_STATIC, "UndefStatic"}, {COFF_CLASS_ENUM_TAG, "EnumTag"}, {COFF_CLASS_MEMBER_OF_ENUM, "EnumMem"}, {COFF_CLASS_REGISTER_PARAM, "RegisterParameter"}, {COFF_CLASS_BIT_FIELD, "BitField"}, {COFF_CLASS_AUTO_ARGUMENT, "AutoArgument"}, {COFF_CLASS_LASTENTRY, "DummyLastEntry"}, {COFF_CLASS_BLOCK, "bb/eb_block"}, {COFF_CLASS_FUNCTION, "Function_bf/ef"}, {COFF_CLASS_END_OF_STRUCT, "EndOfStruct"}, {COFF_CLASS_FILE, "FileName"}, {COFF_CLASS_LINE, "LineNumber"}, {COFF_CLASS_SECTION, "SectionLineNumber"}, {COFF_CLASS_ALIAS, "Alias"}, {COFF_CLASS_WEAK_EXTERNAL, "WeakExternal"}, {COFF_CLASS_HIDDEN, "Hidden"} }; // Names of section characteristics SIntTxt COFFSectionFlagNames[] = { {PE_SCN_CNT_CODE, "Text"}, {PE_SCN_CNT_INIT_DATA, "Data"}, {PE_SCN_CNT_UNINIT_DATA, "BSS"}, {PE_SCN_LNK_INFO, "Comments"}, {PE_SCN_LNK_REMOVE, "Remove"}, {PE_SCN_LNK_COMDAT, "Comdat"}, /* {PE_SCN_ALIGN_1, "Align by 1"}, {PE_SCN_ALIGN_2, "Align by 2"}, {PE_SCN_ALIGN_4, "Align by 4"}, {PE_SCN_ALIGN_8, "Align by 8"}, {PE_SCN_ALIGN_16, "Align by 16"}, {PE_SCN_ALIGN_32, "Align by 32"}, {PE_SCN_ALIGN_64, "Align by 64"}, {PE_SCN_ALIGN_128, "Align by 128"}, {PE_SCN_ALIGN_256, "Align by 256"}, {PE_SCN_ALIGN_512, "Align by 512"}, {PE_SCN_ALIGN_1024, "Align by 1024"}, {PE_SCN_ALIGN_2048, "Align by 2048"}, {PE_SCN_ALIGN_4096, "Align by 4096"}, {PE_SCN_ALIGN_8192, "Align by 8192"}, */ {PE_SCN_LNK_NRELOC_OVFL, "extended relocations"}, {PE_SCN_MEM_DISCARDABLE, "Discardable"}, {PE_SCN_MEM_NOT_CACHED, "Cannot be cached"}, {PE_SCN_MEM_NOT_PAGED, "Not pageable"}, {PE_SCN_MEM_SHARED, "Can be shared"}, {PE_SCN_MEM_EXECUTE, "Executable"}, {PE_SCN_MEM_READ, "Readable"}, {PE_SCN_MEM_WRITE, "Writeable"} }; // Names of image data directories in optional header SIntTxt COFFImageDirNames[] = { {0, "Export_table"}, {1, "Import_table"}, {2, "Resource_table"}, {3, "Exception_table"}, {4, "Certificate_table"}, {5, "Base_relocation_table"}, {6, "Debug_table"}, {7, "Architecture_table"}, {8, "Global_pointer"}, {9, "Thread_local_storage_table"}, {10, "Load_configuration_table"}, {11, "Bound_import_table"}, {12, "Import_address_table"}, {13, "Delay_import_descriptor"}, {14, "Common_language_runtime_header"}, {15, "Reserved_table"} }; // Class CCOFF members: // Constructor CCOFF::CCOFF() { // Set everything to zero memset(this, 0, sizeof(*this)); } void CCOFF::ParseFile(){ // Load and parse file buffer // Get offset to file header uint32 FileHeaderOffset = 0; if ((Get(0) & 0xFFF9) == 0x5A49) { // File has DOS stub uint32 Signature = Get(0x3C); if (Signature + 8 < DataSize && Get(Signature) == 0x4550) { // Executable PE file FileHeaderOffset = Signature + 4; } else { err.submit(9000); return; } } // Find file header FileHeader = &Get(FileHeaderOffset); NSections = FileHeader->NumberOfSections; // check header integrity if ((uint64)FileHeader->PSymbolTable + FileHeader->NumberOfSymbols * SIZE_SCOFF_SymTableEntry > GetDataSize()) err.submit(2035); // Find optional header if executable file if (FileHeader->SizeOfOptionalHeader && FileHeaderOffset) { OptionalHeader = &Get(FileHeaderOffset + sizeof(SCOFF_FileHeader)); // Find image data directories if (OptionalHeader) { if (OptionalHeader->h64.Magic == COFF_Magic_PE64) { // 64 bit version pImageDirs = &(OptionalHeader->h64.ExportTable); NumImageDirs = OptionalHeader->h64.NumberOfRvaAndSizes; EntryPoint = OptionalHeader->h64.AddressOfEntryPoint; ImageBase = OptionalHeader->h64.ImageBase; } else { // 32 bit version pImageDirs = &(OptionalHeader->h32.ExportTable); NumImageDirs = OptionalHeader->h32.NumberOfRvaAndSizes; EntryPoint = OptionalHeader->h32.AddressOfEntryPoint; ImageBase = OptionalHeader->h32.ImageBase; } } } // Allocate buffer for section headers SectionHeaders.SetNum(NSections); SectionHeaders.SetZero(); // Find section headers uint32 SectionOffset = FileHeaderOffset + sizeof(SCOFF_FileHeader) + FileHeader->SizeOfOptionalHeader; for (int i = 0; i < NSections; i++) { SectionHeaders[i] = Get(SectionOffset); SectionOffset += sizeof(SCOFF_SectionHeader); // Check for _ILDATA section if (strcmp(SectionHeaders[i].Name, "_ILDATA") == 0) { // This is an intermediate file for Intel compiler err.submit(2114); } } if (SectionOffset > GetDataSize()) { err.submit(2110); return; // Section table points to outside file } // Find symbol table SymbolTable = &Get(FileHeader->PSymbolTable); NumberOfSymbols = FileHeader->NumberOfSymbols; // Find string table StringTable = (Buf() + FileHeader->PSymbolTable + NumberOfSymbols * SIZE_SCOFF_SymTableEntry); StringTableSize = *(int*)StringTable; // First 4 bytes of string table contains its size } // Debug dump void CCOFF::Dump(int options) { uint32 i, j; if (options & DUMP_FILEHDR) { // File header printf("\nDump of PE/COFF file %s", FileName); printf("\n-----------------------------------------------"); printf("\nFile size: %i", GetDataSize()); printf("\nFile header:"); printf("\nMachine: %s", Lookup(COFFMachineNames,FileHeader->Machine)); printf("\nTimeDate: 0x%08X", FileHeader->TimeDateStamp); printf(" - %s", timestring(FileHeader->TimeDateStamp)); printf("\nNumber of sections: %2i", FileHeader->NumberOfSections); printf("\nNumber of symbols: %2i", FileHeader->NumberOfSymbols); printf("\nOptional header size: %i", FileHeader->SizeOfOptionalHeader); printf("\nFlags: 0x%04X", FileHeader->Flags); // May be removed: printf("\nSymbol table offset: 0x%X", FileHeader->PSymbolTable); printf("\nString table offset: 0x%X", FileHeader->PSymbolTable + FileHeader->NumberOfSymbols * SIZE_SCOFF_SymTableEntry); printf("\nSection headers offset: 0x%X", (uint32)sizeof(SCOFF_FileHeader) + FileHeader->SizeOfOptionalHeader); // Optional header if (OptionalHeader) { printf("\n\nOptional header:"); if (OptionalHeader->h32.Magic != COFF_Magic_PE64) { // 32 bit optional header printf("\nMagic number: 0x%X", OptionalHeader->h32.Magic); printf("\nSize of code: 0x%X", OptionalHeader->h32.SizeOfCode); printf("\nSize of uninitialized data: 0x%X", OptionalHeader->h32.SizeOfInitializedData); printf("\nAddress of entry point: 0x%X", OptionalHeader->h32.AddressOfEntryPoint); printf("\nBase of code: 0x%X", OptionalHeader->h32.BaseOfCode); printf("\nBase of data: 0x%X", OptionalHeader->h32.BaseOfData); printf("\nImage base: 0x%X", OptionalHeader->h32.ImageBase); printf("\nSection alignment: 0x%X", OptionalHeader->h32.SectionAlignment); printf("\nFile alignment: 0x%X", OptionalHeader->h32.FileAlignment); printf("\nSize of image: 0x%X", OptionalHeader->h32.SizeOfImage); printf("\nSize of headers: 0x%X", OptionalHeader->h32.SizeOfHeaders); printf("\nDll characteristics: 0x%X", OptionalHeader->h32.DllCharacteristics); printf("\nSize of stack reserve: 0x%X", OptionalHeader->h32.SizeOfStackReserve); printf("\nSize of stack commit: 0x%X", OptionalHeader->h32.SizeOfStackCommit); printf("\nSize of heap reserve: 0x%X", OptionalHeader->h32.SizeOfHeapReserve); printf("\nSize of heap commit: 0x%X", OptionalHeader->h32.SizeOfHeapCommit); } else { // 64 bit optional header printf("\nMagic number: 0x%X", OptionalHeader->h64.Magic); printf("\nSize of code: 0x%X", OptionalHeader->h64.SizeOfCode); printf("\nSize of uninitialized data: 0x%X", OptionalHeader->h64.SizeOfInitializedData); printf("\nAddress of entry point: 0x%X", OptionalHeader->h64.AddressOfEntryPoint); printf("\nBase of code: 0x%X", OptionalHeader->h64.BaseOfCode); printf("\nImage base: 0x%08X%08X", HighDWord(OptionalHeader->h64.ImageBase), uint32(OptionalHeader->h64.ImageBase)); printf("\nSection alignment: 0x%X", OptionalHeader->h64.SectionAlignment); printf("\nFile alignment: 0x%X", OptionalHeader->h64.FileAlignment); printf("\nSize of image: 0x%X", OptionalHeader->h64.SizeOfImage); printf("\nSize of headers: 0x%X", OptionalHeader->h64.SizeOfHeaders); printf("\nDll characteristics: 0x%X", OptionalHeader->h64.DllCharacteristics); printf("\nSize of stack reserve: 0x%08X%08X", HighDWord(OptionalHeader->h64.SizeOfStackReserve), uint32(OptionalHeader->h64.SizeOfStackReserve)); printf("\nSize of stack commit: 0x%08X%08X", HighDWord(OptionalHeader->h64.SizeOfStackCommit), uint32(OptionalHeader->h64.SizeOfStackCommit)); printf("\nSize of heap reserve: 0x%08X%08X", HighDWord(OptionalHeader->h64.SizeOfHeapReserve), uint32(OptionalHeader->h64.SizeOfHeapReserve)); printf("\nSize of heap commit: 0x%08X%08X", HighDWord(OptionalHeader->h64.SizeOfHeapCommit), uint32(OptionalHeader->h64.SizeOfHeapCommit)); } // Data directories SCOFF_ImageDirAddress dir; for (i = 0; i < NumImageDirs; i++) { if (GetImageDir(i, &dir)) { printf("\nDirectory %2i, %s:\n Address 0x%04X, Size 0x%04X, Section %i, Offset 0x%04X", i, dir.Name, dir.VirtualAddress, dir.Size, dir.Section, dir.SectionOffset); } } } } if ((options & DUMP_STRINGTB) && FileHeader->PSymbolTable && StringTableSize > 4) { // String table char * p = StringTable + 4; uint32 nread = 4, len; printf("\n\nString table:"); while (nread < StringTableSize) { len = (int)strlen(p); if (len > 0) { printf("\n>>%s<<", p); nread += len + 1; p += len + 1; } } } // Symbol tables if (options & DUMP_SYMTAB) { // Symbol table (object file) if (NumberOfSymbols) PrintSymbolTable(-1); // Import and export tables (executable file) if (OptionalHeader) PrintImportExport(); } // Section headers if (options & (DUMP_SECTHDR | DUMP_SYMTAB | DUMP_RELTAB)) { for (j = 0; j < (uint32)NSections; j++) { SCOFF_SectionHeader * SectionHeader = &SectionHeaders[j]; printf("\n\n%2i Section %s", j+1, GetSectionName(SectionHeader->Name)); //printf("\nFile offset of header: 0x%X", (int)((int8*)SectionHeader-buffer)); printf("\nVirtual size: 0x%X", SectionHeader->VirtualSize); if (SectionHeader->VirtualAddress) { printf("\nVirtual address: 0x%X", SectionHeader->VirtualAddress);} if (SectionHeader->PRawData || SectionHeader->SizeOfRawData) { printf("\nSize of raw data: 0x%X", SectionHeader->SizeOfRawData); printf("\nRaw data pointer: 0x%X", SectionHeader->PRawData); } printf("\nCharacteristics: "); PrintSegmentCharacteristics(SectionHeader->Flags); // print relocations if ((options & DUMP_RELTAB) && SectionHeader->NRelocations > 0) { printf("\nRelocation entries: %i", SectionHeader->NRelocations); printf("\nRelocation entries pointer: 0x%X", SectionHeader->PRelocations); // Pointer to relocation entry union { SCOFF_Relocation * p; // pointer to record int8 * b; // used for address calculation and incrementing } Reloc; Reloc.b = Buf() + SectionHeader->PRelocations; printf("\nRelocations:"); for (i = 0; i < SectionHeader->NRelocations; i++) { printf("\nAddr: 0x%X, symi: %i, type: %s", Reloc.p->VirtualAddress, Reloc.p->SymbolTableIndex, (WordSize == 32) ? Lookup(COFF32RelNames,Reloc.p->Type) : Lookup(COFF64RelNames,Reloc.p->Type)); if (Reloc.p->Type < COFF32_RELOC_SEG12) { // Check if address is within file if (SectionHeader->PRawData + Reloc.p->VirtualAddress < GetDataSize()) { int32 addend = *(int32*)(Buf() + SectionHeader->PRawData + Reloc.p->VirtualAddress); if (addend) printf(", Implicit addend: %i", addend); } else { printf(". Error: Address is outside file"); } } PrintSymbolTable(Reloc.p->SymbolTableIndex); Reloc.b += SIZE_SCOFF_Relocation; // Next relocation record } } // print line numbers if (SectionHeader->NLineNumbers > 0) { printf("\nLine number entries: %i", SectionHeader->NLineNumbers); printf(" Line number pointer: %i\nLines:", SectionHeader->PLineNumbers); // Pointer to line number entry union { SCOFF_LineNumbers * p; // pointer to record int8 * b; // used for address calculation and incrementing } Linnum; Linnum.b = Buf() + SectionHeader->PLineNumbers; for (i = 0; i < SectionHeader->NLineNumbers; i++) { if (Linnum.p->Line) { // Record contains line number printf(" %i:%i", Linnum.p->Line, Linnum.p->Addr); } else { // Record contains function name } Linnum.b += SIZE_SCOFF_LineNumbers; // Next line number record } } } } } char const * CCOFF::GetSymbolName(int8* Symbol) { // Get symbol name from 8 byte entry static char text[16]; if (*(uint32*)Symbol != 0) { // Symbol name not more than 8 bytes memcpy(text, Symbol, 8); // Copy to local buffer text[8] = 0; // Append terminating zero return text; // Return text } else { // Longer than 8 bytes. Get offset into string table uint32 offset = *(uint32*)(Symbol + 4); if (offset >= StringTableSize || offset >= GetDataSize()) {err.submit(2035); return "";} char * s = StringTable + offset; if (*s) return s; // Return string table entry } return "NULL"; // String table entry was empty } char const * CCOFF::GetSectionName(int8* Symbol) { // Get section name from 8 byte entry static char text[16]; memcpy(text, Symbol, 8); // Copy to local buffer text[8] = 0; // Append terminating zero if (text[0] == '/') { // Long name is in string table. // Convert decimal ASCII number to string table index uint32 sindex = atoi(text + 1); // Get name from string table if (sindex < StringTableSize) { char * s = StringTable + sindex; if (*s) return s;} // Return string table entry } else { // Short name is in text buffer return text; } return "NULL"; // In case of error } char const * CCOFF::GetStorageClassName(uint8 sc) { // Get storage class name return Lookup(COFFStorageClassNames, sc); } void CCOFF::PrintSegmentCharacteristics(uint32 flags) { // Print segment characteristics int n = 0; // Loop through all bits of integer for (uint32 i = 1; i != 0; i <<= 1) { if (i & flags & ~PE_SCN_ALIGN_MASK) { if (n++) printf(", "); printf("%s", Lookup(COFFSectionFlagNames, i)); } } if (flags & PE_SCN_ALIGN_MASK) { int a = 1 << (((flags & PE_SCN_ALIGN_MASK) / PE_SCN_ALIGN_1) - 1); printf(", Align by 0x%X", a); n++; } if (n == 0) printf("None"); } const char * CCOFF::GetFileName(SCOFF_SymTableEntry * syme) { // Get file name from records in symbol table if (syme->s.NumAuxSymbols < 1 || syme->s.StorageClass != COFF_CLASS_FILE) { return ""; // No file name found } // Set limit to file name length = 576 const uint32 MAXCOFFFILENAMELENGTH = 32 * SIZE_SCOFF_SymTableEntry; // Buffer to store file name. Must be static static char text[MAXCOFFFILENAMELENGTH+1]; // length of name in record uint32 len = syme->s.NumAuxSymbols * SIZE_SCOFF_SymTableEntry; if (len > MAXCOFFFILENAMELENGTH) len = MAXCOFFFILENAMELENGTH; // copy name from auxiliary records memcpy(text, (int8*)syme + SIZE_SCOFF_SymTableEntry, len); // Terminate string text[len] = 0; // Return name return text; } const char * CCOFF::GetShortFileName(SCOFF_SymTableEntry * syme) { // Same as above. Strips path before filename // Full file name const char * fullname = GetFileName(syme); // Length uint32 len = (uint32)strlen(fullname); if (len < 1) return fullname; // Scan backwards for '/', '\', ':' for (int scan = len-2; scan >= 0; scan--) { char c = fullname[scan]; if (c == '/' || c == '\\' || c == ':') { // Path found. Short name starts after this character return fullname + scan + 1; } } // No path found. Return full name return fullname; } void CCOFF::PrintSymbolTable(int symnum) { // Print one or all public symbols for object file. // Dump symbol table if symnum = -1, or // Dump symbol number symnum (zero based) when symnum >= 0 int isym = 0; // current symbol table entry int jsym = 0; // auxiliary entry number union { // Pointer to symbol table SCOFF_SymTableEntry * p; // Normal pointer int8 * b; // Used for address calculation } Symtab; Symtab.p = SymbolTable; // Set pointer to begin of SymbolTable if (symnum == -1) printf("\n\nSymbol table:"); if (symnum >= 0) { // Print one symbol only if (symnum >= NumberOfSymbols) { printf("\nSymbol %i not found", symnum); return; } isym = symnum; Symtab.b += SIZE_SCOFF_SymTableEntry * isym; } while (isym < NumberOfSymbols) { // Print symbol table entry SCOFF_SymTableEntry *s0; printf("\n"); if (symnum >= 0) printf(" "); printf("Symbol %i - Name: %s\n Value=%i, ", isym, GetSymbolName(Symtab.p->s.Name), Symtab.p->s.Value); if (Symtab.p->s.SectionNumber > 0) { printf("Section=%i", Symtab.p->s.SectionNumber); } else { // Special section numbers switch (Symtab.p->s.SectionNumber) { case COFF_SECTION_UNDEF: printf("External"); break; case COFF_SECTION_ABSOLUTE: printf("Absolute"); break; case COFF_SECTION_DEBUG: printf("Debug"); break; case COFF_SECTION_N_TV: printf("Preload transfer"); break; case COFF_SECTION_P_TV: printf("Postload transfer"); break; } } printf(", Type=0x%X, StorClass=%s, NumAux=%i", Symtab.p->s.Type, GetStorageClassName(Symtab.p->s.StorageClass), Symtab.p->s.NumAuxSymbols); if (Symtab.p->s.StorageClass == COFF_CLASS_FILE && Symtab.p->s.NumAuxSymbols > 0) { printf("\n File name: %s", GetFileName(Symtab.p)); } // Increment point s0 = Symtab.p; Symtab.b += SIZE_SCOFF_SymTableEntry; isym++; jsym = 0; // Get auxiliary records while (jsym < s0->s.NumAuxSymbols && isym + jsym < NumberOfSymbols) { // Print auxiliary symbol table entry SCOFF_SymTableEntry * sa = Symtab.p; // Detect auxiliary entry type if (s0->s.StorageClass == COFF_CLASS_EXTERNAL && s0->s.Type == COFF_TYPE_FUNCTION && s0->s.SectionNumber > 0) { // This is a function definition aux record printf("\n Aux function definition:"); printf("\n .bf_tag_index: 0x%X, code_size: %i, PLineNumRec: %i, PNext: %i", sa->func.TagIndex, sa->func.TotalSize, sa->func.PointerToLineNumber, sa->func.PointerToNextFunction); } else if (strcmp(s0->s.Name,".bf")==0 || strcmp(s0->s.Name,".ef")==0) { // This is a .bf or .ef aux record printf("\n Aux .bf/.ef definition:"); printf("\n Source line number: %i", sa->bfef.SourceLineNumber); if (strcmp(s0->s.Name,".bf")==0 ) { printf(", PNext: %i", sa->bfef.PointerToNextFunction); } } else if (s0->s.StorageClass == COFF_CLASS_EXTERNAL && s0->s.SectionNumber == COFF_SECTION_UNDEF && s0->s.Value == 0) { // This is a Weak external aux record printf("\n Aux Weak external definition:"); printf("\n Symbol2 index: %i, Characteristics: 0x%X", sa->weak.TagIndex, sa->weak.Characteristics); } else if (s0->s.StorageClass == COFF_CLASS_FILE) { // This is filename aux record. Contents has already been printed } else if (s0->s.StorageClass == COFF_CLASS_STATIC) { // This is section definition aux record printf("\n Aux section definition record:"); printf("\n Length: %i, Num. relocations: %i, Num linenums: %i, checksum 0x%X," "\n Number: %i, Selection: %i", sa->section.Length, sa->section.NumberOfRelocations, sa->section.NumberOfLineNumbers, sa->section.CheckSum, sa->section.Number, sa->section.Selection); } else if (s0->s.StorageClass == COFF_CLASS_ALIAS) { // This is section definition aux record printf("\n Aux alias definition record:"); printf("\n symbol index: %i, ", sa->weak.TagIndex); switch (sa->weak.Characteristics) { case IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY: printf("no library search"); break; case IMAGE_WEAK_EXTERN_SEARCH_LIBRARY: printf("library search"); break; case IMAGE_WEAK_EXTERN_SEARCH_ALIAS: printf("alias symbol"); break; default: printf("unknown characteristics 0x%X", sa->weak.Characteristics); } } else { // Unknown aux record type printf("\n Unknown Auxiliary record type %i", s0->s.StorageClass); } Symtab.b += SIZE_SCOFF_SymTableEntry; jsym++; } isym += jsym; if (symnum >= 0) break; } } void CCOFF::PublicNames(CMemoryBuffer * Strings, CSList * Index, int m) { // Make list of public names in object file // Strings will receive ASCIIZ strings // Index will receive records of type SStringEntry with Member = m // Interpret header: ParseFile(); int isym = 0; // current symbol table entry union { // Pointer to symbol table SCOFF_SymTableEntry * p; // Normal pointer int8 * b; // Used for address calculation } Symtab; // Loop through symbol table Symtab.p = SymbolTable; while (isym < NumberOfSymbols) { // Check within buffer if (Symtab.b >= Buf() + DataSize) { err.submit(2040); break; } // Search for public symbol if (Symtab.p->s.SectionNumber > 0 && Symtab.p->s.StorageClass == COFF_CLASS_EXTERNAL) { // Public symbol found SStringEntry se; se.Member = m; // Store name se.String = Strings->PushString(GetSymbolName(Symtab.p->s.Name)); // Store name index Index->Push(se); } if ((int8)Symtab.p->s.NumAuxSymbols < 0) Symtab.p->s.NumAuxSymbols = 0; // Increment point isym += Symtab.p->s.NumAuxSymbols + 1; Symtab.b += (1 + Symtab.p->s.NumAuxSymbols) * SIZE_SCOFF_SymTableEntry; } } int CCOFF::GetImageDir(uint32 n, SCOFF_ImageDirAddress * dir) { // Find address of image directory for executable files int32 Section; uint32 FileOffset; if (pImageDirs == 0 || n >= NumImageDirs || dir == 0) { // Failure return 0; } // Get virtual address and size of directory dir->VirtualAddress = pImageDirs[n].VirtualAddress; dir->Size = pImageDirs[n].Size; dir->Name = Lookup(COFFImageDirNames, n); // Check if nonzero if (dir->VirtualAddress == 0 || dir->Size == 0) { // Empty return 0; } // Search for section containing this address for (Section = 0; Section < NSections; Section++) { if (dir->VirtualAddress >= SectionHeaders[Section].VirtualAddress && dir->VirtualAddress < SectionHeaders[Section].VirtualAddress + SectionHeaders[Section].SizeOfRawData) { // Found section dir->Section = Section + 1; // Section-relative offset dir->SectionOffset = dir->VirtualAddress - SectionHeaders[Section].VirtualAddress; // Calculate file offset FileOffset = SectionHeaders[Section].PRawData + dir->SectionOffset; if (FileOffset == 0 || FileOffset >= DataSize) { // points outside file err.submit(2035); return 0; } // FileOffset is within range dir->FileOffset = FileOffset; // Maximum allowed offset dir->MaxOffset = SectionHeaders[Section].SizeOfRawData - dir->SectionOffset; // Return success return Section; } } // Import section not found return 0; } void CCOFF::PrintImportExport() { // Print imported and exported symbols // Table directory address SCOFF_ImageDirAddress dir; uint32 i; // Index into OrdinalTable and NamePointerTable uint32 Ordinal; // Index into ExportAddressTable uint32 Address; // Virtual address of exported symbol uint32 NameOffset; // Section offset of symbol name uint32 SectionOffset; // Section offset of table const char * Name; // Name of symbol // Check if 64 bit int Is64bit = OptionalHeader->h64.Magic == COFF_Magic_PE64; // Exported names if (GetImageDir(0, &dir)) { // Beginning of export section is export directory SCOFF_ExportDirectory * pExportDirectory = &Get(dir.FileOffset); // Find ExportAddressTable SectionOffset = pExportDirectory->ExportAddressTableRVA - dir.VirtualAddress; if (SectionOffset == 0 || SectionOffset >= dir.MaxOffset) { // Points outside section err.submit(2035); return; } uint32 * pExportAddressTable = &Get(dir.FileOffset + SectionOffset); // Find ExportNameTable SectionOffset = pExportDirectory->NamePointerTableRVA - dir.VirtualAddress; if (SectionOffset == 0 || SectionOffset >= dir.MaxOffset) { // Points outside section err.submit(2035); return; } uint32 * pExportNameTable = &Get(dir.FileOffset + SectionOffset); // Find ExportOrdinalTable SectionOffset = pExportDirectory->OrdinalTableRVA - dir.VirtualAddress; if (SectionOffset == 0 || SectionOffset >= dir.MaxOffset) { // Points outside section err.submit(2035); return; } uint16 * pExportOrdinalTable = &Get(dir.FileOffset + SectionOffset); // Get further properties uint32 NumExports = pExportDirectory->AddressTableEntries; uint32 NumExportNames = pExportDirectory->NamePointerEntries; uint32 OrdinalBase = pExportDirectory->OrdinalBase; // Print exported names printf("\n\nExported symbols:"); // Loop through export tables for (i = 0; i < NumExports; i++) { Address = 0; Name = "(None)"; // Get ordinal from table Ordinal = pExportOrdinalTable[i]; // Address table is indexed by ordinal if (Ordinal < NumExports) { Address = pExportAddressTable[Ordinal]; } // Find name if there is a name list entry if (i < NumExportNames) { NameOffset = pExportNameTable[i] - dir.VirtualAddress; if (NameOffset && NameOffset < dir.MaxOffset) { Name = &Get(dir.FileOffset + NameOffset); } } // Print ordinal, address and name printf("\n Ordinal %3i, Address %6X, Name %s", Ordinal + OrdinalBase, Address, Name); } } // Imported names if (GetImageDir(1, &dir)) { // Print imported names printf("\n\nImported symbols:"); // Pointer to current import directory entry SCOFF_ImportDirectory * ImportEntry = &Get(dir.FileOffset); // Pointer to current import lookup table entry int32 * LookupEntry = 0; // Pointer to current hint/name table entry SCOFF_ImportHintName * HintNameEntry; // Loop through import directory until null entry while (ImportEntry->DLLNameRVA) { // Get DLL name NameOffset = ImportEntry->DLLNameRVA - dir.VirtualAddress; if (NameOffset < dir.MaxOffset) { Name = &Get(dir.FileOffset + NameOffset); } else { Name = "Error"; } // Print DLL name printf("\nFrom %s", Name); // Get lookup table SectionOffset = ImportEntry->ImportLookupTableRVA; if (SectionOffset == 0) SectionOffset = ImportEntry->ImportAddressTableRVA; if (SectionOffset == 0) continue; SectionOffset -= dir.VirtualAddress; if (SectionOffset >= dir.MaxOffset) break; // Out of range LookupEntry = &Get(dir.FileOffset + SectionOffset); // Loop through lookup table while (LookupEntry[0]) { if (LookupEntry[Is64bit] < 0) { // Imported by ordinal printf("\n Ordinal %i", uint16(LookupEntry[0])); } else { // Find entry in hint/name table SectionOffset = (LookupEntry[0] & 0x7FFFFFFF) - dir.VirtualAddress;; if (SectionOffset >= dir.MaxOffset) continue; // Out of range HintNameEntry = &Get(dir.FileOffset + SectionOffset); // Print name printf("\n %04X %s", HintNameEntry->Hint, HintNameEntry->Name); // Check if exported if (HintNameEntry->Hint) { // printf(", Export entry %i", HintNameEntry->Hint); } } // Loop next LookupEntry += Is64bit ? 2 : 1; } // Loop next ImportEntry++; } } } // Functions for manipulating COFF files uint32 COFF_PutNameInSymbolTable(SCOFF_SymTableEntry & sym, const char * name, CMemoryBuffer & StringTable) { // Function to put a name into SCOFF_SymTableEntry. // Put name in string table if longer than 8 characters. // Returns index into StringTable if StringTable used int len = (int)strlen(name); // Length of name if (len <= 8) { // Short name. store in section header memcpy(sym.s.Name, name, len); // Pad with zeroes for (; len < 8; len++) sym.s.Name[len] = 0; } else { // Long name. store in string table sym.stringindex.zeroes = 0; sym.stringindex.offset = StringTable.PushString(name); // Second integer = entry into string table return sym.stringindex.offset; } return 0; } void COFF_PutNameInSectionHeader(SCOFF_SectionHeader & sec, const char * name, CMemoryBuffer & StringTable) { // Function to put a name into SCOFF_SectionHeader. // Put name in string table if longer than 8 characters int len = (int)strlen(name); // Length of name if (len <= 8) { // Short name. store in section header memcpy(sec.Name, name, len); // Pad with zeroes for (; len < 8; len++) sec.Name[len] = 0; } else { // Long name. store in string table sprintf(sec.Name, "/%i", StringTable.PushString(name)); } }