mirror of
https://github.com/wader/fq.git
synced 2024-11-26 10:33:53 +03:00
87b2c6c10c
Markdown is used as is in online documentation and in cli the markdown decoder is used to decode and the some jq code massages it into something cli friendly. Was just too much of a mess to have doc in jq.
942 lines
26 KiB
Go
942 lines
26 KiB
Go
package macho
|
|
|
|
// https://github.com/aidansteele/osx-abi-macho-file-format-reference
|
|
|
|
import (
|
|
"embed"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/wader/fq/format"
|
|
"github.com/wader/fq/pkg/bitio"
|
|
"github.com/wader/fq/pkg/decode"
|
|
"github.com/wader/fq/pkg/interp"
|
|
"github.com/wader/fq/pkg/scalar"
|
|
)
|
|
|
|
//go:embed macho.md
|
|
var machoFS embed.FS
|
|
|
|
func init() {
|
|
interp.RegisterFormat(decode.Format{
|
|
Name: format.MACHO,
|
|
Description: "Mach-O macOS executable",
|
|
Groups: []string{format.PROBE},
|
|
DecodeFn: machoDecode,
|
|
})
|
|
interp.RegisterFS(machoFS)
|
|
}
|
|
|
|
func strIndexNull(idx int, s string) string {
|
|
if idx > len(s) {
|
|
return ""
|
|
}
|
|
i := strings.IndexByte(s[idx:], 0)
|
|
if i == -1 {
|
|
return ""
|
|
}
|
|
return s[idx : idx+i]
|
|
}
|
|
|
|
type strTable string
|
|
|
|
func (m strTable) MapScalar(s scalar.S) (scalar.S, error) {
|
|
s.Sym = strIndexNull(int(s.ActualU()), string(m))
|
|
return s, nil
|
|
}
|
|
|
|
//nolint:revive
|
|
const (
|
|
MH_MAGIC = 0xfeed_face
|
|
MH_CIGAM = 0xcefa_edfe
|
|
MH_MAGIC_64 = 0xfeed_facf
|
|
MH_CIGAM_64 = 0xcffa_edfe
|
|
)
|
|
|
|
var magicSymMapper = scalar.UToScalar{
|
|
MH_MAGIC: scalar.S{Sym: "32le", Description: "32-bit little endian"},
|
|
MH_CIGAM: scalar.S{Sym: "32be", Description: "32-bit big endian"},
|
|
MH_MAGIC_64: scalar.S{Sym: "64le", Description: "64-bit little endian"},
|
|
MH_CIGAM_64: scalar.S{Sym: "64be", Description: "64-bit big endian"},
|
|
}
|
|
|
|
var cpuTypes = scalar.UToSymStr{
|
|
0xff_ff_ff_ff: "any",
|
|
1: "vax",
|
|
2: "romp",
|
|
4: "ns32032",
|
|
5: "ns32332",
|
|
6: "mc680x0",
|
|
7: "x86",
|
|
8: "mips",
|
|
9: "ns32532",
|
|
10: "mc98000",
|
|
11: "hppa",
|
|
12: "arm",
|
|
13: "mc88000",
|
|
14: "sparc",
|
|
15: "i860",
|
|
16: "i860_little",
|
|
17: "rs6000",
|
|
18: "powerpc",
|
|
0x1000007: "x86_64",
|
|
0x100000c: "arm64",
|
|
0x1000013: "powerpc64",
|
|
255: "veo",
|
|
}
|
|
|
|
func intelSubTypeHelper(f, m uint64) uint64 {
|
|
return f + (m << 4)
|
|
}
|
|
|
|
var cpuSubTypes = map[uint64]scalar.UToSymStr{
|
|
0xff_ff_ff_ff: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
},
|
|
1: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "vax_all",
|
|
1: "vax780",
|
|
2: "vax785",
|
|
3: "vax750",
|
|
4: "vax730",
|
|
5: "uvaxi",
|
|
6: "uvaxii",
|
|
7: "vax8200",
|
|
8: "vax8500",
|
|
9: "vax8600",
|
|
10: "vax8650",
|
|
11: "vax8800",
|
|
12: "uvaxiii",
|
|
},
|
|
6: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
1: "mc680x0_all", // 1: mc68030
|
|
2: "mc68040",
|
|
3: "mc68030_only",
|
|
},
|
|
7: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
intelSubTypeHelper(3, 0): "i386_all", // i386
|
|
intelSubTypeHelper(4, 0): "i486",
|
|
intelSubTypeHelper(4, 8): "486sx",
|
|
intelSubTypeHelper(5, 0): "pent",
|
|
intelSubTypeHelper(6, 1): "pentpro",
|
|
intelSubTypeHelper(6, 3): "pentii_m3",
|
|
intelSubTypeHelper(6, 5): "pentii_m5",
|
|
intelSubTypeHelper(7, 6): "celeron",
|
|
intelSubTypeHelper(7, 7): "celeron_mobile",
|
|
intelSubTypeHelper(8, 0): "pentium_3",
|
|
intelSubTypeHelper(8, 1): "pentium_3_m",
|
|
intelSubTypeHelper(8, 2): "pentium_3_xeon",
|
|
intelSubTypeHelper(9, 0): "pentium_m",
|
|
intelSubTypeHelper(10, 0): "pentium_4",
|
|
intelSubTypeHelper(10, 1): "pentium_4_m",
|
|
intelSubTypeHelper(11, 0): "itanium",
|
|
intelSubTypeHelper(11, 1): "itanium_2",
|
|
intelSubTypeHelper(12, 0): "xeon",
|
|
intelSubTypeHelper(12, 1): "xeon_2",
|
|
},
|
|
8: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "mips_all",
|
|
1: "mips_r2300",
|
|
2: "mips_r2600",
|
|
3: "mips_r2800",
|
|
4: "mips_r2000a",
|
|
5: "mips_r2000",
|
|
6: "mips_r3000a",
|
|
7: "mips_r3000",
|
|
},
|
|
10: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "mc98000_all",
|
|
1: "mc98001",
|
|
},
|
|
11: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "hppa_all",
|
|
1: "hppa_7100",
|
|
2: "hppa_7100_lc",
|
|
},
|
|
12: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "arm_all",
|
|
5: "arm_v4t",
|
|
6: "arm_v6",
|
|
7: "arm_v5tej",
|
|
8: "arm_xscale",
|
|
9: "arm_v7",
|
|
10: "arm_v7f",
|
|
11: "arm_v7s",
|
|
12: "arm_v7k",
|
|
13: "arm_v8",
|
|
14: "arm_v6m",
|
|
15: "arm_v7m",
|
|
16: "arm_v7em",
|
|
},
|
|
13: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "mc88000_all",
|
|
1: "mc88100",
|
|
2: "mc88110",
|
|
},
|
|
14: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "sparc_all",
|
|
},
|
|
15: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "i860_all",
|
|
1: "i860_a860",
|
|
},
|
|
18: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "powerpc_all",
|
|
1: "powerpc_601",
|
|
2: "powerpc_602",
|
|
3: "powerpc_603",
|
|
4: "powerpc_603e",
|
|
5: "powerpc_603ev",
|
|
6: "powerpc_604",
|
|
7: "powerpc_604e",
|
|
8: "powerpc_620",
|
|
9: "powerpc_750",
|
|
10: "powerpc_7400",
|
|
11: "powerpc_7450",
|
|
100: "powerpc_970",
|
|
},
|
|
0x1000012: {
|
|
0xff_ff_ff_ff: "multiple",
|
|
0: "arm64_all",
|
|
1: "arm64_v8",
|
|
2: "arm64_e",
|
|
},
|
|
}
|
|
|
|
var fileTypes = scalar.UToSymStr{
|
|
0x1: "object",
|
|
0x2: "execute",
|
|
0x3: "fvmlib",
|
|
0x4: "core",
|
|
0x5: "preload",
|
|
0x6: "dylib",
|
|
0x7: "dylinker",
|
|
0x8: "bundle",
|
|
0x9: "dylib_stub",
|
|
0xa: "dsym",
|
|
0xb: "kext_bundle",
|
|
}
|
|
|
|
//nolint:revive
|
|
const (
|
|
LC_REQ_DYLD = 0x80000000
|
|
LC_SEGMENT = 0x1
|
|
LC_SYMTAB = 0x2
|
|
LC_SYMSEG = 0x3
|
|
LC_THREAD = 0x4
|
|
LC_UNIXTHREAD = 0x5
|
|
LC_LOADFVMLIB = 0x6
|
|
LC_IDFVMLIB = 0x7
|
|
LC_IDENT = 0x8 // not implemented
|
|
LC_FVMFILE = 0x9 // not implemented
|
|
LC_PREPAGE = 0xa // not implemented
|
|
LC_DYSYMTAB = 0xb
|
|
LC_LOAD_DYLIB = 0xc
|
|
LC_ID_DYLIB = 0xd
|
|
LC_LOAD_DYLINKER = 0xe
|
|
LC_ID_DYLINKER = 0xf
|
|
LC_PREBOUND_DYLIB = 0x10
|
|
LC_ROUTINES = 0x11
|
|
LC_SUB_FRAMEWORK = 0x12
|
|
LC_SUB_UMBRELLA = 0x13
|
|
LC_SUB_CLIENT = 0x14
|
|
LC_SUB_LIBRARY = 0x15
|
|
LC_TWOLEVEL_HINTS = 0x16
|
|
LC_PREBIND_CKSUM = 0x17 // not implemented
|
|
LC_LOAD_WEAK_DYLIB = 0x80000018
|
|
LC_SEGMENT_64 = 0x19
|
|
LC_ROUTINES_64 = 0x1a
|
|
LC_UUID = 0x1b
|
|
LC_RPATH = 0x8000001c
|
|
LC_CODE_SIGNATURE = 0x1d
|
|
LC_SEGMENT_SPLIT_INFO = 0x1e
|
|
LC_REEXPORT_DYLIB = 0x8000001f
|
|
LC_LAZY_LOAD_DYLIB = 0x20
|
|
LC_ENCRYPTION_INFO = 0x21
|
|
LC_DYLD_INFO = 0x22
|
|
LC_DYLD_INFO_ONLY = 0x80000022
|
|
LC_LOAD_UPWARD_DYLIB = 0x80000023
|
|
LC_VERSION_MIN_MACOSX = 0x24
|
|
LC_VERSION_MIN_IPHONEOS = 0x25
|
|
LC_FUNCTION_STARTS = 0x26
|
|
LC_DYLD_ENVIRONMENT = 0x27
|
|
LC_MAIN = 0x80000028
|
|
LC_DATA_IN_CODE = 0x29
|
|
LC_SOURCE_VERSION = 0x2a
|
|
LC_DYLIB_CODE_SIGN_DRS = 0x2b
|
|
LC_ENCRYPTION_INFO_64 = 0x2c
|
|
LC_LINKER_OPTION = 0x2d
|
|
LC_LINKER_OPTIMIZATION_HINT = 0x2e
|
|
LC_VERSION_MIN_TVOS = 0x2f
|
|
LC_VERSION_MIN_WATCHOS = 0x30
|
|
LC_NOTE = 0x31 // not implemented
|
|
LC_BUILD_VERSION = 0x32
|
|
)
|
|
|
|
var loadCommands = scalar.UToSymStr{
|
|
LC_REQ_DYLD: "req_dyld",
|
|
LC_SEGMENT: "segment",
|
|
LC_SYMTAB: "symtab",
|
|
LC_SYMSEG: "symseg",
|
|
LC_THREAD: "thread",
|
|
LC_UNIXTHREAD: "unixthread",
|
|
LC_LOADFVMLIB: "loadfvmlib",
|
|
LC_IDFVMLIB: "idfvmlib",
|
|
LC_IDENT: "ident",
|
|
LC_FVMFILE: "fvmfile",
|
|
LC_PREPAGE: "prepage",
|
|
LC_DYSYMTAB: "dysymtab",
|
|
LC_LOAD_DYLIB: "load_dylib",
|
|
LC_ID_DYLIB: "id_dylib",
|
|
LC_LOAD_DYLINKER: "load_dylinker",
|
|
LC_ID_DYLINKER: "id_dylinker",
|
|
LC_PREBOUND_DYLIB: "prebound_dylib",
|
|
LC_ROUTINES: "routines",
|
|
LC_SUB_FRAMEWORK: "sub_framework",
|
|
LC_SUB_UMBRELLA: "sub_umbrella",
|
|
LC_SUB_CLIENT: "sub_client",
|
|
LC_SUB_LIBRARY: "sub_library",
|
|
LC_TWOLEVEL_HINTS: "twolevel_hints",
|
|
LC_PREBIND_CKSUM: "prebind_cksum",
|
|
LC_LOAD_WEAK_DYLIB: "load_weak_dylib",
|
|
LC_SEGMENT_64: "segment_64",
|
|
LC_ROUTINES_64: "routines_64",
|
|
LC_UUID: "uuid",
|
|
LC_RPATH: "rpath",
|
|
LC_CODE_SIGNATURE: "code_signature",
|
|
LC_SEGMENT_SPLIT_INFO: "segment_split_info",
|
|
LC_REEXPORT_DYLIB: "reexport_dylib",
|
|
LC_LAZY_LOAD_DYLIB: "lazy_load_dylib",
|
|
LC_ENCRYPTION_INFO: "encryption_info",
|
|
LC_DYLD_INFO: "dyld_info",
|
|
LC_DYLD_INFO_ONLY: "dyld_info_only",
|
|
LC_LOAD_UPWARD_DYLIB: "load_upward_dylib",
|
|
LC_VERSION_MIN_MACOSX: "version_min_macosx",
|
|
LC_VERSION_MIN_IPHONEOS: "version_min_iphoneos",
|
|
LC_FUNCTION_STARTS: "function_starts",
|
|
LC_DYLD_ENVIRONMENT: "dyld_environment",
|
|
LC_MAIN: "main",
|
|
LC_DATA_IN_CODE: "data_in_code",
|
|
LC_SOURCE_VERSION: "source_version",
|
|
LC_DYLIB_CODE_SIGN_DRS: "dylib_code_sign_drs",
|
|
LC_ENCRYPTION_INFO_64: "encryption_info_64",
|
|
LC_LINKER_OPTION: "linker_option",
|
|
LC_LINKER_OPTIMIZATION_HINT: "linker_optimization_hint",
|
|
LC_VERSION_MIN_TVOS: "version_min_tvos",
|
|
LC_VERSION_MIN_WATCHOS: "version_min_watchos",
|
|
LC_NOTE: "note",
|
|
LC_BUILD_VERSION: "build_version",
|
|
}
|
|
|
|
var sectionTypes = scalar.UToSymStr{
|
|
0x0: "regular",
|
|
0x1: "zerofill",
|
|
0x2: "cstring_literals",
|
|
0x3: "4byte_literals",
|
|
0x4: "8byte_literals",
|
|
0x5: "literal_pointers",
|
|
0x6: "non_lazy_symbol_pointers",
|
|
0x7: "lazy_symbol_pointers",
|
|
0x8: "symbol_stubs",
|
|
0x9: "mod_init_func_pointers",
|
|
0xa: "mod_term_func_pointers",
|
|
0xb: "coalesced",
|
|
0xc: "gb_zerofill",
|
|
0xd: "interposing",
|
|
0xe: "16byte_literals",
|
|
0xf: "dtrace_dof",
|
|
0x10: "lazy_dylib_symbol_pointers",
|
|
0x11: "thread_local_regular",
|
|
0x12: "thread_local_zerofill",
|
|
0x13: "thread_local_variables",
|
|
0x14: "thread_local_variable_pointers",
|
|
0x15: "thread_local_init_function_pointers",
|
|
}
|
|
|
|
func machoDecode(d *decode.D, _ any) any {
|
|
var archBits int
|
|
var cpuType uint64
|
|
var ncmds uint64
|
|
magicBuffer := d.U32LE()
|
|
|
|
if magicBuffer == MH_MAGIC || magicBuffer == MH_MAGIC_64 {
|
|
d.Endian = decode.LittleEndian
|
|
if magicBuffer == MH_MAGIC {
|
|
archBits = 32
|
|
} else {
|
|
archBits = 64
|
|
}
|
|
} else if magicBuffer == MH_CIGAM || magicBuffer == MH_CIGAM_64 {
|
|
d.Endian = decode.BigEndian
|
|
if magicBuffer == MH_CIGAM {
|
|
archBits = 32
|
|
} else {
|
|
archBits = 64
|
|
}
|
|
} else {
|
|
d.Fatalf("invalid magic")
|
|
}
|
|
|
|
d.SeekRel(-4 * 8)
|
|
d.FieldStruct("header", func(d *decode.D) {
|
|
d.FieldValueS("arch_bits", int64(archBits))
|
|
d.FieldU32("magic", magicSymMapper, scalar.ActualHex)
|
|
d.FieldValueU("bits", uint64(archBits))
|
|
cpuType = d.FieldU32("cputype", cpuTypes, scalar.ActualHex)
|
|
d.FieldU32("cpusubtype", cpuSubTypes[cpuType], scalar.ActualHex)
|
|
d.FieldU32("filetype", fileTypes)
|
|
ncmds = d.FieldU32("ncdms")
|
|
d.FieldU32("sizeofncdms")
|
|
d.FieldStruct("flags", parseMachHeaderFlags)
|
|
if archBits == 64 {
|
|
d.FieldRawLen("reserved", 4*8, d.BitBufIsZero())
|
|
}
|
|
})
|
|
loadCommandsNext := d.Pos()
|
|
d.FieldArray("load_commands", func(d *decode.D) {
|
|
for i := uint64(0); i < ncmds; i++ {
|
|
d.FieldStruct("load_command", func(d *decode.D) {
|
|
d.SeekAbs(loadCommandsNext)
|
|
|
|
cmd := d.FieldU32("cmd", loadCommands, scalar.ActualHex)
|
|
cmdSize := d.FieldU32("cmdsize")
|
|
if cmdSize == 0 {
|
|
d.Fatalf("cmdSize is zero")
|
|
}
|
|
|
|
loadCommandsNext += int64(cmdSize) * 8
|
|
|
|
switch cmd {
|
|
case LC_UUID:
|
|
d.FieldStruct("uuid_command", func(d *decode.D) {
|
|
d.FieldRawLen("uuid", 16*8)
|
|
})
|
|
case LC_SEGMENT,
|
|
LC_SEGMENT_64:
|
|
// nsect := (cmdsize - uint64(archBits)) / uint64(archBits)
|
|
|
|
var vmaddr int64
|
|
var fileoff int64
|
|
|
|
var nsects uint64
|
|
d.FieldStruct("segment_command", func(d *decode.D) {
|
|
d.FieldValueS("arch_bits", int64(archBits))
|
|
d.FieldUTF8NullFixedLen("segname", 16) // OPCODE_DECODER segname==__TEXT
|
|
if archBits == 32 {
|
|
vmaddr = int64(d.FieldU32("vmaddr", scalar.ActualHex))
|
|
d.FieldU32("vmsize")
|
|
fileoff = int64(d.FieldU32("fileoff", scalar.ActualHex))
|
|
d.FieldU32("tfilesize")
|
|
} else {
|
|
vmaddr = int64(d.FieldU64("vmaddr", scalar.ActualHex))
|
|
d.FieldU64("vmsize")
|
|
fileoff = int64(d.FieldU64("fileoff", scalar.ActualHex))
|
|
d.FieldU64("tfilesize")
|
|
}
|
|
d.FieldS32("initprot")
|
|
d.FieldS32("maxprot")
|
|
nsects = d.FieldU32("nsects")
|
|
d.FieldStruct("flags", parseSegmentFlags)
|
|
})
|
|
d.FieldArray("sections", func(d *decode.D) {
|
|
for i := uint64(0); i < nsects; i++ {
|
|
d.FieldStruct("section", func(d *decode.D) {
|
|
// OPCODE_DECODER sectname==__text
|
|
sectName := d.FieldUTF8NullFixedLen("sectname", 16)
|
|
d.FieldUTF8NullFixedLen("segname", 16)
|
|
var size uint64
|
|
if archBits == 32 {
|
|
d.FieldU32("address", scalar.ActualHex)
|
|
size = d.FieldU32("size")
|
|
} else {
|
|
d.FieldU64("address", scalar.ActualHex)
|
|
size = d.FieldU64("size")
|
|
}
|
|
offset := d.FieldU32("offset", scalar.ActualHex)
|
|
d.FieldU32("align")
|
|
d.FieldU32("reloff")
|
|
d.FieldU32("nreloc")
|
|
// get section type
|
|
d.FieldStruct("flags", parseSectionFlags)
|
|
d.FieldU8("type", sectionTypes)
|
|
d.FieldU32("reserved1")
|
|
d.FieldU32("reserved2")
|
|
if archBits == 64 {
|
|
d.FieldU32("reserved3")
|
|
}
|
|
|
|
switch sectName {
|
|
case "__bss", // uninitialized data
|
|
"__common": // allocated by linker
|
|
// skip, no data from file
|
|
// TODO: more?
|
|
default:
|
|
d.RangeFn(int64(offset)*8, int64(size)*8, func(d *decode.D) {
|
|
switch sectName {
|
|
case "__cstring":
|
|
d.FieldArray("cstrings", func(d *decode.D) {
|
|
for !d.End() {
|
|
d.FieldUTF8Null("cstring")
|
|
}
|
|
})
|
|
case "__ustring":
|
|
d.FieldArray("ustrings", func(d *decode.D) {
|
|
for !d.End() {
|
|
// TODO: always LE?
|
|
d.FieldUTF16LENull("ustring")
|
|
}
|
|
})
|
|
case "__cfstring":
|
|
d.FieldArray("cfstrings", func(d *decode.D) {
|
|
for !d.End() {
|
|
d.FieldStruct("cfstring", func(d *decode.D) {
|
|
// https://github.com/llvm-mirror/clang/blob/aa231e4be75ac4759c236b755c57876f76e3cf05/lib/CodeGen/CodeGenModule.cpp#L4708
|
|
const flagUTF8 = 0x07c8
|
|
const flagUTF16 = 0x07d0
|
|
|
|
d.FieldU("isa_vmaddr", archBits)
|
|
flag := d.FieldU("flags", archBits, scalar.ActualHex, scalar.UToSymStr{
|
|
flagUTF8: "utf8",
|
|
flagUTF16: "utf16",
|
|
})
|
|
dataPtr := int64(d.FieldU("data_ptr", archBits, scalar.ActualHex))
|
|
length := int64(d.FieldU("length", archBits))
|
|
|
|
offset := ((dataPtr - vmaddr) + fileoff) * 8
|
|
switch flag {
|
|
case flagUTF8:
|
|
d.RangeFn(offset, length*8, func(d *decode.D) { d.FieldUTF8("string", int(length)) })
|
|
case flagUTF16:
|
|
// TODO: endian?
|
|
d.RangeFn(offset, length*8*2, func(d *decode.D) { d.FieldUTF16("string", int(length*2)) })
|
|
}
|
|
})
|
|
}
|
|
})
|
|
default:
|
|
d.FieldRawLen("data", d.BitsLeft())
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
})
|
|
case LC_TWOLEVEL_HINTS:
|
|
d.FieldU32("offset", scalar.ActualHex)
|
|
d.FieldU32("nhints")
|
|
case LC_LOAD_DYLIB,
|
|
LC_ID_DYLIB,
|
|
LC_LOAD_UPWARD_DYLIB,
|
|
LC_LOAD_WEAK_DYLIB,
|
|
LC_LAZY_LOAD_DYLIB,
|
|
LC_REEXPORT_DYLIB:
|
|
d.FieldStruct("dylib_command", func(d *decode.D) {
|
|
offset := d.FieldU32("offset", scalar.ActualHex)
|
|
d.FieldU32("timestamp", timestampMapper)
|
|
d.FieldU32("current_version")
|
|
d.FieldU32("compatibility_version")
|
|
d.FieldUTF8NullFixedLen("name", int(cmdSize)-int(offset))
|
|
})
|
|
case LC_LOAD_DYLINKER,
|
|
LC_ID_DYLINKER,
|
|
LC_DYLD_ENVIRONMENT:
|
|
offset := d.FieldU32("offset", scalar.ActualHex)
|
|
d.FieldUTF8NullFixedLen("name", int(cmdSize)-int(offset))
|
|
case LC_RPATH:
|
|
offset := d.FieldU32("offset", scalar.ActualHex)
|
|
d.FieldUTF8NullFixedLen("name", int(cmdSize)-int(offset))
|
|
case LC_PREBOUND_DYLIB:
|
|
// https://github.com/aidansteele/osx-abi-macho-file-format-reference#prebound_dylib_command
|
|
d.U32() // name_offset
|
|
nmodules := d.FieldU32("nmodules")
|
|
d.U32() // linked_modules_offset
|
|
d.FieldUTF8Null("name")
|
|
d.FieldBitBufFn("linked_modules", func(d *decode.D) bitio.ReaderAtSeeker {
|
|
return d.RawLen(int64((nmodules / 8) + (nmodules % 8)))
|
|
})
|
|
case LC_THREAD,
|
|
LC_UNIXTHREAD:
|
|
d.FieldU32("flavor")
|
|
count := d.FieldU32("count")
|
|
switch cpuType {
|
|
case 0x7:
|
|
d.FieldStruct("state", threadStateI386Decode)
|
|
case 0xC:
|
|
d.FieldStruct("state", threadStateARM32Decode)
|
|
case 0x13:
|
|
d.FieldStruct("state", threadStatePPC32Decode)
|
|
case 0x1000007:
|
|
d.FieldStruct("state", threadStateX8664Decode)
|
|
case 0x100000C:
|
|
d.FieldStruct("state", threadStateARM64Decode)
|
|
case 0x1000013:
|
|
d.FieldStruct("state", threadStatePPC64Decode)
|
|
default:
|
|
d.FieldRawLen("state", int64(count*32))
|
|
}
|
|
case LC_ROUTINES,
|
|
LC_ROUTINES_64:
|
|
if archBits == 32 {
|
|
d.FieldU32("init_address", scalar.ActualHex)
|
|
d.FieldU32("init_module")
|
|
d.FieldU32("reserved1")
|
|
d.FieldU32("reserved2")
|
|
d.FieldU32("reserved3")
|
|
d.FieldU32("reserved4")
|
|
d.FieldU32("reserved5")
|
|
d.FieldU32("reserved6")
|
|
} else {
|
|
d.FieldU64("init_address", scalar.ActualHex)
|
|
d.FieldU64("init_module")
|
|
d.FieldU64("reserved1")
|
|
d.FieldU64("reserved2")
|
|
d.FieldU64("reserved3")
|
|
d.FieldU64("reserved4")
|
|
d.FieldU64("reserved5")
|
|
d.FieldU64("reserved6")
|
|
}
|
|
case LC_SUB_UMBRELLA,
|
|
LC_SUB_LIBRARY,
|
|
LC_SUB_CLIENT,
|
|
LC_SUB_FRAMEWORK:
|
|
offset := d.FieldU32("offset", scalar.ActualHex)
|
|
d.FieldUTF8NullFixedLen("name", int(cmdSize)-int(offset))
|
|
case LC_SYMTAB:
|
|
symOff := d.FieldU32("symoff")
|
|
nSyms := d.FieldU32("nsyms")
|
|
strOff := d.FieldU32("stroff")
|
|
strSize := d.FieldU32("strsize")
|
|
|
|
d.RangeFn(int64(strOff)*8, int64(strSize)*8, func(d *decode.D) {
|
|
d.FieldRawLen("str_table", d.BitsLeft())
|
|
})
|
|
symTabTable := strTable(string(d.BytesRange(int64(strOff)*8, int(strSize))))
|
|
|
|
d.SeekAbs(int64(symOff) * 8)
|
|
d.FieldArray("symbols", func(d *decode.D) {
|
|
for i := 0; i < int(nSyms); i++ {
|
|
symbolTypeMap := scalar.UToSymStr{
|
|
0x0: "undef",
|
|
0x1: "abs",
|
|
0x5: "indr",
|
|
0x6: "pbud",
|
|
0x7: "sect",
|
|
}
|
|
|
|
d.FieldStruct("symbol", func(d *decode.D) {
|
|
d.FieldU32("strx", symTabTable)
|
|
d.FieldStruct("type", func(d *decode.D) {
|
|
d.FieldU3("stab")
|
|
d.FieldU1("pext")
|
|
d.FieldU3("type", symbolTypeMap)
|
|
d.FieldU1("ext")
|
|
})
|
|
d.FieldU8("sect")
|
|
d.FieldU16("desc")
|
|
d.FieldU("value", archBits, scalar.ActualHex)
|
|
})
|
|
}
|
|
})
|
|
case LC_DYSYMTAB:
|
|
d.FieldU32("ilocalsym")
|
|
d.FieldU32("nlocalsym")
|
|
d.FieldU32("iextdefsym")
|
|
d.FieldU32("nextdefsym")
|
|
d.FieldU32("iundefsym")
|
|
d.FieldU32("nundefsym")
|
|
d.FieldU32("tocoff")
|
|
d.FieldU32("ntoc")
|
|
d.FieldU32("modtaboff")
|
|
d.FieldU32("nmodtab")
|
|
d.FieldU32("extrefsymoff")
|
|
d.FieldU32("nextrefsyms")
|
|
d.FieldU32("indirectsymoff")
|
|
d.FieldU32("nindirectsyms")
|
|
|
|
d.FieldU32("extreloff")
|
|
d.FieldU32("nextrel")
|
|
d.FieldU32("locreloff")
|
|
d.FieldU32("nlocrel")
|
|
case LC_BUILD_VERSION:
|
|
d.FieldU32("platform")
|
|
d.FieldU32("minos")
|
|
d.FieldU32("sdk")
|
|
ntools := d.FieldU32("ntools")
|
|
var ntoolsIdx uint64
|
|
d.FieldStructArrayLoop("tools", "tool", func() bool {
|
|
return ntoolsIdx < ntools
|
|
}, func(d *decode.D) {
|
|
d.FieldU32("tool")
|
|
d.FieldU32("version")
|
|
ntoolsIdx++
|
|
})
|
|
case LC_CODE_SIGNATURE,
|
|
LC_SEGMENT_SPLIT_INFO,
|
|
LC_FUNCTION_STARTS,
|
|
LC_DATA_IN_CODE,
|
|
LC_DYLIB_CODE_SIGN_DRS,
|
|
LC_LINKER_OPTIMIZATION_HINT:
|
|
d.FieldStruct("linkedit_data", func(d *decode.D) {
|
|
d.FieldU32("off")
|
|
d.FieldU32("size")
|
|
})
|
|
case LC_VERSION_MIN_IPHONEOS,
|
|
LC_VERSION_MIN_MACOSX,
|
|
LC_VERSION_MIN_TVOS,
|
|
LC_VERSION_MIN_WATCHOS:
|
|
d.FieldU32("version")
|
|
d.FieldU32("sdk")
|
|
case LC_DYLD_INFO,
|
|
LC_DYLD_INFO_ONLY:
|
|
d.FieldStruct("dyld_info", func(d *decode.D) {
|
|
d.FieldU32("rebase_off", scalar.ActualHex)
|
|
d.FieldU32("rebase_size")
|
|
d.FieldU32("bind_off", scalar.ActualHex)
|
|
d.FieldU32("bind_size")
|
|
d.FieldU32("weak_bind_off", scalar.ActualHex)
|
|
d.FieldU32("weak_bind_size")
|
|
d.FieldU32("lazy_bind_off", scalar.ActualHex)
|
|
d.FieldU32("lazy_bind_size")
|
|
d.FieldU32("export_off", scalar.ActualHex)
|
|
d.FieldU32("export_size")
|
|
})
|
|
case LC_MAIN:
|
|
d.FieldStruct("entrypoint", func(d *decode.D) {
|
|
d.FieldU64("entryoff", scalar.ActualHex)
|
|
d.FieldU64("stacksize")
|
|
})
|
|
case LC_SOURCE_VERSION:
|
|
d.FieldStruct("source_version_tag", func(d *decode.D) {
|
|
d.FieldU64("tag")
|
|
})
|
|
case LC_LINKER_OPTION:
|
|
d.FieldStruct("linker_option", func(d *decode.D) {
|
|
count := d.FieldU32("count")
|
|
d.FieldUTF8NullFixedLen("option", int(count))
|
|
})
|
|
case LC_ENCRYPTION_INFO,
|
|
LC_ENCRYPTION_INFO_64:
|
|
d.FieldStruct("encryption_info", func(d *decode.D) {
|
|
offset := d.FieldU32("offset", scalar.ActualHex)
|
|
size := d.FieldU32("size")
|
|
d.FieldU32("id")
|
|
d.RangeFn(int64(offset)*8, int64(size)*8, func(d *decode.D) {
|
|
d.FieldRawLen("data", d.BitsLeft())
|
|
})
|
|
})
|
|
if cmd == LC_ENCRYPTION_INFO_64 {
|
|
// 64 bit align
|
|
d.FieldU32("pad")
|
|
}
|
|
case LC_IDFVMLIB,
|
|
LC_LOADFVMLIB:
|
|
d.FieldStruct("fvmlib", func(d *decode.D) {
|
|
offset := d.FieldU32("offset", scalar.ActualHex)
|
|
d.FieldU32("minor_version")
|
|
d.FieldU32("header_addr", scalar.ActualHex)
|
|
d.FieldUTF8NullFixedLen("name", int(cmdSize)-int(offset))
|
|
})
|
|
default:
|
|
// ignore
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseMachHeaderFlags(d *decode.D) {
|
|
d.FieldRawLen("reserved", 6)
|
|
d.FieldBool("app_extension_safe")
|
|
d.FieldBool("no_heap_execution")
|
|
|
|
d.FieldBool("has_tlv_descriptors")
|
|
d.FieldBool("dead_strippable_dylib")
|
|
d.FieldBool("pie")
|
|
d.FieldBool("no_reexported_dylibs")
|
|
|
|
d.FieldBool("setuid_safe")
|
|
d.FieldBool("root_safe")
|
|
d.FieldBool("allow_stack_execution")
|
|
d.FieldBool("binds_to_weak")
|
|
|
|
d.FieldBool("weak_defines")
|
|
d.FieldBool("canonical")
|
|
d.FieldBool("subsections_via_symbols")
|
|
d.FieldBool("allmodsbound")
|
|
|
|
d.FieldBool("prebindable")
|
|
d.FieldBool("nofixprebinding")
|
|
d.FieldBool("nomultidefs")
|
|
d.FieldBool("force_flat")
|
|
|
|
d.FieldBool("twolevel")
|
|
d.FieldBool("lazy_init")
|
|
d.FieldBool("split_segs")
|
|
d.FieldBool("prebound")
|
|
|
|
d.FieldBool("bindatload")
|
|
d.FieldBool("dyldlink")
|
|
d.FieldBool("incrlink")
|
|
d.FieldBool("noundefs")
|
|
}
|
|
|
|
func parseSegmentFlags(d *decode.D) {
|
|
d.FieldRawLen("reserved", 28)
|
|
d.FieldBool("protected_version_1")
|
|
d.FieldBool("noreloc")
|
|
d.FieldBool("fvmlib")
|
|
d.FieldBool("highvm")
|
|
}
|
|
|
|
func parseSectionFlags(d *decode.D) {
|
|
d.FieldBool("attr_pure_instructions")
|
|
d.FieldBool("attr_no_toc")
|
|
d.FieldBool("attr_strip_static_syms")
|
|
d.FieldBool("attr_no_dead_strip")
|
|
|
|
d.FieldBool("attr_live_support")
|
|
d.FieldBool("attr_self_modifying_code")
|
|
d.FieldBool("attr_debug")
|
|
d.FieldRawLen("reserved", 14)
|
|
|
|
d.FieldBool("attr_some_instructions")
|
|
d.FieldBool("attr_ext_reloc")
|
|
d.FieldBool("attr_loc_reloc")
|
|
}
|
|
|
|
var timestampMapper = scalar.Fn(func(s scalar.S) (scalar.S, error) {
|
|
s.Sym = time.UnixMilli(int64(s.ActualU())).UTC().String()
|
|
return s, nil
|
|
})
|
|
|
|
func threadStateI386Decode(d *decode.D) {
|
|
d.FieldU32("eax")
|
|
d.FieldU32("ebx")
|
|
d.FieldU32("ecx")
|
|
d.FieldU32("edx")
|
|
d.FieldU32("edi")
|
|
d.FieldU32("esi")
|
|
d.FieldU32("ebp")
|
|
d.FieldU32("esp")
|
|
d.FieldU32("ss")
|
|
d.FieldU32("eflags")
|
|
d.FieldU32("eip")
|
|
d.FieldU32("cs")
|
|
d.FieldU32("ds")
|
|
d.FieldU32("es")
|
|
d.FieldU32("fs")
|
|
d.FieldU32("gs")
|
|
}
|
|
|
|
func threadStateX8664Decode(d *decode.D) {
|
|
d.FieldU64("rax")
|
|
d.FieldU64("rbx")
|
|
d.FieldU64("rcx")
|
|
d.FieldU64("rdx")
|
|
d.FieldU64("rdi")
|
|
d.FieldU64("rsi")
|
|
d.FieldU64("rbp")
|
|
d.FieldU64("rsp")
|
|
d.FieldU64("r8")
|
|
d.FieldU64("r9")
|
|
d.FieldU64("r10")
|
|
d.FieldU64("r11")
|
|
d.FieldU64("r12")
|
|
d.FieldU64("r13")
|
|
d.FieldU64("r14")
|
|
d.FieldU64("r15")
|
|
d.FieldU64("rip")
|
|
d.FieldU64("rflags")
|
|
d.FieldU64("cs")
|
|
d.FieldU64("fs")
|
|
d.FieldU64("gs")
|
|
}
|
|
|
|
func threadStateARM32Decode(d *decode.D) {
|
|
rIdx := 0
|
|
d.FieldStructArrayLoop("r", "r", func() bool {
|
|
return rIdx < 13
|
|
}, func(d *decode.D) {
|
|
d.FieldU32("value")
|
|
rIdx++
|
|
})
|
|
d.FieldU32("sp")
|
|
d.FieldU32("lr")
|
|
d.FieldU32("pc")
|
|
d.FieldU32("cpsr")
|
|
}
|
|
|
|
func threadStateARM64Decode(d *decode.D) {
|
|
rIdx := 0
|
|
d.FieldStructArrayLoop("r", "r", func() bool {
|
|
return rIdx < 29
|
|
}, func(d *decode.D) {
|
|
d.FieldU64("value")
|
|
rIdx++
|
|
})
|
|
d.FieldU64("fp")
|
|
d.FieldU64("lr")
|
|
d.FieldU64("sp")
|
|
d.FieldU64("pc")
|
|
d.FieldU32("cpsr")
|
|
d.FieldU32("pad")
|
|
}
|
|
|
|
func threadStatePPC32Decode(d *decode.D) {
|
|
srrIdx := 0
|
|
d.FieldStructArrayLoop("srr", "srr", func() bool {
|
|
return srrIdx < 2
|
|
}, func(d *decode.D) {
|
|
d.FieldU32("value")
|
|
srrIdx++
|
|
})
|
|
rIdx := 0
|
|
d.FieldStructArrayLoop("r", "r", func() bool {
|
|
return rIdx < 32
|
|
}, func(d *decode.D) {
|
|
d.FieldU32("value")
|
|
rIdx++
|
|
})
|
|
d.FieldU32("ct")
|
|
d.FieldU32("xer")
|
|
d.FieldU32("lr")
|
|
d.FieldU32("ctr")
|
|
d.FieldU32("mq")
|
|
d.FieldU32("vrsave")
|
|
}
|
|
|
|
func threadStatePPC64Decode(d *decode.D) {
|
|
srrIdx := 0
|
|
d.FieldStructArrayLoop("srr", "srr", func() bool {
|
|
return srrIdx < 2
|
|
}, func(d *decode.D) {
|
|
d.FieldU64("value")
|
|
srrIdx++
|
|
})
|
|
rIdx := 0
|
|
d.FieldStructArrayLoop("r", "r", func() bool {
|
|
return rIdx < 32
|
|
}, func(d *decode.D) {
|
|
d.FieldU64("value")
|
|
rIdx++
|
|
})
|
|
d.FieldU32("ct")
|
|
d.FieldU64("xer")
|
|
d.FieldU64("lr")
|
|
d.FieldU64("ctr")
|
|
d.FieldU32("vrsave")
|
|
}
|