Compare commits
35 Commits
0.1.0-alph
...
x86_64
Author | SHA1 | Date | |
---|---|---|---|
07f361d8ac | |||
263596b1a1 | |||
7a6a41b4d8 | |||
0a070941b1 | |||
4dea12dd9e | |||
011abfd8db | |||
67157c91ef | |||
26d002cdb1 | |||
c805fc56b3 | |||
1b3eb467a7 | |||
54f61f36ab | |||
f88861a87e | |||
4016abf40d | |||
4ee62a2d93 | |||
6815ea6556 | |||
557eed0254 | |||
eccd0bf845 | |||
ed2f09348e | |||
2eede8f9b2 | |||
b8d8343835 | |||
e15d1e8d6f | |||
a2f9ca82e7 | |||
693c2b6c95 | |||
7eb43a35fb | |||
f5144fec4f | |||
ebd52d9acb | |||
3aaa85520e | |||
ed793b1df6 | |||
9f54720e7b | |||
fdccdca8a0 | |||
e2c75bf718 | |||
a257286d2e | |||
91e5b1f6b3 | |||
887c53ed44 | |||
88bb0aa09d |
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
[submodule "apple/dyld"]
|
||||
path = apple/dyld
|
||||
url = git@github.com:apple-oss-distributions/dyld.git
|
||||
[submodule "apple/objc4"]
|
||||
path = apple/objc4
|
||||
url = git@github.com:apple-oss-distributions/objc4.git
|
1
apple/dyld
Submodule
1
apple/dyld
Submodule
Submodule apple/dyld added at c8a445f88f
1
apple/objc4
Submodule
1
apple/objc4
Submodule
Submodule apple/objc4 added at 689525d556
29
docs/links.md
Normal file
29
docs/links.md
Normal file
@ -0,0 +1,29 @@
|
||||
## Direct References in Apple
|
||||
|
||||
[dyld_info](https://github.com/apple-oss-distributions/dyld/blob/main/other-tools/dyld_info.cpp)
|
||||
|
||||
[macho layout](https://github.com/apple-oss-distributions/dyld/blob/main/common/MachOLayout.cpp#L714)
|
||||
|
||||
[mod init func](https://github.com/apple-oss-distributions/dyld/blob/c8a445f88f9fc1713db34674e79b00e30723e79d/dyld/Loader.cpp#L1879)
|
||||
|
||||
[dlsym](https://github.com/apple-oss-distributions/dyld/blob/c8a445f88f9fc1713db34674e79b00e30723e79d/common/MachOLoaded.cpp#L263)
|
||||
|
||||
[dyld_all_image_infos](https://opensource.apple.com/source/dyld/dyld-195.6/include/mach-o/dyld_images.h.auto.html)
|
||||
|
||||
[dyld env vars](https://stackoverflow.com/questions/51504439/what-environment-variables-control-dyld)
|
||||
|
||||
## Blogposts
|
||||
|
||||
[Basic Mach-O (Old)](https://www.m4b.io/reverse/engineering/mach/binaries/2015/03/29/mach-binaries.html)
|
||||
|
||||
[Basic Mach-O memory loader](https://blog.xpnsec.com/building-a-mach-o-memory-loader-part-1/)
|
||||
|
||||
[My Mach-O posts](https://blog.efiens.com/post/luibo/osx/)
|
||||
|
||||
## Tools
|
||||
|
||||
[llios](https://github.com/qyang-nj/llios)
|
||||
|
||||
[fishhook](https://github.com/facebook/fishhook/blob/main/fishhook.c)
|
||||
|
||||
[blacktop/go-macho](https://github.com/blacktop/go-macho)
|
@ -5,10 +5,11 @@ go 1.17
|
||||
require (
|
||||
github.com/alecthomas/kong v0.2.16
|
||||
github.com/sirupsen/logrus v1.8.0
|
||||
google.golang.org/protobuf v1.26.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/magefile/mage v1.10.0 // indirect
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
|
||||
|
@ -3,6 +3,8 @@ github.com/alecthomas/kong v0.2.16/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QL
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g=
|
||||
@ -22,3 +24,5 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
type removeClassicSymbol struct{}
|
||||
|
||||
func (action *removeClassicSymbol) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveClassicSymbol()
|
||||
mf.Context().RemoveSymbolTable()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
20
macho-go/internal/wrapper/action/remove_exports.go
Normal file
20
macho-go/internal/wrapper/action/remove_exports.go
Normal file
@ -0,0 +1,20 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type removeExports struct{}
|
||||
|
||||
func (action *removeExports) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveExportTrie()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *removeExports) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewRemoveExportsAction() *removeExports {
|
||||
return &removeExports{}
|
||||
}
|
@ -8,8 +8,6 @@ type removeImports struct{}
|
||||
|
||||
func (action *removeImports) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveBindSymbols()
|
||||
mf.Context().RemoveSymbolTable()
|
||||
mf.Context().RemoveExportTrie()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -20,4 +18,3 @@ func (action *removeImports) withFat(ff *FatFile) error {
|
||||
func NewRemoveImportsAction() *removeImports {
|
||||
return &removeImports{}
|
||||
}
|
||||
|
||||
|
25
macho-go/internal/wrapper/action/rewrite_imports.go
Normal file
25
macho-go/internal/wrapper/action/rewrite_imports.go
Normal file
@ -0,0 +1,25 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type rewriteImports struct {
|
||||
symbols []string
|
||||
}
|
||||
|
||||
func (action *rewriteImports) withMacho(mf *MachoFile) error {
|
||||
mc := mf.Context()
|
||||
mc.RewriteImportsTable(action.symbols)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *rewriteImports) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewRewriteImportsWithKeepSymbolsAction(symbols []string) *rewriteImports {
|
||||
return &rewriteImports{
|
||||
symbols,
|
||||
}
|
||||
}
|
152
macho-go/internal/wrapper/action/save_imports.go
Normal file
152
macho-go/internal/wrapper/action/save_imports.go
Normal file
@ -0,0 +1,152 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
// log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (action *saveImports) saveToInfo(mf *MachoFile) error {
|
||||
// calculateHash := func(name string) uint32 {
|
||||
// var h uint32 = 0x811c9dc5
|
||||
// for _, s := range name {
|
||||
// h ^= uint32(s)
|
||||
// h *= 0x01000193
|
||||
// }
|
||||
// return h
|
||||
// }
|
||||
|
||||
mc := mf.Context()
|
||||
// symbols_storage := []*protomodel.MachoInfo_AllImportedSymbols{}
|
||||
symbols_raw := mc.CollectBindSymbols()
|
||||
sort.Slice(symbols_raw, func(i, j int) bool {
|
||||
orderedByLibrary := symbols_raw[i].Dylib() < symbols_raw[j].Dylib()
|
||||
if symbols_raw[i].Dylib() == symbols_raw[j].Dylib() {
|
||||
orderedBySymbol := symbols_raw[i].Name() < symbols_raw[j].Name()
|
||||
return orderedBySymbol
|
||||
}
|
||||
return orderedByLibrary
|
||||
})
|
||||
|
||||
libs := []string{}
|
||||
symbols := []string{}
|
||||
tables := []*protomodel.MachoInfo_LibraryImportedSymbols{}
|
||||
|
||||
var current_table *protomodel.MachoInfo_LibraryImportedSymbols
|
||||
|
||||
current_lib := ""
|
||||
current_symbol := ""
|
||||
|
||||
current_lib_idx := -1
|
||||
current_symbol_idx := -1
|
||||
|
||||
intlSymbols := []string{}
|
||||
for _, symbol := range symbols_raw {
|
||||
if symbol.Dylib() == "/usr/local/opt/gettext/lib/libintl.8.dylib" {
|
||||
intlSymbols = append(intlSymbols, symbol.Name())
|
||||
}
|
||||
}
|
||||
action.keepSymbols = append(action.keepSymbols, intlSymbols...)
|
||||
|
||||
// now we expect everything is sorted and easier to build strings tables
|
||||
// this is not fully optimized, there can be repeated symbol name in different libraries
|
||||
for _, symbol := range symbols_raw {
|
||||
if symbol.Type() != "lazy" {
|
||||
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]
|
||||
}
|
||||
|
||||
if symbol.Name() != name {
|
||||
continue
|
||||
}
|
||||
if lib == "" || lib == symbol.Dylib() {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
|
||||
// dylib_hash := calculateHash(symbol.Dylib())
|
||||
seg := mc.Segments()[symbol.Segment()]
|
||||
|
||||
var offset uint64
|
||||
|
||||
if symbol.Address() >= seg.Vmaddr() {
|
||||
// this is virtual address
|
||||
offset = symbol.Address() - seg.Vmaddr()
|
||||
} else {
|
||||
// this is file address
|
||||
offset = symbol.Address() - seg.Fileoff()
|
||||
}
|
||||
|
||||
if current_lib != symbol.Dylib() {
|
||||
current_lib_idx += len(current_lib) + 1
|
||||
current_lib = symbol.Dylib()
|
||||
libs = append(libs, symbol.Dylib())
|
||||
tables = append(tables, &protomodel.MachoInfo_LibraryImportedSymbols{
|
||||
LibIndex: uint32(current_lib_idx),
|
||||
Nsymbols: 0,
|
||||
Symbols: []*protomodel.MachoInfo_SymbolTable{},
|
||||
})
|
||||
current_table = tables[len(tables)-1]
|
||||
}
|
||||
if current_symbol != symbol.Name() {
|
||||
current_symbol_idx += len(current_symbol) + 1
|
||||
current_symbol = symbol.Name()
|
||||
symbols = append(symbols, symbol.Name())
|
||||
}
|
||||
current_table.Nsymbols += 1
|
||||
current_table.Symbols = append(current_table.Symbols, &protomodel.MachoInfo_SymbolTable{
|
||||
SymbolIndex: uint32(current_symbol_idx),
|
||||
SegmentIndex: symbol.Segment(),
|
||||
Offset: uint32(offset),
|
||||
})
|
||||
}
|
||||
mf.Info().Symbols = &protomodel.MachoInfo_AllImportedSymbols{
|
||||
Libs: libs,
|
||||
Symbols: symbols,
|
||||
Tables: tables,
|
||||
}
|
||||
|
||||
mf.Info().Main = mc.Main()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *saveImports) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewSaveImportsAction(keepSymbols []string) *saveImports {
|
||||
return &saveImports{
|
||||
keepSymbols,
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
// "strings"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -63,6 +65,10 @@ func Cli() {
|
||||
arg := cli.Lipo.Join
|
||||
fat.FatJoin(arg.Macho, arg.Out)
|
||||
return
|
||||
} else if command == "bcell2header" {
|
||||
arg := cli.BcellToHeader
|
||||
bcell2header(arg.Bcell, arg.Out)
|
||||
return
|
||||
}
|
||||
|
||||
var pc ProgramContext
|
||||
@ -73,7 +79,7 @@ func Cli() {
|
||||
arg := cli.Wrap
|
||||
ofile = NewOFile(arg.OFile)
|
||||
pc.remove_codesign = true
|
||||
pc.strip_init_pointers = true
|
||||
pc.remove_inits = true
|
||||
pc.dylib_to_add = []string{"@rpath/bcell.framework/bcell"}
|
||||
pc.rpath_to_add = []string{"@executable_path/Frameworks"}
|
||||
pc.outfile = arg.Out
|
||||
@ -99,6 +105,30 @@ func Cli() {
|
||||
ofile = NewOFile(arg.OFile)
|
||||
pc.remove_imports = true
|
||||
pc.outfile = arg.Out
|
||||
pc.bcellfile = arg.Bcell
|
||||
|
||||
case "pepe":
|
||||
arg := cli.Pepe
|
||||
ofile = NewOFile(arg.OFile)
|
||||
if arg.FullRemoval {
|
||||
pc.remove_exports = true
|
||||
pc.remove_symbol_table = true
|
||||
pc.remove_imports = true
|
||||
pc.remove_inits = true
|
||||
pc.remove_codesign = true
|
||||
pc.remove_others = true
|
||||
}
|
||||
pc.remove_imports = arg.RemoveBindSymbols
|
||||
pc.remove_codesign = arg.RemoveCodeSign
|
||||
pc.remove_inits = arg.RemoveInitFunctions
|
||||
pc.remove_others = arg.RemoveOthers
|
||||
pc.remove_exports = arg.RemoveExports
|
||||
pc.remove_symbol_table = arg.RemoveSymbolTable
|
||||
pc.dylib_to_add = arg.Dylibs
|
||||
pc.rpath_to_add = arg.Rpath
|
||||
pc.outfile = arg.Out
|
||||
pc.bcellfile = arg.Bcell
|
||||
pc.symbols_keep = arg.KeepImports
|
||||
|
||||
default:
|
||||
return
|
||||
@ -165,6 +195,12 @@ func displayBcell(bfile string) {
|
||||
init_ptr.Value,
|
||||
)
|
||||
}
|
||||
fmt.Printf(" | Bind Symbols:\n")
|
||||
// for _, symbol := range info.Symbols {
|
||||
// lib := strings.Replace(symbol.Libname, "/System/Library/Frameworks/", "", 1)
|
||||
// fmt.Printf(" | %s offset=0x%x segmentID=0x%x\n", symbol.Name, symbol.Offset, symbol.Segment)
|
||||
// fmt.Printf(" | from=%s\n", lib)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,3 +230,76 @@ func resolveAddresses(dwarf string, load string, addresses []string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func bcell2header(bfile string, header string) {
|
||||
raw_data, err := ioutil.ReadFile(bfile)
|
||||
data := &protomodel.BcellFile{}
|
||||
err = proto.Unmarshal(raw_data, data)
|
||||
if err != nil {
|
||||
log.Panic("Invalid Protobuf<BcellFile> bcell.dat")
|
||||
}
|
||||
|
||||
f, err := os.Create(header)
|
||||
if err != nil {
|
||||
log.Panic("Cannot open header file for writing")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w := bufio.NewWriter(f)
|
||||
|
||||
// fmt.Printf("[+] User Config: %+v\n", data.BcellConfig)
|
||||
fmt.Fprintf(w, "#include<stdint.h>\n")
|
||||
fmt.Fprintf(w, "namespace bshield_data{\n")
|
||||
for arch, info := range data.MachoInfos {
|
||||
fmt.Fprintf(w, "const char* arch = \"%s\";\n", arch)
|
||||
fmt.Fprintf(w, "unsigned int pointer_size = %d;\n", info.PointerSize)
|
||||
fmt.Fprintf(w, "uint64_t image_base = 0x%x;\n", info.ImageBase)
|
||||
fmt.Fprintf(w, "uint64_t main = 0x%x;\n", info.Main)
|
||||
|
||||
fmt.Fprintf(w, "struct init_pointer {uint64_t offset; uint64_t value;};\n")
|
||||
fmt.Fprintf(w, "int num_init_pointers = %d;\n", len(info.InitPointers))
|
||||
fmt.Fprintf(w, "struct init_pointer init_pointers_offsets[] = {\n")
|
||||
for _, init_ptr := range info.InitPointers {
|
||||
fmt.Fprintf(w, " {0x%x, 0x%x},\n", init_ptr.Offset, init_ptr.Value)
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char libs[] =\n")
|
||||
for _, lib := range info.Symbols.Libs {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", lib)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char symbols[] =\n")
|
||||
for _, symbol := range info.Symbols.Symbols {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", symbol)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "// very compact symbol table,\n")
|
||||
fmt.Fprintf(w, "// [lib idx/*4 bytes*/, nsymbol/*4 byte*/]\n")
|
||||
fmt.Fprintf(w, "// repeat nsymbol times [name offset/*3 bytes*/, segment idx/**/, offset /*4 btyes*/]\n")
|
||||
fmt.Fprintf(w, "// name offset is 3 bytes because we don't think we should have a table size > 2^(3 * 8)\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t encoded_table[] = {\n")
|
||||
n_instructions := 0
|
||||
for i, table := range info.Symbols.Tables {
|
||||
fmt.Fprintf(w, " // %s\n", info.Symbols.Libs[i])
|
||||
fmt.Fprintf(w, " %d/*lib offset*/,\n", table.LibIndex)
|
||||
fmt.Fprintf(w, " %d/*nsymbols*/,\n", table.Nsymbols)
|
||||
n_instructions += 2
|
||||
for _, symbol := range table.Symbols {
|
||||
fmt.Fprintf(w, " %d, 0x%x,\n", (symbol.SymbolIndex<<8)|symbol.SegmentIndex, symbol.Offset)
|
||||
n_instructions += 2
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
fmt.Fprintf(w, "uint32_t n_instructions = %d;\n", n_instructions)
|
||||
}
|
||||
fmt.Fprintf(w, "}// namespace bshield_data\n")
|
||||
w.Flush()
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ type RemoveCodesignArgument struct {
|
||||
|
||||
type RemoveImportsArgument struct {
|
||||
Out string `short:"o" required name:"outfile" help:"Modified Mach-O/Fat binary output file" type:"path"`
|
||||
Bcell string `short:"b" required help:"bcell.dat output file" type:"path"`
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
}
|
||||
|
||||
@ -55,7 +56,28 @@ type LipoArgument struct {
|
||||
}
|
||||
|
||||
type PepeArgument struct {
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
Out string `short:"o" required name:"outfile" help:"Output file after transformation" type:"path"`
|
||||
Bcell string `short:"b" required help:"bcell.dat output file" type:"path"`
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
Dylibs []string `short:"l" help:"Add more LC_DYLIB"`
|
||||
Rpath []string `short:"r" help:"Add more LC_RPATH"`
|
||||
RemoveCodeSign bool `default:"false" negatable:"" help:"Remove LC_CODE_SIGNATURE"`
|
||||
RemoveExports bool `default:"false" negatable:"" help:"Clear the export table/trie"`
|
||||
RemoveSymbolTable bool `default:"false" negatable:"" help:"Remove LC_SYMTAB and LC_DYSYMTAB"`
|
||||
RemoveOthers bool `default:"false" negatable:"" help:"Remove LC_FUNCTION_STARTS, LC_DATA_IN_CODE, ..."`
|
||||
RemoveID bool `default:"false" negatable:"" help:"(TODO) Remove LC_ID_DYLIB"`
|
||||
RemoveInitFunctions bool `default:"false" name:"remove-inits" negatable:"" help:"Clear MOD_INIT_FUNC section"`
|
||||
RemoveBindSymbols bool `default:"false" name:"remove-imports" negatable:"" help:"Remove all bind symbols information"`
|
||||
RemoveObjCString bool `default:"false" negatable:"" help:"(TODO) Remove references, string litteral to Objctive-C strings"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type BcellToHeaderArgument struct {
|
||||
Out string `short:"o" required name:"outfile" help:"Header file name to output to" type:"path"`
|
||||
Bcell string `short:"b" required help:"Input bcell.dat file" type:"existingfile"`
|
||||
}
|
||||
|
||||
type Argument struct {
|
||||
@ -68,5 +90,14 @@ type Argument struct {
|
||||
DisplayBcell DisplayBcellArgument `cmd name:"display-bcell" help:"Display Protobuf<BcellFile> content"`
|
||||
Addr2Line Addr2LineArgument `cmd name:"addr2line" help:"Resolve crash address from DWARF file"`
|
||||
Lipo LipoArgument `cmd help:"split Fat binary or join Mach-O binares"`
|
||||
Pepe PepeArgument `cmd help:"split Fat binary or join Mach-O binares"`
|
||||
Pepe PepeArgument `cmd help:"custom command"`
|
||||
BcellToHeader BcellToHeaderArgument `cmd name:"bcell2header" help:"Build C header file from bcell file"`
|
||||
AddSection struct {
|
||||
Out string `short:"o" required name:"outfile" help:"Output file after transformation"`
|
||||
Data string `arg help:"Input data file to fill in this section, or null if not provided" type:"existingfile"`
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
Segment string `required help:"Segment name"`
|
||||
Section string `required help:"Section name"`
|
||||
Size int `required help:"Size of segment/section"`
|
||||
} `cmd:"" default:"false" help:"(TODO) Add a new segment with 1 section, segment starts at the end of last segment"`
|
||||
}
|
||||
|
@ -35,16 +35,16 @@ func (printer *InfoPrinter) Print() {
|
||||
}
|
||||
|
||||
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(),
|
||||
)
|
||||
}
|
||||
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("======")
|
||||
}
|
||||
|
@ -38,11 +38,15 @@ func (uc *UserConfig) Protomodel() *protomodel.Config {
|
||||
}
|
||||
|
||||
type ProgramContext struct {
|
||||
strip_init_pointers bool
|
||||
remove_inits bool
|
||||
remove_codesign bool
|
||||
remove_imports bool
|
||||
remove_others bool
|
||||
remove_exports bool
|
||||
remove_symbol_table bool
|
||||
dylib_to_add []string
|
||||
rpath_to_add []string
|
||||
symbols_keep []string
|
||||
|
||||
outfile string
|
||||
|
||||
@ -84,24 +88,28 @@ 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())
|
||||
}
|
||||
if pc.strip_init_pointers {
|
||||
if pc.remove_inits {
|
||||
pc.AddAction(NewSaveInitPointerAction())
|
||||
pc.AddAction(NewRemoveInitPointerAction())
|
||||
}
|
||||
ExperimentalFeature("Remove Unnecessary Info", func() {
|
||||
pc.AddAction(NewRemoveUnnecessaryInfoAction())
|
||||
})
|
||||
ExperimentalFeature("Remove Classic Symbol", func() {
|
||||
if pc.remove_imports {
|
||||
pc.AddAction(NewSaveImportsAction(pc.symbols_keep))
|
||||
pc.AddAction(NewRemoveImportsAction())
|
||||
pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep))
|
||||
}
|
||||
if pc.remove_symbol_table {
|
||||
pc.AddAction(NewRemoveClassicSymbolAction())
|
||||
}
|
||||
if pc.remove_exports {
|
||||
pc.AddAction(NewRemoveExportsAction())
|
||||
}
|
||||
ExperimentalFeature("Remove Unnecessary Info", func() {
|
||||
if pc.remove_others {
|
||||
pc.AddAction(NewRemoveUnnecessaryInfoAction())
|
||||
}
|
||||
})
|
||||
pc.AddAction(NewAddRpathAction(pc.rpath_to_add))
|
||||
pc.AddAction(NewAddDylibAction(pc.dylib_to_add))
|
||||
|
@ -4,9 +4,9 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
"unsafe"
|
||||
// "bufio"
|
||||
"io"
|
||||
"io"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
@ -19,16 +19,19 @@ import "C"
|
||||
|
||||
type ImportSymbol struct {
|
||||
name string
|
||||
typ string
|
||||
typ string
|
||||
dylib string
|
||||
segment uint32
|
||||
segment_offset uint32
|
||||
address uint64
|
||||
file_address uint64
|
||||
lib_ordinal uint32
|
||||
|
||||
// push number
|
||||
pnum uint32
|
||||
stub uint64
|
||||
|
||||
next int // only for LC_DYLD_CHAINED_FIXUPS
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Name() string {
|
||||
@ -43,6 +46,10 @@ func (sym *ImportSymbol) Dylib() string {
|
||||
return sym.dylib
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) LibOrdinal() uint32 {
|
||||
return sym.lib_ordinal
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Address() uint64 {
|
||||
return sym.address
|
||||
}
|
||||
@ -55,6 +62,10 @@ func (sym *ImportSymbol) Stub() uint64 {
|
||||
return sym.stub
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Segment() uint32 {
|
||||
return sym.segment
|
||||
}
|
||||
|
||||
func (mc *MachoContext) CollectBindSymbols() []*ImportSymbol {
|
||||
if mc.dyldinfo == nil {
|
||||
return mc.CollectBindSymbolsModern()
|
||||
@ -64,135 +75,142 @@ func (mc *MachoContext) CollectBindSymbols() []*ImportSymbol {
|
||||
}
|
||||
|
||||
func (mc *MachoContext) findSegmentIndexAt(address uint64) int {
|
||||
for i, segment := range mc.Segments() {
|
||||
if segment.Fileoff() <= address && segment.Fileoff() + segment.Filesize() > address {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
for i, segment := range mc.Segments() {
|
||||
if segment.Fileoff() <= address && segment.Fileoff()+segment.Filesize() > address {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// New convention using LC_DYLD_CHAINED_FIXUPS
|
||||
func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
start := mc.fixups.dataoff
|
||||
size := mc.fixups.datasize
|
||||
buf := mc.buf[start:start+size]
|
||||
start := mc.fixups.dataoff
|
||||
size := mc.fixups.datasize
|
||||
buf := mc.buf[start : start+size]
|
||||
|
||||
// all pointers used are based from this **buf**
|
||||
// until buf is freed, all pointers are valid
|
||||
// remember to copy before moving out
|
||||
header := (*C.uchar)(unsafe.Pointer(&buf[0]))
|
||||
imports_table := C.GetImportsTable(header);
|
||||
// all pointers used are based from this **buf**
|
||||
// until buf is freed, all pointers are valid
|
||||
// remember to copy before moving out
|
||||
header := (*C.uchar)(unsafe.Pointer(&buf[0]))
|
||||
imports_table := C.GetImportsTable(header)
|
||||
|
||||
// for i := 0; i < int(imports_table.size); i++ {
|
||||
// s := C.GetImportsAt(&imports_table, C.int(i))
|
||||
// name := C.GoString(s.name)
|
||||
// symbol.dylib := string(mc.dylibs[int(s.lib_ordinal)-1].name[:])
|
||||
// symbol_table = append(symbol_table, symbol)
|
||||
// fmt.Printf("id=%d lib=%s name=%s\n", i, dylib, name)
|
||||
// }
|
||||
// for i := 0; i < int(imports_table.size); i++ {
|
||||
// s := C.GetImportsAt(&imports_table, C.int(i))
|
||||
// name := C.GoString(s.name)
|
||||
// symbol.dylib := string(mc.dylibs[int(s.lib_ordinal)-1].name[:])
|
||||
// symbol_table = append(symbol_table, symbol)
|
||||
// fmt.Printf("id=%d lib=%s name=%s\n", i, dylib, name)
|
||||
// }
|
||||
|
||||
var syms []*ImportSymbol
|
||||
var sym ImportSymbol
|
||||
|
||||
segment_i := 0
|
||||
for {
|
||||
var fix C.struct_SegmentFix
|
||||
fix_ptr := (*C.struct_SegmentFix)(unsafe.Pointer(&fix))
|
||||
status := int(C.GetSegmentFixAt(header, C.uint(segment_i), fix_ptr))
|
||||
segment_i += 1
|
||||
if status == 2 {
|
||||
break;
|
||||
}
|
||||
if status == 3 {
|
||||
continue
|
||||
}
|
||||
// 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))
|
||||
for page_i := 0; page_i < int(fix.page_count); page_i++ {
|
||||
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
||||
segment_i := 0
|
||||
for {
|
||||
var fix C.struct_SegmentFix
|
||||
fix_ptr := (*C.struct_SegmentFix)(unsafe.Pointer(&fix))
|
||||
status := int(C.GetSegmentFixAt(header, C.uint(segment_i), fix_ptr))
|
||||
segment_i += 1
|
||||
if status == 2 {
|
||||
break
|
||||
}
|
||||
if status == 3 {
|
||||
continue
|
||||
}
|
||||
// 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)
|
||||
for page_i := 0; page_i < int(fix.page_count); page_i++ {
|
||||
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
||||
|
||||
address := int64(fix.segment) + int64(pages[page_i])
|
||||
mc.file.Seek(address, io.SeekStart)
|
||||
address := int64(fix.segment) + int64(pages[page_i])
|
||||
reader.Seek(address, io.SeekStart)
|
||||
|
||||
code := make([]byte, 8)
|
||||
code := make([]byte, 8)
|
||||
|
||||
for {
|
||||
mc.file.Read(code)
|
||||
v := binary.LittleEndian.Uint64(code)
|
||||
for {
|
||||
reader.Read(code)
|
||||
v := mc.byteorder.Uint64(code)
|
||||
|
||||
var bind C.int
|
||||
var ret1 C.ulonglong
|
||||
var ret2 C.ulonglong
|
||||
next := C.ParseFixValue(C.int(fix.format), C.ulonglong(v),
|
||||
&bind, &ret1, &ret2)
|
||||
var bind C.int
|
||||
var ret1 C.ulonglong
|
||||
var ret2 C.ulonglong
|
||||
next := C.ParseFixValue(C.int(fix.format), C.ulonglong(v),
|
||||
&bind, &ret1, &ret2)
|
||||
|
||||
if bind == 1 {
|
||||
s := C.GetImportsAt(&imports_table, C.int(ret1))
|
||||
name := C.GoString(s.name)
|
||||
dylib := string(mc.dylibs[int(s.lib_ordinal)-1].name[:])
|
||||
if bind == 1 {
|
||||
s := C.GetImportsAt(&imports_table, C.int(ret1))
|
||||
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
|
||||
sym.dylib = dylib
|
||||
sym.typ = "lazy"
|
||||
sym.address = uint64(address)
|
||||
sym.name = name
|
||||
sym.dylib = dylib
|
||||
sym.typ = "lazy"
|
||||
sym.lib_ordinal = uint32(s.lib_ordinal)
|
||||
|
||||
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
||||
sym.file_address = uint64(address)
|
||||
new_sym := sym
|
||||
syms = append(syms, &new_sym)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
}
|
||||
}
|
||||
if int(next) == 0 {
|
||||
break
|
||||
}
|
||||
// because the pointer move up 8 bytes already so we minus 8
|
||||
address += int64(next * 4)
|
||||
reader.Seek(int64(next*4)-8, io.SeekCurrent)
|
||||
}
|
||||
reader.Seek(0, io.SeekStart)
|
||||
}
|
||||
}
|
||||
return syms
|
||||
}
|
||||
|
||||
// Old convention using LC_DYLD_INFO_ONLY section and bytecode runner
|
||||
func (mc *MachoContext) CollectBindSymbolsLegacy() []*ImportSymbol {
|
||||
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")
|
||||
})()
|
||||
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")
|
||||
})()
|
||||
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")
|
||||
})()
|
||||
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...)
|
||||
var symbols []*ImportSymbol
|
||||
symbols = append(symbols, noLazy...)
|
||||
symbols = append(symbols, lazy...)
|
||||
symbols = append(symbols, weak...)
|
||||
|
||||
return symbols
|
||||
return symbols
|
||||
}
|
||||
|
||||
func (mc *MachoContext) readBindStream(buf *bytes.Buffer, typ string) []*ImportSymbol {
|
||||
size := buf.Len()
|
||||
size := buf.Len()
|
||||
if size == 0 {
|
||||
return []*ImportSymbol{}
|
||||
}
|
||||
@ -226,7 +244,7 @@ func (mc *MachoContext) readBindStream(buf *bytes.Buffer, typ string) []*ImportS
|
||||
case BIND_OPCODE_DO_BIND:
|
||||
if sym.name != "" {
|
||||
new_sym := sym
|
||||
new_sym.typ = typ
|
||||
new_sym.typ = typ
|
||||
syms = append(syms, &new_sym)
|
||||
// fmt.Printf("Offset 0x%x: Symbol %+v\n", symoffset, sym)
|
||||
|
||||
@ -235,7 +253,7 @@ func (mc *MachoContext) readBindStream(buf *bytes.Buffer, typ string) []*ImportS
|
||||
"symbol": sym.name,
|
||||
}).Trace("Bind")
|
||||
sym.name = ""
|
||||
sym.address += 8
|
||||
sym.address += 8
|
||||
}
|
||||
offset += 1
|
||||
break
|
||||
@ -264,9 +282,9 @@ func (mc *MachoContext) readBindStream(buf *bytes.Buffer, typ string) []*ImportS
|
||||
|
||||
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]
|
||||
// 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
|
||||
|
||||
@ -290,12 +308,12 @@ func (mc *MachoContext) readBindStream(buf *bytes.Buffer, typ string) []*ImportS
|
||||
offset += br
|
||||
break
|
||||
|
||||
case BIND_OPCODE_SET_TYPE_IMM:
|
||||
fmt.Println("// symbol type", imm)
|
||||
break
|
||||
case BIND_OPCODE_SET_TYPE_IMM:
|
||||
fmt.Println("// symbol type", imm)
|
||||
break
|
||||
|
||||
default:
|
||||
fmt.Println("BIND OPCODE NOT SUPPORTED", op, imm)
|
||||
fmt.Println("BIND OPCODE NOT SUPPORTED", op, imm)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,23 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
// #include "fixups.h"
|
||||
import "C"
|
||||
|
||||
func rewriteLoadcommandsWithoutCodesignature(mc *MachoContext) {
|
||||
if mc.Is64bit() {
|
||||
mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
||||
@ -185,10 +194,6 @@ func (mc *MachoContext) RemoveUnnecessaryInfo() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RemoveClassicSymbol() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddLoadCmd(lcmd LoadCommand) {
|
||||
var offset uint64
|
||||
payload := lcmd.Serialize(mc)
|
||||
@ -270,140 +275,474 @@ func (mc *MachoContext) UpdateHeaderRemoveLcmd(size uint32) {
|
||||
|
||||
func (mc *MachoContext) RemoveBindSymbols() {
|
||||
if !mc.WriteEnabled() {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
if mc.dyldinfo == nil {
|
||||
mc.removeBindSymbolsModern()
|
||||
} else {
|
||||
mc.removeBindSymbolsLegacy()
|
||||
}
|
||||
// Objective-C stub replaces main which can only appears in executable
|
||||
if mc.Header().IsExecutable() {
|
||||
mc.ReworkForObjc()
|
||||
}
|
||||
|
||||
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
|
||||
for _, symbol := range mc.CollectBindSymbols() {
|
||||
if symbol.Type() != "lazy" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 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("struct imported_symbol imported_table[] = {")
|
||||
count := 0
|
||||
for _, symbol := range mc.CollectBindSymbols() {
|
||||
if symbol.Type() != "lazy" {
|
||||
continue
|
||||
}
|
||||
count += 1
|
||||
dylib_hash := calculateHash(symbol.Dylib())
|
||||
seg := mc.segments[symbol.segment]
|
||||
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
|
||||
|
||||
var offset uint64
|
||||
|
||||
if symbol.address >= seg.Vmaddr() {
|
||||
// this is virtual address
|
||||
offset = symbol.address - seg.Vmaddr()
|
||||
} else {
|
||||
// this is file address
|
||||
offset = symbol.address - seg.Fileoff()
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
fmt.Println("};")
|
||||
fmt.Printf("uint32_t nimports = %d;\n", count);
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *MachoContext) removeBindSymbolsModern() {
|
||||
// we don't mess up the chain
|
||||
// we clear the imports table, and the raw opcodes
|
||||
// clearing imports table disables static analysis
|
||||
// clearing opcodes forces runtime manual mapping
|
||||
// we don't mess up the chain
|
||||
// we clear the imports table, and the raw opcodes
|
||||
// clearing imports table disables static analysis
|
||||
// clearing opcodes forces runtime manual mapping
|
||||
|
||||
// imports item are defined by mc.fixups.imports_format
|
||||
// basic case is dyld_chained_import, 4 bytes
|
||||
// imports item are defined by mc.fixups.imports_format
|
||||
// basic case is dyld_chained_import, 4 bytes
|
||||
|
||||
start := mc.fixups.dataoff
|
||||
size := mc.fixups.datasize
|
||||
fixups := new(Fixups)
|
||||
fixups.Deserialize(mc, mc.buf[start:start+size])
|
||||
start := mc.fixups.dataoff
|
||||
size := mc.fixups.datasize
|
||||
fixups := new(Fixups)
|
||||
fixups.Deserialize(mc, mc.buf[start:start+size])
|
||||
|
||||
start = mc.fixups.dataoff + fixups.imports_offset
|
||||
size = fixups.imports_count * 4
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), int64(start))
|
||||
start = mc.fixups.dataoff + fixups.imports_offset
|
||||
size = fixups.imports_count * 4
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), int64(start))
|
||||
|
||||
// string reference are at the end of this section
|
||||
start = mc.fixups.dataoff + fixups.symbols_offset
|
||||
size = mc.fixups.Datasize() - fixups.symbols_offset
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), int64(start))
|
||||
// string reference are at the end of this section
|
||||
start = mc.fixups.dataoff + fixups.symbols_offset
|
||||
size = mc.fixups.Datasize() - fixups.symbols_offset
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), int64(start))
|
||||
|
||||
fixups.imports_count = 0
|
||||
mc.file.WriteAt(fixups.Serialize(mc), int64(mc.fixups.dataoff))
|
||||
fixups.imports_count = 0
|
||||
mc.file.WriteAt(fixups.Serialize(mc), int64(mc.fixups.dataoff))
|
||||
}
|
||||
|
||||
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))
|
||||
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))
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RemoveSymbolTable() {
|
||||
// try to remove symtab and dysymtab
|
||||
mc.removeSymtabCommand()
|
||||
mc.removeDySymtabCommand()
|
||||
}
|
||||
func (mc *MachoContext) ReworkForObjc() {
|
||||
text_start := 0
|
||||
data_end := 0
|
||||
lc_main_offset := int64(0)
|
||||
|
||||
func (mc *MachoContext) removeSymtabCommand() {
|
||||
ptr := int64(0)
|
||||
ptr := int64(0)
|
||||
if mc.Is64bit() {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
||||
} else {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size), io.SeekStart)
|
||||
}
|
||||
|
||||
var symtab_fix *Symtab
|
||||
for _, cmd := range mc.commands {
|
||||
if cmd.Cmd() != LC_SYMTAB {
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
if cmd.Cmd() == LC_MAIN {
|
||||
lc_main_offset = ptr + 8
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
continue
|
||||
}
|
||||
var symtab = cmd.(*Symtab)
|
||||
symtab_fix = symtab
|
||||
if cmd.Cmd() != LC_SEGMENT_64 {
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
continue
|
||||
}
|
||||
var segment = cmd.(*Segment64)
|
||||
|
||||
// erase strings referenced
|
||||
start := int64(symtab_fix.stroff)
|
||||
size := symtab_fix.strsize
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
|
||||
section_ptr := ptr + 0x40 + 8
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__text")) == 0 {
|
||||
text_start = int(section.Offset())
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__init_offsets")) == 0 {
|
||||
// mc.file.WriteAt([]byte("__init_offsetx"), section_ptr)
|
||||
// edit flags to not S_MOD_INIT_FUNC
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0}, section_ptr+0x40)
|
||||
}
|
||||
section_ptr += 16*2 + 8*2 + 4*8
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA_CONST")) == 0 {
|
||||
section_ptr := ptr + 0x40 + 8
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classlist")) == 0 {
|
||||
mc.file.WriteAt([]byte("__objc_classbruh"), section_ptr)
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_nlclslist")) == 0 {
|
||||
mc.file.WriteAt([]byte("__objc_nlclsbruh"), section_ptr)
|
||||
}
|
||||
section_ptr += 16*2 + 8*2 + 4*8
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA")) == 0 {
|
||||
// end of __DATA segment, should have enough space for a pointer
|
||||
|
||||
// erase nlist64 symbol items
|
||||
start = int64(symtab_fix.symoff)
|
||||
size = symtab_fix.nsyms * 16
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
// __bss section is dynamically allocated at the end to or something, hmmge
|
||||
// assume that it order correctly, which it should if compiled and not modified
|
||||
// each section has their addr field which we can use that with segment virtual address
|
||||
// to calculate the offset of the last section from segment starts
|
||||
// then use the size of section to calculate the end of segment in file
|
||||
sections := segment.Sections()
|
||||
last := sections[len(sections)-1]
|
||||
data_end = int(last.Addr() - segment.Vmaddr() + segment.Fileoff() + last.Size())
|
||||
}
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
}
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
|
||||
symtab_fix.symoff = 0
|
||||
symtab_fix.nsyms = 0
|
||||
symtab_fix.stroff = 0
|
||||
symtab_fix.strsize = 0
|
||||
// dummy value past the end of __DATA segment (logical size),
|
||||
// its physical size is still a page
|
||||
// mc.file.WriteAt([]byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, int64(0x81d8))
|
||||
|
||||
// we use 2 registers, x8 x9
|
||||
// stack values:
|
||||
// [ return address, header, argc, argv, env, apple ]
|
||||
// we need to store the return address, and parameters passed to main
|
||||
// we also store our header address to not calculate many times
|
||||
|
||||
// must expand stack to store arguments passed
|
||||
// must use newly allocated stack region
|
||||
// must save return address
|
||||
// must recover stack to same value before calling main
|
||||
// must recover link register before calling main
|
||||
|
||||
// we use shorthand store/load multiple
|
||||
// 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
|
||||
|
||||
stp x30, x8, [sp], #-0x10
|
||||
stp x3, x2, [sp], #-0x10
|
||||
stp x1, x0, [sp], #-0x10
|
||||
|
||||
# custom intializer
|
||||
ldr x9, [x8]
|
||||
blr x9
|
||||
|
||||
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
|
||||
*/
|
||||
shellcode := []uint32{}
|
||||
ins_size_byte := 4
|
||||
main_offset := int(mc.entryoff)
|
||||
var shellcode_offset int
|
||||
|
||||
isArm := (mc.header.cputype & 0xff) == 12
|
||||
if isArm {
|
||||
shellcode = []uint32{
|
||||
0x10000008,
|
||||
0, // x9 = (offset end of __DATA) - (offset shellcode)
|
||||
0x8B090108,
|
||||
0xA8BF23FE,
|
||||
0xA8BF0BE3,
|
||||
0xA8BF03E1,
|
||||
0xF9400109,
|
||||
0xD63F0120,
|
||||
0xA9C103E1,
|
||||
0xA9C10BE3,
|
||||
0xA9C123FE,
|
||||
0xF9400509,
|
||||
0xD61F0120,
|
||||
}
|
||||
|
||||
shellcode_offset = text_start - (ins_size_byte * len(shellcode))
|
||||
|
||||
encode_movz := func(v int) uint32 {
|
||||
return uint32(uint32(v)<<5 | uint32(0x694)<<21 | uint32(0x09))
|
||||
}
|
||||
|
||||
// movz_shellcode_offset := encode_movz(shellcode_offset)
|
||||
// movz_main_offset := encode_movz(main_offset)
|
||||
// movz_data_end_offset := encode_movz(data_end)
|
||||
movz_calculate_offset := encode_movz(data_end - shellcode_offset)
|
||||
|
||||
shellcode[1] = movz_calculate_offset
|
||||
// shellcode[10] = movz_data_end_offset
|
||||
// shellcode[19] = movz_main_offset
|
||||
|
||||
fmt.Printf("// shellcode_offset=%x\n", shellcode_offset)
|
||||
fmt.Printf("// main_offset=%x\n", main_offset)
|
||||
fmt.Printf("// data_end=%x\n", data_end)
|
||||
fmt.Printf("// movz_calculate_offset=%x\n", movz_calculate_offset)
|
||||
// fmt.Printf("// movz_shellcode_offset=%x\n", movz_shellcode_offset)
|
||||
// fmt.Printf("// movz_main_offset=%x\n", movz_main_offset)
|
||||
// fmt.Printf("// movz_data_end_offset=%x\n", movz_data_end_offset)
|
||||
fmt.Printf("// lc_main_offset=%x\n", lc_main_offset)
|
||||
} else {
|
||||
shellcode_start := []uint8{
|
||||
0x4c, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x49, 0xC7, 0xC1,
|
||||
}
|
||||
|
||||
shellcode_end := []uint8{
|
||||
0x4d, 0x01, 0xc8,
|
||||
0x57,
|
||||
0x56,
|
||||
0x52,
|
||||
0x51,
|
||||
0x41, 0x50,
|
||||
0x4d, 0x8b, 0x08,
|
||||
0x41, 0xff, 0xd1,
|
||||
0x41,
|
||||
0x58,
|
||||
0x59,
|
||||
0x5a,
|
||||
0x5e,
|
||||
0x5f, 0x4d, 0x8b, 0x48, 0x08,
|
||||
0x41, 0xff, 0xe1,
|
||||
// pad to %4
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
offset := []uint8{0x00, 0x00, 0x00, 0x00} // offset
|
||||
shellcode_size := len(shellcode_start) + len(offset) + len(shellcode_end)
|
||||
|
||||
// could use buffer encoding, but for correctness,
|
||||
// we do this by hand
|
||||
encode_movz := func(v int) {
|
||||
for i := 0; i < 4; i++ {
|
||||
offset[i] = uint8(v >> (i * 8))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ┌─────────────────┐
|
||||
// │ │
|
||||
// shellcode starts ────┼─────────────────┼───── │ │instruction
|
||||
// │ │ │ │fetch RIP size
|
||||
// RIP returns ────┼─────────────────┼───── ▲ │ │
|
||||
// │ │ │ │
|
||||
// │ │ │ │ shellcode length
|
||||
// shellcode ends │ │ │ offset │
|
||||
// __text ────┼─────────────────┼───── │ range │
|
||||
// │ │ │ │ __DATA ends - __text
|
||||
// │ │ │ │
|
||||
// __DATA ends ────┼─────────────────┼───── ▼ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// └─────────────────┘
|
||||
encode_movz((data_end - text_start) + (shellcode_size - len(shellcode_start)))
|
||||
|
||||
shellcode_offset = text_start - shellcode_size
|
||||
shellcode_bytes := append(shellcode_start, offset...)
|
||||
shellcode_bytes = append(shellcode_bytes, shellcode_end...)
|
||||
|
||||
for i := 0; i < len(shellcode_bytes); i += 4 {
|
||||
val := 0
|
||||
// little endian
|
||||
val |= int(shellcode_bytes[i+0]) << 0
|
||||
val |= int(shellcode_bytes[i+1]) << 8
|
||||
val |= int(shellcode_bytes[i+2]) << 16
|
||||
val |= int(shellcode_bytes[i+3]) << 24
|
||||
shellcode = append(shellcode, uint32(val))
|
||||
}
|
||||
|
||||
fmt.Printf("// shellcode_offset=%x\n", shellcode_offset)
|
||||
fmt.Printf("// main_offset=%x\n", main_offset)
|
||||
fmt.Printf("// data_end=%x\n", data_end)
|
||||
fmt.Printf("// lc_main_offset=%x\n", lc_main_offset)
|
||||
}
|
||||
|
||||
offset := int64(shellcode_offset)
|
||||
{
|
||||
// fix main to point to our newly created shellcode
|
||||
bs := make([]byte, 8)
|
||||
mc.byteorder.PutUint64(bs, uint64(offset))
|
||||
mc.file.WriteAt(bs, int64(lc_main_offset))
|
||||
}
|
||||
|
||||
bs := make([]byte, 4)
|
||||
for _, ins := range shellcode {
|
||||
mc.byteorder.PutUint32(bs, ins)
|
||||
mc.file.WriteAt(bs, offset)
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RewriteImportsTable(keepSymbols []string) {
|
||||
allSymbols := mc.CollectBindSymbols()
|
||||
fixups, fixupsOffset := mc.Fixups()
|
||||
|
||||
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
|
||||
|
||||
// some symbols are annoyingly distributed by another library
|
||||
// dispite the name asking for X, the dyld loads a Y library
|
||||
// LC_DYLD_ID of Y is equal to X and dyld can resolve these symbols
|
||||
// because we do not search for library using LC_DYLD_ID,
|
||||
// paths are mistaken and will not resolve symbols
|
||||
//
|
||||
// the most common library that has this behavior is libintl
|
||||
// and fixing the resolver takes time, we temporarily ignore this library
|
||||
// and so we keep symbols referenced by libintl
|
||||
intlSymbols := []string{}
|
||||
for _, symbol := range allSymbols {
|
||||
if symbol.Dylib() == "/usr/local/opt/gettext/lib/libintl.8.dylib" {
|
||||
intlSymbols = append(intlSymbols, symbol.Name())
|
||||
}
|
||||
}
|
||||
keepSymbols = append(keepSymbols, intlSymbols...)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
fixups.imports_count = keepCount
|
||||
mc.file.WriteAt(fixups.Serialize(mc), int64(mc.fixups.dataoff))
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RemoveSymbolTable() {
|
||||
// try to remove symtab and dysymtab
|
||||
mc.removeSymtabCommand()
|
||||
mc.removeDySymtabCommand()
|
||||
}
|
||||
|
||||
func (mc *MachoContext) removeSymtabCommand() {
|
||||
ptr := int64(0)
|
||||
if mc.Is64bit() {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
||||
} else {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size), io.SeekStart)
|
||||
}
|
||||
|
||||
var symtab_fix *Symtab
|
||||
for _, cmd := range mc.commands {
|
||||
if cmd.Cmd() != LC_SYMTAB {
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
continue
|
||||
}
|
||||
var symtab = cmd.(*Symtab)
|
||||
symtab_fix = symtab
|
||||
|
||||
// erase strings referenced
|
||||
start := int64(symtab_fix.stroff)
|
||||
size := symtab_fix.strsize
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
|
||||
// erase nlist64 symbol items
|
||||
start = int64(symtab_fix.symoff)
|
||||
size = symtab_fix.nsyms * 16
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
|
||||
symtab_fix.symoff = 0
|
||||
symtab_fix.nsyms = 0
|
||||
symtab_fix.stroff = 0
|
||||
symtab_fix.strsize = 0
|
||||
mc.file.Seek(ptr, io.SeekStart)
|
||||
mc.file.Write(symtab_fix.Serialize(mc))
|
||||
break
|
||||
break
|
||||
}
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
}
|
||||
|
||||
func (mc *MachoContext) removeDySymtabCommand() {
|
||||
ptr := int64(0)
|
||||
ptr := int64(0)
|
||||
if mc.Is64bit() {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
||||
} else {
|
||||
@ -411,33 +750,160 @@ func (mc *MachoContext) removeDySymtabCommand() {
|
||||
}
|
||||
for _, cmd := range mc.commands {
|
||||
if cmd.Cmd() != LC_DYSYMTAB {
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
continue
|
||||
}
|
||||
var dysymtab = cmd.(*DySymtab)
|
||||
dysymtab_fix := dysymtab
|
||||
dysymtab_fix.indirectsymoff = 0
|
||||
dysymtab_fix.nindirectsyms = 0
|
||||
var dysymtab = cmd.(*DySymtab)
|
||||
dysymtab_fix := dysymtab
|
||||
dysymtab_fix.indirectsymoff = 0
|
||||
dysymtab_fix.nindirectsyms = 0
|
||||
mc.file.Seek(ptr, io.SeekStart)
|
||||
mc.file.Write(dysymtab_fix.Serialize(mc))
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RemoveExportTrie() {
|
||||
var start int64
|
||||
var size int
|
||||
if mc.dyldinfo != nil {
|
||||
// legacy export trie
|
||||
start = int64(mc.dyldinfo.export_off)
|
||||
size = int(mc.dyldinfo.export_size)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
} else if mc.exports != nil {
|
||||
// modern export trie
|
||||
start = int64(mc.exports.dataoff)
|
||||
size = int(mc.exports.datasize)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
} else {
|
||||
// no export trie (??)
|
||||
// should never occur unless this binary is modified
|
||||
}
|
||||
var start int64
|
||||
var size int
|
||||
if mc.dyldinfo != nil {
|
||||
// legacy export trie
|
||||
start = int64(mc.dyldinfo.export_off)
|
||||
size = int(mc.dyldinfo.export_size)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
} else if mc.exports != nil {
|
||||
// modern export trie
|
||||
start = int64(mc.exports.dataoff)
|
||||
size = int(mc.exports.datasize)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
} else {
|
||||
// no export trie (??)
|
||||
// should never occur unless this binary is modified
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
for _, command := range mc.commands {
|
||||
switch command.(type) {
|
||||
case *Segment64:
|
||||
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()
|
||||
// }
|
||||
}
|
||||
|
||||
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)")
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
fmt.Printf("Add a new section with addr=0x%x, fileoffset=0x%x\n", section.addr, section.offset)
|
||||
|
||||
ret = §ion
|
||||
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
|
||||
}
|
||||
|
||||
func (mc *MachoContext) WriteInfoToData(info *protomodel.MachoInfo) {
|
||||
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)
|
||||
for _, symbol := range table.Symbols {
|
||||
binary.Write(buffer, mc.byteorder, (symbol.SymbolIndex<<8)|symbol.SegmentIndex)
|
||||
binary.Write(buffer, mc.byteorder, symbol.Offset)
|
||||
}
|
||||
}
|
||||
instructions := buffer.Bytes()
|
||||
|
||||
buffer = new(bytes.Buffer)
|
||||
for _, lib := range info.Symbols.Libs {
|
||||
buffer.WriteString(lib)
|
||||
buffer.WriteByte(0)
|
||||
}
|
||||
liblist := buffer.Bytes()
|
||||
|
||||
buffer = new(bytes.Buffer)
|
||||
for _, symbol := range info.Symbols.Symbols {
|
||||
buffer.WriteString(symbol)
|
||||
buffer.WriteByte(0)
|
||||
}
|
||||
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)
|
||||
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,38 @@ 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->bind = 0;
|
||||
b->next = next;
|
||||
b->target = target;
|
||||
b->high8 = high8;
|
||||
return value;
|
||||
}
|
||||
|
||||
uint64_t MakeBindFixupOpcodeFromRebase(uint64_t rebaseOpcode, uint32_t ordinal) {
|
||||
printf("fix bind value\n");
|
||||
uint64_t ret;
|
||||
struct dyld_chained_ptr_64_bind* b = (struct dyld_chained_ptr_64_bind*)&ret;
|
||||
b->next = ((struct dyld_chained_ptr_64_rebase*)&rebaseOpcode)->next;
|
||||
b->bind = 1;
|
||||
b->ordinal = ordinal;
|
||||
b->addend = 0;
|
||||
b->reserved = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t MakeImportTableEntry(uint32_t lib_ordinal, uint32_t name_offset) {
|
||||
printf("append import table\n");
|
||||
uint32_t ret;
|
||||
struct dyld_chained_import* import = (struct dyld_chained_import*)&ret;
|
||||
import->lib_ordinal = lib_ordinal;
|
||||
import->name_offset = name_offset;
|
||||
import->weak_import = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -30,4 +30,8 @@ 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);
|
||||
|
||||
uint64_t MakeBindFixupOpcodeFromRebase(uint64_t rebaseOpcode, uint32_t ordinal);
|
||||
uint32_t MakeImportTableEntry(uint32_t lib_ordinal, uint32_t name_offset);
|
||||
#endif
|
||||
|
@ -37,6 +37,14 @@ func (h *Header) Cpusubtype() uint32 {
|
||||
return h.cpusubtype
|
||||
}
|
||||
|
||||
func (h *Header) IsExecutable() bool {
|
||||
return h.filetype == 0x2
|
||||
}
|
||||
|
||||
func (h *Header) IsDylib() bool {
|
||||
return h.filetype == 0x6
|
||||
}
|
||||
|
||||
func (h *Header) Serialize(mc *MachoContext) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, mc.byteorder, h.magic)
|
||||
@ -127,6 +135,16 @@ func (lcmd *LoadCmd) Cmdname() string {
|
||||
return "LC_DATA_IN_CODE"
|
||||
case LC_SYMTAB:
|
||||
return "LC_SYMTAB"
|
||||
case LC_BUILD_VERSION:
|
||||
return "LC_BUILD_VERSION"
|
||||
case LC_UUID:
|
||||
return "LC_UUID"
|
||||
case LC_SOURCE_VERSION:
|
||||
return "LC_SOURCE_VERSION"
|
||||
case LC_DYLD_CHAINED_FIXUPS:
|
||||
return "LC_DYLD_CHAINED_FIXUPS"
|
||||
case LC_DYLD_EXPORTS_TRIE:
|
||||
return "LC_DYLD_EXPORTS_TRIE"
|
||||
default:
|
||||
// TODO: Update
|
||||
return fmt.Sprintf("LC_DONT_KNOW_0x%x", lcmd.Cmd())
|
||||
@ -397,13 +415,21 @@ func (lcmd *DyldInfo) Deserialize(mc *MachoContext, buf []byte) {
|
||||
}
|
||||
|
||||
type Fixups struct {
|
||||
fixups_version uint32
|
||||
starts_offset uint32
|
||||
imports_offset uint32
|
||||
symbols_offset uint32
|
||||
fixups_version uint32
|
||||
starts_offset uint32
|
||||
imports_offset uint32
|
||||
symbols_offset uint32
|
||||
imports_count uint32
|
||||
imports_format uint32
|
||||
symbols_format uint32
|
||||
symbols_format uint32
|
||||
}
|
||||
|
||||
func (lcmd *Fixups) ImportsOffset(fixups_offset uint32) uint32 {
|
||||
return lcmd.imports_offset + fixups_offset
|
||||
}
|
||||
|
||||
func (lcmd *Fixups) SymbolsOffset(fixups_offset uint32) uint32 {
|
||||
return lcmd.symbols_offset + fixups_offset
|
||||
}
|
||||
|
||||
func (lcmd *Fixups) Serialize(mc *MachoContext) []byte {
|
||||
@ -516,25 +542,25 @@ func (lcmd *Symtab) Deserialize(mc *MachoContext, buf []byte) {
|
||||
}
|
||||
|
||||
type DySymtab struct {
|
||||
c LoadCmd
|
||||
ilocalsym uint32
|
||||
nlocalsym uint32
|
||||
iextdefsym uint32
|
||||
nextdefsym uint32
|
||||
iundefsym uint32
|
||||
nundefsym uint32
|
||||
tocoff uint32
|
||||
ntoc uint32
|
||||
modtaboff uint32
|
||||
nmodtab uint32
|
||||
extrefsymoff uint32
|
||||
nextrefsyms uint32
|
||||
indirectsymoff uint32
|
||||
nindirectsyms uint32
|
||||
extreloff uint32
|
||||
nextrel uint32
|
||||
localrefoff uint32
|
||||
nlocref uint32
|
||||
c LoadCmd
|
||||
ilocalsym uint32
|
||||
nlocalsym uint32
|
||||
iextdefsym uint32
|
||||
nextdefsym uint32
|
||||
iundefsym uint32
|
||||
nundefsym uint32
|
||||
tocoff uint32
|
||||
ntoc uint32
|
||||
modtaboff uint32
|
||||
nmodtab uint32
|
||||
extrefsymoff uint32
|
||||
nextrefsyms uint32
|
||||
indirectsymoff uint32
|
||||
nindirectsyms uint32
|
||||
extreloff uint32
|
||||
nextrel uint32
|
||||
localrefoff uint32
|
||||
nlocref uint32
|
||||
}
|
||||
|
||||
func (lcmd *DySymtab) Cmd() uint32 {
|
||||
@ -558,20 +584,20 @@ func (lcmd *DySymtab) Serialize(mc *MachoContext) []byte {
|
||||
binary.Write(buf, mc.byteorder, lcmd.nlocalsym)
|
||||
binary.Write(buf, mc.byteorder, lcmd.iextdefsym)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nextdefsym)
|
||||
binary.Write(buf, mc.byteorder, lcmd.iundefsym)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nundefsym)
|
||||
binary.Write(buf, mc.byteorder, lcmd.tocoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.ntoc)
|
||||
binary.Write(buf, mc.byteorder, lcmd.modtaboff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nmodtab)
|
||||
binary.Write(buf, mc.byteorder, lcmd.extrefsymoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nextrefsyms)
|
||||
binary.Write(buf, mc.byteorder, lcmd.indirectsymoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nindirectsyms)
|
||||
binary.Write(buf, mc.byteorder, lcmd.extreloff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nextrel)
|
||||
binary.Write(buf, mc.byteorder, lcmd.localrefoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nlocref)
|
||||
binary.Write(buf, mc.byteorder, lcmd.iundefsym)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nundefsym)
|
||||
binary.Write(buf, mc.byteorder, lcmd.tocoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.ntoc)
|
||||
binary.Write(buf, mc.byteorder, lcmd.modtaboff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nmodtab)
|
||||
binary.Write(buf, mc.byteorder, lcmd.extrefsymoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nextrefsyms)
|
||||
binary.Write(buf, mc.byteorder, lcmd.indirectsymoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nindirectsyms)
|
||||
binary.Write(buf, mc.byteorder, lcmd.extreloff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nextrel)
|
||||
binary.Write(buf, mc.byteorder, lcmd.localrefoff)
|
||||
binary.Write(buf, mc.byteorder, lcmd.nlocref)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
@ -583,18 +609,18 @@ func (lcmd *DySymtab) Deserialize(mc *MachoContext, buf []byte) {
|
||||
binary.Read(r, mc.byteorder, &lcmd.nlocalsym)
|
||||
binary.Read(r, mc.byteorder, &lcmd.iextdefsym)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nextdefsym)
|
||||
binary.Read(r, mc.byteorder, &lcmd.iundefsym)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nundefsym)
|
||||
binary.Read(r, mc.byteorder, &lcmd.tocoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.ntoc)
|
||||
binary.Read(r, mc.byteorder, &lcmd.modtaboff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nmodtab)
|
||||
binary.Read(r, mc.byteorder, &lcmd.extrefsymoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nextrefsyms)
|
||||
binary.Read(r, mc.byteorder, &lcmd.indirectsymoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nindirectsyms)
|
||||
binary.Read(r, mc.byteorder, &lcmd.extreloff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nextrel)
|
||||
binary.Read(r, mc.byteorder, &lcmd.localrefoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nlocref)
|
||||
binary.Read(r, mc.byteorder, &lcmd.iundefsym)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nundefsym)
|
||||
binary.Read(r, mc.byteorder, &lcmd.tocoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.ntoc)
|
||||
binary.Read(r, mc.byteorder, &lcmd.modtaboff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nmodtab)
|
||||
binary.Read(r, mc.byteorder, &lcmd.extrefsymoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nextrefsyms)
|
||||
binary.Read(r, mc.byteorder, &lcmd.indirectsymoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nindirectsyms)
|
||||
binary.Read(r, mc.byteorder, &lcmd.extreloff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nextrel)
|
||||
binary.Read(r, mc.byteorder, &lcmd.localrefoff)
|
||||
binary.Read(r, mc.byteorder, &lcmd.nlocref)
|
||||
}
|
||||
|
@ -29,11 +29,11 @@ type MachoContext struct {
|
||||
linkedits []*LinkEdit
|
||||
segments []Segment
|
||||
symtab *Symtab
|
||||
dysymtab *DySymtab
|
||||
dysymtab *DySymtab
|
||||
|
||||
dyldinfo *DyldInfo
|
||||
fixups *LinkEdit
|
||||
exports *LinkEdit
|
||||
dyldinfo *DyldInfo
|
||||
fixups *LinkEdit
|
||||
exports *LinkEdit
|
||||
}
|
||||
|
||||
func (mc *MachoContext) FileSize() uint32 {
|
||||
@ -64,6 +64,18 @@ func (mc *MachoContext) Segments() []Segment {
|
||||
return mc.segments
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Main() uint64 {
|
||||
return mc.entryoff
|
||||
}
|
||||
|
||||
func (mc *MachoContext) Fixups() (*Fixups, uint64) {
|
||||
start := mc.fixups.dataoff
|
||||
size := mc.fixups.datasize
|
||||
fixups := new(Fixups)
|
||||
fixups.Deserialize(mc, mc.buf[start:start+size])
|
||||
return fixups, uint64(mc.fixups.dataoff)
|
||||
}
|
||||
|
||||
func (mc *MachoContext) WriteEnabled() bool {
|
||||
return mc.file != nil
|
||||
}
|
||||
@ -235,12 +247,12 @@ func (mc *MachoContext) Parse(r *bufio.Reader) error {
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.linkedits = append(mc.linkedits, lcmd)
|
||||
if lcmd.Cmd() == LC_DYLD_CHAINED_FIXUPS {
|
||||
mc.fixups = lcmd
|
||||
}
|
||||
if lcmd.Cmd() == LC_DYLD_EXPORTS_TRIE {
|
||||
mc.exports = lcmd
|
||||
}
|
||||
if lcmd.Cmd() == LC_DYLD_CHAINED_FIXUPS {
|
||||
mc.fixups = lcmd
|
||||
}
|
||||
if lcmd.Cmd() == LC_DYLD_EXPORTS_TRIE {
|
||||
mc.exports = lcmd
|
||||
}
|
||||
break
|
||||
|
||||
case LC_SYMTAB:
|
||||
@ -248,12 +260,14 @@ func (mc *MachoContext) Parse(r *bufio.Reader) error {
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.symtab = lcmd
|
||||
break
|
||||
|
||||
case LC_DYSYMTAB:
|
||||
lcmd := new(DySymtab)
|
||||
lcmd.Deserialize(mc, command_buf)
|
||||
mc.commands = append(mc.commands, lcmd)
|
||||
mc.dysymtab = lcmd
|
||||
break
|
||||
|
||||
default:
|
||||
lcmd := new(LoadCmd)
|
||||
|
@ -72,7 +72,6 @@ func (mc *MachoContext) CollectSymbols() []*Symbol {
|
||||
var flags uint8
|
||||
var sect uint8
|
||||
var desc uint16
|
||||
var value32 uint32
|
||||
var value64 uint64
|
||||
|
||||
binary.Read(symtab_buffer, mc.byteorder, &strx)
|
||||
@ -83,6 +82,7 @@ func (mc *MachoContext) CollectSymbols() []*Symbol {
|
||||
binary.Read(symtab_buffer, mc.byteorder, &value64)
|
||||
} else {
|
||||
// always use value64
|
||||
var value32 uint32
|
||||
binary.Read(symtab_buffer, mc.byteorder, &value32)
|
||||
value64 = uint64(value32)
|
||||
}
|
||||
|
@ -17,18 +17,38 @@ message MachoInfo {
|
||||
uint64 value = 2; // address of the init function
|
||||
}
|
||||
|
||||
// iOS library rewrite these as opcodes and dyld processes
|
||||
// message LazySymbol {
|
||||
// string name = 1;
|
||||
// int32 dylib_ordinal = 2; // could be -1 -2
|
||||
// uint32 segment = 3;
|
||||
// uint32 segment_offset = 4;
|
||||
// }
|
||||
// right now we waste memory to store name/hash for all symbols
|
||||
// should consider compress them, dyld stores the index in list of LC_DYLIB
|
||||
message BindSymbol {
|
||||
string name = 1;
|
||||
string libname = 2;
|
||||
uint32 libhash = 3;
|
||||
uint32 segment = 4; // segment index
|
||||
uint64 offset = 5; // offset in segment
|
||||
}
|
||||
|
||||
message SymbolTable {
|
||||
uint32 symbolIndex = 1;
|
||||
uint32 segmentIndex = 2;
|
||||
uint32 offset = 3;
|
||||
}
|
||||
message LibraryImportedSymbols {
|
||||
uint32 libIndex = 1;
|
||||
uint32 nsymbols = 2;
|
||||
repeated SymbolTable symbols = 3;
|
||||
}
|
||||
|
||||
message AllImportedSymbols {
|
||||
repeated string libs = 1;
|
||||
repeated string symbols = 2;
|
||||
repeated LibraryImportedSymbols tables = 3;
|
||||
}
|
||||
|
||||
PointerSize pointer_size = 1;
|
||||
uint64 image_base = 2;
|
||||
repeated InitPointer init_pointers = 3;
|
||||
// saved or read from header -> dyld_info -> lazyoff
|
||||
// uint64 lazy_symbol_address = 4;
|
||||
// repeated LazySymbol lazy_symbols = 5;
|
||||
uint64 main = 3;
|
||||
repeated InitPointer init_pointers = 4;
|
||||
// repeated BindSymbol symbols = 5;
|
||||
AllImportedSymbols symbols = 5;
|
||||
|
||||
}
|
||||
|
57
research/custom_loader/a.mm
Normal file
57
research/custom_loader/a.mm
Normal file
@ -0,0 +1,57 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@interface Foo : NSObject
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
- (void)bar {
|
||||
NSLog(@"%@", self);
|
||||
}
|
||||
@end
|
||||
|
||||
@interface Bar : NSObject
|
||||
@end
|
||||
|
||||
@implementation Bar
|
||||
static int x;
|
||||
|
||||
+ (void)load {
|
||||
NSLog(@"%@", self);
|
||||
// NSLog(@"x=%d", x)
|
||||
printf("printf in [Bar load]\n");
|
||||
x = 1;
|
||||
}
|
||||
|
||||
- (void)dummy {
|
||||
NSLog(@"dummy bar x=%d", x);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
__attribute__((constructor)) static void
|
||||
hmmge(int argc, char** argv) {
|
||||
// create a dummy blank function to be replaced to call OBJC load
|
||||
printf("hmmge=%p\n", hmmge);
|
||||
printf("hmmge argc=%d\n", argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
printf(" hmmge argv[%d]=%s\n", i, argv[i]);
|
||||
}
|
||||
NSLog(@"hmmge in objc-c");
|
||||
Bar *bar = [[Bar alloc] init];
|
||||
[bar dummy];
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
NSLog(@"Hello, World!");
|
||||
Foo *foo = [[Foo alloc] init];
|
||||
[foo bar];
|
||||
}
|
||||
|
||||
printf("argc=%d\n", argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
printf(" argv[%d]=%s\n", i, argv[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
|
||||
VERSION=${1:-14}
|
||||
OUT=./out
|
||||
LOGIC=2
|
||||
LOGIC=${2}
|
||||
|
||||
mkdir -p $OUT
|
||||
|
||||
@ -39,21 +39,80 @@ clang++ -mmacosx-version-min=$VERSION -o $OUT/libc.dylib -shared c.cc
|
||||
# create our dummy lib first
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib dummy.cc
|
||||
# build a references libb
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.cc
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -Xlinker -no_data_const -lb a.cc
|
||||
|
||||
# extract symbols from a
|
||||
../../macho-go/bin/ios-wrapper remove-imports $OUT/a -o $OUT/a > $OUT/b.h
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports --remove-symbol-table $OUT/a
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
|
||||
# build libb with symbols extracted from a
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc
|
||||
|
||||
out/a
|
||||
codesign --force --deep -s - $OUT/a-fixed
|
||||
$OUT/a-fixed
|
||||
|
||||
elif [[ $LOGIC -eq 3 ]]
|
||||
then
|
||||
# remove imports test
|
||||
|
||||
# libc to test reexport custom lib
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libc.dylib -shared c.cc
|
||||
|
||||
# create our dummy lib first
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib dummy.cc
|
||||
# build a references libb
|
||||
clang -fobjc-arc -ObjC -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.mm
|
||||
|
||||
# extract symbols from a
|
||||
# ../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports --remove-symbol-table --keep-imports _printf $OUT/a
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports $OUT/a
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
|
||||
# build libb with symbols extracted from a
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc
|
||||
# ../../macho-go/bin/ios-wrapper pepe -o $OUT/libb.dylib -b $OUT/libb.bcell --remove-imports --remove-exports --keep-imports _dyld_get_sdk_version --keep-imports _malloc --keep-imports _printf --keep-imports ___stack_chk_guard $OUT/libb.dylib
|
||||
|
||||
# resign
|
||||
codesign --force --deep -s - $OUT/a-fixed
|
||||
codesign --force --deep -s - $OUT/libb.dylib
|
||||
|
||||
# export OBJC_PRINT_LOAD_METHODS=1
|
||||
# export OBJC_PRINT_CLASS_SETUP=1
|
||||
$OUT/a-fixed
|
||||
# unset OBJC_PRINT_LOAD_METHODS
|
||||
# unset OBJC_PRINT_CLASS_SETUP
|
||||
|
||||
else
|
||||
|
||||
# dummy test build
|
||||
# remove imports test
|
||||
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libc.dylib -shared c.cc
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.cc
|
||||
# test rpath
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libc.dylib -install_name @rpath/libc.dylib -shared c.cc
|
||||
# linked with libd
|
||||
# with rpath = $OUT
|
||||
clang++ -mmacosx-version-min=$VERSION -Xlinker -no_data_const -o $OUT/a \
|
||||
-rpath ./heheeeekkkkkkk \
|
||||
-rpath $OUT \
|
||||
-rpath ./hehe \
|
||||
-rpath ./haha \
|
||||
$OUT/libc.dylib a.cc \
|
||||
|
||||
|
||||
# extract symbols from a
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell -l out/libb.dylib --remove-imports --remove-exports $OUT/a
|
||||
|
||||
# build restoration libb with symbols extracted from a
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared b.cc
|
||||
|
||||
# obfuscate libb (bugged)
|
||||
# ../../macho-go/bin/ios-wrapper pepe -o $OUT/libb.dylib -b $OUT/libb.bcell --remove-imports --remove-exports --keep-imports _dyld_get_sdk_version --keep-imports _malloc --keep-imports _printf --keep-imports ___stack_chk_guard $OUT/libb.dylib
|
||||
|
||||
# resign
|
||||
codesign --force --deep -s - $OUT/a-fixed
|
||||
codesign --force --deep -s - $OUT/libb.dylib
|
||||
|
||||
# export OBJC_PRINT_LOAD_METHODS=1
|
||||
# export OBJC_PRINT_CLASS_SETUP=1
|
||||
$OUT/a-fixed
|
||||
# unset OBJC_PRINT_LOAD_METHODS
|
||||
# unset OBJC_PRINT_CLASS_SETUP
|
||||
fi
|
||||
|
1375
research/custom_loader/objc-symtab.txt
Normal file
1375
research/custom_loader/objc-symtab.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user