diff --git a/macho-go/pkg/ios/macho/edit.go b/macho-go/pkg/ios/macho/edit.go index 8cdce51..4508626 100644 --- a/macho-go/pkg/ios/macho/edit.go +++ b/macho-go/pkg/ios/macho/edit.go @@ -10,6 +10,8 @@ import ( "strings" "time" + "unsafe" + log "github.com/sirupsen/logrus" . "ios-wrapper/pkg/ios" @@ -17,6 +19,7 @@ import ( ) // #include "fixups.h" +// #include "arm.h" import "C" func rewriteLoadcommandsWithoutCodesignature(mc *MachoContext) { @@ -342,6 +345,7 @@ func (mc *MachoContext) RemoveBindSymbols() { value := C.MakeRebaseFixupOpcode(C.int(symbol.next), C.ulonglong(target), C.ulonglong(high8)) v := make([]byte, 8) mc.byteorder.PutUint64(v, uint64(value)) + fmt.Printf("change to rebase at %x\n", symbol.file_address) mc.file.WriteAt(v, int64(symbol.file_address)) } } @@ -582,10 +586,6 @@ func (mc *MachoContext) RemoveStrings() { // their reference down too, // symtab and dysymtab can be ignored, by removing them lmao - fmt.Println("TODO: Removing strings") - - // identify the cstring region - var cstring *Section64; for _, command := range mc.commands { switch command.(type) { @@ -605,7 +605,6 @@ func (mc *MachoContext) RemoveStrings() { } } - // no section __cstring in __TEXT if cstring == nil { return } @@ -661,7 +660,7 @@ func (mc *MachoContext) RemoveStrings() { // size of the section and segment is defined by the total // space for strings required - section := Section64{ + new_cstring_section := Section64{ sectname: secname, segname: segname, addr: secstart, @@ -686,7 +685,7 @@ func (mc *MachoContext) RemoveStrings() { initprot: 3, // read/write nsects: 1, flags: 0, - sections: []*Section64{§ion}, + sections: []*Section64{&new_cstring_section}, } // rewrite the header to be correct @@ -793,19 +792,88 @@ func (mc *MachoContext) RemoveStrings() { // so we should be very free, because very few program goes this far // but if this happens, god bless you - // for the demo, we can try to hard code, (addresses are virtual) - // text should be in 0x100003f70 - // string to reference is 0x100008000 - // encoding ADRP is actually hard hmmge? - // revert to offset - mc.file.WriteAt([]byte{0x20, 0x00, 0x00, 0xb0}, int64(0x000003f70)) - mc.file.WriteAt([]byte{0x00, 0x00, 0x00, 0x91}, int64(0x000003f74)) + // this part uses file offsets for calculations + + in_cstring := func (offset uint64) bool { + cstring_start := uint64(cstring.Offset()) + cstring_end := cstring_start + cstring.Size() + return (offset >= cstring_start) && (offset < cstring_end) + } + + text := mc.segments[1] + text_start := text.Fileoff() + text_end := text_start + text.Filesize() + + inst := make([]byte, 4) + for addr := text_start; addr < text_end; addr = addr + 4 { + mc.file.ReadAt(inst, int64(addr)) + inst_adrp := binary.LittleEndian.Uint32(inst) + mc.file.ReadAt(inst, int64(addr + 4)) + inst_add := binary.LittleEndian.Uint32(inst) + + if !(C.is_adrp(C.uint(inst_adrp)) != 0 && C.is_add(C.uint(inst_add)) != 0) { + continue + } + + base := (addr >> 12) << 12 + + // calculate the old string reference + ref_base := C.adrp_imm_get(C.uint(inst_adrp)) + ref_offset := C.add_imm_get(C.uint(inst_add)) + ref := base + uint64(ref_base + ref_offset) + + if (!in_cstring(ref)) { + continue + } + + oldstr := uint64(ref) + oldstr_relative := oldstr - uint64(cstring.Offset()) + + // find the new string address + // using oldstr relative address to cstring section + newstr := uint64(new_cstring_section.Offset()) + oldstr_relative + newstr_base := (newstr >> 12) << 12 // to calculate new offset in adrp + newstr_offset := newstr - newstr_base // to calculate new offset in add + + C.adrp_imm_set((*C.uint32_t)(unsafe.Pointer(&inst_adrp)), C.uint(newstr_base - base)) + C.add_imm_set((*C.uint32_t)(unsafe.Pointer(&inst_add)), C.uint(newstr_offset)) + + binary.LittleEndian.PutUint32(inst, inst_adrp) + mc.file.WriteAt(inst, int64(addr)) + binary.LittleEndian.PutUint32(inst, inst_add) + mc.file.WriteAt(inst, int64(addr + 4)) + } // modify the rebase table (for both opcode and fixups chain versions) // this is for pointer references + isModernSymbol := mc.dyldinfo == nil + isLegacySymbol := !isModernSymbol + for _, symbol := range mc.CollectBindSymbols() { + if isLegacySymbol { + } else { + // (high8 << 56 | target) - mach_header + + ref := uint64(symbol.high8 << 56 | symbol.target) + if (!in_cstring(ref)) { + continue + } + + oldstr := ref + oldstr_relative := oldstr - uint64(cstring.Offset()) + newstr := uint64(new_cstring_section.Offset()) + oldstr_relative + + target := newstr & 0x00FFFFFFFFFFFFFF + high8 := newstr >> 56 + value := C.MakeRebaseFixupOpcode(C.int(symbol.next), C.ulonglong(target), C.ulonglong(high8)) + v := make([]byte, 8) + mc.byteorder.PutUint64(v, uint64(value)) + fmt.Printf("change to rebase at %x\n", symbol.file_address) + mc.file.WriteAt(v, int64(symbol.file_address)) + } + } } func (mc *MachoContext) AddSection(segname string, name string, size int) Section {