add old go tooling
This commit is contained in:
159
macho-go/pkg/ios/const.go
Normal file
159
macho-go/pkg/ios/const.go
Normal file
@ -0,0 +1,159 @@
|
||||
package ios
|
||||
|
||||
// Magic values of Mach-O and Fat binaries
|
||||
const (
|
||||
Magic32 uint32 = 0xfeedface
|
||||
Cigam32 uint32 = 0xcefaedfe
|
||||
Magic64 uint32 = 0xfeedfacf
|
||||
Cigam64 uint32 = 0xcffaedfe
|
||||
MagicFat uint32 = 0xcafebabe
|
||||
CigamFat uint32 = 0xbebafeca
|
||||
)
|
||||
|
||||
// Flags defined in mach_header.flags
|
||||
const (
|
||||
FlagNoUndefs uint32 = 0x1
|
||||
FlagIncrLink uint32 = 0x2
|
||||
FlagDyldLink uint32 = 0x4
|
||||
FlagBindAtLoad uint32 = 0x8
|
||||
FlagPrebound uint32 = 0x10
|
||||
FlagSplitSegs uint32 = 0x20
|
||||
FlagLazyInit uint32 = 0x40
|
||||
FlagTwoLevel uint32 = 0x80
|
||||
FlagForceFlat uint32 = 0x100
|
||||
FlagNoMultiDefs uint32 = 0x200
|
||||
FlagNoFixPrebinding uint32 = 0x400
|
||||
FlagPrebindable uint32 = 0x800
|
||||
FlagAllModsBound uint32 = 0x1000
|
||||
FlagSubsectionsViaSymbols uint32 = 0x2000
|
||||
FlagCanonical uint32 = 0x4000
|
||||
FlagWeakDefines uint32 = 0x8000
|
||||
FlagBindsToWeak uint32 = 0x10000
|
||||
FlagAllowStackExecution uint32 = 0x20000
|
||||
FlagRootSafe uint32 = 0x40000
|
||||
FlagSetuidSafe uint32 = 0x80000
|
||||
FlagNoReexportedDylibs uint32 = 0x100000
|
||||
FlagPIE uint32 = 0x200000
|
||||
FlagDeadStrippableDylib uint32 = 0x400000
|
||||
FlagHasTLVDescriptors uint32 = 0x800000
|
||||
FlagNoHeapExecution uint32 = 0x1000000
|
||||
FlagAppExtensionSafe uint32 = 0x2000000
|
||||
)
|
||||
|
||||
// Types of Load command
|
||||
const (
|
||||
LC_DUMMY uint32 = 0x0
|
||||
LC_SEGMENT uint32 = 0x1
|
||||
LC_SYMTAB uint32 = 0x2
|
||||
LC_SYMSEG uint32 = 0x3
|
||||
LC_THREAD uint32 = 0x4
|
||||
LC_UNIXTHREAD uint32 = 0x5
|
||||
LC_LOADFVMLIB uint32 = 0x6
|
||||
LC_IDFVMLIB uint32 = 0x7
|
||||
LC_IDENT uint32 = 0x8
|
||||
LC_FVMFILE uint32 = 0x9
|
||||
LC_PREPAGE uint32 = 0xa
|
||||
LC_DYSYMTAB uint32 = 0xb
|
||||
LC_LOAD_DYLIB uint32 = 0xc
|
||||
LC_ID_DYLIB uint32 = 0xd
|
||||
LC_LOAD_DYLINKER uint32 = 0xe
|
||||
LC_ID_DYLINKER uint32 = 0xf
|
||||
LC_PREBOUND_DYLIB uint32 = 0x10
|
||||
LC_ROUTINES uint32 = 0x11
|
||||
LC_SUB_FRAMEWORK uint32 = 0x12
|
||||
LC_SUB_UMBRELLA uint32 = 0x13
|
||||
LC_SUB_CLIENT uint32 = 0x14
|
||||
LC_SUB_LIBRARY uint32 = 0x15
|
||||
LC_TWOLEVEL_HINTS uint32 = 0x16
|
||||
LC_PREBIND_CKSUM uint32 = 0x17
|
||||
LC_LOAD_WEAK_DYLIB uint32 = 0x18 | 0x80000000
|
||||
LC_SEGMENT_64 uint32 = 0x19
|
||||
LC_ROUTINES_64 uint32 = 0x1a
|
||||
LC_UUID uint32 = 0x1b
|
||||
LC_RPATH uint32 = 0x1c | 0x80000000
|
||||
LC_CODE_SIGNATURE uint32 = 0x1d
|
||||
LC_SEGMENT_SPLIT_INFO uint32 = 0x1e
|
||||
LC_REEXPORT_DYLIB uint32 = 0x1f | 0x80000000
|
||||
LC_LAZY_LOAD_DYLIB uint32 = 0x20
|
||||
LC_ENCRYPTION_INFO uint32 = 0x21
|
||||
LC_DYLD_INFO uint32 = 0x22
|
||||
LC_DYLD_INFO_ONLY uint32 = 0x22 | 0x80000000
|
||||
LC_VERSION_MIN_MACOSX uint32 = 0x24
|
||||
LC_VERSION_MIN_IPHONEOS uint32 = 0x25
|
||||
LC_FUNCTION_STARTS uint32 = 0x26
|
||||
LC_DYLD_ENVIRONMENT uint32 = 0x27
|
||||
LC_MAIN uint32 = 0x28 | 0x80000000
|
||||
LC_DATA_IN_CODE uint32 = 0x29
|
||||
LC_SOURCE_VERSION uint32 = 0x2A
|
||||
LC_DYLIB_CODE_SIGN_DRS uint32 = 0x2B
|
||||
LC_ENCRYPTION_INFO_64 uint32 = 0x2C
|
||||
LC_LINKER_OPTION uint32 = 0x2D
|
||||
LC_LINKER_OPTIMIZATION_HINT uint32 = 0x2E
|
||||
LC_VERSION_MIN_TVOS uint32 = 0x2F
|
||||
LC_VERSION_MIN_WATCHOS uint32 = 0x30
|
||||
LC_BUILD_VERSION uint32 = 0x32
|
||||
LC_DYLD_EXPORTS_TRIE uint32 = 0x33 | 0x80000000
|
||||
LC_DYLD_CHAINED_FIXUPS uint32 = 0x34 | 0x80000000
|
||||
)
|
||||
|
||||
const (
|
||||
Header_size uint64 = 28
|
||||
Header_size_64 uint64 = 28 + 4
|
||||
)
|
||||
|
||||
const (
|
||||
CPU_ARCH_ABI64 uint32 = 0x0100_0000
|
||||
|
||||
CPU_TYPE_ARM uint32 = 12
|
||||
CPU_TYPE_ARM64 uint32 = CPU_TYPE_ARM | CPU_ARCH_ABI64
|
||||
CPU_TYPE_POWERPC uint32 = 18
|
||||
CPU_TYPE_POWERPC64 uint32 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64
|
||||
CPU_TYPE_I386 uint32 = 7
|
||||
CPU_TYPE_X86_64 uint32 = CPU_TYPE_I386 | CPU_ARCH_ABI64
|
||||
)
|
||||
|
||||
const (
|
||||
// Section Type
|
||||
S_REGULAR uint8 = 0x0
|
||||
S_ZEROFILL uint8 = 0x1
|
||||
S_CSTRING_LITERALS uint8 = 0x2
|
||||
S_4BYTE_LITERALS uint8 = 0x3
|
||||
S_8BYTE_LITERALS uint8 = 0x4
|
||||
S_LITERAL_POINTERS uint8 = 0x5
|
||||
S_NON_LAZY_SYMBOL_POINTERS uint8 = 0x6
|
||||
S_LAZY_SYMBOL_POINTERS uint8 = 0x7
|
||||
S_SYMBOL_STUBS uint8 = 0x8
|
||||
S_MOD_INIT_FUNC_POINTERS uint8 = 0x9
|
||||
S_MOD_TERM_FUNC_POINTERS uint8 = 0xa
|
||||
S_COALESCED uint8 = 0xb
|
||||
S_GB_ZEROFILL uint8 = 0xc
|
||||
S_INTERPOSING uint8 = 0xd
|
||||
S_16BYTE_LITERALS uint8 = 0xe
|
||||
S_DTRACE_DOF uint8 = 0xf
|
||||
S_LAZY_DYLIB_SYMBOL_POINTERS uint8 = 0x10
|
||||
S_THREAD_LOCAL_REGULAR uint8 = 0x11
|
||||
S_THREAD_LOCAL_ZEROFILL uint8 = 0x12
|
||||
S_THREAD_LOCAL_VARIABLES uint8 = 0x13
|
||||
S_THREAD_LOCAL_VARIABLE_POINTERS uint8 = 0x14
|
||||
S_THREAD_LOCAL_INIT_FUNCTION_POINTERS uint8 = 0x15
|
||||
)
|
||||
|
||||
const (
|
||||
// Section Attribute
|
||||
)
|
||||
|
||||
const (
|
||||
BIND_OPCODE_DONE uint8 = 0x00
|
||||
BIND_OPCODE_SET_DYLIB_ORDINAL_IMM uint8 = 0x10
|
||||
BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB uint8 = 0x20
|
||||
BIND_OPCODE_SET_DYLIB_SPECIAL_IMM uint8 = 0x30
|
||||
BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM uint8 = 0x40
|
||||
BIND_OPCODE_SET_TYPE_IMM uint8 = 0x50
|
||||
BIND_OPCODE_SET_ADDEND_SLEB uint8 = 0x60
|
||||
BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB uint8 = 0x70
|
||||
BIND_OPCODE_ADD_ADDR_ULEB uint8 = 0x80
|
||||
BIND_OPCODE_DO_BIND uint8 = 0x90
|
||||
BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB uint8 = 0xA0
|
||||
BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED uint8 = 0xB0
|
||||
BIND_OPCODE_DO_BIND_ADD_ULEB_TIMES_SKIPPING_ULEB uint8 = 0xC0
|
||||
)
|
184
macho-go/pkg/ios/fat/create-fat.go
Normal file
184
macho-go/pkg/ios/fat/create-fat.go
Normal file
@ -0,0 +1,184 @@
|
||||
/// Contains the declaration of FatHeader and FatArch
|
||||
/// These structs are always written using Big-Endian,
|
||||
/// as documented in the mach-o/fat.h
|
||||
/// This file also has a CreateFat function to generate
|
||||
/// Fat file from a list of MachoContext
|
||||
package fat
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
macho "ios-wrapper/pkg/ios/macho"
|
||||
)
|
||||
|
||||
/// Get the alignment for the Mach-O in Fat binary
|
||||
/// The returned value is the multiplier of 2
|
||||
func GetAlignment(h *macho.Header) uint32 {
|
||||
switch h.Cputype() {
|
||||
case CPU_TYPE_ARM, CPU_TYPE_ARM64:
|
||||
return 0xe // log2(0x4000)
|
||||
case CPU_TYPE_POWERPC,
|
||||
CPU_TYPE_POWERPC64,
|
||||
CPU_TYPE_I386,
|
||||
CPU_TYPE_X86_64:
|
||||
return 0xc // log2(0x1000)
|
||||
default:
|
||||
return 0xd // log2(0x2000)
|
||||
}
|
||||
}
|
||||
|
||||
func MachosToFatArchs(machos []*macho.MachoContext) []*FatArch {
|
||||
var fa []*FatArch
|
||||
for _, m := range machos {
|
||||
fa = append(fa, &FatArch{
|
||||
Cputype: m.Header().Cputype(),
|
||||
Cpusubtype: m.Header().Cpusubtype(),
|
||||
Size: m.FileSize(),
|
||||
Offset: 0,
|
||||
Align: GetAlignment(m.Header()),
|
||||
})
|
||||
}
|
||||
return fa
|
||||
}
|
||||
|
||||
/// Create a Fat binary from MachoContext
|
||||
/// Convert MachoContext to FatArch
|
||||
/// Calculate the alignment, now using basic calculation
|
||||
/// because iOS always has valid archs ARM
|
||||
///
|
||||
/// Sort the Fat arch as per the cctools/lipo
|
||||
/// Calculate the offset with each Mach-O
|
||||
///
|
||||
/// Write the FatHeader
|
||||
/// Write the FatArchs
|
||||
/// Write the Mach-Os
|
||||
func CreateFat(machos []*macho.MachoContext, outfilename string) error {
|
||||
archs := MachosToFatArchs(machos)
|
||||
sort.SliceStable(archs, func(i, j int) bool {
|
||||
if archs[i].Cputype == archs[j].Cputype {
|
||||
return archs[i].Cpusubtype < archs[i].Cpusubtype
|
||||
}
|
||||
if archs[i].Cputype == CPU_TYPE_ARM64 {
|
||||
return false
|
||||
}
|
||||
if archs[j].Cputype == CPU_TYPE_ARM64 {
|
||||
return true
|
||||
}
|
||||
return archs[i].Cpusubtype < archs[j].Cpusubtype
|
||||
})
|
||||
|
||||
// calculate the offset for each FatArch
|
||||
offset := uint32(8) // size of fat header, bytes
|
||||
// offset to the first macho
|
||||
offset += uint32(len(archs)) * 5 // size of fat_arch, bytes
|
||||
|
||||
for _, arch := range archs {
|
||||
offset = uint32(OffsetRounder(uint64(offset), 1<<arch.Align))
|
||||
arch.Offset = offset
|
||||
offset += arch.Size
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"cputype": arch.Cputype,
|
||||
"cpusubtype": arch.Cpusubtype,
|
||||
"size": arch.Size,
|
||||
"offset": arch.Offset,
|
||||
"algin": arch.Align,
|
||||
}).Debug("Arch to add")
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(outfilename, os.O_CREATE|os.O_WRONLY, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
// log.Fatal("Cannot open output file")
|
||||
}
|
||||
|
||||
var w fatWriter
|
||||
w.WriteFatHeader(file, &FatHeader{
|
||||
Magic: MagicFat,
|
||||
Nfat_arch: uint32(len(archs)),
|
||||
})
|
||||
w.WriteFatArchs(file, archs)
|
||||
w.WriteMachos(file, archs, machos)
|
||||
return w.err
|
||||
}
|
||||
|
||||
type fatWriter struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (fw *fatWriter) WriteFatHeader(w io.Writer, h *FatHeader) {
|
||||
if fw.err != nil {
|
||||
return
|
||||
}
|
||||
b := h.Serialize()
|
||||
_, err := w.Write(b)
|
||||
fw.err = err
|
||||
}
|
||||
|
||||
func (fw *fatWriter) WriteFatArchs(w io.Writer, archs []*FatArch) {
|
||||
for _, arch := range archs {
|
||||
if fw.err != nil {
|
||||
return
|
||||
}
|
||||
b := arch.Serialize()
|
||||
_, err := w.Write(b)
|
||||
fw.err = err
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"cputype": arch.Cputype,
|
||||
"cpusubtype": arch.Cpusubtype,
|
||||
"size": arch.Size,
|
||||
"offset": arch.Offset,
|
||||
"algin": arch.Align,
|
||||
}).Info("Attempt to write Fat Arch")
|
||||
}
|
||||
}
|
||||
|
||||
/// for each fat arch sorted, we locate the MachoContext and
|
||||
/// use it to Write to the buffer
|
||||
/// locating the macho by its cputype and cpusubtype
|
||||
func (fw *fatWriter) WriteMachos(
|
||||
w io.WriteSeeker,
|
||||
archs []*FatArch,
|
||||
machos []*macho.MachoContext,
|
||||
) {
|
||||
for _, arch := range archs {
|
||||
for _, macho := range machos {
|
||||
if fw.err != nil {
|
||||
return
|
||||
}
|
||||
cputype := macho.Header().Cputype()
|
||||
cpusubtype := macho.Header().Cpusubtype()
|
||||
if cputype == arch.Cputype &&
|
||||
cpusubtype == arch.Cpusubtype {
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"cputype": cputype,
|
||||
"cpusubtype": cpusubtype,
|
||||
"offset": arch.Offset,
|
||||
"size": arch.Size,
|
||||
}).Info("Attempt to write Mach-O to file")
|
||||
|
||||
_, err1 := w.Seek(int64(arch.Offset), io.SeekStart)
|
||||
_, err2 := macho.WriteBufferTo(w)
|
||||
if err1 != nil {
|
||||
fw.err = err1
|
||||
} else if err2 != nil {
|
||||
fw.err = err2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func OffsetRounder(v, r uint64) uint64 {
|
||||
r--
|
||||
v += r
|
||||
v &= uint64(^int64(r))
|
||||
return v
|
||||
}
|
110
macho-go/pkg/ios/fat/fat.go
Normal file
110
macho-go/pkg/ios/fat/fat.go
Normal file
@ -0,0 +1,110 @@
|
||||
package fat
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
"ios-wrapper/pkg/ios/macho"
|
||||
)
|
||||
|
||||
type FatHeader struct {
|
||||
Magic uint32
|
||||
Nfat_arch uint32
|
||||
}
|
||||
|
||||
func (h *FatHeader) Serialize() []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, binary.BigEndian, h.Magic)
|
||||
binary.Write(buf, binary.BigEndian, h.Nfat_arch)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
type FatArch struct {
|
||||
Cputype uint32
|
||||
Cpusubtype uint32
|
||||
Offset uint32
|
||||
Size uint32
|
||||
Align uint32
|
||||
}
|
||||
|
||||
func (arch *FatArch) Serialize() []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, binary.BigEndian, arch.Cputype)
|
||||
binary.Write(buf, binary.BigEndian, arch.Cpusubtype)
|
||||
binary.Write(buf, binary.BigEndian, arch.Offset)
|
||||
binary.Write(buf, binary.BigEndian, arch.Size)
|
||||
binary.Write(buf, binary.BigEndian, arch.Align)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// don't know when to use
|
||||
// it seems that only use if the size of any file is > 4GB
|
||||
// else just use FatArch
|
||||
type FatArch64 struct {
|
||||
Cputype uint32
|
||||
Cpusubtype uint32
|
||||
Offset uint64
|
||||
Size uint64
|
||||
Align uint32
|
||||
}
|
||||
|
||||
type FatContext struct {
|
||||
fatArch []*FatArch
|
||||
}
|
||||
|
||||
func (fc *FatContext) ParseFile(file *os.File) {
|
||||
r := bufio.NewReader(file)
|
||||
|
||||
{ // read magic to define byteorder and pointersize
|
||||
var magic uint32
|
||||
magic_buf := make([]byte, 4)
|
||||
r.Read(magic_buf)
|
||||
magic_r := bytes.NewReader(magic_buf)
|
||||
binary.Read(magic_r, binary.BigEndian, &magic)
|
||||
|
||||
if magic != MagicFat {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var nfat_arch uint32
|
||||
binary.Read(r, binary.BigEndian, &nfat_arch)
|
||||
|
||||
for i := uint32(0); i < nfat_arch; i++ {
|
||||
var fat_arch FatArch
|
||||
binary.Read(r, binary.BigEndian, &fat_arch.Cputype)
|
||||
binary.Read(r, binary.BigEndian, &fat_arch.Cpusubtype)
|
||||
binary.Read(r, binary.BigEndian, &fat_arch.Offset)
|
||||
binary.Read(r, binary.BigEndian, &fat_arch.Size)
|
||||
binary.Read(r, binary.BigEndian, &fat_arch.Align)
|
||||
fc.fatArch = append(fc.fatArch, &fat_arch)
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *FatContext) Machos() []*FatArch {
|
||||
return fc.fatArch
|
||||
}
|
||||
|
||||
func FatSplit(path string) ([]string, error) {
|
||||
return CreateMachosFromFat(path)
|
||||
}
|
||||
|
||||
// Parse files into Mach-O, calculate fat_arch and join
|
||||
// into a Fat binary
|
||||
// @paths: paths to Mach-O binaries
|
||||
// @out: output Fat file path
|
||||
// returns success?
|
||||
func FatJoin(paths []string, out string) error {
|
||||
machos, err := macho.MachosFromFiles(paths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if macho.CheckDuplicateArch(machos) {
|
||||
return errors.New("Duplicate Arch")
|
||||
}
|
||||
return CreateFat(machos, out)
|
||||
}
|
62
macho-go/pkg/ios/fat/split-fat.go
Normal file
62
macho-go/pkg/ios/fat/split-fat.go
Normal file
@ -0,0 +1,62 @@
|
||||
package fat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Split a Fat binary into multiple Mach-O binaries
|
||||
// @ifile: path to Fat binary
|
||||
// returns a list of file path of splited Mach-O binaries
|
||||
func CreateMachosFromFat(ifile string) ([]string, error) {
|
||||
f, err := os.OpenFile(ifile, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot open file")
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
var fc FatContext
|
||||
fc.ParseFile(f)
|
||||
|
||||
var r []string
|
||||
for _, arch := range fc.Machos() {
|
||||
offset := arch.Offset
|
||||
size := arch.Size
|
||||
buf := make([]byte, size)
|
||||
filename := fmt.Sprintf(
|
||||
"%s_%x",
|
||||
strings.ReplaceAll(ifile, " ", "_"),
|
||||
offset,
|
||||
)
|
||||
|
||||
var err error
|
||||
_, err = f.Seek(int64(offset), io.SeekStart)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
_, err = f.Read(buf)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
outfile, err := os.OpenFile(
|
||||
filename,
|
||||
os.O_CREATE|os.O_WRONLY,
|
||||
0777,
|
||||
)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
_, err = outfile.Write(buf)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
r = append(r, filename) // everything is find, append new file path
|
||||
}
|
||||
return r, nil
|
||||
}
|
55
macho-go/pkg/ios/macho/dwarf.go
Normal file
55
macho-go/pkg/ios/macho/dwarf.go
Normal file
@ -0,0 +1,55 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"ios-wrapper/pkg/dwarf"
|
||||
)
|
||||
|
||||
type DebuglineInfo struct {
|
||||
debuglines []*dwarf.DebuglineParser
|
||||
}
|
||||
|
||||
type LineInfo struct {
|
||||
Line uint32
|
||||
Directory string
|
||||
File string
|
||||
}
|
||||
|
||||
func (dinfo *DebuglineInfo) Find(address uint64) *LineInfo {
|
||||
for _, debugline := range dinfo.debuglines {
|
||||
found, line, directory, file := debugline.Find(address)
|
||||
if found {
|
||||
return &LineInfo{
|
||||
line, directory, file,
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse the __debug_line into a list of DebugLine
|
||||
//
|
||||
// reference from Crystal dwarf parser
|
||||
// https://github.com/crystal-lang/crystal/blob/421fa11bf05b11441b74bbeb2762b673f96fec3f/src/crystal/dwarf/line_numbers.cr
|
||||
func (mc *MachoContext) DebugLineInfo() *DebuglineInfo {
|
||||
debug_line_section := mc.FindSection("__debug_line")
|
||||
if debug_line_section == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
start := uint64(debug_line_section.Offset())
|
||||
end := start + debug_line_section.Size()
|
||||
buf := bytes.NewBuffer(mc.buf[start:end])
|
||||
|
||||
debugLine := []*dwarf.DebuglineParser{}
|
||||
|
||||
for buf.Len() != 0 {
|
||||
parser := &dwarf.DebuglineParser{}
|
||||
parser.Parse(buf, mc.byteorder)
|
||||
debugLine = append(debugLine, parser)
|
||||
}
|
||||
|
||||
return &DebuglineInfo{
|
||||
debugLine,
|
||||
}
|
||||
}
|
237
macho-go/pkg/ios/macho/dyld_info.go
Normal file
237
macho-go/pkg/ios/macho/dyld_info.go
Normal file
@ -0,0 +1,237 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
. "ios-wrapper/pkg/leb128"
|
||||
)
|
||||
|
||||
type ImportSymbol struct {
|
||||
name string
|
||||
dylib string
|
||||
segment uint32
|
||||
segment_offset uint32
|
||||
address uint64
|
||||
file_address uint64
|
||||
|
||||
// push number
|
||||
pnum uint32
|
||||
stub uint64
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Name() string {
|
||||
return sym.name
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Dylib() string {
|
||||
return sym.dylib
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Address() uint64 {
|
||||
return sym.address
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Pnum() uint32 {
|
||||
return sym.pnum
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Stub() uint64 {
|
||||
return sym.stub
|
||||
}
|
||||
|
||||
func (mc *MachoContext) CollectLazyBindSymbols() []*ImportSymbol {
|
||||
if mc.dyldinfo == nil {
|
||||
return mc.CollectLazyBindSymbolsModern()
|
||||
} else {
|
||||
return mc.CollectLazyBindSymbolsLegacy()
|
||||
}
|
||||
}
|
||||
|
||||
// New convention using LC_DYLD_CHAINED_FIXUPS
|
||||
func (mc *MachoContext) CollectLazyBindSymbolsModern() []*ImportSymbol {
|
||||
var buf []byte
|
||||
for _, cmd := range mc.Linkedits() {
|
||||
if (cmd.Cmd() != LC_DYLD_CHAINED_FIXUPS) {
|
||||
continue
|
||||
}
|
||||
|
||||
count_offset := int64(cmd.Dataoff()) + 16
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0}, count_offset)
|
||||
|
||||
symbol_offset := int64(0x4000)
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0, 0, 0, 0, 0}, symbol_offset)
|
||||
|
||||
buf = mc.buf[cmd.Dataoff():cmd.Dataoff() + cmd.Datasize()]
|
||||
break
|
||||
}
|
||||
|
||||
r := bytes.NewReader(buf)
|
||||
rr := bufio.NewReader(r)
|
||||
|
||||
var fixups_version int32
|
||||
var starts_offset int32
|
||||
var imports_offset int32
|
||||
var symbols_offset int32
|
||||
var imports_count int32
|
||||
var imports_format int32
|
||||
var symbols_format int32
|
||||
binary.Read(rr, mc.byteorder, &fixups_version)
|
||||
binary.Read(rr, mc.byteorder, &starts_offset)
|
||||
binary.Read(rr, mc.byteorder, &imports_offset)
|
||||
binary.Read(rr, mc.byteorder, &symbols_offset)
|
||||
binary.Read(rr, mc.byteorder, &imports_count)
|
||||
binary.Read(rr, mc.byteorder, &imports_format)
|
||||
binary.Read(rr, mc.byteorder, &symbols_format)
|
||||
fmt.Printf(
|
||||
"HMMGE %x %x\n",
|
||||
imports_offset,
|
||||
imports_count,
|
||||
)
|
||||
|
||||
return []*ImportSymbol{}
|
||||
}
|
||||
|
||||
// Old convention using LC_DYLD_INFO_ONLY section and bytecode runner
|
||||
func (mc *MachoContext) CollectLazyBindSymbolsLegacy() []*ImportSymbol {
|
||||
start := mc.dyldinfo.lazy_bind_off
|
||||
size := mc.dyldinfo.lazy_bind_size
|
||||
end := start + size
|
||||
|
||||
if size == 0 {
|
||||
return []*ImportSymbol{}
|
||||
}
|
||||
|
||||
// clear this whole section to 0x00 BIND_OPCODE_DONE
|
||||
dummy := []byte{
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
}
|
||||
// make LINK EDIT section writable
|
||||
// mc.file.WriteAt([]byte{0x03}, int64(0x3f8))
|
||||
// set number of symbols to 0
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0}, int64(0x444))
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0}, int64(0x44c))
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0}, int64(0x48c))
|
||||
mc.file.WriteAt(dummy, int64(start))
|
||||
|
||||
buf := bytes.NewBuffer(mc.buf[start:end])
|
||||
offset := uint(0)
|
||||
|
||||
var syms []*ImportSymbol
|
||||
var sym ImportSymbol
|
||||
// symoffset := offset
|
||||
lastop_done := false
|
||||
for offset < uint(size) {
|
||||
d, _ := buf.ReadByte()
|
||||
op := d & 0xf0
|
||||
imm := d & 0x0f
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"op": fmt.Sprintf("0x%x", op),
|
||||
"imm": fmt.Sprintf("0x%x", imm),
|
||||
}).Trace("Bytecode")
|
||||
|
||||
if op != BIND_OPCODE_DONE && lastop_done {
|
||||
// symoffset = offset
|
||||
lastop_done = false
|
||||
}
|
||||
|
||||
switch op {
|
||||
case BIND_OPCODE_DONE:
|
||||
lastop_done = true
|
||||
offset += 1
|
||||
break
|
||||
|
||||
case BIND_OPCODE_DO_BIND:
|
||||
if sym.name != "" {
|
||||
new_sym := sym
|
||||
syms = append(syms, &new_sym)
|
||||
// fmt.Printf("Offset 0x%x: Symbol %+v\n", symoffset, sym)
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
// "offset": fmt.Sprintf("0x%x", symoffset),
|
||||
"symbol": sym.name,
|
||||
}).Trace("Bind")
|
||||
sym.name = ""
|
||||
}
|
||||
offset += 1
|
||||
break
|
||||
|
||||
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
|
||||
sym.dylib = string(mc.dylibs[int32(imm)-1].name[:])
|
||||
offset += 1
|
||||
break
|
||||
|
||||
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
|
||||
uleb, br := ReadULEB(buf)
|
||||
sym.dylib = string(mc.dylibs[int32(uleb)-1].name[:])
|
||||
offset += br
|
||||
break
|
||||
|
||||
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
|
||||
if imm == 0 {
|
||||
sym.dylib = "dylib_0"
|
||||
} else if imm == 0x0f {
|
||||
sym.dylib = "dylib_-1"
|
||||
} else if imm == 0x0e {
|
||||
sym.dylib = "dylib_-2"
|
||||
}
|
||||
offset += 1
|
||||
break
|
||||
|
||||
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
|
||||
sym.name, _ = buf.ReadString(0)
|
||||
offset += uint(len(sym.name))
|
||||
break
|
||||
|
||||
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
|
||||
uleb, br := ReadULEB(buf)
|
||||
sym.segment = uint32(imm)
|
||||
sym.segment_offset = uint32(uleb)
|
||||
|
||||
seg := mc.segments[sym.segment]
|
||||
sym.address = seg.Vmaddr() + uint64(sym.segment_offset)
|
||||
sym.file_address = seg.Fileoff() + uint64(sym.segment_offset)
|
||||
|
||||
offset += br
|
||||
break
|
||||
|
||||
case BIND_OPCODE_ADD_ADDR_ULEB:
|
||||
uleb, br := ReadULEB(buf)
|
||||
sym.segment_offset += uint32(uleb)
|
||||
sym.address += uint64(uleb)
|
||||
sym.file_address += uint64(uleb)
|
||||
offset += br
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, sym := range syms {
|
||||
switch mc.ArchName() {
|
||||
case "armv7", "armv7s":
|
||||
var addr uint32
|
||||
b := mc.buf[sym.file_address : sym.file_address+4]
|
||||
r := bytes.NewBuffer(b)
|
||||
binary.Read(r, mc.byteorder, &addr)
|
||||
sym.stub = uint64(addr)
|
||||
break
|
||||
case "arm64", "arm64e":
|
||||
b := mc.buf[sym.file_address : sym.file_address+8]
|
||||
r := bytes.NewBuffer(b)
|
||||
binary.Read(r, mc.byteorder, &sym.stub)
|
||||
break
|
||||
default:
|
||||
sym.pnum = 0
|
||||
sym.stub = 0
|
||||
}
|
||||
}
|
||||
return syms
|
||||
}
|
268
macho-go/pkg/ios/macho/edit.go
Normal file
268
macho-go/pkg/ios/macho/edit.go
Normal file
@ -0,0 +1,268 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
)
|
||||
|
||||
func rewriteLoadcommandsWithoutCodesignature(mc *MachoContext) {
|
||||
if mc.Is64bit() {
|
||||
mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
||||
} else {
|
||||
mc.file.Seek(int64(Header_size), io.SeekStart)
|
||||
}
|
||||
for _, cmd := range mc.commands {
|
||||
if cmd.Cmd() == LC_CODE_SIGNATURE {
|
||||
continue
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"cmd": cmd.Cmd(),
|
||||
"cmdsize": cmd.Cmdsize(),
|
||||
"name": cmd.Cmdname(),
|
||||
}).Trace("Rewrite Load command")
|
||||
mc.file.Write(cmd.Serialize(mc))
|
||||
}
|
||||
}
|
||||
|
||||
// Remove Codesign data from Mach-O binary
|
||||
// There are many caveats to this problem.
|
||||
// Mostly related to how codesign-allocate works.
|
||||
// Removing codesign data at the end of __LINKEDIT sement
|
||||
// is probably easier than removing them at an abitrary location.
|
||||
//
|
||||
// Assuming the codesign data is at the end of __LINKEDIT segment.
|
||||
// The binary is probably signed at the last step, which is common.
|
||||
//
|
||||
// CODE_SIGNATURE load commands points to the codesign data offset and size.
|
||||
// __LINKEDIT section points to data offset and size.
|
||||
// We have:
|
||||
// linkedit = (section*) LC_SEGMENT.section[0] // name="__LINKEDIT"
|
||||
// codesign = (linkedit_data_command*) LC_CODE_SIGNATURE
|
||||
// BinarySize = { f.seek(0, SEEKEND); return f.tell() }
|
||||
//
|
||||
// linkedit->fileoff + linkedit->filesize == codesign->dataOff + codesign->dataSize
|
||||
// linkedit->fileoff + linkedit->filesize == BinarySize
|
||||
//
|
||||
// To remove the codesign data, we truncate the file to remove the codesign data.
|
||||
// Then we update the linkedit->filesize to linkedit->filesize - codesign->dataSize
|
||||
// We also fix the header to not read the LC_CODE_SIGNATURE,
|
||||
// because codesign is the last step, the last load command is LC_CODE_SIGNATURE.
|
||||
// Fix header->ncmds -= 1, header->sizeofcmds -= sizeof(linkedit_data_command)
|
||||
//
|
||||
// There's one more caveat, as mentioned by `insert_dylib`. Codesign data is aligned
|
||||
// by 0x10. So it would have some padding before codesign data. Now that we have
|
||||
// removed the data, but the padding still exists and is not prefered by codesign-allocate.
|
||||
// To fix this issue, **again** we assume that the previous section is the String table
|
||||
// (referenced by symtab) and increase the size of the string table to the end of data.
|
||||
//
|
||||
// A better approach could be an extended search for section pointing to the previous data.
|
||||
// But general cases are String table.
|
||||
//
|
||||
// Implementation warnings:
|
||||
// We have two implementation designs.
|
||||
// - Re-parse the load commands and edit the value at offset
|
||||
// - Use parsed load commands data, edit the values, and rewrite header + load commands
|
||||
func (mc *MachoContext) RemoveCodesign() bool {
|
||||
if !mc.WriteEnabled() {
|
||||
return false
|
||||
}
|
||||
|
||||
linkedit := mc.FindSegment("__LINKEDIT")
|
||||
if linkedit == nil {
|
||||
log.Warn("The binary has no __LINKEDIT Segment")
|
||||
return false
|
||||
}
|
||||
|
||||
var codesign *LinkEdit
|
||||
for _, cmd := range mc.Linkedits() {
|
||||
if cmd.Cmd() == LC_CODE_SIGNATURE {
|
||||
codesign = cmd
|
||||
break
|
||||
}
|
||||
}
|
||||
if codesign == nil {
|
||||
log.Warn("The binary is not signed, no LC_CODE_SIGNATURE found")
|
||||
return false
|
||||
}
|
||||
|
||||
filesize := uint64(len(mc.buf))
|
||||
linkedit_end := uint64(linkedit.Fileoff()) + linkedit.Filesize()
|
||||
codesign_end := uint64(codesign.Dataoff() + codesign.Datasize())
|
||||
|
||||
// linkedit is the last item in Mach-O binary
|
||||
if linkedit_end != filesize {
|
||||
log.Panic("Link edit is not the last item")
|
||||
return false
|
||||
}
|
||||
// codesign is the last in linkedit
|
||||
if linkedit_end != codesign_end {
|
||||
log.Panic("Code sign data is not the last item in link edit segment")
|
||||
return false
|
||||
}
|
||||
|
||||
linkedit_newsize := linkedit.Filesize() - uint64(codesign.Datasize())
|
||||
|
||||
if (mc.symtab.stroff+mc.symtab.strsize)%0x10 == 0 {
|
||||
// codesign requires padding of 0x10
|
||||
// if strtab+strsize & 0xff < 0x8, it will pad til 0x10
|
||||
// when removed codesign, we truncate the file to offset at 0x8 rather at 0x10
|
||||
} else {
|
||||
linkedit_newsize -= 8
|
||||
}
|
||||
|
||||
mc.file.Truncate(int64(uint64(linkedit.Fileoff()) + linkedit_newsize))
|
||||
mc.symtab.strsize = uint32(linkedit.Fileoff()+linkedit_newsize) - mc.symtab.stroff
|
||||
|
||||
// fix linkedit section
|
||||
if mc.Is64bit() {
|
||||
linkedit.(*Segment64).filesize = linkedit_newsize
|
||||
} else {
|
||||
linkedit.(*Segment32).filesize = uint32(linkedit_newsize)
|
||||
}
|
||||
|
||||
// rewrite load commands
|
||||
mc.UpdateHeaderRemoveLcmd(codesign.Cmdsize())
|
||||
rewriteLoadcommandsWithoutCodesignature(mc)
|
||||
|
||||
// erase old LC_CODE_SIGNATURE data
|
||||
old_codesign_lcmd_offset := func() uint64 {
|
||||
loadcmd_size := int64(mc.Header().sizeofcmds)
|
||||
if mc.Is64bit() {
|
||||
return uint64(loadcmd_size) + Header_size_64
|
||||
} else {
|
||||
return uint64(loadcmd_size) + Header_size
|
||||
}
|
||||
}()
|
||||
// size of codesign = sizeof linkedit_data_command = 4 * 4
|
||||
mc.file.WriteAt(make([]byte, 4*4), int64(old_codesign_lcmd_offset))
|
||||
// mc.file.Seek(old_codesign_lcmd_offset, io.SeekStart)
|
||||
// mc.file.Write(make([]byte, 4*4))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RemoveInitFunctions() bool {
|
||||
if mc.WriteEnabled() {
|
||||
for _, ptr := range mc.InitFunctions() {
|
||||
if mc.Is64bit() {
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0, 0, 0, 0, 0}, int64(ptr.offset))
|
||||
} else {
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0}, int64(ptr.offset))
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"offset": fmt.Sprintf("0x%x", ptr.Offset()),
|
||||
"value": fmt.Sprintf("0x%x", ptr.Value()),
|
||||
}).Debug("Remove init pointer")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RemoveUnnecessaryInfo() bool {
|
||||
for _, command := range mc.commands {
|
||||
switch command.(type) {
|
||||
case *LinkEdit:
|
||||
var le = command.(*LinkEdit)
|
||||
if le.Cmdname() != "LC_FUNCTION_STARTS" && le.Cmdname() != "LC_DATA_IN_CODE" {
|
||||
continue
|
||||
}
|
||||
var start int64 = int64(le.dataoff)
|
||||
var end int64 = start + int64(le.datasize)
|
||||
for i := start; i < end; i++ {
|
||||
mc.file.WriteAt([]byte{0}, i)
|
||||
}
|
||||
continue
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RemoveClassicSymbol() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddLoadCmd(lcmd LoadCommand) {
|
||||
var offset uint64
|
||||
payload := lcmd.Serialize(mc)
|
||||
if uint64(len(payload)) > mc.PaddingSize() {
|
||||
log.WithFields(log.Fields{
|
||||
"cmd": lcmd,
|
||||
"len(cmd)": len(payload),
|
||||
"available": mc.PaddingSize(),
|
||||
}).Panic("Not enough space to add load command")
|
||||
}
|
||||
|
||||
if mc.Is64bit() {
|
||||
offset = Header_size_64 + uint64(mc.header.sizeofcmds)
|
||||
} else {
|
||||
offset = Header_size + uint64(mc.header.sizeofcmds)
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"cmd": lcmd.Cmdname(),
|
||||
"size": len(payload),
|
||||
"offset": offset,
|
||||
}).Debug("Adding Load Command")
|
||||
mc.file.WriteAt(payload, int64(offset))
|
||||
mc.UpdateHeaderAddLcmd(uint32(len(payload)))
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddDylib(dylib string) {
|
||||
if mc.DylibExisted(dylib) {
|
||||
log.WithFields(log.Fields{
|
||||
"dylib": dylib,
|
||||
}).Debug("Adding dylib but existed")
|
||||
return
|
||||
}
|
||||
name := []byte(dylib)
|
||||
name = append(name, 0)
|
||||
dylib_lcmd := Dylib{
|
||||
c: LoadCmd{cmd: LC_LOAD_DYLIB, cmdsize: 0},
|
||||
name: name,
|
||||
nameoff: 24,
|
||||
timestamp: 0,
|
||||
current_version: 0,
|
||||
compatibility_version: 0,
|
||||
}
|
||||
|
||||
mc.AddLoadCmd(&dylib_lcmd)
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddRPath(rpath string) {
|
||||
if mc.RPathExisted(rpath) {
|
||||
log.WithFields(log.Fields{
|
||||
"rpath": rpath,
|
||||
}).Debug("Adding rpath but existed")
|
||||
return
|
||||
}
|
||||
path := []byte(rpath)
|
||||
path = append(path, 0)
|
||||
rpath_lcmd := RPath{
|
||||
c: LoadCmd{cmd: LC_RPATH, cmdsize: 0},
|
||||
offset: 12,
|
||||
path: path,
|
||||
}
|
||||
mc.AddLoadCmd(&rpath_lcmd)
|
||||
}
|
||||
|
||||
func (mc *MachoContext) UpdateHeaderAddLcmd(size uint32) {
|
||||
if mc.WriteEnabled() {
|
||||
mc.header.ncmds += 1
|
||||
mc.header.sizeofcmds += size
|
||||
mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *MachoContext) UpdateHeaderRemoveLcmd(size uint32) {
|
||||
if mc.WriteEnabled() {
|
||||
mc.header.ncmds -= 1
|
||||
mc.header.sizeofcmds -= size
|
||||
mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||
}
|
||||
}
|
13
macho-go/pkg/ios/macho/error.go
Normal file
13
macho-go/pkg/ios/macho/error.go
Normal file
@ -0,0 +1,13 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ParseError struct {
|
||||
Expect string
|
||||
}
|
||||
|
||||
func (e *ParseError) Error() string {
|
||||
return fmt.Sprintf("Parse Error: %s", e.Expect)
|
||||
}
|
384
macho-go/pkg/ios/macho/lc_segment.go
Normal file
384
macho-go/pkg/ios/macho/lc_segment.go
Normal file
@ -0,0 +1,384 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Section interface {
|
||||
// SegName() []byte
|
||||
SectName() []byte
|
||||
Addr() uint64
|
||||
Size() uint64
|
||||
Offset() uint32
|
||||
Type() uint8
|
||||
Attribute() uint32
|
||||
}
|
||||
|
||||
type Segment interface {
|
||||
SegName() []byte
|
||||
Vmaddr() uint64
|
||||
Vmsize() uint64
|
||||
Fileoff() uint64
|
||||
Filesize() uint64
|
||||
Flags() uint32
|
||||
Sections() []Section
|
||||
}
|
||||
|
||||
type Segment32 struct {
|
||||
c LoadCmd
|
||||
segname []byte
|
||||
vmaddr uint32
|
||||
vmsize uint32
|
||||
fileoff uint32
|
||||
filesize uint32
|
||||
maxprot uint32
|
||||
initprot uint32
|
||||
nsects uint32
|
||||
flags uint32
|
||||
sections []*Section32
|
||||
}
|
||||
|
||||
func (lcmd *Segment32) Cmd() uint32 {
|
||||
return lcmd.c.Cmd()
|
||||
}
|
||||
|
||||
func (lcmd *Segment32) Cmdsize() uint32 {
|
||||
segment_size := uint32(56)
|
||||
section_size := uint32(4*9 + 16*2)
|
||||
return segment_size + section_size*lcmd.nsects
|
||||
}
|
||||
|
||||
func (lcmd *Segment32) Cmdname() string {
|
||||
var s string
|
||||
s += fmt.Sprintf(
|
||||
"%s %s vm[vmaddr:0x%x vmsize:0x%x] [fileoff:0x%x filesize:0x%x]",
|
||||
lcmd.c.Cmdname(),
|
||||
string(lcmd.segname),
|
||||
lcmd.vmaddr,
|
||||
lcmd.vmsize,
|
||||
lcmd.fileoff,
|
||||
lcmd.filesize,
|
||||
)
|
||||
for i, section := range lcmd.sections {
|
||||
s += fmt.Sprintf("\n %d. %s [addr:0x%x size:0x%x offset:0x%x]",
|
||||
i, string(section.sectname),
|
||||
section.addr, section.size, section.offset,
|
||||
)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (lcmd *Segment32) SegName() []byte {
|
||||
return lcmd.segname
|
||||
}
|
||||
func (lcmd *Segment32) Vmaddr() uint64 {
|
||||
return uint64(lcmd.vmaddr)
|
||||
}
|
||||
func (lcmd *Segment32) Vmsize() uint64 {
|
||||
return uint64(lcmd.vmaddr)
|
||||
}
|
||||
func (lcmd *Segment32) Fileoff() uint64 {
|
||||
return uint64(lcmd.fileoff)
|
||||
}
|
||||
func (lcmd *Segment32) Filesize() uint64 {
|
||||
return uint64(lcmd.filesize)
|
||||
}
|
||||
func (lcmd *Segment32) Flags() uint32 {
|
||||
return lcmd.flags
|
||||
}
|
||||
func (lcmd *Segment32) Sections() []Section {
|
||||
var sections []Section
|
||||
for _, section := range lcmd.sections {
|
||||
sections = append(sections, section)
|
||||
}
|
||||
return sections
|
||||
}
|
||||
|
||||
func (lcmd *Segment32) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
buf.Write(lcmd.segname)
|
||||
binary.Write(buf, mc.byteorder, lcmd.vmaddr)
|
||||
binary.Write(buf, mc.byteorder, lcmd.vmsize)
|
||||
binary.Write(buf, mc.byteorder, lcmd.fileoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.filesize)
|
||||
binary.Write(buf, mc.byteorder, lcmd.maxprot)
|
||||
binary.Write(buf, mc.byteorder, lcmd.initprot)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nsects)
|
||||
binary.Write(buf, mc.byteorder, lcmd.flags)
|
||||
for _, section := range lcmd.sections {
|
||||
binary.Write(buf, mc.byteorder, section.Serialize(mc))
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *Segment32) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
lcmd.segname = r.Next(16)
|
||||
binary.Read(r, mc.byteorder, &lcmd.vmaddr)
|
||||
binary.Read(r, mc.byteorder, &lcmd.vmsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.fileoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.filesize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.maxprot)
|
||||
binary.Read(r, mc.byteorder, &lcmd.initprot)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nsects)
|
||||
binary.Read(r, mc.byteorder, &lcmd.flags)
|
||||
|
||||
for i := uint32(0); i < lcmd.nsects; i++ {
|
||||
section_buf := make([]byte, 68)
|
||||
r.Read(section_buf)
|
||||
|
||||
var section Section32
|
||||
section.Deserialize(mc, section_buf)
|
||||
lcmd.sections = append(lcmd.sections, §ion)
|
||||
}
|
||||
}
|
||||
|
||||
type Segment64 struct {
|
||||
c LoadCmd
|
||||
segname []byte
|
||||
vmaddr uint64
|
||||
vmsize uint64
|
||||
fileoff uint64
|
||||
filesize uint64
|
||||
maxprot uint32
|
||||
initprot uint32
|
||||
nsects uint32
|
||||
flags uint32
|
||||
sections []*Section64
|
||||
}
|
||||
|
||||
func (lcmd *Segment64) Cmd() uint32 {
|
||||
return lcmd.c.Cmd()
|
||||
}
|
||||
|
||||
func (lcmd *Segment64) Cmdsize() uint32 {
|
||||
segment_size := uint32(72)
|
||||
section_size := uint32(8*2 + 4*8 + 16*2)
|
||||
return segment_size + section_size*lcmd.nsects
|
||||
}
|
||||
|
||||
func (lcmd *Segment64) Cmdname() string {
|
||||
var s string
|
||||
s += fmt.Sprintf(
|
||||
"%s %s vm[vmaddr:0x%x vmsize:0x%x] [fileoff:0x%x filesize:0x%x]",
|
||||
lcmd.c.Cmdname(),
|
||||
string(lcmd.segname),
|
||||
lcmd.vmaddr,
|
||||
lcmd.vmsize,
|
||||
lcmd.fileoff,
|
||||
lcmd.filesize,
|
||||
)
|
||||
for i, section := range lcmd.sections {
|
||||
s += fmt.Sprintf("\n %d. %s [addr:0x%x size:0x%x offset:0x%x]",
|
||||
i, string(section.sectname),
|
||||
section.addr, section.size, section.offset,
|
||||
)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (lcmd *Segment64) SegName() []byte {
|
||||
return lcmd.segname
|
||||
}
|
||||
func (lcmd *Segment64) Vmaddr() uint64 {
|
||||
return lcmd.vmaddr
|
||||
}
|
||||
func (lcmd *Segment64) Vmsize() uint64 {
|
||||
return lcmd.vmaddr
|
||||
}
|
||||
func (lcmd *Segment64) Fileoff() uint64 {
|
||||
return lcmd.fileoff
|
||||
}
|
||||
func (lcmd *Segment64) Filesize() uint64 {
|
||||
return lcmd.filesize
|
||||
}
|
||||
func (lcmd *Segment64) Flags() uint32 {
|
||||
return lcmd.flags
|
||||
}
|
||||
func (lcmd *Segment64) Sections() []Section {
|
||||
var sections []Section
|
||||
for _, section := range lcmd.sections {
|
||||
sections = append(sections, section)
|
||||
}
|
||||
return sections
|
||||
}
|
||||
|
||||
func (lcmd *Segment64) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
buf.Write(lcmd.segname)
|
||||
binary.Write(buf, mc.byteorder, lcmd.vmaddr)
|
||||
binary.Write(buf, mc.byteorder, lcmd.vmsize)
|
||||
binary.Write(buf, mc.byteorder, lcmd.fileoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.filesize)
|
||||
binary.Write(buf, mc.byteorder, lcmd.maxprot)
|
||||
binary.Write(buf, mc.byteorder, lcmd.initprot)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nsects)
|
||||
binary.Write(buf, mc.byteorder, lcmd.flags)
|
||||
for _, section := range lcmd.sections {
|
||||
binary.Write(buf, mc.byteorder, section.Serialize(mc))
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *Segment64) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
lcmd.segname = r.Next(16)
|
||||
binary.Read(r, mc.byteorder, &lcmd.vmaddr)
|
||||
binary.Read(r, mc.byteorder, &lcmd.vmsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.fileoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.filesize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.maxprot)
|
||||
binary.Read(r, mc.byteorder, &lcmd.initprot)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nsects)
|
||||
binary.Read(r, mc.byteorder, &lcmd.flags)
|
||||
|
||||
for i := uint32(0); i < lcmd.nsects; i++ {
|
||||
section_buf := make([]byte, 80)
|
||||
r.Read(section_buf)
|
||||
|
||||
var section Section64
|
||||
section.Deserialize(mc, section_buf)
|
||||
lcmd.sections = append(lcmd.sections, §ion)
|
||||
}
|
||||
}
|
||||
|
||||
type Section32 struct {
|
||||
sectname []byte
|
||||
segname []byte
|
||||
addr uint32
|
||||
size uint32
|
||||
offset uint32
|
||||
align uint32
|
||||
reloff uint32
|
||||
nreloc uint32
|
||||
flags uint32
|
||||
reserved1 uint32
|
||||
reserved2 uint32
|
||||
}
|
||||
|
||||
func (sec *Section32) SectName() []byte {
|
||||
return sec.sectname
|
||||
}
|
||||
func (sec *Section32) Addr() uint64 {
|
||||
return uint64(sec.addr)
|
||||
}
|
||||
func (sec *Section32) Size() uint64 {
|
||||
return uint64(sec.size)
|
||||
}
|
||||
func (sec *Section32) Offset() uint32 {
|
||||
return sec.offset
|
||||
}
|
||||
func (sec *Section32) Type() uint8 {
|
||||
return uint8(sec.flags & 0xff)
|
||||
}
|
||||
func (sec *Section32) Attribute() uint32 {
|
||||
return sec.flags & 0xffffff00
|
||||
}
|
||||
|
||||
func (section *Section32) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Write(section.sectname)
|
||||
buf.Write(section.segname)
|
||||
binary.Write(buf, mc.byteorder, section.addr)
|
||||
binary.Write(buf, mc.byteorder, section.size)
|
||||
binary.Write(buf, mc.byteorder, section.offset)
|
||||
binary.Write(buf, mc.byteorder, section.align)
|
||||
binary.Write(buf, mc.byteorder, section.reloff)
|
||||
binary.Write(buf, mc.byteorder, section.nreloc)
|
||||
binary.Write(buf, mc.byteorder, section.flags)
|
||||
binary.Write(buf, mc.byteorder, section.reserved1)
|
||||
binary.Write(buf, mc.byteorder, section.reserved2)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (section *Section32) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
section.sectname = r.Next(16)
|
||||
section.segname = r.Next(16)
|
||||
binary.Read(r, mc.byteorder, §ion.addr)
|
||||
binary.Read(r, mc.byteorder, §ion.size)
|
||||
binary.Read(r, mc.byteorder, §ion.offset)
|
||||
binary.Read(r, mc.byteorder, §ion.align)
|
||||
binary.Read(r, mc.byteorder, §ion.reloff)
|
||||
binary.Read(r, mc.byteorder, §ion.nreloc)
|
||||
binary.Read(r, mc.byteorder, §ion.flags)
|
||||
binary.Read(r, mc.byteorder, §ion.reserved1)
|
||||
binary.Read(r, mc.byteorder, §ion.reserved2)
|
||||
}
|
||||
|
||||
type Section64 struct {
|
||||
sectname []byte
|
||||
segname []byte
|
||||
addr uint64
|
||||
size uint64
|
||||
offset uint32
|
||||
align uint32
|
||||
reloff uint32
|
||||
nreloc uint32
|
||||
flags uint32
|
||||
reserved1 uint32
|
||||
reserved2 uint32
|
||||
reserved3 uint32
|
||||
}
|
||||
|
||||
func (sec *Section64) SectName() []byte {
|
||||
return sec.sectname
|
||||
}
|
||||
func (sec *Section64) Addr() uint64 {
|
||||
return sec.addr
|
||||
}
|
||||
func (sec *Section64) Size() uint64 {
|
||||
return sec.size
|
||||
}
|
||||
func (sec *Section64) Offset() uint32 {
|
||||
return sec.offset
|
||||
}
|
||||
func (sec *Section64) Type() uint8 {
|
||||
return uint8(sec.flags & 0xff)
|
||||
}
|
||||
func (sec *Section64) Attribute() uint32 {
|
||||
return sec.flags & 0xffffff00
|
||||
}
|
||||
|
||||
func (section *Section64) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Write(section.sectname)
|
||||
buf.Write(section.segname)
|
||||
binary.Write(buf, mc.byteorder, section.addr)
|
||||
binary.Write(buf, mc.byteorder, section.size)
|
||||
binary.Write(buf, mc.byteorder, section.offset)
|
||||
binary.Write(buf, mc.byteorder, section.align)
|
||||
binary.Write(buf, mc.byteorder, section.reloff)
|
||||
binary.Write(buf, mc.byteorder, section.nreloc)
|
||||
binary.Write(buf, mc.byteorder, section.flags)
|
||||
binary.Write(buf, mc.byteorder, section.reserved1)
|
||||
binary.Write(buf, mc.byteorder, section.reserved2)
|
||||
binary.Write(buf, mc.byteorder, section.reserved3)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (section *Section64) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
section.sectname = r.Next(16)
|
||||
section.segname = r.Next(16)
|
||||
binary.Read(r, mc.byteorder, §ion.addr)
|
||||
binary.Read(r, mc.byteorder, §ion.size)
|
||||
binary.Read(r, mc.byteorder, §ion.offset)
|
||||
binary.Read(r, mc.byteorder, §ion.align)
|
||||
binary.Read(r, mc.byteorder, §ion.reloff)
|
||||
binary.Read(r, mc.byteorder, §ion.nreloc)
|
||||
binary.Read(r, mc.byteorder, §ion.flags)
|
||||
binary.Read(r, mc.byteorder, §ion.reserved1)
|
||||
binary.Read(r, mc.byteorder, §ion.reserved2)
|
||||
binary.Read(r, mc.byteorder, §ion.reserved3)
|
||||
}
|
483
macho-go/pkg/ios/macho/load_commands.go
Normal file
483
macho-go/pkg/ios/macho/load_commands.go
Normal file
@ -0,0 +1,483 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
)
|
||||
|
||||
// A Serializable interface
|
||||
// Serialize to bytes and Deserialize to struct
|
||||
// with MachoContext to know the byteorder
|
||||
type Serializable interface {
|
||||
Serialize(mc *MachoContext) []byte
|
||||
Deserialize(mc *MachoContext, buf []byte)
|
||||
}
|
||||
|
||||
// macho_header and macho_header64
|
||||
type Header struct {
|
||||
magic uint32
|
||||
cputype uint32
|
||||
cpusubtype uint32
|
||||
filetype uint32
|
||||
ncmds uint32
|
||||
sizeofcmds uint32
|
||||
flags uint32
|
||||
reserved uint32
|
||||
}
|
||||
|
||||
func (h *Header) Cputype() uint32 {
|
||||
return h.cputype
|
||||
}
|
||||
|
||||
func (h *Header) Cpusubtype() uint32 {
|
||||
return h.cpusubtype
|
||||
}
|
||||
|
||||
func (h *Header) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, h.magic)
|
||||
binary.Write(buf, mc.byteorder, h.cputype)
|
||||
binary.Write(buf, mc.byteorder, h.cpusubtype)
|
||||
binary.Write(buf, mc.byteorder, h.filetype)
|
||||
binary.Write(buf, mc.byteorder, h.ncmds)
|
||||
binary.Write(buf, mc.byteorder, h.sizeofcmds)
|
||||
binary.Write(buf, mc.byteorder, h.flags)
|
||||
if mc.Is64bit() {
|
||||
binary.Write(buf, mc.byteorder, h.reserved)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (h *Header) Deserialize(mc *MachoContext, r *bufio.Reader) {
|
||||
binary.Read(r, mc.byteorder, &h.magic)
|
||||
binary.Read(r, mc.byteorder, &h.cputype)
|
||||
binary.Read(r, mc.byteorder, &h.cpusubtype)
|
||||
binary.Read(r, mc.byteorder, &h.filetype)
|
||||
binary.Read(r, mc.byteorder, &h.ncmds)
|
||||
binary.Read(r, mc.byteorder, &h.sizeofcmds)
|
||||
binary.Read(r, mc.byteorder, &h.flags)
|
||||
if mc.Is64bit() {
|
||||
binary.Read(r, mc.byteorder, &h.reserved)
|
||||
}
|
||||
}
|
||||
|
||||
type LoadCommand interface {
|
||||
Serializable
|
||||
Cmd() uint32
|
||||
Cmdsize() uint32
|
||||
Cmdname() string
|
||||
}
|
||||
|
||||
// Stores the common value of load command
|
||||
// Raw stores the rest of the command
|
||||
// where no struct defined for cmd
|
||||
type LoadCmd struct {
|
||||
cmd uint32
|
||||
cmdsize uint32
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
func (lcmd *LoadCmd) Cmd() uint32 {
|
||||
return lcmd.cmd
|
||||
}
|
||||
|
||||
func (lcmd *LoadCmd) Cmdsize() uint32 {
|
||||
return lcmd.cmdsize
|
||||
}
|
||||
|
||||
func (lcmd *LoadCmd) Cmdname() string {
|
||||
switch lcmd.cmd {
|
||||
case LC_RPATH:
|
||||
return "LC_RPATH"
|
||||
case LC_DYLD_INFO:
|
||||
return "LC_DYLD_INFO"
|
||||
case LC_DYLD_INFO_ONLY:
|
||||
return "LC_DYLD_INFO_ONLY"
|
||||
case LC_CODE_SIGNATURE:
|
||||
return "LC_CODE_SIGNATURE"
|
||||
case LC_LAZY_LOAD_DYLIB:
|
||||
return "LC_LAZY_LOAD_DYLIB"
|
||||
case LC_LOAD_DYLIB:
|
||||
return "LC_LOAD_DYLIB"
|
||||
case LC_ID_DYLIB:
|
||||
return "LC_ID_DYLIB"
|
||||
case LC_REEXPORT_DYLIB:
|
||||
return "LC_REEXPORT_DYLIB"
|
||||
case LC_ENCRYPTION_INFO:
|
||||
return "LC_ENCRYPTION_INFO"
|
||||
case LC_ENCRYPTION_INFO_64:
|
||||
return "LC_ENCRYPTION_INFO_64"
|
||||
case LC_DYSYMTAB:
|
||||
return "LC_DYSYMTAB"
|
||||
case LC_LOAD_WEAK_DYLIB:
|
||||
return "LC_LOAD_WEAK_DYLIB"
|
||||
case LC_SEGMENT:
|
||||
return "LC_SEGMENT"
|
||||
case LC_SEGMENT_64:
|
||||
return "LC_SEGMENT_64"
|
||||
case LC_MAIN:
|
||||
return "LC_MAIN"
|
||||
case LC_FUNCTION_STARTS:
|
||||
return "LC_FUNCTION_STARTS"
|
||||
case LC_DATA_IN_CODE:
|
||||
return "LC_DATA_IN_CODE"
|
||||
case LC_SYMTAB:
|
||||
return "LC_SYMTAB"
|
||||
default:
|
||||
// TODO: Update
|
||||
return fmt.Sprintf("LC_DONT_KNOW_0x%x", lcmd.Cmd())
|
||||
}
|
||||
}
|
||||
|
||||
func (lcmd *LoadCmd) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Raw)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *LoadCmd) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.cmdsize)
|
||||
lcmd.Raw = make([]byte, lcmd.cmdsize-8)
|
||||
r.Read(lcmd.Raw)
|
||||
}
|
||||
|
||||
type RPath struct {
|
||||
c LoadCmd
|
||||
offset uint32
|
||||
path []byte
|
||||
}
|
||||
|
||||
func (lcmd *RPath) Cmd() uint32 {
|
||||
return LC_RPATH
|
||||
}
|
||||
|
||||
func (lcmd *RPath) Cmdsize() uint32 {
|
||||
raw_cmd_size := 8 + 4 + len(lcmd.path) + 1
|
||||
if raw_cmd_size%8 == 0 {
|
||||
return uint32(raw_cmd_size)
|
||||
}
|
||||
padded_cmd_size := raw_cmd_size + (8 - (raw_cmd_size % 8))
|
||||
return uint32(padded_cmd_size)
|
||||
}
|
||||
|
||||
func (lcmd *RPath) Cmdname() string {
|
||||
return fmt.Sprintf("%s %s", lcmd.c.Cmdname(), string(lcmd.path))
|
||||
}
|
||||
|
||||
func (lcmd *RPath) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
binary.Write(buf, mc.byteorder, lcmd.offset)
|
||||
binary.Write(buf, mc.byteorder, []byte(lcmd.path))
|
||||
buf.WriteByte(0)
|
||||
if buf.Len()%8 != 0 {
|
||||
bytes_padded := 8 - (buf.Len() % 8)
|
||||
binary.Write(buf, mc.byteorder, make([]byte, bytes_padded))
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *RPath) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.offset)
|
||||
lcmd.path, _ = r.ReadBytes(0)
|
||||
lcmd.path = bytes.Trim(lcmd.path, "\x00")
|
||||
}
|
||||
|
||||
type Dylib struct {
|
||||
c LoadCmd
|
||||
nameoff uint32
|
||||
name []byte
|
||||
timestamp uint32
|
||||
current_version uint32
|
||||
compatibility_version uint32
|
||||
}
|
||||
|
||||
func (lcmd *Dylib) Cmd() uint32 {
|
||||
return lcmd.c.Cmd()
|
||||
}
|
||||
|
||||
func (lcmd *Dylib) Cmdsize() uint32 {
|
||||
raw_cmd_size := 8 + 16 + len(lcmd.name) + 1
|
||||
if raw_cmd_size%8 == 0 {
|
||||
return uint32(raw_cmd_size)
|
||||
}
|
||||
padded_cmd_size := raw_cmd_size + (8 - (raw_cmd_size % 8))
|
||||
return uint32(padded_cmd_size)
|
||||
}
|
||||
|
||||
func (lcmd *Dylib) Cmdname() string {
|
||||
return fmt.Sprintf("%s %s", lcmd.c.Cmdname(), string(lcmd.name))
|
||||
}
|
||||
|
||||
func (lcmd *Dylib) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
binary.Write(buf, mc.byteorder, lcmd.nameoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.timestamp)
|
||||
binary.Write(buf, mc.byteorder, lcmd.current_version)
|
||||
binary.Write(buf, mc.byteorder, lcmd.compatibility_version)
|
||||
binary.Write(buf, mc.byteorder, lcmd.name)
|
||||
buf.WriteByte(0)
|
||||
|
||||
// size must align with 8
|
||||
if buf.Len()%8 != 0 {
|
||||
bytes_padded := 8 - (buf.Len() % 8)
|
||||
binary.Write(buf, mc.byteorder, make([]byte, bytes_padded))
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *Dylib) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nameoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.timestamp)
|
||||
binary.Read(r, mc.byteorder, &lcmd.current_version)
|
||||
binary.Read(r, mc.byteorder, &lcmd.compatibility_version)
|
||||
lcmd.name, _ = r.ReadBytes(0)
|
||||
lcmd.name = bytes.Trim(lcmd.name, "\x00")
|
||||
}
|
||||
|
||||
type EncryptionInfo struct {
|
||||
c LoadCmd
|
||||
cryptoff uint32
|
||||
cryptsize uint32
|
||||
cryptid uint32
|
||||
pad uint32
|
||||
}
|
||||
|
||||
func (lcmd *EncryptionInfo) Cmd() uint32 {
|
||||
return lcmd.c.Cmd()
|
||||
}
|
||||
|
||||
func (lcmd *EncryptionInfo) Cmdsize() uint32 {
|
||||
if lcmd.Cmd() == LC_ENCRYPTION_INFO {
|
||||
return uint32(8 + 4*3)
|
||||
} else {
|
||||
return uint32(8 + 4*4)
|
||||
}
|
||||
}
|
||||
|
||||
func (lcmd *EncryptionInfo) Cmdname() string {
|
||||
return fmt.Sprintf("%s start:0x%x size:0x%x encrypted:%d",
|
||||
lcmd.c.Cmdname(), lcmd.cryptoff, lcmd.cryptsize, lcmd.cryptid)
|
||||
}
|
||||
|
||||
func (lcmd *EncryptionInfo) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
binary.Write(buf, mc.byteorder, lcmd.cryptoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.cryptsize)
|
||||
binary.Write(buf, mc.byteorder, lcmd.cryptid)
|
||||
if mc.Is64bit() {
|
||||
binary.Write(buf, mc.byteorder, lcmd.pad)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *EncryptionInfo) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.cryptoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.cryptsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.cryptid)
|
||||
if mc.Is64bit() {
|
||||
binary.Read(r, mc.byteorder, &lcmd.pad)
|
||||
}
|
||||
}
|
||||
|
||||
type EntryPoint struct {
|
||||
c LoadCmd
|
||||
entryoff uint64
|
||||
stacksize uint64
|
||||
}
|
||||
|
||||
func (lcmd *EntryPoint) Cmd() uint32 {
|
||||
return lcmd.c.Cmd()
|
||||
}
|
||||
|
||||
func (lcmd *EntryPoint) Cmdsize() uint32 {
|
||||
return uint32(8 + 8*2)
|
||||
}
|
||||
|
||||
func (lcmd *EntryPoint) Cmdname() string {
|
||||
return lcmd.c.Cmdname()
|
||||
}
|
||||
|
||||
func (lcmd *EntryPoint) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
binary.Write(buf, mc.byteorder, lcmd.entryoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.stacksize)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *EntryPoint) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.entryoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.stacksize)
|
||||
}
|
||||
|
||||
type DyldInfo struct {
|
||||
c LoadCmd
|
||||
rebase_off uint32
|
||||
rebase_size uint32
|
||||
bind_off uint32
|
||||
bind_size uint32
|
||||
weak_bind_off uint32
|
||||
weak_bind_size uint32
|
||||
lazy_bind_off uint32
|
||||
lazy_bind_size uint32
|
||||
export_off uint32
|
||||
export_size uint32
|
||||
}
|
||||
|
||||
func (lcmd *DyldInfo) Cmd() uint32 {
|
||||
return lcmd.c.Cmd()
|
||||
}
|
||||
|
||||
func (lcmd *DyldInfo) Cmdsize() uint32 {
|
||||
return uint32(8 + 4*10)
|
||||
}
|
||||
|
||||
func (lcmd *DyldInfo) Cmdname() string {
|
||||
return lcmd.c.Cmdname()
|
||||
}
|
||||
|
||||
func (lcmd *DyldInfo) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
binary.Write(buf, mc.byteorder, lcmd.rebase_off)
|
||||
binary.Write(buf, mc.byteorder, lcmd.rebase_size)
|
||||
binary.Write(buf, mc.byteorder, lcmd.bind_off)
|
||||
binary.Write(buf, mc.byteorder, lcmd.bind_size)
|
||||
binary.Write(buf, mc.byteorder, lcmd.weak_bind_off)
|
||||
binary.Write(buf, mc.byteorder, lcmd.weak_bind_size)
|
||||
binary.Write(buf, mc.byteorder, lcmd.lazy_bind_off)
|
||||
binary.Write(buf, mc.byteorder, lcmd.lazy_bind_size)
|
||||
binary.Write(buf, mc.byteorder, lcmd.export_off)
|
||||
binary.Write(buf, mc.byteorder, lcmd.export_size)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *DyldInfo) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.rebase_off)
|
||||
binary.Read(r, mc.byteorder, &lcmd.rebase_size)
|
||||
binary.Read(r, mc.byteorder, &lcmd.bind_off)
|
||||
binary.Read(r, mc.byteorder, &lcmd.bind_size)
|
||||
binary.Read(r, mc.byteorder, &lcmd.weak_bind_off)
|
||||
binary.Read(r, mc.byteorder, &lcmd.weak_bind_size)
|
||||
binary.Read(r, mc.byteorder, &lcmd.lazy_bind_off)
|
||||
binary.Read(r, mc.byteorder, &lcmd.lazy_bind_size)
|
||||
binary.Read(r, mc.byteorder, &lcmd.export_off)
|
||||
binary.Read(r, mc.byteorder, &lcmd.export_size)
|
||||
}
|
||||
|
||||
type LinkEdit struct {
|
||||
c LoadCmd
|
||||
dataoff uint32
|
||||
datasize uint32
|
||||
}
|
||||
|
||||
func (lcmd *LinkEdit) Dataoff() uint32 {
|
||||
return lcmd.dataoff
|
||||
}
|
||||
|
||||
func (lcmd *LinkEdit) Datasize() uint32 {
|
||||
return lcmd.datasize
|
||||
}
|
||||
|
||||
func (lcmd *LinkEdit) Cmd() uint32 {
|
||||
return lcmd.c.Cmd()
|
||||
}
|
||||
|
||||
func (lcmd *LinkEdit) Cmdsize() uint32 {
|
||||
return uint32(8 + 4*2)
|
||||
}
|
||||
|
||||
func (lcmd *LinkEdit) Cmdname() string {
|
||||
return lcmd.c.Cmdname()
|
||||
}
|
||||
|
||||
func (lcmd *LinkEdit) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
binary.Write(buf, mc.byteorder, lcmd.dataoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.datasize)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *LinkEdit) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
binary.Read(r, mc.byteorder, &lcmd.dataoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.datasize)
|
||||
}
|
||||
|
||||
type Symtab struct {
|
||||
c LoadCmd
|
||||
symoff uint32
|
||||
nsyms uint32
|
||||
stroff uint32
|
||||
strsize uint32
|
||||
}
|
||||
|
||||
func (lcmd *Symtab) Cmd() uint32 {
|
||||
return lcmd.c.Cmd()
|
||||
}
|
||||
|
||||
func (lcmd *Symtab) Cmdsize() uint32 {
|
||||
return uint32(8 + 4*4)
|
||||
}
|
||||
|
||||
func (lcmd *Symtab) Cmdname() string {
|
||||
return fmt.Sprintf("%s symoff:0x%x nsyms:%d stroff:0x%x strsize:0x%x",
|
||||
lcmd.c.Cmdname(), lcmd.symoff, lcmd.nsyms, lcmd.stroff, lcmd.strsize)
|
||||
}
|
||||
|
||||
func (lcmd *Symtab) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmd())
|
||||
binary.Write(buf, mc.byteorder, lcmd.Cmdsize())
|
||||
binary.Write(buf, mc.byteorder, lcmd.symoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nsyms)
|
||||
binary.Write(buf, mc.byteorder, lcmd.stroff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.strsize)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (lcmd *Symtab) Deserialize(mc *MachoContext, buf []byte) {
|
||||
r := bytes.NewBuffer(buf)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmd)
|
||||
binary.Read(r, mc.byteorder, &lcmd.c.cmdsize)
|
||||
|
||||
binary.Read(r, mc.byteorder, &lcmd.symoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nsyms)
|
||||
binary.Read(r, mc.byteorder, &lcmd.stroff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.strsize)
|
||||
}
|
265
macho-go/pkg/ios/macho/macho.go
Normal file
265
macho-go/pkg/ios/macho/macho.go
Normal file
@ -0,0 +1,265 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
)
|
||||
|
||||
// A Mach-O binary context
|
||||
// holds byteorder, pointersize, header
|
||||
// And load commands
|
||||
type MachoContext struct {
|
||||
file *os.File // backed file enabled write
|
||||
buf []byte // backed up data
|
||||
byteorder binary.ByteOrder
|
||||
pointersize uint32
|
||||
entryoff uint64
|
||||
|
||||
header Header
|
||||
commands []LoadCommand
|
||||
rpaths []*RPath
|
||||
dylibs []*Dylib
|
||||
linkedits []*LinkEdit
|
||||
segments []Segment
|
||||
symtab *Symtab
|
||||
dyldinfo *DyldInfo
|
||||
}
|
||||
|
||||
func (mc *MachoContext) FileSize() uint32 {
|
||||
return uint32(len(mc.buf))
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Header() *Header {
|
||||
return &mc.header
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Commands() []LoadCommand {
|
||||
return mc.commands
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Rpaths() []*RPath {
|
||||
return mc.rpaths
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Dylibs() []*Dylib {
|
||||
return mc.dylibs
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Linkedits() []*LinkEdit {
|
||||
return mc.linkedits
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Segments() []Segment {
|
||||
return mc.segments
|
||||
}
|
||||
|
||||
func (mc *MachoContext) WriteEnabled() bool {
|
||||
return mc.file != nil
|
||||
}
|
||||
|
||||
func (mc *MachoContext) WriteBufferTo(w io.Writer) (int, error) {
|
||||
return w.Write(mc.buf)
|
||||
}
|
||||
|
||||
// Parse the provided Mach-O binary from a file
|
||||
// The first 4 bytes of the file must be the MachO magic
|
||||
// That is:
|
||||
// file.Seek(0, io.SeekStart)
|
||||
// magic := make([]byte, 4)
|
||||
// file.Read(magic)
|
||||
// assert magic == []byte{macho magic bytes}
|
||||
// or else, parsing error is panic
|
||||
func (mc *MachoContext) ParseFile(file *os.File, length int) error {
|
||||
file.Seek(0, io.SeekStart)
|
||||
mc.file = file
|
||||
|
||||
// save the file content to buf
|
||||
if length == 0 {
|
||||
last, _ := mc.file.Seek(0, io.SeekEnd)
|
||||
mc.buf = make([]byte, last)
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
} else {
|
||||
mc.buf = make([]byte, length)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(mc.file, mc.buf); err != nil {
|
||||
return err
|
||||
}
|
||||
mc.file.Seek(0, io.SeekStart) // reset peek, safety reason
|
||||
r := bufio.NewReader(file)
|
||||
return mc.Parse(r)
|
||||
}
|
||||
|
||||
// A macho context can be made from a buffer
|
||||
// In this case write is disable
|
||||
// Every information accessed will be through
|
||||
// this byte buffer
|
||||
func (mc *MachoContext) ParseBuffer(buf []byte) error {
|
||||
mc.file = nil
|
||||
mc.buf = buf
|
||||
r := bytes.NewReader(buf)
|
||||
rr := bufio.NewReader(r)
|
||||
return mc.Parse(rr)
|
||||
}
|
||||
|
||||
// Parse the provided Mach-O binary from a buffer
|
||||
func (mc *MachoContext) Parse(r *bufio.Reader) error {
|
||||
{ // read magic to define byteorder and pointersize
|
||||
var magic uint32
|
||||
magic_buf, _ := r.Peek(4)
|
||||
magic_r := bytes.NewReader(magic_buf)
|
||||
binary.Read(magic_r, binary.LittleEndian, &magic)
|
||||
|
||||
if magic != Magic32 && magic != Magic64 && magic != Cigam32 &&
|
||||
magic != Cigam64 {
|
||||
log.WithFields(log.Fields{
|
||||
"magic": magic,
|
||||
}).Error("Magic for Mach-O does not match")
|
||||
return &ParseError{
|
||||
Expect: "Magic for Mach-O does not match",
|
||||
}
|
||||
}
|
||||
|
||||
if magic == Magic32 || magic == Magic64 {
|
||||
mc.byteorder = binary.LittleEndian
|
||||
} else {
|
||||
mc.byteorder = binary.BigEndian
|
||||
}
|
||||
|
||||
if magic == Magic32 {
|
||||
mc.pointersize = 4
|
||||
} else {
|
||||
mc.pointersize = 8
|
||||
}
|
||||
}
|
||||
|
||||
mc.header.Deserialize(mc, r)
|
||||
|
||||
for i := uint32(0); i < mc.header.ncmds; i++ {
|
||||
var cmd uint32
|
||||
var cmdsize uint32
|
||||
load_buf, _ := r.Peek(8)
|
||||
load_r := bytes.NewReader(load_buf)
|
||||
|
||||
binary.Read(load_r, mc.byteorder, &cmd)
|
||||
binary.Read(load_r, mc.byteorder, &cmdsize)
|
||||
log.WithFields(log.Fields{
|
||||
"nth": i,
|
||||
"cmd": cmd,
|
||||
"cmdsize": cmdsize,
|
||||
}).Trace("Load command")
|
||||
|
||||
command_buf := make([]byte, cmdsize)
|
||||
if bytesread, err := io.ReadFull(r, command_buf); err != nil ||
|
||||
uint32(bytesread) != cmdsize {
|
||||
log.WithFields(log.Fields{
|
||||
"nth": i,
|
||||
"cmdsize": cmdsize,
|
||||
"bytesread": bytesread,
|
||||
}).Error("Cannot read load command")
|
||||
return &ParseError{
|
||||
Expect: "Cannot read load command",
|
||||
}
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case LC_RPATH:
|
||||
lcmd := new(RPath)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.rpaths = append(mc.rpaths, lcmd)
|
||||
break
|
||||
|
||||
case LC_ID_DYLIB,
|
||||
LC_LOAD_DYLIB,
|
||||
LC_LAZY_LOAD_DYLIB,
|
||||
LC_REEXPORT_DYLIB,
|
||||
LC_LOAD_WEAK_DYLIB:
|
||||
lcmd := new(Dylib)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
if cmd != LC_ID_DYLIB {
|
||||
mc.dylibs = append(mc.dylibs, lcmd)
|
||||
}
|
||||
break
|
||||
|
||||
case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
|
||||
lcmd := new(EncryptionInfo)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
break
|
||||
|
||||
case LC_SEGMENT:
|
||||
lcmd := new(Segment32)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.segments = append(mc.segments, lcmd)
|
||||
break
|
||||
|
||||
case LC_SEGMENT_64:
|
||||
lcmd := new(Segment64)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.segments = append(mc.segments, lcmd)
|
||||
break
|
||||
|
||||
case LC_MAIN:
|
||||
lcmd := new(EntryPoint)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.entryoff = lcmd.entryoff
|
||||
break
|
||||
|
||||
case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
|
||||
lcmd := new(DyldInfo)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.dyldinfo = lcmd
|
||||
break
|
||||
|
||||
case LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_CODE_SIGNATURE, LC_DYLD_CHAINED_FIXUPS, LC_DYLD_EXPORTS_TRIE:
|
||||
lcmd := new(LinkEdit)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.linkedits = append(mc.linkedits, lcmd)
|
||||
break
|
||||
|
||||
case LC_SYMTAB:
|
||||
lcmd := new(Symtab)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.symtab = lcmd
|
||||
|
||||
default:
|
||||
lcmd := new(LoadCmd)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Is64bit() bool {
|
||||
return mc.pointersize == 8
|
||||
}
|
||||
|
||||
func (mc *MachoContext) PointerSize() uint32 {
|
||||
return mc.pointersize
|
||||
}
|
||||
|
||||
func (mc *MachoContext) ImageBase() uint64 {
|
||||
for _, segment := range mc.segments {
|
||||
if segment.Fileoff() == 0 && segment.Filesize() != 0 {
|
||||
return segment.Vmaddr()
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
111
macho-go/pkg/ios/macho/symtab.go
Normal file
111
macho-go/pkg/ios/macho/symtab.go
Normal file
@ -0,0 +1,111 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
N_STAB uint8 = 0xe0 /* if any of these bits set, a symbolic debugging entry */
|
||||
N_PEXT uint8 = 0x10 /* private external symbol bit */
|
||||
N_TYPE uint8 = 0x0e /* mask for the type bits */
|
||||
N_EXT uint8 = 0x01 /* external symbol bit, set for external symbols */
|
||||
|
||||
// Values for N_TYPE bits of the n_type field.
|
||||
N_UNDF uint8 = 0x0 /* undefined, n_sect == NO_SECT */
|
||||
N_ABS uint8 = 0x2 /* absolute, n_sect == NO_SECT */
|
||||
N_SECT uint8 = 0xe /* defined in section number n_sect */
|
||||
N_PBUD uint8 = 0xc /* prebound undefined (defined in a dylib) */
|
||||
N_INDR uint8 = 0xa /* indirect */
|
||||
|
||||
NO_SECT uint8 = 0 /* symbol is not in any section */
|
||||
|
||||
)
|
||||
|
||||
type Symbol struct {
|
||||
name string
|
||||
address uint64
|
||||
}
|
||||
|
||||
func (sym *Symbol) Name() string {
|
||||
return sym.name
|
||||
}
|
||||
|
||||
func (sym *Symbol) Address() uint64 {
|
||||
return sym.address
|
||||
}
|
||||
|
||||
func (mc *MachoContext) CollectSymbols() []*Symbol {
|
||||
// for referencing section
|
||||
// sections := []Section{}
|
||||
// for _, segment := range mc.segments {
|
||||
// for _, section := range segment.Sections() {
|
||||
// sections = append(sections, section)
|
||||
// }
|
||||
// }
|
||||
|
||||
symbols := []*Symbol{}
|
||||
|
||||
if mc.symtab.nsyms == 0 {
|
||||
return []*Symbol{}
|
||||
}
|
||||
symtab_buffer := func() *bytes.Buffer {
|
||||
start := mc.symtab.symoff
|
||||
if mc.Is64bit() {
|
||||
end := start + mc.symtab.nsyms*16
|
||||
return bytes.NewBuffer(mc.buf[start:end])
|
||||
} else {
|
||||
end := start + mc.symtab.nsyms*12
|
||||
return bytes.NewBuffer(mc.buf[start:end])
|
||||
}
|
||||
}()
|
||||
|
||||
strtable := func() *bytes.Reader {
|
||||
start := mc.symtab.stroff
|
||||
end := start + mc.symtab.strsize
|
||||
return bytes.NewReader(mc.buf[start:end])
|
||||
}()
|
||||
|
||||
for i := uint32(0); i < mc.symtab.nsyms; i++ {
|
||||
var strx uint32
|
||||
var flags uint8
|
||||
var sect uint8
|
||||
var desc uint16
|
||||
var value32 uint32
|
||||
var value64 uint64
|
||||
|
||||
binary.Read(symtab_buffer, mc.byteorder, &strx)
|
||||
binary.Read(symtab_buffer, mc.byteorder, &flags)
|
||||
binary.Read(symtab_buffer, mc.byteorder, §)
|
||||
binary.Read(symtab_buffer, mc.byteorder, &desc)
|
||||
if mc.Is64bit() {
|
||||
binary.Read(symtab_buffer, mc.byteorder, &value64)
|
||||
} else {
|
||||
// always use value64
|
||||
binary.Read(symtab_buffer, mc.byteorder, &value32)
|
||||
value64 = uint64(value32)
|
||||
}
|
||||
|
||||
mangled_name := func() string {
|
||||
strtable.Seek(int64(strx), io.SeekStart)
|
||||
name := bytes.NewBufferString("")
|
||||
for {
|
||||
b, _ := strtable.ReadByte()
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
name.WriteByte(b)
|
||||
}
|
||||
return name.String()
|
||||
}()
|
||||
|
||||
// fmt.Printf("0x%x %s\n", value64, mangled_name)
|
||||
symbols = append(symbols, &Symbol{
|
||||
name: mangled_name,
|
||||
address: value64,
|
||||
})
|
||||
}
|
||||
|
||||
return symbols
|
||||
}
|
197
macho-go/pkg/ios/macho/util.go
Normal file
197
macho-go/pkg/ios/macho/util.go
Normal file
@ -0,0 +1,197 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
)
|
||||
|
||||
func (mc *MachoContext) ArchName() string {
|
||||
if mc.header.cputype == 12 && mc.header.cpusubtype == 9 {
|
||||
return "armv7"
|
||||
}
|
||||
if mc.header.cputype == 12 && mc.header.cpusubtype == 11 {
|
||||
return "armv7s"
|
||||
}
|
||||
if mc.header.cputype == 0x100000C && mc.header.cpusubtype == 0 {
|
||||
return "arm64"
|
||||
}
|
||||
if mc.header.cputype == 0x100000C && mc.header.cpusubtype == 2 {
|
||||
return "arm64e"
|
||||
}
|
||||
if mc.header.cputype == 7 && mc.header.cpusubtype == 3 {
|
||||
return "i386"
|
||||
}
|
||||
if mc.header.cputype == 0x1000007 && mc.header.cpusubtype == 3 {
|
||||
return "x86_64"
|
||||
}
|
||||
if mc.header.cputype == 0x1000007 && mc.header.cpusubtype == 0x80000003 {
|
||||
return "x86_64-lib64"
|
||||
}
|
||||
return fmt.Sprintf("arch-%x-%x", mc.header.cputype, mc.header.cpusubtype)
|
||||
}
|
||||
|
||||
func MachosFromFiles(files []string) ([]*MachoContext, error) {
|
||||
var r []*MachoContext
|
||||
for _, filename := range files {
|
||||
var mc MachoContext
|
||||
f, _ := os.OpenFile(filename, os.O_RDWR, 0644)
|
||||
err := mc.ParseFile(f, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = append(r, &mc)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func CheckDuplicateArch(machos []*MachoContext) bool {
|
||||
for i := 0; i < len(machos)-1; i++ {
|
||||
for j := i + 1; j < len(machos); j++ {
|
||||
i_cputype := machos[i].Header().Cputype()
|
||||
j_cputype := machos[j].Header().Cputype()
|
||||
i_cpusubtype := machos[i].Header().Cpusubtype()
|
||||
j_cpusubtype := machos[j].Header().Cpusubtype()
|
||||
if i_cputype == j_cputype &&
|
||||
i_cpusubtype == j_cpusubtype {
|
||||
log.WithFields(log.Fields{
|
||||
"cputype": i_cputype,
|
||||
"cpusubtype": i_cpusubtype,
|
||||
}).Warn("Duplicate Mach-O Arch")
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) PaddingSize() uint64 {
|
||||
if mc.Is64bit() {
|
||||
return mc.entryoff - (Header_size_64 + uint64(mc.header.sizeofcmds))
|
||||
} else {
|
||||
return mc.entryoff - (Header_size + uint64(mc.header.sizeofcmds))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (mc *MachoContext) DylibExisted(name string) bool {
|
||||
// simple check
|
||||
// Advanced check requires expansion of @rpath
|
||||
for _, dylib := range mc.dylibs {
|
||||
dylib_ := bytes.Trim(dylib.name, "\x00")
|
||||
match := bytes.Compare(dylib_, []byte(name)) == 0
|
||||
log.WithFields(log.Fields{
|
||||
"left": dylib_,
|
||||
"right": []byte(name),
|
||||
"match": match,
|
||||
}).Trace("Dylib check")
|
||||
if match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RPathExisted(path string) bool {
|
||||
// simple check
|
||||
// Advanced check requires expansion of @rpath
|
||||
for _, rpath := range mc.rpaths {
|
||||
rpath_ := bytes.Trim(rpath.path, "\x00")
|
||||
match := bytes.Compare(rpath_, []byte(path)) == 0
|
||||
log.WithFields(log.Fields{
|
||||
"left": rpath_,
|
||||
"right": []byte(path),
|
||||
"match": match,
|
||||
}).Trace("Rpath check")
|
||||
if match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) FindSection(name string) Section {
|
||||
for _, segment := range mc.segments {
|
||||
for _, section := range segment.Sections() {
|
||||
sectname := bytes.Trim(section.SectName(), "\x00")
|
||||
if bytes.Compare(sectname, []byte(name)) == 0 {
|
||||
return section
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *MachoContext) FindSegment(name string) Segment {
|
||||
for _, segment := range mc.segments {
|
||||
sectname := bytes.Trim(segment.SegName(), "\x00")
|
||||
if bytes.Compare(sectname, []byte(name)) == 0 {
|
||||
return segment
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Cut(offset uint64, size uint64) []byte {
|
||||
return mc.buf[offset : offset + size];
|
||||
}
|
||||
|
||||
// INIT POINTER
|
||||
|
||||
type InitPointer struct {
|
||||
offset uint64
|
||||
low uint32
|
||||
high uint32
|
||||
}
|
||||
|
||||
func (ptr *InitPointer) Offset() uint64 {
|
||||
return ptr.offset
|
||||
}
|
||||
|
||||
func (ptr *InitPointer) Value() uint64 {
|
||||
return (uint64(ptr.high) << 32) | uint64(ptr.low)
|
||||
}
|
||||
|
||||
func (ptr *InitPointer) String() string {
|
||||
return fmt.Sprintf("0x%x -> 0x%x",
|
||||
ptr.offset, (uint64(ptr.high)<<32)|uint64(ptr.low))
|
||||
}
|
||||
|
||||
func (mc *MachoContext) InitFunctions() []InitPointer {
|
||||
var valid_sections []Section
|
||||
for _, segment := range mc.segments {
|
||||
for _, section := range segment.Sections() {
|
||||
if section.Type() == S_MOD_INIT_FUNC_POINTERS {
|
||||
valid_sections = append(valid_sections, section)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var funcs []InitPointer
|
||||
for _, section := range valid_sections {
|
||||
offset := uint64(section.Offset())
|
||||
size := section.Size()
|
||||
|
||||
data := mc.buf[offset : offset+size]
|
||||
|
||||
// TODO: assert len(data) // mc.pointersize
|
||||
|
||||
count := size / uint64(mc.pointersize)
|
||||
data_buf := bytes.NewBuffer(data)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
var fun InitPointer
|
||||
fun.offset = offset + i*uint64(mc.pointersize)
|
||||
binary.Read(data_buf, mc.byteorder, &fun.low)
|
||||
if mc.Is64bit() {
|
||||
binary.Read(data_buf, mc.byteorder, &fun.high)
|
||||
}
|
||||
funcs = append(funcs, fun)
|
||||
}
|
||||
}
|
||||
return funcs
|
||||
}
|
Reference in New Issue
Block a user