Files
nexus/internal/leviathan/binsnoop/sig_executables.go
T
2026-06-16 11:51:07 +01:00

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
}