486 lines
12 KiB
Go
486 lines
12 KiB
Go
package binsnoop
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ELF
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const ELFDescription = "ELF binary"
|
|
|
|
func ELFMagic() [][]byte {
|
|
return [][]byte{
|
|
{0x7F, 0x45, 0x4C, 0x46}, // "\x7FELF"
|
|
}
|
|
}
|
|
|
|
func ELFParser(data []byte, offset int) (*SignatureResult, error) {
|
|
const elfIdentSize = 16
|
|
const elfInfoStructSize = 8
|
|
const expectedVersion = 1
|
|
|
|
elfIdentFields := []StructField{
|
|
{"magic", "u32"},
|
|
{"class", "u8"},
|
|
{"endianness", "u8"},
|
|
{"version", "u8"},
|
|
{"osabi", "u8"},
|
|
{"abiversion", "u8"},
|
|
{"padding_1", "u32"},
|
|
{"padding_2", "u24"},
|
|
}
|
|
|
|
elfInfoFields := []StructField{
|
|
{"type", "u16"},
|
|
{"machine", "u16"},
|
|
{"version", "u32"},
|
|
}
|
|
|
|
elfClasses := map[int]int{
|
|
1: 32,
|
|
2: 64,
|
|
}
|
|
|
|
elfEndianness := map[int]string{
|
|
1: "little",
|
|
2: "big",
|
|
}
|
|
|
|
elfOSABI := map[int]string{
|
|
0: "System-V (Unix)",
|
|
1: "HP-UX",
|
|
2: "NetBSD",
|
|
3: "Linux",
|
|
4: "GNU Hurd",
|
|
5: "86Open",
|
|
6: "Solaris",
|
|
7: "AIX",
|
|
8: "IRIX",
|
|
9: "FreeBSD",
|
|
10: "Tru64",
|
|
11: "Novell Modesto",
|
|
12: "OpenBSD",
|
|
13: "OpenVMS",
|
|
14: "NonStop Kernel",
|
|
15: "AROS",
|
|
16: "FenixOS",
|
|
17: "Nuxi CloudABI",
|
|
18: "OpenVOS",
|
|
97: "ARM ABI",
|
|
102: "Cell LV2",
|
|
202: "Cafe OS",
|
|
255: "embedded",
|
|
}
|
|
|
|
elfTypes := map[int]string{
|
|
0: "no file type",
|
|
1: "relocatable",
|
|
2: "executable",
|
|
3: "shared object",
|
|
4: "core file",
|
|
}
|
|
|
|
elfMachines := map[int]string{
|
|
0: "no machine",
|
|
1: "AT&T WE 32100",
|
|
2: "SPARC",
|
|
3: "x86",
|
|
4: "Motorola 68k",
|
|
5: "Motorola 88k",
|
|
6: "Intel MCU",
|
|
7: "Intel 80860",
|
|
8: "MIPS",
|
|
9: "IBM System/370",
|
|
10: "MIPS RS3000",
|
|
11: "RS6000",
|
|
15: "HP PA-RISC",
|
|
16: "nCUBE",
|
|
17: "Fujitsu VPP500",
|
|
18: "SPARC32PLUS",
|
|
19: "Intel 80960",
|
|
20: "PowerPC",
|
|
21: "PowerPC 64-bit",
|
|
22: "S390",
|
|
23: "IBM SPU/SPC",
|
|
24: "cisco SVIP",
|
|
25: "cisco 7200",
|
|
36: "NEC V800",
|
|
37: "Fujitsu FR20",
|
|
38: "TRW RH-32",
|
|
39: "Motorola RCE",
|
|
40: "ARM",
|
|
41: "Digital Alpha",
|
|
42: "SuperH",
|
|
43: "SPARCv9",
|
|
44: "Siemens TriCore embedded processor",
|
|
45: "Argonaut RISC Core",
|
|
46: "Hitachi H8/300",
|
|
47: "Hitachi H8/300H",
|
|
48: "Hitachi H8S",
|
|
49: "Hitachi H8/500",
|
|
50: "IA-64",
|
|
51: "Stanford MIPS-X",
|
|
52: "Motorola ColdFire",
|
|
53: "Motorola M68HC12",
|
|
54: "Fujitsu MMA Multimedia Accelerator",
|
|
55: "Siemens PCP",
|
|
56: "Sony nCPU embedded RISC processor",
|
|
57: "Denso NDR1 microprocessor",
|
|
58: "Motorola StarCore",
|
|
59: "Toyota ME16",
|
|
60: "STMicroelectronics ST100",
|
|
61: "Advanced Logic TinyJ embedded processor",
|
|
62: "AMD X86-64",
|
|
63: "Sony DSP processor",
|
|
64: "PDP-10",
|
|
65: "PDP-11",
|
|
66: "Siemens FX66",
|
|
67: "STMicroelectronics ST9+",
|
|
68: "STMicroelectronics ST7",
|
|
69: "Motorola MC68HC16",
|
|
70: "Motorola MC68HC11",
|
|
71: "Motorola MC68HC08",
|
|
72: "Motorola MC68HC05",
|
|
73: "Silicon Graphics SVx",
|
|
74: "STMicroelectonrics ST19",
|
|
75: "Digital VAX",
|
|
76: "Axis Communications 32-bit CPU",
|
|
77: "Infineon Technologies 32-bit CPU",
|
|
78: "Element 14 64-bit DSP",
|
|
79: "LSI Logic 16-bit DSP",
|
|
80: "MMIX",
|
|
81: "Harvard machine-independent",
|
|
82: "SiTera Prism",
|
|
83: "Atmel AVR 8-bit",
|
|
84: "Fujitsu FR30",
|
|
85: "Mitsubishi D10V",
|
|
86: "Mitsubishi D30V",
|
|
87: "NEC v850",
|
|
88: "Renesas M32R",
|
|
89: "Matsushita MN10300",
|
|
90: "Matsushita MN10200",
|
|
91: "picoJava",
|
|
92: "OpenRISC",
|
|
93: "Synopsys ARCompact ARC700 cores",
|
|
94: "Tensilica Xtensa",
|
|
95: "Alphamosaic VideoCore",
|
|
96: "Thompson Multimedia",
|
|
97: "NatSemi 32k",
|
|
98: "Tenor Network TPC",
|
|
99: "Trebia SNP 1000",
|
|
100: "STMicroelectronics ST200",
|
|
101: "Ubicom IP2022",
|
|
102: "MAX Processor",
|
|
103: "NatSemi CompactRISC",
|
|
104: "Fujitsu F2MC16",
|
|
105: "TI msp430",
|
|
106: "Analog Devices Blackfin",
|
|
107: "S1C33 Family of Seiko Epson",
|
|
108: "Sharp embedded",
|
|
109: "Arca RISC",
|
|
110: "PKU-Unity Ltd.",
|
|
111: "eXcess: 16/32/64-bit",
|
|
112: "Icera Deep Execution Processor",
|
|
113: "Altera Nios II",
|
|
114: "NatSemi CRX",
|
|
115: "Motorola XGATE",
|
|
116: "Infineon C16x/XC16x",
|
|
117: "Renesas M16C series",
|
|
118: "Microchip dsPIC30F",
|
|
119: "Freescale RISC core",
|
|
120: "Renesas M32C series",
|
|
131: "Altium TSK3000 core",
|
|
132: "Freescale RS08",
|
|
134: "Cyan Technology eCOG2",
|
|
135: "Sunplus S+core7 RISC",
|
|
136: "New Japan Radio (NJR) 24-bit DSP",
|
|
137: "Broadcom VideoCore III processor",
|
|
138: "LatticeMico32",
|
|
139: "Seiko Epson C17 family",
|
|
140: "TMS320C6000",
|
|
141: "TMS320C2000",
|
|
142: "TMS320C55x",
|
|
144: "TI Programmable Realtime Unit",
|
|
160: "STMicroelectronics 64bit VLIW DSP",
|
|
161: "Cypress M8C",
|
|
162: "Renesas R32C series",
|
|
163: "NXP TriMedia family",
|
|
164: "Qualcomm DSP6",
|
|
165: "Intel 8051 and variants",
|
|
166: "STMicroelectronics STxP7x family",
|
|
167: "Andes embedded RISC",
|
|
168: "Cyan eCOG1X family",
|
|
169: "Dallas MAXQ30",
|
|
170: "New Japan Radio (NJR) 16-bit DSP",
|
|
171: "M2000 Reconfigurable RISC",
|
|
172: "Cray NV2 vector architecture",
|
|
173: "Renesas RX family",
|
|
174: "META",
|
|
175: "MCST Elbrus e2k",
|
|
176: "Cyan Technology eCOG16 family",
|
|
177: "NatSemi CompactRISC",
|
|
178: "Freescale Extended Time Processing Unit",
|
|
179: "Infineon SLE9X",
|
|
180: "Intel L1OM",
|
|
181: "Intel K1OM",
|
|
183: "ARM 64-bit",
|
|
185: "Atmel 32-bit family",
|
|
186: "STMicroeletronics STM8 8-bit",
|
|
187: "Tilera TILE64",
|
|
188: "Tilera TILEPro",
|
|
189: "Xilinx MicroBlaze 32-bit RISC",
|
|
190: "NVIDIA CUDA architecture",
|
|
191: "Tilera TILE-Gx",
|
|
195: "Synopsys ARCv2/HS3x/HS4x cores",
|
|
197: "Renesas RL78 family",
|
|
199: "Renesas 78K0R",
|
|
200: "Freescale 56800EX",
|
|
201: "Beyond BA1",
|
|
202: "Beyond BA2",
|
|
203: "XMOS xCORE",
|
|
204: "Microchip 8-bit PIC(r)",
|
|
210: "KM211 KM32",
|
|
211: "KM211 KMX32",
|
|
212: "KM211 KMX16",
|
|
213: "KM211 KMX8",
|
|
214: "KM211 KVARC",
|
|
215: "Paneve CDP",
|
|
216: "Cognitive Smart Memory",
|
|
217: "iCelero CoolEngine",
|
|
218: "Nanoradio Optimized RISC",
|
|
219: "CSR Kalimba architecture family",
|
|
220: "Zilog Z80",
|
|
221: "Controls and Data Services VISIUMcore processor",
|
|
222: "FTDI Chip FT32 high performance 32-bit RISC architecture",
|
|
223: "Moxie processor family",
|
|
224: "AMD GPU architecture",
|
|
243: "RISC-V",
|
|
244: "Lanai 32-bit processor",
|
|
245: "CEVA Processor Architecture Family",
|
|
246: "CEVA X2 Processor Family",
|
|
247: "Berkeley Packet Filter",
|
|
248: "Graphcore Intelligent Processing Unit",
|
|
249: "Imagination Technologies",
|
|
250: "Netronome Flow Processor",
|
|
251: "NEC Vector Engine",
|
|
252: "C-SKY processor family",
|
|
253: "Synopsys ARCv3 64-bit ISA/HS6x cores",
|
|
254: "MOS Technology MCS 6502 processor",
|
|
255: "Synopsys ARCv3 32-bit",
|
|
256: "Kalray VLIW core of the MPPA family",
|
|
257: "WDC 65C816",
|
|
258: "LoongArch",
|
|
259: "ChipON KungFu32",
|
|
}
|
|
|
|
elfData := data[offset:]
|
|
if len(elfData) < elfIdentSize+elfInfoStructSize {
|
|
return nil, fmt.Errorf("ELF: not enough data")
|
|
}
|
|
|
|
// Parse the e_ident (endianness doesn't matter for single-byte fields)
|
|
eIdent, err := Parse(elfData, elfIdentFields, "little")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ELF: e_ident parse error: %w", err)
|
|
}
|
|
|
|
// Sanity check padding fields
|
|
if eIdent["padding_1"] != 0 || eIdent["padding_2"] != 0 {
|
|
return nil, fmt.Errorf("ELF: non-zero padding in e_ident")
|
|
}
|
|
if eIdent["version"] != expectedVersion {
|
|
return nil, fmt.Errorf("ELF: unexpected e_ident version %d", eIdent["version"])
|
|
}
|
|
|
|
classVal, classOK := elfClasses[eIdent["class"]]
|
|
if !classOK {
|
|
return nil, fmt.Errorf("ELF: unknown class %d", eIdent["class"])
|
|
}
|
|
|
|
endianStr, endianOK := elfEndianness[eIdent["endianness"]]
|
|
if !endianOK {
|
|
return nil, fmt.Errorf("ELF: unknown endianness %d", eIdent["endianness"])
|
|
}
|
|
|
|
osabiStr, osabiOK := elfOSABI[eIdent["osabi"]]
|
|
if !osabiOK {
|
|
return nil, fmt.Errorf("ELF: unknown OS/ABI %d", eIdent["osabi"])
|
|
}
|
|
|
|
// Parse the rest of the ELF header with the correct endianness
|
|
elfInfoData := elfData[elfIdentSize : elfIdentSize+elfInfoStructSize]
|
|
elfInfo, err := Parse(elfInfoData, elfInfoFields, endianStr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ELF: info parse error: %w", err)
|
|
}
|
|
|
|
if elfInfo["version"] != expectedVersion {
|
|
return nil, fmt.Errorf("ELF: unexpected info version %d", elfInfo["version"])
|
|
}
|
|
|
|
typeStr, typeOK := elfTypes[elfInfo["type"]]
|
|
if !typeOK {
|
|
return nil, fmt.Errorf("ELF: unknown type %d", elfInfo["type"])
|
|
}
|
|
|
|
machineStr := "Unknown"
|
|
if m, ok := elfMachines[elfInfo["machine"]]; ok {
|
|
machineStr = m
|
|
}
|
|
|
|
return &SignatureResult{
|
|
Offset: offset,
|
|
Name: "elf",
|
|
Description: fmt.Sprintf("%s, %d-bit %s, %s for %s, %s endian", ELFDescription, classVal, typeStr, machineStr, osabiStr, endianStr),
|
|
Confidence: ConfidenceMedium,
|
|
}, nil
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// PE
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const PEDescription = "Windows PE binary"
|
|
|
|
func PEMagic() [][]byte {
|
|
// First 16 bytes of common DOS headers (e_magic through e_ss)
|
|
return [][]byte{
|
|
{0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00},
|
|
{0x4D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
}
|
|
}
|
|
|
|
func PEParser(data []byte, offset int) (*SignatureResult, error) {
|
|
const peMagicValue = 0x00004550 // "PE\x00\x00"
|
|
|
|
dosFields := []StructField{
|
|
{"e_magic", "u16"},
|
|
{"e_cblp", "u16"},
|
|
{"e_cp", "u16"},
|
|
{"e_crlc", "u16"},
|
|
{"e_cparhdr", "u16"},
|
|
{"e_minalloc", "u16"},
|
|
{"e_maxalloc", "u16"},
|
|
{"e_ss", "u16"},
|
|
{"e_sp", "u16"},
|
|
{"e_csum", "u16"},
|
|
{"e_ip", "u16"},
|
|
{"e_cs", "u16"},
|
|
{"e_lfarlc", "u16"},
|
|
{"e_ovno", "u16"},
|
|
{"e_res_1", "u16"},
|
|
{"e_res_2", "u16"},
|
|
{"e_res_3", "u16"},
|
|
{"e_res_4", "u16"},
|
|
{"e_oemid", "u16"},
|
|
{"e_oeminfo", "u16"},
|
|
{"e_res_5", "u16"},
|
|
{"e_res_6", "u16"},
|
|
{"e_res_7", "u16"},
|
|
{"e_res_8", "u16"},
|
|
{"e_res_9", "u16"},
|
|
{"e_res_10", "u16"},
|
|
{"e_res_11", "u16"},
|
|
{"e_res_12", "u16"},
|
|
{"e_res_13", "u16"},
|
|
{"e_res_14", "u16"},
|
|
{"e_lfanew", "u32"},
|
|
}
|
|
|
|
peFields := []StructField{
|
|
{"magic", "u32"},
|
|
{"machine", "u16"},
|
|
{"number_of_sections", "u16"},
|
|
{"timestamp", "u32"},
|
|
{"symbol_table_ptr", "u32"},
|
|
{"number_of_symbols", "u32"},
|
|
{"optional_header_size", "u16"},
|
|
{"characteristics", "u16"},
|
|
}
|
|
|
|
knownMachineTypes := map[int]string{
|
|
0x0: "Unknown",
|
|
0x184: "Alpha32",
|
|
0x284: "Alpha64",
|
|
0x1D3: "Matsushita AM33",
|
|
0x8664: "Intel x86-64",
|
|
0x1C0: "ARM",
|
|
0xAA64: "ARM-64",
|
|
0x1C4: "ARM Thumb2",
|
|
0xEBC: "EFI",
|
|
0x14C: "Intel x86",
|
|
0x200: "Intel Itanium",
|
|
0x6232: "LoongArch 32-bit",
|
|
0x6264: "LoongArch 64-bit",
|
|
0x9041: "Mitsubishi M32R",
|
|
0x266: "MIPS16",
|
|
0x366: "MIPS with FPU",
|
|
0x466: "MIPS16 with FPU",
|
|
0x1F0: "PowerPC",
|
|
0x1F1: "PowerPC with FPU",
|
|
0x5032: "RISC-V 32-bit",
|
|
0x5064: "RISC-V 64-bit",
|
|
0x5128: "RISC-V 128-bit",
|
|
0x1A2: "Hitachi SH3",
|
|
0x1A3: "Hitachi SH3 DSP",
|
|
0x1A6: "Hitachi SH4",
|
|
0x1A8: "Hitachi SH5",
|
|
0x1C2: "Thumb",
|
|
0x169: "MIPS WCEv2",
|
|
}
|
|
|
|
peData := data[offset:]
|
|
dosStructSize := StructSize(dosFields)
|
|
peStructSize := StructSize(peFields)
|
|
|
|
if len(peData) < dosStructSize {
|
|
return nil, fmt.Errorf("PE: not enough data for DOS header")
|
|
}
|
|
|
|
dosHeader, err := Parse(peData, dosFields, "little")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("PE: DOS header parse error: %w", err)
|
|
}
|
|
|
|
// Sanity check the reserved header fields; they should all be 0
|
|
for _, field := range []string{
|
|
"e_res_1", "e_res_2", "e_res_3", "e_res_4",
|
|
"e_res_5", "e_res_6", "e_res_7", "e_res_8",
|
|
"e_res_9", "e_res_10", "e_res_11", "e_res_12",
|
|
"e_res_13", "e_res_14",
|
|
} {
|
|
if dosHeader[field] != 0 {
|
|
return nil, fmt.Errorf("PE: reserved field %s is non-zero", field)
|
|
}
|
|
}
|
|
|
|
peHeaderStart := dosHeader["e_lfanew"]
|
|
peHeaderEnd := peHeaderStart + peStructSize
|
|
|
|
if peHeaderEnd > len(peData) {
|
|
return nil, fmt.Errorf("PE: e_lfanew points beyond available data")
|
|
}
|
|
|
|
peHeader, err := Parse(peData[peHeaderStart:peHeaderEnd], peFields, "little")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("PE: PE header parse error: %w", err)
|
|
}
|
|
|
|
if peHeader["magic"] != peMagicValue {
|
|
return nil, fmt.Errorf("PE: invalid PE magic 0x%08X", peHeader["magic"])
|
|
}
|
|
|
|
machineStr, machineOK := knownMachineTypes[peHeader["machine"]]
|
|
if !machineOK {
|
|
return nil, fmt.Errorf("PE: unknown machine type 0x%X", peHeader["machine"])
|
|
}
|
|
|
|
return &SignatureResult{
|
|
Offset: offset,
|
|
Description: fmt.Sprintf("%s, machine type: %s", PEDescription, machineStr),
|
|
Confidence: ConfidenceMedium,
|
|
}, nil
|
|
}
|