发布于  更新于 

COFF文件格式解析

COFF

COFF全称是Common Object File Format,可以通过编译工具生成。

在CobaltStrike中被用于扩展更多的功能

https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics/beacon-object-files_main.htm

生成

1
2
3
4
5
#include <stdio.h>

void main() {
printf("Hello World");
}

i686-w64-mingw32-gcc -c hello.c -o hello_mingw32.o

010editor

为了学习COFF结构,实现了一个010editor的解析插件如下

COFF.bt

//------------------------------------------------
//--- 010 Editor v4.0.3 Binary Template
//
//      File: COFF.bt
//   Authors: guage
//    E-mail: zhaopeiyuan6@gmail.com
//   Website: https://guage.cool https://github.com/howmp/
//   Version: 0.1
//   Purpose: Parse Common Object File Format files.
//            Supports 32 and 64 bit.
//  Category: Executable
// File Mask: *.o,*.obj
//   History:
//   0.1     2022-08-12 guage: Initial commit
//
// Recommended reading:
// 1. https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#other-contents-of-the-file
// 2. https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/BinaryFormat/COFF.h

typedef struct
{
WORD IMAGE_FILE_RELOCS_STRIPPED:1 <comment=”0x0001 Relocation info stripped from file”>;
WORD IMAGE_FILE_EXECUTABLE_IMAGE:1 <comment=”0x0002 File is executable”>;
WORD IMAGE_FILE_LINE_NUMS_STRIPPED:1 <comment=”0x0004 Line nunbers stripped from file”>;
WORD IMAGE_FILE_LOCAL_SYMS_STRIPPED:1 <comment=”0x0008 Local symbols stripped from file”>;
WORD IMAGE_FILE_AGGRESIVE_WS_TRIM:1 <comment=”0x0010 Agressively trim working set”>;
WORD IMAGE_FILE_LARGE_ADDRESS_AWARE:1 <comment=”0x0020 App can handle >2gb addresses”>;
WORD :1 <comment=”0x0040 Reserved”,hidden=true>;
WORD IMAGE_FILE_BYTES_REVERSED_LO:1 <comment=”0x0080 Bytes of machine word are reversed”>;
WORD IMAGE_FILE_32BIT_MACHINE:1 <comment=”0x0100 32 bit word machine”>;
WORD IMAGE_FILE_DEBUG_STRIPPED:1 <comment=”0x0200 Debugging info stripped from file in .DBG file”>;
WORD IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP:1 <comment=”0x0400 If Image is on removable media, copy and run from the swap file”>;
WORD IMAGE_FILE_NET_RUN_FROM_SWAP:1 <comment=”0x0800 If Image is on Net, copy and run from the swap file”>;
WORD IMAGE_FILE_SYSTEM:1 <comment=”0x1000 System File”>;
WORD IMAGE_FILE_DLL:1 <comment=”0x2000 File is a DLL”>;
WORD IMAGE_FILE_UP_SYSTEM_ONLY:1 <comment=”0x4000 File should only be run on a UP machine”>;
WORD IMAGE_FILE_BYTES_REVERSED_HI:1 <comment=”0x8000 Bytes of machine word are reversed”>;
} FILE_CHARACTERISTICS <comment=”WORD”>;

typedef enum
{
IMAGE_MACHINE_UNKNOWN = 0,
I386 = 0x014c, // Intel 386 or later processors and compatible processors
R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
R4000 = 0x0166, // MIPS little-endian
R10000 = 0x0168, // MIPS little-endian
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
ALPHA = 0x0184, // Alpha_AXP
SH3 = 0x01a2, // Hitachi SH3
SH3DSP = 0x01a3, // Hitachi SH3 DSP
SH3E = 0x01a4, // Hitachi SH3E little-endian
SH4 = 0x01a6, // Hitachi SH4
SH5 = 0x01a8, // Hitachi SH5
ARM = 0x01c0, // ARM little-endian
THUMB = 0x01c2, // Thumb
ARMNT = 0x01c4, // Arm Thumb-2 little-endian
AM33 = 0x01d3, // Matsushita AM33
POWERPC = 0x01f0, // Power PC little endian
POWERPCFP = 0x01f1, // Power PC with floating point support
IA64 = 0x0200, // Intel Itanium processor family
MIPS16 = 0x0266, // MIPS16
ALPHA64 = 0x0284, // ALPHA64
MIPSFPU = 0x0366, // MIPS with FPU
MIPSFPU16 = 0x0466, // MIPS16 with FPU
TRICORE = 0x0520, // Infineon
CEF = 0x0CEF,
EBC = 0x0EBC, // EFI Byte Code
RISCV32 = 0x5032, // RISC-V 32-bit address space
RISCV64 = 0x5064, // RISC-V 64-bit address space
RISCV128 = 0x5128, // RISC-V 128-bit address space
AMD64 = 0x8664, // x64
M32R = 0x9041, // Mitsubishi M32R little-endian
ARM64 = 0xAA64, // ARM64 little-endian
CEE = 0xC0EE
} IMAGE_MACHINE <comment=”WORD”>;

typedef enum {
I386_ABSOLUTE = 0x0000,
I386_DIR16 = 0x0001,
I386_REL16 = 0x0002,
I386_DIR32 = 0x0006,
I386_DIR32NB = 0x0007,
I386_SEG12 = 0x0009,
I386_SECTION = 0x000A,
I386_SECREL = 0x000B,
I386_TOKEN = 0x000C,
I386_SECREL7 = 0x000D,
I386_REL32 = 0x0014
} RelocationTypeI386;

typedef enum {
AMD64_ABSOLUTE = 0x0000,
AMD64_ADDR64 = 0x0001,
AMD64_ADDR32 = 0x0002,
AMD64_ADDR32NB = 0x0003,
AMD64_REL32 = 0x0004,
AMD64_REL32_1 = 0x0005,
AMD64_REL32_2 = 0x0006,
AMD64_REL32_3 = 0x0007,
AMD64_REL32_4 = 0x0008,
AMD64_REL32_5 = 0x0009,
AMD64_SECTION = 0x000A,
AMD64_SECREL = 0x000B,
AMD64_SECREL7 = 0x000C,
AMD64_TOKEN = 0x000D,
AMD64_SREL32 = 0x000E,
AMD64_PAIR = 0x000F,
AMD64_SSPAN32 = 0x0010
} RelocationTypeAMD64;

typedef enum {
ARM_ABSOLUTE = 0x0000,
ARM_ADDR32 = 0x0001,
ARM_ADDR32NB = 0x0002,
ARM_BRANCH24 = 0x0003,
ARM_BRANCH11 = 0x0004,
ARM_TOKEN = 0x0005,
ARM_BLX24 = 0x0008,
ARM_BLX11 = 0x0009,
ARM_REL32 = 0x000A,
ARM_SECTION = 0x000E,
ARM_SECREL = 0x000F,
ARM_MOV32A = 0x0010,
ARM_MOV32T = 0x0011,
ARM_BRANCH20T = 0x0012,
ARM_BRANCH24T = 0x0014,
ARM_BLX23T = 0x0015,
ARM_PAIR = 0x0016,
} RelocationTypesARM;

typedef enum {
ARM64_ABSOLUTE = 0x0000,
ARM64_ADDR32 = 0x0001,
ARM64_ADDR32NB = 0x0002,
ARM64_BRANCH26 = 0x0003,
ARM64_PAGEBASE_REL21 = 0x0004,
ARM64_REL21 = 0x0005,
ARM64_PAGEOFFSET_12A = 0x0006,
ARM64_PAGEOFFSET_12L = 0x0007,
ARM64_SECREL = 0x0008,
ARM64_SECREL_LOW12A = 0x0009,
ARM64_SECREL_HIGH12A = 0x000A,
ARM64_SECREL_LOW12L = 0x000B,
ARM64_TOKEN = 0x000C,
ARM64_SECTION = 0x000D,
ARM64_ADDR64 = 0x000E,
ARM64_BRANCH19 = 0x000F,
ARM64_BRANCH14 = 0x0010,
ARM64_REL32 = 0x0011,
} RelocationTypesARM64;

typedef struct
{
ULONG IMAGE_SCN_TYPE_DSECT:1 <comment=”0x00000001 Reserved”,hidden=true>;
ULONG IMAGE_SCN_TYPE_NOLOAD:1 <comment=”0x00000002 Reserved”,hidden=true>;
ULONG IMAGE_SCN_TYPE_GROUP:1 <comment=”0x00000004 Reserved”,hidden=true>;
ULONG IMAGE_SCN_TYPE_NO_PAD:1 <comment=”0x00000008 Reserved”>;
ULONG IMAGE_SCN_TYPE_COPY:1 <comment=”0x00000010 Reserved”,hidden=true>;

ULONG IMAGE_SCN_CNT_CODE:1                   <comment="0x00000020 Section contains code">;
ULONG IMAGE_SCN_CNT_INITIALIZED_DATA:1       <comment="0x00000040 Section contains initialized data">;
ULONG IMAGE_SCN_CNT_UNINITIALIZED_DATA:1     <comment="0x00000080 Section contains uninitialized data">;

ULONG IMAGE_SCN_LNK_OTHER:1                  <comment="0x00000100 Reserved">;
ULONG IMAGE_SCN_LNK_INFO:1                   <comment="0x00000200 Section contains comments or some other type of information">;
ULONG IMAGE_SCN_TYPE_OVER:1                  <comment="0x00000400 Reserved",hidden=true>;
ULONG IMAGE_SCN_LNK_REMOVE:1                 <comment="0x00000800 Section contents will not become part of image">;
ULONG IMAGE_SCN_LNK_COMDAT:1                 <comment="0x00001000 Section contents comdat">;
ULONG                      :1                <comment="0x00002000 Reserved">;
ULONG IMAGE_SCN_NO_DEFER_SPEC_EXC:1          <comment="0x00004000 Reset speculative exceptions handling bits in the TLB entries for this section.",hidden=true>;
ULONG IMAGE_SCN_GPREL:1                      <comment="0x00008000 Section content can be accessed relative to GP">;
ULONG IMAGE_SCN_MEM_SYSHEAP:1                <comment="0x00010000 Obsolete",hidden=true>;
ULONG IMAGE_SCN_MEM_16BIT:1                  <comment="0x00020000">;
ULONG IMAGE_SCN_MEM_LOCKED:1                 <comment="0x00040000 ">;
ULONG IMAGE_SCN_MEM_PRELOAD:1                <comment="0x00080000">;

ULONG IMAGE_SCN_ALIGN_1BYTES:1               <comment="0x00100000">;
ULONG IMAGE_SCN_ALIGN_2BYTES:1               <comment="0x00200000">;
ULONG IMAGE_SCN_ALIGN_8BYTES:1               <comment="0x00400000">;
ULONG IMAGE_SCN_ALIGN_128BYTES:1             <comment="0x00800000">;

ULONG IMAGE_SCN_LNK_NRELOC_OVFL:1            <comment="0x01000000 Section contains extended relocations">;
ULONG IMAGE_SCN_MEM_DISCARDABLE:1            <comment="0x02000000 Section can be discarded.">;
ULONG IMAGE_SCN_MEM_NOT_CACHED:1             <comment="0x04000000 Section is not cachable">;
ULONG IMAGE_SCN_MEM_NOT_PAGED:1              <comment="0x08000000 Section is not pageable.">;
ULONG IMAGE_SCN_MEM_SHARED:1                 <comment="0x10000000 Section is shareable">;
ULONG IMAGE_SCN_MEM_EXECUTE:1                <comment="0x20000000 Section is executable">;
ULONG IMAGE_SCN_MEM_READ:1                   <comment="0x40000000 Section is readable">;
ULONG IMAGE_SCN_MEM_WRITE:1                  <comment="0x80000000 Section is writeable">;

} SECTION_CHARACTERISTICS;

typedef enum {
IMAGE_SYM_TYPE_NULL = 0,
IMAGE_SYM_TYPE_VOID = 1,
IMAGE_SYM_TYPE_CHAR = 2,
IMAGE_SYM_TYPE_SHORT = 3,
IMAGE_SYM_TYPE_INT = 4,
IMAGE_SYM_TYPE_LONG = 5,
IMAGE_SYM_TYPE_FLOAT = 6,
IMAGE_SYM_TYPE_DOUBLE = 7,
IMAGE_SYM_TYPE_STRUCT = 8,
IMAGE_SYM_TYPE_UNION = 9,
IMAGE_SYM_TYPE_ENUM = 10,
IMAGE_SYM_TYPE_MOE = 11,
IMAGE_SYM_TYPE_BYTE = 12,
IMAGE_SYM_TYPE_WORD = 13,
IMAGE_SYM_TYPE_UINT = 14,
IMAGE_SYM_TYPE_DWORD = 15
} SymbolBaseType;

typedef enum {
IMAGE_SYM_DTYPE_NULL = 0,
IMAGE_SYM_DTYPE_POINTER = 1,
IMAGE_SYM_DTYPE_FUNCTION = 2,
IMAGE_SYM_DTYPE_ARRAY = 3,
} SymbolComplexType;

/// Storage class tells where and what the symbol represents
typedef enum {

IMAGE_SYM_CLASS_END_OF_FUNCTION = -1,
IMAGE_SYM_CLASS_NULL = 0,
IMAGE_SYM_CLASS_AUTOMATIC = 1,
IMAGE_SYM_CLASS_EXTERNAL = 2,
IMAGE_SYM_CLASS_STATIC = 3,
IMAGE_SYM_CLASS_REGISTER = 4,
IMAGE_SYM_CLASS_EXTERNAL_DEF = 5,
IMAGE_SYM_CLASS_LABEL = 6,
IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7,
IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8,
IMAGE_SYM_CLASS_ARGUMENT = 9,
IMAGE_SYM_CLASS_STRUCT_TAG = 10,
IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11,
IMAGE_SYM_CLASS_UNION_TAG = 12,
IMAGE_SYM_CLASS_TYPE_DEFINITION = 13,
IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14,
IMAGE_SYM_CLASS_ENUM_TAG = 15,
IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16,
IMAGE_SYM_CLASS_REGISTER_PARAM = 17,
IMAGE_SYM_CLASS_BIT_FIELD = 18,
IMAGE_SYM_CLASS_BLOCK = 100,
IMAGE_SYM_CLASS_FUNCTION = 101,
IMAGE_SYM_CLASS_END_OF_STRUCT = 102,
IMAGE_SYM_CLASS_FILE = 103,
IMAGE_SYM_CLASS_SECTION = 104,
IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105,
IMAGE_SYM_CLASS_CLR_TOKEN = 107
} SymbolStorageClass;
typedef struct
{
IMAGE_MACHINE Machine <fgcolor=cPurple,format=hex,comment=”WORD”>;
WORD NumberOfSections <fgcolor=cBlue,comment=”Section num”>;
time_t TimeDateStamp <format=hex,comment=”DWORD,from 01/01/1970 12:00 AM”>;
DWORD PointerToSymbolTable <format=hex>;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
FILE_CHARACTERISTICS Characteristics <comment=”WORD”>;
} IMAGE_FILE_HEADER;

typedef struct
{
BYTE Name[8] <comment=”can end without zero”>;
DWORD VirtualSize;
DWORD VirtualAddress <format=hex>;
DWORD SizeOfRawData <format=hex>;
DWORD PointerToRawData <format=hex>;
DWORD PointerToRelocations <format=hex>;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
SECTION_CHARACTERISTICS Characteristics <format=hex>;
} IMAGE_SECTION_HEADER <read=ReadImageSectionHeader>;

typedef struct (IMAGE_SECTION_HEADER& SecHeader)
{
local string sSecName = SecHeader.Name;
if( sSecName == “.text” )
{
// Disassemble .text section
if( FileHeader.Machine == AMD64 )
DisasmSetMode( DISASM_X86_64 );
else if ( FileHeader.Machine == I386 )
DisasmSetMode( DISASM_X86_32 );
else if ( FileHeader.Machine == ARM )
DisasmSetMode( DISASM_ARM_32 );
else if ( FileHeader.Machine == ARM64 )
DisasmSetMode( DISASM_ARM_64 );
Opcode Data[SecHeader.SizeOfRawData];
}
else
UCHAR Data[SecHeader.SizeOfRawData];
} IMAGE_SECTION_DATA <read=ReadSectionData>;

typedef struct (IMAGE_SECTION_HEADER& SecHeader)
{
local string sSecName = SecHeader.Name;
DWORD VirtualAddress <format=hex>;
DWORD SymbolTableIndex;
if( FileHeader.Machine == AMD64 )
RelocationTypeAMD64 Type;
else if ( FileHeader.Machine == I386 )
RelocationTypeI386 Type;
else if ( FileHeader.Machine == ARM )
RelocationTypesARM Type;
else if ( FileHeader.Machine == ARM64 )
RelocationTypesARM64 Type;
} IMAGE_RELOC_DATA <read=ReadRelocData>;

typedef struct {
union {
BYTE Name[8];
struct {
DWORD Zeroes <comment=”A field that is set to all zeros if the name is longer than 8 bytes.”>;
DWORD Offset <comment=”An offset into the string table.”>;
}LongName;
} Name;
DWORD Value <format=hex>;
WORD SectionNumber;
SymbolBaseType BaseType:4;
SymbolComplexType ComplexType:4;
BYTE Reserved <comment=”Reserved”,hidden=true>;
SymbolStorageClass StorageClass;
BYTE NumberOfAuxSymbols;
} IMAGE_SYMBOL_DATA <read=ReadImageSymbolData>;

typedef struct {
BYTE Name[];
}String <read=ReadStringData>;;
string ReadImageSectionHeader(IMAGE_SECTION_HEADER& h)
{
return h.Name;
}
string ReadRelocData(IMAGE_RELOC_DATA& r){
return ReadImageSymbolData(Symbol[r.SymbolTableIndex]);
}
string ReadImageSymbolData(IMAGE_SYMBOL_DATA& s)
{
if(s.StorageClass == IMAGE_SYM_CLASS_NULL)
return “”;
if(s.Name.LongName.Zeroes !=0)
return s.Name.Name;
return ReadString(dwStringTableOffset + s.Name.LongName.Offset);
}
string ReadSectionData(IMAGE_SECTION_DATA& SecData)
{
if (SecData.sSecName[0] == ‘/‘){
return ReadString(dwStringTableOffset + Atoi(SubStr(SecData.sSecName,1)));
}
return SecData.sSecName;
}
string ReadStringData(String& s)
{
return s.Name;
}

/**** PARSING CODE ****/
LittleEndian();
// COFF Header
IMAGE_FILE_HEADER FileHeader <bgcolor=cLtPurple>;
// COFF Sections
IMAGE_SECTION_HEADER SectionHeaders[FileHeader.NumberOfSections] <bgcolor=cYellow>;
// COFF Sections Data
local ULONG sIndex=0;
local ULONG rIndex=0;
for (sIndex=0; sIndex < FileHeader.NumberOfSections; sIndex++)
{
if ( 0 == SectionHeaders[sIndex].PointerToRawData )
{
continue;
}
if ( 0 == SectionHeaders[sIndex].SizeOfRawData )
{
continue;
}
FSeek(SectionHeaders[sIndex].PointerToRawData);
IMAGE_SECTION_DATA Section(SectionHeaders[sIndex]) <bgcolor=cBlue>;
}
// COFF Relocations Data
for (sIndex=0; sIndex < FileHeader.NumberOfSections; sIndex++)
{
if ( 0 == SectionHeaders[sIndex].NumberOfRelocations )
{
continue;
}
FSeek(SectionHeaders[sIndex].PointerToRelocations);
for (rIndex=0; rIndex < SectionHeaders[sIndex].NumberOfRelocations; rIndex++)
{
IMAGE_RELOC_DATA Reloc(SectionHeaders[sIndex]) <bgcolor=cAqua>;
}
}
// COFF Symbols Data
FSeek(FileHeader.PointerToSymbolTable);
for (sIndex=0; sIndex < FileHeader.NumberOfSymbols; sIndex++)
{
IMAGE_SYMBOL_DATA Symbol <bgcolor=cSilver>;
}
// COFF String Data
local DWORD dwStringTableOffset = FTell();
DWORD StringTableLen;
while(!FEof()){
String StringTable <bgcolor=cLtYellow>;
}

效果如下