From f88861a87e1f8585ee2af55bb7f878d04e02c596 Mon Sep 17 00:00:00 2001 From: nganhkhoa Date: Wed, 12 Jul 2023 13:37:54 +0700 Subject: [PATCH] format code --- .../wrapper/action/rewrite_imports.go | 13 +- .../internal/wrapper/action/save_imports.go | 62 +-- macho-go/internal/wrapper/cli.go | 2 +- macho-go/internal/wrapper/cli_parser.go | 2 +- macho-go/internal/wrapper/program_context.go | 4 +- macho-go/pkg/ios/macho/dyld_info.go | 6 +- macho-go/pkg/ios/macho/edit.go | 354 ++++++------ macho-go/pkg/ios/macho/load_commands.go | 10 +- macho-go/pkg/ios/macho/macho.go | 2 +- research/custom_loader/b.cc | 502 ++++++++++-------- 10 files changed, 505 insertions(+), 452 deletions(-) diff --git a/macho-go/internal/wrapper/action/rewrite_imports.go b/macho-go/internal/wrapper/action/rewrite_imports.go index 4d17624..1624666 100644 --- a/macho-go/internal/wrapper/action/rewrite_imports.go +++ b/macho-go/internal/wrapper/action/rewrite_imports.go @@ -4,13 +4,13 @@ import ( . "ios-wrapper/internal/wrapper/ofile" ) -type rewriteImports struct{ - symbols []string +type rewriteImports struct { + symbols []string } func (action *rewriteImports) withMacho(mf *MachoFile) error { - mc := mf.Context() - mc.RewriteImportsTable(action.symbols) + mc := mf.Context() + mc.RewriteImportsTable(action.symbols) return nil } @@ -20,7 +20,6 @@ func (action *rewriteImports) withFat(ff *FatFile) error { func NewRewriteImportsWithKeepSymbolsAction(symbols []string) *rewriteImports { return &rewriteImports{ - symbols, - } + symbols, + } } - diff --git a/macho-go/internal/wrapper/action/save_imports.go b/macho-go/internal/wrapper/action/save_imports.go index 0daac7f..2d80397 100644 --- a/macho-go/internal/wrapper/action/save_imports.go +++ b/macho-go/internal/wrapper/action/save_imports.go @@ -3,24 +3,24 @@ package action import ( // "fmt" "sort" - "strings" + "strings" // log "github.com/sirupsen/logrus" . "ios-wrapper/internal/wrapper/ofile" "ios-wrapper/pkg/protomodel" ) -type saveImports struct{ - keepSymbols []string +type saveImports struct { + keepSymbols []string } func (action *saveImports) withMacho(mf *MachoFile) error { - action.saveToInfo(mf) - mc := mf.Context() - if mc.Header().IsDylib() { - mc.WriteInfoToData(mf.Info()) - } - return nil + action.saveToInfo(mf) + mc := mf.Context() + if mc.Header().IsDylib() { + mc.WriteInfoToData(mf.Info()) + } + return nil } func (action *saveImports) saveToInfo(mf *MachoFile) error { @@ -64,28 +64,28 @@ func (action *saveImports) saveToInfo(mf *MachoFile) error { continue } - skip := false - for _, keep := range action.keepSymbols { - name := keep - lib := "" - parts := strings.Split(keep, ",") - if len(parts) == 2 { - name = parts[0] - lib = parts[1] - } + skip := false + for _, keep := range action.keepSymbols { + name := keep + lib := "" + parts := strings.Split(keep, ",") + if len(parts) == 2 { + name = parts[0] + lib = parts[1] + } - if symbol.Name() != name { - continue - } - if lib == "" || lib == symbol.Dylib() { - skip = true - break - } - } + if symbol.Name() != name { + continue + } + if lib == "" || lib == symbol.Dylib() { + skip = true + break + } + } - if skip { - continue - } + if skip { + continue + } // dylib_hash := calculateHash(symbol.Dylib()) seg := mc.Segments()[symbol.Segment()] @@ -139,6 +139,6 @@ func (action *saveImports) withFat(ff *FatFile) error { func NewSaveImportsAction(keepSymbols []string) *saveImports { return &saveImports{ - keepSymbols, - } + keepSymbols, + } } diff --git a/macho-go/internal/wrapper/cli.go b/macho-go/internal/wrapper/cli.go index a47e94a..ca198ae 100644 --- a/macho-go/internal/wrapper/cli.go +++ b/macho-go/internal/wrapper/cli.go @@ -128,7 +128,7 @@ func Cli() { pc.rpath_to_add = arg.Rpath pc.outfile = arg.Out pc.bcellfile = arg.Bcell - pc.symbols_keep = arg.KeepImports + pc.symbols_keep = arg.KeepImports default: return diff --git a/macho-go/internal/wrapper/cli_parser.go b/macho-go/internal/wrapper/cli_parser.go index ce15c82..31ea0e4 100644 --- a/macho-go/internal/wrapper/cli_parser.go +++ b/macho-go/internal/wrapper/cli_parser.go @@ -72,7 +72,7 @@ type PepeArgument struct { RebuildLinkEdit bool `default:"false" negatable:"" help:"(TODO) Rebuild the LINKEDIT section"` FullRemoval bool `default:"false" help:"Apply every removal possible"` - KeepImports []string `short:"keep" help:"Do not remove import symbols and allow dyld resolve. If symbols is found with more than 1 occurrances, specify the library with a comma or else all occurrances will be kept. e.g., _symbol,libLIB.dylib"` + KeepImports []string `short:"keep" help:"Do not remove import symbols and allow dyld resolve. If symbols is found with more than 1 occurrances, specify the library with a comma or else all occurrances will be kept. e.g., _symbol,libLIB.dylib"` } type BcellToHeaderArgument struct { diff --git a/macho-go/internal/wrapper/program_context.go b/macho-go/internal/wrapper/program_context.go index 94e049d..4cb706a 100644 --- a/macho-go/internal/wrapper/program_context.go +++ b/macho-go/internal/wrapper/program_context.go @@ -46,7 +46,7 @@ type ProgramContext struct { remove_symbol_table bool dylib_to_add []string rpath_to_add []string - symbols_keep []string + symbols_keep []string outfile string @@ -98,7 +98,7 @@ func (pc *ProgramContext) Process(ofile OFile) { if pc.remove_imports { pc.AddAction(NewSaveImportsAction(pc.symbols_keep)) pc.AddAction(NewRemoveImportsAction()) - pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep)) + pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep)) } if pc.remove_symbol_table { pc.AddAction(NewRemoveClassicSymbolAction()) diff --git a/macho-go/pkg/ios/macho/dyld_info.go b/macho-go/pkg/ios/macho/dyld_info.go index f0a271c..423afa5 100644 --- a/macho-go/pkg/ios/macho/dyld_info.go +++ b/macho-go/pkg/ios/macho/dyld_info.go @@ -25,7 +25,7 @@ type ImportSymbol struct { segment_offset uint32 address uint64 file_address uint64 - lib_ordinal uint32 + lib_ordinal uint32 // push number pnum uint32 @@ -121,7 +121,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol { // fmt.Printf("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count) // fmt.Printf("pages=%x\n", fix.pages) pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count)) - reader := bytes.NewReader(mc.buf) + reader := bytes.NewReader(mc.buf) for page_i := 0; page_i < int(fix.page_count); page_i++ { // fmt.Printf(" page offset=%x\n", pages[page_i]) @@ -151,7 +151,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol { sym.name = name sym.dylib = dylib sym.typ = "lazy" - sym.lib_ordinal = uint32(s.lib_ordinal) + sym.lib_ordinal = uint32(s.lib_ordinal) sym.segment = uint32(mc.findSegmentIndexAt(uint64(address))) sym.file_address = uint64(address) diff --git a/macho-go/pkg/ios/macho/edit.go b/macho-go/pkg/ios/macho/edit.go index 9189e8b..4900e18 100644 --- a/macho-go/pkg/ios/macho/edit.go +++ b/macho-go/pkg/ios/macho/edit.go @@ -2,17 +2,17 @@ package macho import ( "bytes" + "encoding/binary" "fmt" "io" "math/rand" + "strings" "time" - "strings" - "encoding/binary" log "github.com/sirupsen/logrus" - "ios-wrapper/pkg/protomodel" . "ios-wrapper/pkg/ios" + "ios-wrapper/pkg/protomodel" ) // #include "fixups.h" @@ -285,10 +285,10 @@ func (mc *MachoContext) RemoveBindSymbols() { } else { mc.removeBindSymbolsLegacy() } - // Objective-C stub replaces main which can only appears in executable - if mc.Header().IsExecutable() { - mc.ReworkForObjc() - } + // Objective-C stub replaces main which can only appears in executable + if mc.Header().IsExecutable() { + mc.ReworkForObjc() + } // due to some limitations when design this tool // we write the c code to stdout lol @@ -445,27 +445,27 @@ func (mc *MachoContext) ReworkForObjc() { // arm also has different indexing instruction, so be careful // https://developer.arm.com/documentation/102374/0101/Loads-and-stores---addressing /* - adr x8, 0 - # x9 = (offset end of __DATA) - (offset shellcode) - movz x9, #0x9999 - add x8, x8, x9 + adr x8, 0 + # x9 = (offset end of __DATA) - (offset shellcode) + movz x9, #0x9999 + add x8, x8, x9 - stp x30, x8, [sp], #-0x10 - stp x3, x2, [sp], #-0x10 - stp x1, x0, [sp], #-0x10 + stp x30, x8, [sp], #-0x10 + stp x3, x2, [sp], #-0x10 + stp x1, x0, [sp], #-0x10 - # custom intializer - ldr x9, [x8] - blr x9 + # custom intializer + ldr x9, [x8] + blr x9 - ldp x1, x0, [sp, #0x10]! - ldp x3, x2, [sp, #0x10]! - ldp x30, x8, [sp, #0x10]! + ldp x1, x0, [sp, #0x10]! + ldp x3, x2, [sp, #0x10]! + ldp x30, x8, [sp, #0x10]! - # original main - # link register is set so jump only - ldr x9, [x8, #8] - br x9 + # original main + # link register is set so jump only + ldr x9, [x8, #8] + br x9 */ // TODO: fix to work with offset larger than 0xffff @@ -529,72 +529,72 @@ func (mc *MachoContext) ReworkForObjc() { func (mc *MachoContext) RewriteImportsTable(keepSymbols []string) { allSymbols := mc.CollectBindSymbols() - fixups, fixupsOffset := mc.Fixups() + fixups, fixupsOffset := mc.Fixups() - importTable := fixups.ImportsOffset(uint32(fixupsOffset)) - symbolTable := fixups.SymbolsOffset(uint32(fixupsOffset)) - symbolTablePtr := symbolTable + importTable := fixups.ImportsOffset(uint32(fixupsOffset)) + symbolTable := fixups.SymbolsOffset(uint32(fixupsOffset)) + symbolTablePtr := symbolTable - // in removeBindSymbolsModern, we erase these pointers in file - // but because we keep a few symbols, we need to rewrite the pointers - // as well as rebuild the import table and strings table, and bind values + // in removeBindSymbolsModern, we erase these pointers in file + // but because we keep a few symbols, we need to rewrite the pointers + // as well as rebuild the import table and strings table, and bind values - keepCount := uint32(0) - for _, symbol := range keepSymbols { - name := symbol - lib := "" - parts := strings.Split(symbol, ",") - if len(parts) == 2 { - name = parts[0] - lib = parts[1] - } + keepCount := uint32(0) + for _, symbol := range keepSymbols { + name := symbol + lib := "" + parts := strings.Split(symbol, ",") + if len(parts) == 2 { + name = parts[0] + lib = parts[1] + } - symbolInfo := (*ImportSymbol)(nil) - for _, s := range allSymbols { - if s.Name() != name { - continue - } - if lib == "" || lib == s.Dylib() { - symbolInfo = s - break - } - } - if symbolInfo == nil { - // symbol not found - continue - } + symbolInfo := (*ImportSymbol)(nil) + for _, s := range allSymbols { + if s.Name() != name { + continue + } + if lib == "" || lib == s.Dylib() { + symbolInfo = s + break + } + } + if symbolInfo == nil { + // symbol not found + continue + } - fmt.Printf("keep symbol %s\n", name) - fmt.Printf("importTable at 0x%x; stringTable at 0x%x\n", importTable, symbolTablePtr) - fmt.Printf("bind value at 0x%x\n", symbolInfo.file_address) + fmt.Printf("keep symbol %s\n", name) + fmt.Printf("importTable at 0x%x; stringTable at 0x%x\n", importTable, symbolTablePtr) + fmt.Printf("bind value at 0x%x\n", symbolInfo.file_address) - // write string to string table - mc.file.WriteAt([]byte(name), int64(symbolTablePtr)) - // fix bind value - rebaseOpcodeBytes := make([]byte, 8) - mc.file.ReadAt(rebaseOpcodeBytes, int64(symbolInfo.file_address)) - rebaseOpcode := mc.byteorder.Uint64(rebaseOpcodeBytes) - bindOpcode := C.MakeBindFixupOpcodeFromRebase(C.uint64_t(rebaseOpcode), C.uint(keepCount)) + // write string to string table + mc.file.WriteAt([]byte(name), int64(symbolTablePtr)) + // fix bind value + rebaseOpcodeBytes := make([]byte, 8) + mc.file.ReadAt(rebaseOpcodeBytes, int64(symbolInfo.file_address)) + rebaseOpcode := mc.byteorder.Uint64(rebaseOpcodeBytes) + bindOpcode := C.MakeBindFixupOpcodeFromRebase(C.uint64_t(rebaseOpcode), C.uint(keepCount)) - { - v := make([]byte, 8) - mc.byteorder.PutUint64(v, uint64(bindOpcode)) - mc.file.WriteAt(v, int64(symbolInfo.file_address)) - } - // write import data to import table - entry := C.MakeImportTableEntry(C.uint(symbolInfo.LibOrdinal()), C.uint(symbolTablePtr - symbolTable)) - { - v := make([]byte, 4) - mc.byteorder.PutUint32(v, uint32(entry)) - mc.file.WriteAt(v, int64(importTable)) - } + { + v := make([]byte, 8) + mc.byteorder.PutUint64(v, uint64(bindOpcode)) + mc.file.WriteAt(v, int64(symbolInfo.file_address)) + } + // write import data to import table + entry := C.MakeImportTableEntry(C.uint(symbolInfo.LibOrdinal()), C.uint(symbolTablePtr-symbolTable)) + { + v := make([]byte, 4) + mc.byteorder.PutUint32(v, uint32(entry)) + mc.file.WriteAt(v, int64(importTable)) + } - keepCount += 1 - importTable += 4 - symbolTablePtr += uint32(len(name)) + 1 - } + keepCount += 1 + importTable += 4 + symbolTablePtr += uint32(len(name)) + 1 + } - fixups.imports_count = keepCount + fixups.imports_count = keepCount mc.file.WriteAt(fixups.Serialize(mc), int64(mc.fixups.dataoff)) } @@ -685,128 +685,128 @@ func (mc *MachoContext) RemoveExportTrie() { } func (mc *MachoContext) AddSection(segname string, name string, size int) Section { - // mc.file.WriteAt(mc.header.Serialize(mc), 0) - var ret Section - var buffer bytes.Buffer + // mc.file.WriteAt(mc.header.Serialize(mc), 0) + var ret Section + var buffer bytes.Buffer for _, command := range mc.commands { switch command.(type) { case *Segment64: - var virtualAddr uint64 - var fileOffset uint64 + var virtualAddr uint64 + var fileOffset uint64 var segment = command.(*Segment64) - if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte(segname)) != 0 { - buffer.Write(segment.Serialize(mc)) - continue - } else { - virtualAddr = segment.Vmaddr() - fileOffset = segment.Fileoff() - for _, section := range segment.Sections() { - virtualAddr += section.Size() - // if section.Offset() != 0 { - fileOffset += section.Size() - // } - } + if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte(segname)) != 0 { + buffer.Write(segment.Serialize(mc)) + continue + } else { + virtualAddr = segment.Vmaddr() + fileOffset = segment.Fileoff() + for _, section := range segment.Sections() { + virtualAddr += section.Size() + // if section.Offset() != 0 { + fileOffset += section.Size() + // } + } - align := uint64(4) - alignment := align - (fileOffset % align) - fileOffset += alignment - virtualAddr += alignment + align := uint64(4) + alignment := align - (fileOffset % align) + fileOffset += alignment + virtualAddr += alignment - enoughSpace := segment.Fileoff() + segment.Filesize() >= fileOffset + uint64(size) - if !enoughSpace { - fmt.Println("Not enough space to store saved info in __DATA segment, need resize (not supported now)") - panic("Not enough space to store saved info in __DATA segment, need resize (not supported now)") - } - } + enoughSpace := segment.Fileoff()+segment.Filesize() >= fileOffset+uint64(size) + if !enoughSpace { + fmt.Println("Not enough space to store saved info in __DATA segment, need resize (not supported now)") + panic("Not enough space to store saved info in __DATA segment, need resize (not supported now)") + } + } - var section Section64 - section.sectname = make([]byte, 16) - copy(section.sectname, name) - section.segname = make([]byte, 16) - copy(section.segname, segname) - section.size = uint64(size) - section.reloff = 0 - section.nreloc = 0 - section.flags = 0 - section.align = 3 - section.reserved1 = 0 - section.reserved2 = 0 - section.reserved3 = 0 + var section Section64 + section.sectname = make([]byte, 16) + copy(section.sectname, name) + section.segname = make([]byte, 16) + copy(section.segname, segname) + section.size = uint64(size) + section.reloff = 0 + section.nreloc = 0 + section.flags = 0 + section.align = 3 + section.reserved1 = 0 + section.reserved2 = 0 + section.reserved3 = 0 - // addr will increment from the last section - // offset will increment from the last section + size - // be careful of Virtual section (bss) - section.addr = virtualAddr - section.offset = uint32(fileOffset) - segment.nsects += 1 - buffer.Write(segment.Serialize(mc)) - buffer.Write(section.Serialize(mc)) + // addr will increment from the last section + // offset will increment from the last section + size + // be careful of Virtual section (bss) + section.addr = virtualAddr + section.offset = uint32(fileOffset) + segment.nsects += 1 + buffer.Write(segment.Serialize(mc)) + buffer.Write(section.Serialize(mc)) - fmt.Printf("Add a new section with addr=0x%x, fileoffset=0x%x\n", section.addr, section.offset) + fmt.Printf("Add a new section with addr=0x%x, fileoffset=0x%x\n", section.addr, section.offset) - ret = §ion - continue + ret = §ion + continue - default: - buffer.Write(command.Serialize(mc)) - continue - } - } + default: + buffer.Write(command.Serialize(mc)) + continue + } + } - mc.header.sizeofcmds = uint32(buffer.Len()) - header := mc.header.Serialize(mc) - mc.file.WriteAt(header, 0) - mc.file.WriteAt(buffer.Bytes(), int64(len(header))) - return ret + mc.header.sizeofcmds = uint32(buffer.Len()) + header := mc.header.Serialize(mc) + mc.file.WriteAt(header, 0) + mc.file.WriteAt(buffer.Bytes(), int64(len(header))) + return ret } func (mc *MachoContext) WriteInfoToData(info *protomodel.MachoInfo) { - encode := func () []byte { - buffer := new(bytes.Buffer) + encode := func() []byte { + buffer := new(bytes.Buffer) for _, table := range info.Symbols.Tables { - binary.Write(buffer, mc.byteorder, table.LibIndex) - binary.Write(buffer, mc.byteorder, table.Nsymbols) + binary.Write(buffer, mc.byteorder, table.LibIndex) + binary.Write(buffer, mc.byteorder, table.Nsymbols) for _, symbol := range table.Symbols { - binary.Write(buffer, mc.byteorder, (symbol.SymbolIndex<<8)|symbol.SegmentIndex) - binary.Write(buffer, mc.byteorder, symbol.Offset) + binary.Write(buffer, mc.byteorder, (symbol.SymbolIndex<<8)|symbol.SegmentIndex) + binary.Write(buffer, mc.byteorder, symbol.Offset) } } - instructions := buffer.Bytes() + instructions := buffer.Bytes() - buffer = new(bytes.Buffer) + buffer = new(bytes.Buffer) for _, lib := range info.Symbols.Libs { - buffer.WriteString(lib) - buffer.WriteByte(0) + buffer.WriteString(lib) + buffer.WriteByte(0) } - liblist := buffer.Bytes() + liblist := buffer.Bytes() - buffer = new(bytes.Buffer) + buffer = new(bytes.Buffer) for _, symbol := range info.Symbols.Symbols { - buffer.WriteString(symbol) - buffer.WriteByte(0) + buffer.WriteString(symbol) + buffer.WriteByte(0) } - symbollist := buffer.Bytes() + symbollist := buffer.Bytes() - buffer = new(bytes.Buffer) - // offset to liblist - binary.Write(buffer, mc.byteorder, uint32(len(instructions))) - // offset to symbollist - binary.Write(buffer, mc.byteorder, uint32(len(instructions) + len(liblist))) - buffer.Write(instructions) - buffer.Write(liblist) - buffer.Write(symbollist) + buffer = new(bytes.Buffer) + // offset to liblist + binary.Write(buffer, mc.byteorder, uint32(len(instructions))) + // offset to symbollist + binary.Write(buffer, mc.byteorder, uint32(len(instructions)+len(liblist))) + buffer.Write(instructions) + buffer.Write(liblist) + buffer.Write(symbollist) - return buffer.Bytes() - } - encoded := encode() - // encoded := []byte{0x11,0x22,0x33, 0x44} - section := mc.AddSection("__DATA", "selfbind", len(encoded)) - if mc.Is64bit() { - var s *Section64 = section.(*Section64) - mc.file.WriteAt(encoded, int64(s.Offset())) - } else { - var s *Section32 = section.(*Section32) - mc.file.WriteAt(encoded, int64(s.Offset())) - } + return buffer.Bytes() + } + encoded := encode() + // encoded := []byte{0x11,0x22,0x33, 0x44} + section := mc.AddSection("__DATA", "selfbind", len(encoded)) + if mc.Is64bit() { + var s *Section64 = section.(*Section64) + mc.file.WriteAt(encoded, int64(s.Offset())) + } else { + var s *Section32 = section.(*Section32) + mc.file.WriteAt(encoded, int64(s.Offset())) + } } diff --git a/macho-go/pkg/ios/macho/load_commands.go b/macho-go/pkg/ios/macho/load_commands.go index 223c6dd..76ca283 100644 --- a/macho-go/pkg/ios/macho/load_commands.go +++ b/macho-go/pkg/ios/macho/load_commands.go @@ -38,11 +38,11 @@ func (h *Header) Cpusubtype() uint32 { } func (h *Header) IsExecutable() bool { - return h.filetype == 0x2 + return h.filetype == 0x2 } func (h *Header) IsDylib() bool { - return h.filetype == 0x6 + return h.filetype == 0x6 } func (h *Header) Serialize(mc *MachoContext) []byte { @@ -135,7 +135,7 @@ func (lcmd *LoadCmd) Cmdname() string { return "LC_DATA_IN_CODE" case LC_SYMTAB: return "LC_SYMTAB" - case LC_BUILD_VERSION: + case LC_BUILD_VERSION: return "LC_BUILD_VERSION" case LC_UUID: return "LC_UUID" @@ -425,11 +425,11 @@ type Fixups struct { } func (lcmd *Fixups) ImportsOffset(fixups_offset uint32) uint32 { - return lcmd.imports_offset + fixups_offset + return lcmd.imports_offset + fixups_offset } func (lcmd *Fixups) SymbolsOffset(fixups_offset uint32) uint32 { - return lcmd.symbols_offset + fixups_offset + return lcmd.symbols_offset + fixups_offset } func (lcmd *Fixups) Serialize(mc *MachoContext) []byte { diff --git a/macho-go/pkg/ios/macho/macho.go b/macho-go/pkg/ios/macho/macho.go index 6aefe5f..5ba9ba9 100644 --- a/macho-go/pkg/ios/macho/macho.go +++ b/macho-go/pkg/ios/macho/macho.go @@ -73,7 +73,7 @@ func (mc *MachoContext) Fixups() (*Fixups, uint64) { size := mc.fixups.datasize fixups := new(Fixups) fixups.Deserialize(mc, mc.buf[start:start+size]) - return fixups, uint64(mc.fixups.dataoff) + return fixups, uint64(mc.fixups.dataoff) } func (mc *MachoContext) WriteEnabled() bool { diff --git a/research/custom_loader/b.cc b/research/custom_loader/b.cc index 7aa0b46..679b626 100644 --- a/research/custom_loader/b.cc +++ b/research/custom_loader/b.cc @@ -22,19 +22,17 @@ int custom_strcmp(const char *p1, const char *p2) { return c1 - c2; } -int custom_strncmp(const char *s1, const char *s2, register size_t n) -{ +int custom_strncmp(const char *s1, const char *s2, register size_t n) { register unsigned char u1, u2; - while (n-- > 0) - { - u1 = (unsigned char) *s1++; - u2 = (unsigned char) *s2++; - if (u1 != u2) - return u1 - u2; - if (u1 == '\0') - return 0; - } + while (n-- > 0) { + u1 = (unsigned char)*s1++; + u2 = (unsigned char)*s2++; + if (u1 != u2) + return u1 - u2; + if (u1 == '\0') + return 0; + } return 0; } @@ -64,7 +62,7 @@ struct libcache_item { // pointer to segment address uint32_t nsegment; - uint64_t* segment; + uint64_t *segment; }; struct libcache { @@ -216,7 +214,7 @@ uint64_t get_slide(const void *header) { return 0; } -void* get_selfbind(const void *header) { +void *get_selfbind(const void *header) { const uint32_t magic = *(uint32_t *)header; char *ptr = (char *)header; if (magic == magic64) { @@ -226,7 +224,7 @@ void* get_selfbind(const void *header) { } const uint32_t ncmds = *((uint32_t *)header + 4); - char* command_ptr = ptr; + char *command_ptr = ptr; uint64_t slide; for (int i = 0; i < ncmds; i++) { @@ -240,17 +238,17 @@ void* get_selfbind(const void *header) { if (custom_strcmp(name, "__TEXT") == 0) { slide = (uint64_t)header - vmaddr; } else if (custom_strcmp(name, "__DATA") == 0) { - uint64_t nsect = *((uint32_t *)ptr + 8*2); - char* sections_ptr = (char*)((uint32_t*)ptr + 18); + uint64_t nsect = *((uint32_t *)ptr + 8 * 2); + char *sections_ptr = (char *)((uint32_t *)ptr + 18); sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1); for (int sec = 0; sec < nsect; sec++) { - char* secname = sections_ptr; + char *secname = sections_ptr; if (custom_strcmp(secname, "selfbind") == 0) { - uint64_t addr = *((uint64_t*)sections_ptr + 4); - uint64_t size = *((uint64_t*)sections_ptr + 5); - uint32_t *data_ptr = (uint32_t*)(addr + slide); - return (void*)data_ptr; + uint64_t addr = *((uint64_t *)sections_ptr + 4); + uint64_t size = *((uint64_t *)sections_ptr + 5); + uint32_t *data_ptr = (uint32_t *)(addr + slide); + return (void *)data_ptr; } sections_ptr += 16 * 2 + 8 * 2 + 4 * 8; } @@ -302,15 +300,16 @@ void print_macho_summary(const void *header) { printf(" vmaddr=0x%llx fileoffset=0x%llx\n", vmaddr, fileoffset); printf(" vmsize=0x%llx filesize=0x%llx\n", vmsize, filesize); - uint64_t nsect = *((uint32_t *)ptr + 8*2); - char* sections_ptr = (char*)((uint32_t*)ptr + 18); + uint64_t nsect = *((uint32_t *)ptr + 8 * 2); + char *sections_ptr = (char *)((uint32_t *)ptr + 18); for (int sec = 0; sec < nsect; sec++) { - char* secname = sections_ptr; - uint64_t addr = *((uint64_t*)sections_ptr + 4); - uint64_t size = *((uint64_t*)sections_ptr + 5); - uint32_t fileoffset = *((uint32_t*)sections_ptr + 6*2); + char *secname = sections_ptr; + uint64_t addr = *((uint64_t *)sections_ptr + 4); + uint64_t size = *((uint64_t *)sections_ptr + 5); + uint32_t fileoffset = *((uint32_t *)sections_ptr + 6 * 2); printf(" Section %s\n", sections_ptr); - printf(" addr=0x%llx size=0x%llx fileoffset=0x%x\n", addr, size, fileoffset); + printf(" addr=0x%llx size=0x%llx fileoffset=0x%x\n", addr, size, + fileoffset); } } if (cmd == LC_SYMTAB) { @@ -327,15 +326,18 @@ void print_macho_summary(const void *header) { uint64_t value; }; - uint64_t symtab_start = (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr; - uint64_t stroff_start = (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr; + uint64_t symtab_start = + (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr; + uint64_t stroff_start = + (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr; printf(" symtab: offset=0x%x nsym=0x%x\n", symoff, nsym); for (int j = 0; j < nsym; j++) { - struct symbol_t* symtab = (struct symbol_t*)symtab_start; + struct symbol_t *symtab = (struct symbol_t *)symtab_start; struct symbol_t symbol = symtab[j]; - char* name = (char*)stroff_start + symbol.strx; - printf(" %s %llx => %p\n", name, symbol.value, (void*)(symbol.value + slide)); + char *name = (char *)stroff_start + symbol.strx; + printf(" %s %llx => %p\n", name, symbol.value, + (void *)(symbol.value + slide)); } } if (cmd == LC_REEXPORT_DYLIB) { @@ -495,7 +497,7 @@ void *find_in_reexport(struct libcache *cache, struct libcache_item *lib, char *name = (char *)ptr + name_offset; uint32_t hash = calculate_libname_hash(cache, name); for (int j = 0; j < cache->size; j++) { - struct libcache_item* reexport = &cache->libs[j]; + struct libcache_item *reexport = &cache->libs[j]; if (reexport->hash != hash) { continue; } @@ -520,7 +522,7 @@ void *find_in_lib(struct libcache *cache, struct libcache_item *lib, void *custom_dlsym(struct libcache *cache, uint32_t hash, const char *symbol) { for (size_t i = 0; i < cache->size; i++) { - struct libcache_item* cache_lib = &cache->libs[i]; + struct libcache_item *cache_lib = &cache->libs[i]; if (cache_lib->hash == hash) { return find_in_lib(cache, cache_lib, symbol); } @@ -534,8 +536,9 @@ void *custom_dlsym(struct libcache *cache, const char *libname, return custom_dlsym(cache, hash, symbol); } -void bootstrap_libcache_item(struct libcache_item* item, const void* header, const char* name) { - item->header = (void*)header; +void bootstrap_libcache_item(struct libcache_item *item, const void *header, + const char *name) { + item->header = (void *)header; item->trie = get_export_trie(header, item->trie_size); const uint32_t magic = *(uint32_t *)header; @@ -547,7 +550,7 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con } const uint32_t ncmds = *((uint32_t *)header + 4); - char* command_ptr = ptr; + char *command_ptr = ptr; for (int i = 0; i < ncmds; i++) { const uint32_t cmd = *((uint32_t *)ptr + 0); @@ -564,7 +567,7 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con } ptr = command_ptr; - item->segment = (uint64_t*)malloc(sizeof(uint64_t) * item->nsegment); + item->segment = (uint64_t *)malloc(sizeof(uint64_t) * item->nsegment); for (int i = 0, segment_i = 0; i < ncmds; i++) { const uint32_t cmd = *((uint32_t *)ptr + 0); const uint32_t cmdsize = *((uint32_t *)ptr + 1); @@ -577,8 +580,9 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con return; } -struct libcache_item* get_libcache_with_name(struct libcache* cache, const char* name) { - void* to_find = 0; +struct libcache_item *get_libcache_with_name(struct libcache *cache, + const char *name) { + void *to_find = 0; if (custom_strcmp(name, "main") == 0) { to_find = cache->main; } else if (custom_strcmp(name, "thislib") == 0) { @@ -586,7 +590,7 @@ struct libcache_item* get_libcache_with_name(struct libcache* cache, const char* } uint32_t hash = calculate_libname_hash(cache, name); for (int i = 0; i < cache->size; i++) { - struct libcache_item* cache_lib = &cache->libs[i]; + struct libcache_item *cache_lib = &cache->libs[i]; // search by hash or by pointer for special case if (cache_lib->hash == hash || cache_lib->header == to_find) { return cache_lib; @@ -595,13 +599,14 @@ struct libcache_item* get_libcache_with_name(struct libcache* cache, const char* return 0; } -void dump_export_trie(const void* trie, uint32_t size, const char* filename) { +void dump_export_trie(const void *trie, uint32_t size, const char *filename) { FILE *outfile = fopen(filename, "wb"); - fwrite((char*)trie, size, 1, outfile); + fwrite((char *)trie, size, 1, outfile); fclose(outfile); } -void dump_export_trie_of(const char* libname, const libcache* cache, const char* filename) { +void dump_export_trie_of(const char *libname, const libcache *cache, + const char *filename) { uint32_t hash = calculate_libname_hash(cache, libname); for (int i = 0; i < cache->size; i++) { struct libcache_item cache_lib = cache->libs[i]; @@ -611,8 +616,8 @@ void dump_export_trie_of(const char* libname, const libcache* cache, const char* } } -void* find_in_symtab(const libcache_item* lib, const char* find) { - void* header = lib->header; +void *find_in_symtab(const libcache_item *lib, const char *find) { + void *header = lib->header; const uint32_t magic = *(uint32_t *)header; char *ptr = (char *)header; if (magic == magic64) { @@ -622,7 +627,7 @@ void* find_in_symtab(const libcache_item* lib, const char* find) { } const uint32_t ncmds = *((uint32_t *)header + 4); - char* command_ptr = ptr; + char *command_ptr = ptr; uint64_t linkedit_vmaddr; uint64_t linkedit_fileoffset; @@ -644,15 +649,17 @@ void* find_in_symtab(const libcache_item* lib, const char* find) { uint64_t value; }; - uint64_t symtab_start = (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr; - uint64_t stroff_start = (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr; + uint64_t symtab_start = + (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr; + uint64_t stroff_start = + (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr; for (int j = 0; j < nsym; j++) { - struct symbol_t* symtab = (struct symbol_t*)symtab_start; + struct symbol_t *symtab = (struct symbol_t *)symtab_start; struct symbol_t symbol = symtab[j]; - char* name = (char*)stroff_start + symbol.strx; + char *name = (char *)stroff_start + symbol.strx; if (custom_strcmp(name, find) == 0) { - return (void*)(symbol.value + slide); + return (void *)(symbol.value + slide); } } break; @@ -673,7 +680,8 @@ void* find_in_symtab(const libcache_item* lib, const char* find) { return 0; } -void* find_in_symtab(const char* libname, const libcache* cache, const char* find) { +void *find_in_symtab(const char *libname, const libcache *cache, + const char *find) { uint32_t hash = calculate_libname_hash(cache, libname); struct libcache_item *cache_lib = 0; for (int i = 0; i < cache->size; i++) { @@ -697,10 +705,10 @@ int hook_printf(const char *format, ...) { } typedef void *(*readClass_t)(void *, bool, bool); -typedef void *(*realizeClassWithoutSwift_t)(void *, void*); +typedef void *(*realizeClassWithoutSwift_t)(void *, void *); typedef void *(*remapClass_t)(void *); -typedef void *(*load_method_t)(void*, void*); -typedef void *(*sel_lookUpByName_t)(const char*); +typedef void *(*load_method_t)(void *, void *); +typedef void *(*sel_lookUpByName_t)(const char *); typedef void (*addClassTableEntry_t)(void *); typedef void (*schedule_class_load_t)(void *); @@ -709,24 +717,23 @@ typedef void (*objc_autoreleasePoolPop_t)(void *); struct custom_initializer_t { // used for Objective-C load methods - uint64_t* loadable_classes; - uint32_t* loadable_classes_used; + uint64_t *loadable_classes; + uint32_t *loadable_classes_used; sel_lookUpByName_t sel_lookUpByName; objc_autoreleasePoolPush_t objc_autoreleasePoolPush; objc_autoreleasePoolPop_t objc_autoreleasePoolPop; remapClass_t remapClass; schedule_class_load_t schedule_class_load; - uint64_t* cls; + uint64_t *cls; size_t ncls; // used for constructors - void* programvars; - uint64_t* constructors; + void *programvars; + uint64_t *constructors; size_t nconstructors; }; // global variable for PoC -struct custom_initializer_t* custom_initializer_i; - +struct custom_initializer_t *custom_initializer_i; struct ProgramVars { void *mh; // mach_header or mach_header64 @@ -736,10 +743,10 @@ struct ProgramVars { const char **__prognamePtr; }; -void build_cache(struct libcache& cache, void* main); -void fix(struct libcache& cache); +void build_cache(struct libcache &cache, void *main); +void fix(struct libcache &cache); -void test(struct libcache& cache); +void test(struct libcache &cache); __attribute__((constructor)) static void bruh(int argc, const char *const argv[], const char *const envp[], @@ -760,8 +767,8 @@ bruh(int argc, const char *const argv[], const char *const envp[], // "/usr/lib/libobjc.A.dylib", &cache, // "../scripts/lib_objc_symtab.bin"); - // struct libcache_item* objc = get_libcache_with_name(&cache, "/usr/lib/libobjc.A.dylib"); - // print_macho_summary(objc->header); + // struct libcache_item* objc = get_libcache_with_name(&cache, + // "/usr/lib/libobjc.A.dylib"); print_macho_summary(objc->header); // test(cache); @@ -773,8 +780,9 @@ bruh(int argc, const char *const argv[], const char *const envp[], // (we do not remove them for **our lib**) // - malloc // - free - custom_initializer_i = (custom_initializer_t*)malloc(sizeof(custom_initializer_t)); - custom_initializer_i->programvars = (void*)vars; + custom_initializer_i = + (custom_initializer_t *)malloc(sizeof(custom_initializer_t)); + custom_initializer_i->programvars = (void *)vars; custom_initializer_i->cls = 0; custom_initializer_i->constructors = 0; fix(cache); @@ -786,16 +794,16 @@ bruh(int argc, const char *const argv[], const char *const envp[], printf("=== manual symbol bind completes ===\n"); } -void build_cache(struct libcache& cache, void* main) { +void build_cache(struct libcache &cache, void *main) { const uint64_t main_slide = get_slide(main); // Find our lib (mapped) file const void *thislib = find_header((void *)bruh); // Find dyld lib (mapped) file using a no-sus function const void *libdyld = find_header((void *)dyld_get_sdk_version); - cache.main = (void*)main; - cache.thislib = (void*)thislib; - cache.libdyld = (void*)libdyld; + cache.main = (void *)main; + cache.thislib = (void *)thislib; + cache.libdyld = (void *)libdyld; uint32_t libsystem_hash = calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib"); @@ -855,8 +863,8 @@ void build_cache(struct libcache& cache, void* main) { } } -void fix_binds(struct libcache_item* libfixing, struct libcache* cache, - int n_ins, uint32_t* instructions, char* libs, char* symbols) { +void fix_binds(struct libcache_item *libfixing, struct libcache *cache, + int n_ins, uint32_t *instructions, char *libs, char *symbols) { uint32_t libsystem_hash = calculate_libname_hash(cache, "/usr/lib/libSystem.B.dylib"); @@ -871,12 +879,12 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache, uint64_t page_rw_fixed[10]; // should be dynamic, but works for now int pc = 0; - for (;pc != n_ins;) { + for (; pc != n_ins;) { uint32_t libidx = instructions[pc]; uint32_t nsym = instructions[pc + 1]; pc += 2; - char* lib = libs + libidx; + char *lib = libs + libidx; for (int i = 0; i < nsym; i++) { uint32_t op = instructions[pc]; uint32_t offset = instructions[pc + 1]; @@ -884,22 +892,21 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache, uint32_t symidx = op >> 8; uint32_t segment = op & 0xff; - char* sym = symbols + symidx; + char *sym = symbols + symidx; uint64_t fix_at = offset + libfixing->segment[segment]; // enable WRITE protection for this data segment int need_rw_fix = true; for (int j = 0; j < npage_rw_fixed; j++) { - if (page_rw_fixed[j] <= fix_at && - page_rw_fixed[j] + 0x1000 > fix_at) { + if (page_rw_fixed[j] <= fix_at && page_rw_fixed[j] + 0x1000 > fix_at) { need_rw_fix = false; } } if (need_rw_fix) { uint64_t start_page = fix_at - (fix_at % 0x1000); vm_protect_func(mach_task_self_func(), start_page, 0x1000, 0, - VM_PROT_READ | VM_PROT_WRITE); + VM_PROT_READ | VM_PROT_WRITE); page_rw_fixed[npage_rw_fixed++] = start_page; printf("modify page starts at 0x%llx to RW\n", start_page); } @@ -916,15 +923,14 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache, printf("imports need to fix: %s at 0x%llx\n", sym, fix_at); printf(" from=%s\n", lib); printf(" segment id=%d; offset=0x%x;", segment, offset); - printf(" resolved=%llx(%p)\n", *(uint64_t*)fix_at, resolved); + printf(" resolved=%llx(%p)\n", *(uint64_t *)fix_at, resolved); } } - } -void fix_objc(struct libcache_item* libfixing, struct libcache& cache); -void fix_initializer(struct libcache_item* libfixing, struct libcache& cache); -void fix(struct libcache& cache) { +void fix_objc(struct libcache_item *libfixing, struct libcache &cache); +void fix_initializer(struct libcache_item *libfixing, struct libcache &cache); +void fix(struct libcache &cache) { // now we have function to find exported symbols // it supports full name search or hash search // to reserve space, we use the hash search @@ -960,20 +966,23 @@ void fix(struct libcache& cache) { // OBJC: // In Objective-C, the binary is loaded with the Objective-C runtime // This runtime (a library) install a hook on dyld for all images - // And because this runtime is a system runtime, the bootstrap step is already prepared - // The details on this runtime will be in a seperated document, below are some basics + // And because this runtime is a system runtime, the bootstrap step is already + // prepared The details on this runtime will be in a seperated document, below + // are some basics // - // The compiler for Objective-C emits a bunch of details for the runtime in the binary itself - // These information are stored in sections with prefix name __objc, namely + // The compiler for Objective-C emits a bunch of details for the runtime in + // the binary itself These information are stored in sections with prefix name + // __objc, namely // - __objc_classlist // - __objc_clssrefs // - __objc_selref // - __objc_const // - __objc_data // - // Objective-C stores the class interface in the binary particulary in __objc_data - // This interface contains the superclass, metaclass, and a cache to methods pointers - // These information are either bound (by dyld) or built (by Objective-C runtime) + // Objective-C stores the class interface in the binary particulary in + // __objc_data This interface contains the superclass, metaclass, and a cache + // to methods pointers These information are either bound (by dyld) or built + // (by Objective-C runtime) // // One of the important routine in the Objective-C runtime is readClass. // https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L3385 @@ -983,18 +992,21 @@ void fix(struct libcache& cache) { // // Because __objc_data contains to-be-bound values, // which will be resolved by dyld and referenced by Objective-C runtime later - // if we simply erase this value, reference(s) read by Objective-C runtime ensues a crash - // (through debugging, we know that the crash happens in readClass, realizeClassWithoutSwift) + // if we simply erase this value, reference(s) read by Objective-C runtime + // ensues a crash (through debugging, we know that the crash happens in + // readClass, realizeClassWithoutSwift) // - // However, we can evade this by making the runtime thinks there is no class needs setup - // This can be done by changing the __objc_classlist to some other name or remove this section - // Because the runtime find the __objc_classlist section by name, and the size of the section - // is used to iterate through pointers. - // So if we change the name, the runtime will have no class to run setup. - // Or complete removal and call the setup by ourselves, because we know where the data is + // However, we can evade this by making the runtime thinks there is no class + // needs setup This can be done by changing the __objc_classlist to some other + // name or remove this section Because the runtime find the __objc_classlist + // section by name, and the size of the section is used to iterate through + // pointers. So if we change the name, the runtime will have no class to run + // setup. Or complete removal and call the setup by ourselves, because we know + // where the data is // - // The setup is done through readClass function, as said above, its address can be found - // This function is pure C function so call into this function is easy + // The setup is done through readClass function, as said above, its address + // can be found This function is pure C function so call into this function is + // easy // // Important function with their names: // _readClass(objc_class*, bool, bool) @@ -1022,37 +1034,38 @@ void fix(struct libcache& cache) { // resolve selfbind if exist { // stored inside __DATA,selfbind - struct libcache_item* libfixing = get_libcache_with_name(&cache, "thislib"); + struct libcache_item *libfixing = get_libcache_with_name(&cache, "thislib"); struct selfbind_t { uint32_t liblist_offset; uint32_t symbollist_offset; }; - struct selfbind_t* selfbind = (struct selfbind_t*)get_selfbind(libfixing->header); + struct selfbind_t *selfbind = + (struct selfbind_t *)get_selfbind(libfixing->header); if (selfbind) { - char* libs = (char*)(selfbind+1) + selfbind->liblist_offset; - char* symbols = (char*)(selfbind+1) + selfbind->symbollist_offset; + char *libs = (char *)(selfbind + 1) + selfbind->liblist_offset; + char *symbols = (char *)(selfbind + 1) + selfbind->symbollist_offset; uint64_t n_instructions = ((uint64_t)libs - (uint64_t)(selfbind + 1)) / 4; - uint32_t* encoded_table = (uint32_t*)(selfbind+1); + uint32_t *encoded_table = (uint32_t *)(selfbind + 1); printf("[*] performing selfbind (instructions=%p)\n", selfbind); - fix_binds(libfixing, &cache, - n_instructions, encoded_table, - libs, symbols); + fix_binds(libfixing, &cache, n_instructions, encoded_table, libs, + symbols); } } // the rest of the fixes are in main executable printf("[*] performing bind for main executable\n"); - struct libcache_item* libfixing = get_libcache_with_name(&cache, "main"); - fix_binds(libfixing, &cache, - bshield_data::n_instructions, bshield_data::encoded_table, - bshield_data::libs, bshield_data::symbols); + struct libcache_item *libfixing = get_libcache_with_name(&cache, "main"); + fix_binds(libfixing, &cache, bshield_data::n_instructions, + bshield_data::encoded_table, bshield_data::libs, + bshield_data::symbols); // TODO: Reformat the region as per before, or leave as it // for (int j = 0; j < npage_rw_fixed; j++) { // uint64_t start_page = page_rw_fixed[j]; - // vm_protect_func(mach_task_self_func(), start_page, 0x4000, 0, VM_PROT_READ); + // vm_protect_func(mach_task_self_func(), start_page, 0x4000, 0, + // VM_PROT_READ); // } // Encrypted __TEXT segment @@ -1070,61 +1083,74 @@ void fix(struct libcache& cache) { fix_initializer(libfixing, cache); } -void volatile -custom_initializer(int argc, const char *const argv[], const char *const envp[], const char *const apple[]) { +void volatile custom_initializer(int argc, const char *const argv[], + const char *const envp[], + const char *const apple[]) { printf("run custom initializers %p\n", custom_initializer_i); if (custom_initializer_i->cls != 0) { // for Objective-C load - uint64_t* loadable_classes = custom_initializer_i->loadable_classes; - uint32_t* loadable_classes_used = custom_initializer_i->loadable_classes_used; - sel_lookUpByName_t sel_lookUpByName = custom_initializer_i->sel_lookUpByName; - objc_autoreleasePoolPop_t objc_autoreleasePoolPop = custom_initializer_i->objc_autoreleasePoolPop; - objc_autoreleasePoolPush_t objc_autoreleasePoolPush = custom_initializer_i->objc_autoreleasePoolPush; + uint64_t *loadable_classes = custom_initializer_i->loadable_classes; + uint32_t *loadable_classes_used = + custom_initializer_i->loadable_classes_used; + sel_lookUpByName_t sel_lookUpByName = + custom_initializer_i->sel_lookUpByName; + objc_autoreleasePoolPop_t objc_autoreleasePoolPop = + custom_initializer_i->objc_autoreleasePoolPop; + objc_autoreleasePoolPush_t objc_autoreleasePoolPush = + custom_initializer_i->objc_autoreleasePoolPush; remapClass_t remapClass = custom_initializer_i->remapClass; - schedule_class_load_t schedule_class_load = custom_initializer_i->schedule_class_load; + schedule_class_load_t schedule_class_load = + custom_initializer_i->schedule_class_load; for (int i = 0; i < custom_initializer_i->ncls; i++) { - void* cls0 = (void*)custom_initializer_i->cls[i]; - void* cls = remapClass(cls0); - if (!cls) continue; + void *cls0 = (void *)custom_initializer_i->cls[i]; + void *cls = remapClass(cls0); + if (!cls) + continue; schedule_class_load(cls); } - printf("loadable_classes %llx %x\n", *loadable_classes, *loadable_classes_used); + printf("loadable_classes %llx %x\n", *loadable_classes, + *loadable_classes_used); struct loadable_class_t { - void* cls; - void* method; + void *cls; + void *method; }; - struct loadable_class_t *classes = (struct loadable_class_t*)*loadable_classes; + struct loadable_class_t *classes = + (struct loadable_class_t *)*loadable_classes; int used = *loadable_classes_used; *loadable_classes = 0; // *loadable_classes_allocated = 0; *loadable_classes_used = 0; - void* sel = sel_lookUpByName("load"); + void *sel = sel_lookUpByName("load"); // Call all +loads for the detached list. - void* pool = objc_autoreleasePoolPush(); + void *pool = objc_autoreleasePoolPush(); for (int i = 0; i < used; i++) { - void* cls = classes[i].cls; + void *cls = classes[i].cls; load_method_t load_method = (load_method_t)classes[i].method; printf("call load of class %p %p\n", cls, load_method); - if (!cls) continue; + if (!cls) + continue; (load_method)(cls, sel); } // Destroy the detached list. - if (classes) free(classes); + if (classes) + free(classes); objc_autoreleasePoolPop(pool); } // for constructors if (custom_initializer_i->constructors) { - typedef void *(*constructors_t)(int, void*, void*, void*, void*); + typedef void *(*constructors_t)(int, void *, void *, void *, void *); uint32_t nconst = custom_initializer_i->nconstructors; for (int i = 0; i < nconst; i++) { - constructors_t cons = (constructors_t)custom_initializer_i->constructors[i]; + constructors_t cons = + (constructors_t)custom_initializer_i->constructors[i]; printf("call initializer at %p\n", cons); - cons(argc, (void*)argv, (void*)envp, (void*)apple, custom_initializer_i->programvars); + cons(argc, (void *)argv, (void *)envp, (void *)apple, + custom_initializer_i->programvars); } free(custom_initializer_i->constructors); } @@ -1132,7 +1158,7 @@ custom_initializer(int argc, const char *const argv[], const char *const envp[], free(custom_initializer_i); } -void fix_objc(struct libcache_item* libfixing, struct libcache& cache) { +void fix_objc(struct libcache_item *libfixing, struct libcache &cache) { // Manually run the Objective-C runtime for each class // @@ -1146,7 +1172,7 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) { // "mov rcx, 123;" // "call r12;"); - void* header = libfixing->header; + void *header = libfixing->header; const uint32_t magic = *(uint32_t *)header; char *ptr = (char *)header; if (magic == magic64) { @@ -1156,7 +1182,7 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) { } const uint32_t ncmds = *((uint32_t *)header + 4); - char* command_ptr = ptr; + char *command_ptr = ptr; uint64_t linkedit_vmaddr; uint64_t linkedit_fileoffset; @@ -1173,62 +1199,80 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) { if (custom_strcmp(name, "__TEXT") == 0) { slide = (uint64_t)header - vmaddr; } else if (custom_strcmp(name, "__DATA_CONST") == 0) { - uint64_t nsect = *((uint32_t *)ptr + 8*2); - char* sections_ptr = (char*)((uint32_t*)ptr + 18); + uint64_t nsect = *((uint32_t *)ptr + 8 * 2); + char *sections_ptr = (char *)((uint32_t *)ptr + 18); for (int sec = 0; sec < nsect; sec++) { - char* secname = sections_ptr; + char *secname = sections_ptr; printf("section %s\n", secname); if (custom_strncmp(secname, "__objc_classbruh", 16) == 0) { - uint64_t addr = *((uint64_t*)sections_ptr + 4); - uint64_t size = *((uint64_t*)sections_ptr + 5); - uint64_t *data_ptr = (uint64_t*)(addr + slide); + uint64_t addr = *((uint64_t *)sections_ptr + 4); + uint64_t size = *((uint64_t *)sections_ptr + 5); + uint64_t *data_ptr = (uint64_t *)(addr + slide); - readClass_t readClass = (readClass_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL9readClassP10objc_classbb"); - realizeClassWithoutSwift_t realizeClassWithoutSwift = (realizeClassWithoutSwift_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL24realizeClassWithoutSwiftP10objc_classS0_"); + readClass_t readClass = + (readClass_t)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache, + "__ZL9readClassP10objc_classbb"); + realizeClassWithoutSwift_t realizeClassWithoutSwift = + (realizeClassWithoutSwift_t)find_in_symtab( + "/usr/lib/libobjc.A.dylib", &cache, + "__ZL24realizeClassWithoutSwiftP10objc_classS0_"); for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) { - // this pointer is rebased by dyld and points to the correct class interface - // for some reason, we can skip this and it should still work - void* newCls = readClass((void*)data_ptr[ptr_i], false, false); - if (newCls != (void*)data_ptr[ptr_i]) { + // this pointer is rebased by dyld and points to the correct class + // interface for some reason, we can skip this and it should still + // work + void *newCls = readClass((void *)data_ptr[ptr_i], false, false); + if (newCls != (void *)data_ptr[ptr_i]) { realizeClassWithoutSwift(newCls, 0); } printf("add class init (%llx)%p\n", data_ptr[ptr_i], newCls); } - } - else if (custom_strncmp(secname, "__objc_nlclsbruh", 16) == 0) { - uint64_t addr = *((uint64_t*)sections_ptr + 4); - uint64_t size = *((uint64_t*)sections_ptr + 5); - uint64_t *data_ptr = (uint64_t*)(addr + slide); + } else if (custom_strncmp(secname, "__objc_nlclsbruh", 16) == 0) { + uint64_t addr = *((uint64_t *)sections_ptr + 4); + uint64_t size = *((uint64_t *)sections_ptr + 5); + uint64_t *data_ptr = (uint64_t *)(addr + slide); - uint64_t* loadable_classes = (uint64_t*)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL16loadable_classes"); - uint32_t* loadable_classes_allocated = (uint32_t*)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL26loadable_classes_allocated"); - uint32_t* loadable_classes_used = (uint32_t*)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL21loadable_classes_used"); + uint64_t *loadable_classes = (uint64_t *)find_in_symtab( + "/usr/lib/libobjc.A.dylib", &cache, "__ZL16loadable_classes"); + uint32_t *loadable_classes_allocated = + (uint32_t *)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache, + "__ZL26loadable_classes_allocated"); + uint32_t *loadable_classes_used = + (uint32_t *)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache, + "__ZL21loadable_classes_used"); - remapClass_t remapClass = (remapClass_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL10remapClassP10objc_class"); - schedule_class_load_t schedule_class_load = (schedule_class_load_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL19schedule_class_loadP10objc_class"); - realizeClassWithoutSwift_t realizeClassWithoutSwift = (realizeClassWithoutSwift_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL24realizeClassWithoutSwiftP10objc_classS0_"); - addClassTableEntry_t addClassTableEntry = (addClassTableEntry_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__ZL18addClassTableEntryP10objc_classb"); - sel_lookUpByName_t sel_lookUpByName = (sel_lookUpByName_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "_sel_lookUpByName"); - objc_autoreleasePoolPush_t objc_autoreleasePoolPush = (objc_autoreleasePoolPush_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__objc_autoreleasePoolPush"); - objc_autoreleasePoolPop_t objc_autoreleasePoolPop = (objc_autoreleasePoolPop_t)find_in_symtab( - "/usr/lib/libobjc.A.dylib", &cache, "__objc_autoreleasePoolPop"); + remapClass_t remapClass = + (remapClass_t)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache, + "__ZL10remapClassP10objc_class"); + schedule_class_load_t schedule_class_load = + (schedule_class_load_t)find_in_symtab( + "/usr/lib/libobjc.A.dylib", &cache, + "__ZL19schedule_class_loadP10objc_class"); + realizeClassWithoutSwift_t realizeClassWithoutSwift = + (realizeClassWithoutSwift_t)find_in_symtab( + "/usr/lib/libobjc.A.dylib", &cache, + "__ZL24realizeClassWithoutSwiftP10objc_classS0_"); + addClassTableEntry_t addClassTableEntry = + (addClassTableEntry_t)find_in_symtab( + "/usr/lib/libobjc.A.dylib", &cache, + "__ZL18addClassTableEntryP10objc_classb"); + sel_lookUpByName_t sel_lookUpByName = + (sel_lookUpByName_t)find_in_symtab("/usr/lib/libobjc.A.dylib", + &cache, "_sel_lookUpByName"); + objc_autoreleasePoolPush_t objc_autoreleasePoolPush = + (objc_autoreleasePoolPush_t)find_in_symtab( + "/usr/lib/libobjc.A.dylib", &cache, + "__objc_autoreleasePoolPush"); + objc_autoreleasePoolPop_t objc_autoreleasePoolPop = + (objc_autoreleasePoolPop_t)find_in_symtab( + "/usr/lib/libobjc.A.dylib", &cache, + "__objc_autoreleasePoolPop"); // https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L3822 for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) { - void* cls = remapClass((void*)data_ptr[ptr_i]); - if (!cls) continue; + void *cls = remapClass((void *)data_ptr[ptr_i]); + if (!cls) + continue; addClassTableEntry(cls); realizeClassWithoutSwift(cls, 0); printf("build nonlazy class at (%llx)%p\n", data_ptr[ptr_i], cls); @@ -1237,8 +1281,10 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) { custom_initializer_i->sel_lookUpByName = sel_lookUpByName; custom_initializer_i->loadable_classes = loadable_classes; custom_initializer_i->loadable_classes_used = loadable_classes_used; - custom_initializer_i->objc_autoreleasePoolPush = objc_autoreleasePoolPush; - custom_initializer_i->objc_autoreleasePoolPop = objc_autoreleasePoolPop; + custom_initializer_i->objc_autoreleasePoolPush = + objc_autoreleasePoolPush; + custom_initializer_i->objc_autoreleasePoolPop = + objc_autoreleasePoolPop; custom_initializer_i->schedule_class_load = schedule_class_load; custom_initializer_i->remapClass = remapClass; custom_initializer_i->cls = data_ptr; @@ -1256,13 +1302,14 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) { } } -void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) { +void fix_initializer(struct libcache_item *libfixing, struct libcache &cache) { // fix the initializers // The Objective-C runtime loads the NSObject after this lib booted - // So all calls to NSObject (and its children classes) will segfault/throw error + // So all calls to NSObject (and its children classes) will segfault/throw + // error // - // So we will fix the main initializers, which runs after all Objective-C setup - // The initializers will run these Objective-C classes' load methods + // So we will fix the main initializers, which runs after all Objective-C + // setup The initializers will run these Objective-C classes' load methods // // (THIS IDEA IS TESTED AND WILL NOT WORK) // As of now, we assume the main executable has a __mod_init_func section @@ -1281,29 +1328,32 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) { // // There could be many ways to do this. I discovered 1 method of doing this. // - // The idea is to hijack the main function to do the rest of the initalizations. - // By fixing the LC_MAIN command, we can make dyld jump to anywhere we want as main. - // But the command can't be edited at runtime. - // And pointing to the function we want needs a workaround. + // The idea is to hijack the main function to do the rest of the + // initalizations. By fixing the LC_MAIN command, we can make dyld jump to + // anywhere we want as main. But the command can't be edited at runtime. And + // pointing to the function we want needs a workaround. // - // So we will write a shellcode in the binary and point main to that shellcode. - // The shellcode basically loads the address of the initalization function, - // call it, then call main, and return. + // So we will write a shellcode in the binary and point main to that + // shellcode. The shellcode basically loads the address of the initalization + // function, call it, then call main, and return. // - // The shellcode must be able ot get the current pc address to correctly calculate - // address from any offset. In arm64, we can use `adr x8, 0`. - // If we know where the shellcode is, we can effectively calculate the header of main. - // Now, everything is easy, just need offsets to anywhere we want and we can get it. + // The shellcode must be able ot get the current pc address to correctly + // calculate address from any offset. In arm64, we can use `adr x8, 0`. If we + // know where the shellcode is, we can effectively calculate the header of + // main. Now, everything is easy, just need offsets to anywhere we want and we + // can get it. // - // Now the address of the initalization function can be fetched using many methods, - // but it resides inside this library. To reduce redundance work, we can write the - // address of this function somewhere inside main, which will then be easily found. + // Now the address of the initalization function can be fetched using many + // methods, but it resides inside this library. To reduce redundance work, we + // can write the address of this function somewhere inside main, which will + // then be easily found. // // As a result, we choose the space before __text to write the shellcode, // the space after __DATA to write the address for initalization function. // Because all segment is allocated/pre-allocated with page alignement, // we can be pretty sure that there are free space. - // (note: __TEXT segment is aligned to the end of the page, free space in the middle) + // (note: __TEXT segment is aligned to the end of the page, free space in the + // middle) // // Below is the shellcode. /* @@ -1330,7 +1380,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) { br x9 */ - void* header = libfixing->header; + void *header = libfixing->header; const uint32_t magic = *(uint32_t *)header; char *ptr = (char *)header; if (magic == magic64) { @@ -1340,7 +1390,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) { } const uint32_t ncmds = *((uint32_t *)header + 4); - char* command_ptr = ptr; + char *command_ptr = ptr; uint64_t linkedit_vmaddr; uint64_t linkedit_fileoffset; @@ -1356,23 +1406,26 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) { printf("segment %s\n", name); if (custom_strcmp(name, "__TEXT") == 0) { slide = (uint64_t)header - vmaddr; - uint64_t nsect = *((uint32_t *)ptr + 8*2); - char* sections_ptr = (char*)((uint32_t*)ptr + 18); + uint64_t nsect = *((uint32_t *)ptr + 8 * 2); + char *sections_ptr = (char *)((uint32_t *)ptr + 18); for (int sec = 0; sec < nsect; sec++) { - char* secname = sections_ptr; + char *secname = sections_ptr; printf("section %s\n", secname); if (custom_strcmp(secname, "__init_offsets") == 0) { - uint64_t addr = *((uint64_t*)sections_ptr + 4); - uint64_t size = *((uint64_t*)sections_ptr + 5); - uint32_t *data_ptr = (uint32_t*)(addr + slide); + uint64_t addr = *((uint64_t *)sections_ptr + 4); + uint64_t size = *((uint64_t *)sections_ptr + 5); + uint32_t *data_ptr = (uint32_t *)(addr + slide); printf("found initializer at %p\n", data_ptr); custom_initializer_i->nconstructors = size / 4; - custom_initializer_i->constructors = (uint64_t*)malloc(sizeof(uint64_t) * size/4); + custom_initializer_i->constructors = + (uint64_t *)malloc(sizeof(uint64_t) * size / 4); for (int j = 0; j < size / 4; j++) { - custom_initializer_i->constructors[j] = (uint64_t)header + data_ptr[j]; - printf("registered initializer at %llx\n", custom_initializer_i->constructors[j]); + custom_initializer_i->constructors[j] = + (uint64_t)header + data_ptr[j]; + printf("registered initializer at %llx\n", + custom_initializer_i->constructors[j]); } } if (custom_strcmp(secname, "__mod_init_func") == 0) { @@ -1382,14 +1435,14 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) { sections_ptr += 16 * 2 + 8 * 2 + 4 * 8; } } else if (custom_strcmp(name, "__DATA") == 0) { - uint64_t nsect = *((uint32_t *)ptr + 8*2); - char* sections_ptr = (char*)((uint32_t*)ptr + 18); + uint64_t nsect = *((uint32_t *)ptr + 8 * 2); + char *sections_ptr = (char *)((uint32_t *)ptr + 18); sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1); - uint64_t addr = *((uint64_t*)sections_ptr + 4); - uint64_t size = *((uint64_t*)sections_ptr + 5); + uint64_t addr = *((uint64_t *)sections_ptr + 4); + uint64_t size = *((uint64_t *)sections_ptr + 5); - uint64_t* dummy = (uint64_t*)(addr + slide + size); + uint64_t *dummy = (uint64_t *)(addr + slide + size); dummy[0] = (uint64_t)custom_initializer; dummy[1] = (uint64_t)(header) + bshield_data::main; printf("add custom main-peg at %p\n", dummy); @@ -1402,7 +1455,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) { } } -void test(struct libcache& cache) { +void test(struct libcache &cache) { uint32_t libsystem_hash = calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib"); if (false) { // test search using name @@ -1436,7 +1489,8 @@ void test(struct libcache& cache) { printf("Indirect search: Found=%p Expected=%p\n", vm_protect_func, vm_protect); - void *realpath_func = custom_dlsym(&cache, libsystem_hash, "_realpath$DARWIN_EXTSN"); + void *realpath_func = + custom_dlsym(&cache, libsystem_hash, "_realpath$DARWIN_EXTSN"); printf("Indirect search: Found=%p Expected=%p\n", realpath_func, realpath); } }