diff --git a/macho-go/internal/wrapper/action/remove_imports.go b/macho-go/internal/wrapper/action/remove_imports.go new file mode 100644 index 0000000..dc50d13 --- /dev/null +++ b/macho-go/internal/wrapper/action/remove_imports.go @@ -0,0 +1,21 @@ +package action + +import ( + . "ios-wrapper/internal/wrapper/ofile" +) + +type removeImports struct{} + +func (action *removeImports) withMacho(mf *MachoFile) error { + mf.Context().RemoveBindSymbols() + return nil +} + +func (action *removeImports) withFat(ff *FatFile) error { + return defaultWithFat(action, ff) +} + +func NewRemoveImportsAction() *removeImports { + return &removeImports{} +} + diff --git a/macho-go/internal/wrapper/cli.go b/macho-go/internal/wrapper/cli.go index 8f1cd2b..3c531c3 100644 --- a/macho-go/internal/wrapper/cli.go +++ b/macho-go/internal/wrapper/cli.go @@ -94,6 +94,12 @@ func Cli() { pc.remove_codesign = true pc.outfile = arg.Out + case "remove-imports": + arg := cli.RemoveImports + ofile = NewOFile(arg.OFile) + pc.remove_imports = true + pc.outfile = arg.Out + default: return } diff --git a/macho-go/internal/wrapper/cli_parser.go b/macho-go/internal/wrapper/cli_parser.go index 1b2c7c1..503bd24 100644 --- a/macho-go/internal/wrapper/cli_parser.go +++ b/macho-go/internal/wrapper/cli_parser.go @@ -24,6 +24,11 @@ type RemoveCodesignArgument struct { OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"` } +type RemoveImportsArgument struct { + Out string `short:"o" required name:"outfile" help:"Modified Mach-O/Fat binary output file" type:"path"` + OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"` +} + type SignedBcellArgument struct { Out string `short:"o" required name:"outfile" help:"bcell.dat.signed output file" type:"existingfile"` Bcell string `arg short:"b" required help:"bcell.dat input file" type:"existingfile"` @@ -57,6 +62,7 @@ type Argument struct { Wrap WrapArgument `cmd help:"Modifies Mach-O/Fat binary"` Vltk VltkArgument `cmd help:"Modifies Mach-O/Fat binary"` Info InfoArgument `cmd help:"Show Mach-O/Fat binary information"` + RemoveImports RemoveImportsArgument `cmd aliases:"remove-imports" name:"remove-imports" help:"Remove imports"` RemoveCodesign RemoveCodesignArgument `cmd aliases:"remove-signature" name:"remove-codesign" help:"Remove LC_CODE_SIGNATURE from Mach-O/Fat binary"` SignedBcell SignedBcellArgument `cmd name:"signed-bcell" help:"Change Protobuf into Protobuf"` DisplayBcell DisplayBcellArgument `cmd name:"display-bcell" help:"Display Protobuf content"` diff --git a/macho-go/internal/wrapper/info.go b/macho-go/internal/wrapper/info.go index 635f893..3f2bb8f 100644 --- a/macho-go/internal/wrapper/info.go +++ b/macho-go/internal/wrapper/info.go @@ -34,21 +34,17 @@ func (printer *InfoPrinter) Print() { fmt.Printf("Init functions at offset %s\n", &fun) } - symbols := mc.CollectLazyBindSymbols() - if len(symbols) > 0 { - fmt.Println("Lazy Symbols") - for _, sym := range symbols { - fmt.Printf( - "%s\n\tStub=0x%x Address=0x%x\n\tDylib=%s\n", - sym.Name(), - sym.Stub(), - sym.Address(), - sym.Dylib(), - ) - } - } else { - fmt.Println("No lazy symbols") - } + symbols := mc.CollectBindSymbols() + for _, sym := range symbols { + fmt.Printf( + "%s (%s)\n\tStub=0x%x Address=0x%x\n\tDylib=%s\n", + sym.Name(), + sym.Type(), + sym.Stub(), + sym.Address(), + sym.Dylib(), + ) + } fmt.Println("======") } diff --git a/macho-go/internal/wrapper/program_context.go b/macho-go/internal/wrapper/program_context.go index cf2a80b..73b32d1 100644 --- a/macho-go/internal/wrapper/program_context.go +++ b/macho-go/internal/wrapper/program_context.go @@ -40,6 +40,7 @@ func (uc *UserConfig) Protomodel() *protomodel.Config { type ProgramContext struct { strip_init_pointers bool remove_codesign bool + remove_imports bool dylib_to_add []string rpath_to_add []string @@ -83,6 +84,12 @@ func (pc *ProgramContext) dispatchActions(ofile OFile) { } func (pc *ProgramContext) Process(ofile OFile) { + if pc.remove_imports { + pc.AddAction(NewRemoveImportsAction()) + pc.AddAction(NewWriteFileAction(pc.outfile)) + pc.dispatchActions(ofile) + return + } if pc.remove_codesign { pc.AddAction(NewRemoveCodeSignatureAction()) } diff --git a/macho-go/pkg/ios/macho/dyld_info.go b/macho-go/pkg/ios/macho/dyld_info.go index bb24a91..5053f36 100644 --- a/macho-go/pkg/ios/macho/dyld_info.go +++ b/macho-go/pkg/ios/macho/dyld_info.go @@ -14,6 +14,7 @@ import ( type ImportSymbol struct { name string + typ string dylib string segment uint32 segment_offset uint32 @@ -29,6 +30,10 @@ func (sym *ImportSymbol) Name() string { return sym.name } +func (sym *ImportSymbol) Type() string { + return sym.typ +} + func (sym *ImportSymbol) Dylib() string { return sym.dylib } @@ -45,28 +50,22 @@ func (sym *ImportSymbol) Stub() uint64 { return sym.stub } -func (mc *MachoContext) CollectLazyBindSymbols() []*ImportSymbol { +func (mc *MachoContext) CollectBindSymbols() []*ImportSymbol { if mc.dyldinfo == nil { - return mc.CollectLazyBindSymbolsModern() + return mc.CollectBindSymbolsModern() } else { - return mc.CollectLazyBindSymbolsLegacy() + return mc.CollectBindSymbolsLegacy() } } // New convention using LC_DYLD_CHAINED_FIXUPS -func (mc *MachoContext) CollectLazyBindSymbolsModern() []*ImportSymbol { +func (mc *MachoContext) CollectBindSymbolsModern() []*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 } @@ -98,34 +97,62 @@ func (mc *MachoContext) CollectLazyBindSymbolsModern() []*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 +func (mc *MachoContext) CollectBindSymbolsLegacy() []*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)) + noLazy := (func() []*ImportSymbol { + start := mc.dyldinfo.bind_off + size := mc.dyldinfo.bind_size + end := start + size + buf := bytes.NewBuffer(mc.buf[start:end]) + return mc.readBindStream(buf, "no lazy") + })() + + lazy := (func() []*ImportSymbol { + start := mc.dyldinfo.lazy_bind_off + size := mc.dyldinfo.lazy_bind_size + end := start + size + buf := bytes.NewBuffer(mc.buf[start:end]) + return mc.readBindStream(buf, "lazy") + })() + + weak := (func() []*ImportSymbol { + start := mc.dyldinfo.weak_bind_off + size := mc.dyldinfo.weak_bind_size + end := start + size + buf := bytes.NewBuffer(mc.buf[start:end]) + return mc.readBindStream(buf, "weak") + })() + + var symbols []*ImportSymbol + symbols = append(symbols, noLazy...) + symbols = append(symbols, lazy...) + symbols = append(symbols, weak...) + + return symbols +} + +func (mc *MachoContext) readBindStream(buf *bytes.Buffer, typ string) []*ImportSymbol { + size := buf.Len() 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 + + offset := uint(0) lastop_done := false for offset < uint(size) { d, _ := buf.ReadByte() @@ -151,6 +178,7 @@ func (mc *MachoContext) CollectLazyBindSymbolsLegacy() []*ImportSymbol { case BIND_OPCODE_DO_BIND: if sym.name != "" { new_sym := sym + new_sym.typ = typ syms = append(syms, &new_sym) // fmt.Printf("Offset 0x%x: Symbol %+v\n", symoffset, sym) @@ -187,6 +215,9 @@ func (mc *MachoContext) CollectLazyBindSymbolsLegacy() []*ImportSymbol { case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: sym.name, _ = buf.ReadString(0) + // ReadString input the 0x00 byte to buffer + // while we are string so we can remove that + sym.name = sym.name[:len(sym.name)-1] offset += uint(len(sym.name)) break diff --git a/macho-go/pkg/ios/macho/edit.go b/macho-go/pkg/ios/macho/edit.go index 0e2c741..1938c9a 100644 --- a/macho-go/pkg/ios/macho/edit.go +++ b/macho-go/pkg/ios/macho/edit.go @@ -267,3 +267,51 @@ func (mc *MachoContext) UpdateHeaderRemoveLcmd(size uint32) { mc.file.WriteAt(mc.header.Serialize(mc), 0) } } + +func (mc *MachoContext) RemoveBindSymbols() { + if !mc.WriteEnabled() { + return + } + + if mc.dyldinfo == nil { + mc.removeBindSymbolsModern() + } else { + mc.removeBindSymbolsLegacy() + } +} + +func (mc *MachoContext) removeBindSymbolsModern() {} + +func (mc *MachoContext) removeBindSymbolsLegacy() { + start := mc.dyldinfo.lazy_bind_off + size := mc.dyldinfo.lazy_bind_size + // set lazy opcodes to 0x00 == DO_BIND + // but no symbol state to bind + mc.file.WriteAt(make([]byte, size), int64(start)) + + calculateHash := func(name string) uint32 { + var h uint32 = 0x811c9dc5 + for _, s := range name { + h ^= uint32(s) + h *= 0x01000193 + } + return h + } + + // 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; uint64_t address;};") + fmt.Println("struct imported_symbol imported_table[] = {") + count := 0 + for _, symbol := range mc.CollectBindSymbols() { + if symbol.Type() != "lazy" { + continue + } + count += 1 + dylib_hash := calculateHash(symbol.Dylib()) + fmt.Printf("{\"%s\", \"%s\", 0x%x, 0x%x},\n", + symbol.Name(), symbol.Dylib(), dylib_hash, symbol.Address()); + } + fmt.Println("};") + fmt.Printf("uint32_t nimports = %d;\n", count); +}