From 88bb0aa09d06048ef3944a3568e0169f635ebab6 Mon Sep 17 00:00:00 2001 From: nganhkhoa Date: Wed, 7 Jun 2023 10:49:05 +0700 Subject: [PATCH] fix fixups chain rewrite stops at first entry --- macho-go/pkg/ios/macho/dyld_info.go | 11 ++++++++-- macho-go/pkg/ios/macho/edit.go | 31 ++++++++++++++++++++++++++++- macho-go/pkg/ios/macho/fixups.c | 9 +++++++++ macho-go/pkg/ios/macho/fixups.h | 1 + 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/macho-go/pkg/ios/macho/dyld_info.go b/macho-go/pkg/ios/macho/dyld_info.go index 147bf6f..ab9400f 100644 --- a/macho-go/pkg/ios/macho/dyld_info.go +++ b/macho-go/pkg/ios/macho/dyld_info.go @@ -29,6 +29,8 @@ type ImportSymbol struct { // push number pnum uint32 stub uint64 + + next int // only for LC_DYLD_CHAINED_FIXUPS } func (sym *ImportSymbol) Name() string { @@ -120,7 +122,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol { for { mc.file.Read(code) - v := binary.LittleEndian.Uint64(code) + v := mc.byteorder.Uint64(code) var bind C.int var ret1 C.ulonglong @@ -133,7 +135,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol { name := C.GoString(s.name) dylib := string(mc.dylibs[int(s.lib_ordinal)-1].name[:]) - // fmt.Printf("%x bind=%d (%s)%s\n", address, bind, dylib, name) + fmt.Printf("// 0x%x bind=%d (%s)%s\n", address, bind, dylib, name) sym.address = uint64(address) sym.name = name @@ -142,14 +144,19 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol { sym.segment = uint32(mc.findSegmentIndexAt(uint64(address))) sym.file_address = uint64(address) + sym.next = int(next) new_sym := sym syms = append(syms, &new_sym) + } else { + fmt.Printf("// 0x%x rebase=%d target=0x%x high8=0x%x\n", address, bind, ret1, ret2) } address += 8 if int(next) == 0 { break } + // because the pointer move up 8 bytes already so we minus 8 + mc.file.Seek(int64(next * 4) - 8, io.SeekCurrent) } mc.file.Seek(0, io.SeekStart) } diff --git a/macho-go/pkg/ios/macho/edit.go b/macho-go/pkg/ios/macho/edit.go index 3666296..cc47c73 100644 --- a/macho-go/pkg/ios/macho/edit.go +++ b/macho-go/pkg/ios/macho/edit.go @@ -3,12 +3,17 @@ package macho import ( "fmt" "io" + "math/rand" + "time" log "github.com/sirupsen/logrus" . "ios-wrapper/pkg/ios" ) +// #include "fixups.h" +import "C" + func rewriteLoadcommandsWithoutCodesignature(mc *MachoContext) { if mc.Is64bit() { mc.file.Seek(int64(Header_size_64), io.SeekStart) @@ -273,6 +278,8 @@ func (mc *MachoContext) RemoveBindSymbols() { return } + rand.Seed(time.Now().UnixNano()) + if mc.dyldinfo == nil { mc.removeBindSymbolsModern() } else { @@ -291,6 +298,7 @@ func (mc *MachoContext) RemoveBindSymbols() { // due to some limitations when design this tool // we write the c code to stdout lol fmt.Println("struct imported_symbol {const char* name; const char* lib; uint32_t hash; int segment_i; uint64_t offset;};") + fmt.Println("const char* lib_to_resolve = \"main\";") fmt.Println("struct imported_symbol imported_table[] = {") count := 0 for _, symbol := range mc.CollectBindSymbols() { @@ -313,7 +321,28 @@ func (mc *MachoContext) RemoveBindSymbols() { fmt.Printf("{\"%s\", \"%s\", 0x%x, 0x%x, 0x%x},\n", symbol.Name(), symbol.Dylib(), dylib_hash, symbol.segment, offset); - mc.file.WriteAt(make([]byte, 8), int64(symbol.file_address)) + if mc.dyldinfo != nil { + // for legacy resolve the opcodes can be rewritten as 0x00 + mc.file.WriteAt(make([]byte, 8), int64(symbol.file_address)) + } else { + // for modern resolve the opcodes must not be rewritten as 0x00 + // because it contains 2 types of opcodes, BIND and REBASE + // we only fix BIND and leave REBASE intact + // However, each opcodes has a *next* field to the next opcode + // So if we want to leave the header intact (contains pointers, size) + // We should rewrite this as REBASE opcode (so no symbol lookup happens) + // and it can continue + + // we can write random values, because the loader just do + // (high8 << 56 | target) - mach_header + // or something, so no symbol lookup and no error at runtime + target := rand.Int() + high8 := rand.Int() + value := C.MakeRebaseFixupOpcode(C.int(symbol.next), C.ulonglong(target), C.ulonglong(high8)) + v := make([]byte, 8) + mc.byteorder.PutUint64(v, uint64(value)) + mc.file.WriteAt(v, int64(symbol.file_address)) + } } fmt.Println("};") fmt.Printf("uint32_t nimports = %d;\n", count); diff --git a/macho-go/pkg/ios/macho/fixups.c b/macho-go/pkg/ios/macho/fixups.c index 4442237..c363e85 100644 --- a/macho-go/pkg/ios/macho/fixups.c +++ b/macho-go/pkg/ios/macho/fixups.c @@ -94,6 +94,15 @@ int ParseFixValue(int format, uint64_t value, int* bind, uint64_t* ret1, uint64_ } } +uint64_t MakeRebaseFixupOpcode(int next, uint64_t target, uint64_t high8) { + uint64_t value; + struct dyld_chained_ptr_64_rebase* b = (struct dyld_chained_ptr_64_rebase*)&value; + b->next = next; + b->target = target; + b->high8 = high8; + return value; +} + void ParseFixUps(uint8_t* buffer) { struct dyld_chained_fixups_header* header = (struct dyld_chained_fixups_header*)buffer; printf("starts=0x%x\n", header->starts_offset); diff --git a/macho-go/pkg/ios/macho/fixups.h b/macho-go/pkg/ios/macho/fixups.h index 6ab2a77..4f9be7f 100644 --- a/macho-go/pkg/ios/macho/fixups.h +++ b/macho-go/pkg/ios/macho/fixups.h @@ -30,4 +30,5 @@ struct ImportTable GetImportsTable(uint8_t* header_ptr); struct ImportSymbol GetImportsAt(struct ImportTable* table, int i); int GetSegmentFixAt(uint8_t* buffer, uint32_t i, struct SegmentFix* fix); int ParseFixValue(int format, uint64_t value, int* bind, uint64_t* ret1, uint64_t* ret2); +uint64_t MakeRebaseFixupOpcode(int next, uint64_t target, uint64_t high8); #endif