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 (
|
require (
|
||||||
github.com/alecthomas/kong v0.2.16
|
github.com/alecthomas/kong v0.2.16
|
||||||
github.com/sirupsen/logrus v1.8.0
|
github.com/sirupsen/logrus v1.8.0
|
||||||
google.golang.org/protobuf v1.26.0
|
google.golang.org/protobuf v1.31.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/magefile/mage v1.10.0 // indirect
|
github.com/magefile/mage v1.10.0 // indirect
|
||||||
github.com/pkg/errors v0.8.1 // indirect
|
github.com/pkg/errors v0.8.1 // indirect
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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.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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
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=
|
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-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 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
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{}
|
type removeClassicSymbol struct{}
|
||||||
|
|
||||||
func (action *removeClassicSymbol) withMacho(mf *MachoFile) error {
|
func (action *removeClassicSymbol) withMacho(mf *MachoFile) error {
|
||||||
mf.Context().RemoveClassicSymbol()
|
mf.Context().RemoveSymbolTable()
|
||||||
return nil
|
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 {
|
func (action *removeImports) withMacho(mf *MachoFile) error {
|
||||||
mf.Context().RemoveBindSymbols()
|
mf.Context().RemoveBindSymbols()
|
||||||
mf.Context().RemoveSymbolTable()
|
|
||||||
mf.Context().RemoveExportTrie()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,4 +18,3 @@ func (action *removeImports) withFat(ff *FatFile) error {
|
|||||||
func NewRemoveImportsAction() *removeImports {
|
func NewRemoveImportsAction() *removeImports {
|
||||||
return &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
|
package wrapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
// "strings"
|
||||||
|
|
||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -63,6 +65,10 @@ func Cli() {
|
|||||||
arg := cli.Lipo.Join
|
arg := cli.Lipo.Join
|
||||||
fat.FatJoin(arg.Macho, arg.Out)
|
fat.FatJoin(arg.Macho, arg.Out)
|
||||||
return
|
return
|
||||||
|
} else if command == "bcell2header" {
|
||||||
|
arg := cli.BcellToHeader
|
||||||
|
bcell2header(arg.Bcell, arg.Out)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var pc ProgramContext
|
var pc ProgramContext
|
||||||
@ -73,7 +79,7 @@ func Cli() {
|
|||||||
arg := cli.Wrap
|
arg := cli.Wrap
|
||||||
ofile = NewOFile(arg.OFile)
|
ofile = NewOFile(arg.OFile)
|
||||||
pc.remove_codesign = true
|
pc.remove_codesign = true
|
||||||
pc.strip_init_pointers = true
|
pc.remove_inits = true
|
||||||
pc.dylib_to_add = []string{"@rpath/bcell.framework/bcell"}
|
pc.dylib_to_add = []string{"@rpath/bcell.framework/bcell"}
|
||||||
pc.rpath_to_add = []string{"@executable_path/Frameworks"}
|
pc.rpath_to_add = []string{"@executable_path/Frameworks"}
|
||||||
pc.outfile = arg.Out
|
pc.outfile = arg.Out
|
||||||
@ -99,6 +105,30 @@ func Cli() {
|
|||||||
ofile = NewOFile(arg.OFile)
|
ofile = NewOFile(arg.OFile)
|
||||||
pc.remove_imports = true
|
pc.remove_imports = true
|
||||||
pc.outfile = arg.Out
|
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:
|
default:
|
||||||
return
|
return
|
||||||
@ -165,6 +195,12 @@ func displayBcell(bfile string) {
|
|||||||
init_ptr.Value,
|
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 {
|
type RemoveImportsArgument struct {
|
||||||
Out string `short:"o" required name:"outfile" help:"Modified Mach-O/Fat binary output file" type:"path"`
|
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"`
|
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +56,28 @@ type LipoArgument struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PepeArgument struct {
|
type PepeArgument struct {
|
||||||
|
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"`
|
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 {
|
type Argument struct {
|
||||||
@ -68,5 +90,14 @@ type Argument struct {
|
|||||||
DisplayBcell DisplayBcellArgument `cmd name:"display-bcell" help:"Display Protobuf<BcellFile> content"`
|
DisplayBcell DisplayBcellArgument `cmd name:"display-bcell" help:"Display Protobuf<BcellFile> content"`
|
||||||
Addr2Line Addr2LineArgument `cmd name:"addr2line" help:"Resolve crash address from DWARF file"`
|
Addr2Line Addr2LineArgument `cmd name:"addr2line" help:"Resolve crash address from DWARF file"`
|
||||||
Lipo LipoArgument `cmd help:"split Fat binary or join Mach-O binares"`
|
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"`
|
||||||
}
|
}
|
||||||
|
@ -38,11 +38,15 @@ func (uc *UserConfig) Protomodel() *protomodel.Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProgramContext struct {
|
type ProgramContext struct {
|
||||||
strip_init_pointers bool
|
remove_inits bool
|
||||||
remove_codesign bool
|
remove_codesign bool
|
||||||
remove_imports bool
|
remove_imports bool
|
||||||
|
remove_others bool
|
||||||
|
remove_exports bool
|
||||||
|
remove_symbol_table bool
|
||||||
dylib_to_add []string
|
dylib_to_add []string
|
||||||
rpath_to_add []string
|
rpath_to_add []string
|
||||||
|
symbols_keep []string
|
||||||
|
|
||||||
outfile string
|
outfile string
|
||||||
|
|
||||||
@ -84,24 +88,28 @@ func (pc *ProgramContext) dispatchActions(ofile OFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pc *ProgramContext) Process(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 {
|
if pc.remove_codesign {
|
||||||
pc.AddAction(NewRemoveCodeSignatureAction())
|
pc.AddAction(NewRemoveCodeSignatureAction())
|
||||||
}
|
}
|
||||||
if pc.strip_init_pointers {
|
if pc.remove_inits {
|
||||||
pc.AddAction(NewSaveInitPointerAction())
|
pc.AddAction(NewSaveInitPointerAction())
|
||||||
pc.AddAction(NewRemoveInitPointerAction())
|
pc.AddAction(NewRemoveInitPointerAction())
|
||||||
}
|
}
|
||||||
ExperimentalFeature("Remove Unnecessary Info", func() {
|
if pc.remove_imports {
|
||||||
pc.AddAction(NewRemoveUnnecessaryInfoAction())
|
pc.AddAction(NewSaveImportsAction(pc.symbols_keep))
|
||||||
})
|
pc.AddAction(NewRemoveImportsAction())
|
||||||
ExperimentalFeature("Remove Classic Symbol", func() {
|
pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep))
|
||||||
|
}
|
||||||
|
if pc.remove_symbol_table {
|
||||||
pc.AddAction(NewRemoveClassicSymbolAction())
|
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(NewAddRpathAction(pc.rpath_to_add))
|
||||||
pc.AddAction(NewAddDylibAction(pc.dylib_to_add))
|
pc.AddAction(NewAddDylibAction(pc.dylib_to_add))
|
||||||
|
@ -25,10 +25,13 @@ type ImportSymbol struct {
|
|||||||
segment_offset uint32
|
segment_offset uint32
|
||||||
address uint64
|
address uint64
|
||||||
file_address uint64
|
file_address uint64
|
||||||
|
lib_ordinal uint32
|
||||||
|
|
||||||
// push number
|
// push number
|
||||||
pnum uint32
|
pnum uint32
|
||||||
stub uint64
|
stub uint64
|
||||||
|
|
||||||
|
next int // only for LC_DYLD_CHAINED_FIXUPS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sym *ImportSymbol) Name() string {
|
func (sym *ImportSymbol) Name() string {
|
||||||
@ -43,6 +46,10 @@ func (sym *ImportSymbol) Dylib() string {
|
|||||||
return sym.dylib
|
return sym.dylib
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sym *ImportSymbol) LibOrdinal() uint32 {
|
||||||
|
return sym.lib_ordinal
|
||||||
|
}
|
||||||
|
|
||||||
func (sym *ImportSymbol) Address() uint64 {
|
func (sym *ImportSymbol) Address() uint64 {
|
||||||
return sym.address
|
return sym.address
|
||||||
}
|
}
|
||||||
@ -55,6 +62,10 @@ func (sym *ImportSymbol) Stub() uint64 {
|
|||||||
return sym.stub
|
return sym.stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sym *ImportSymbol) Segment() uint32 {
|
||||||
|
return sym.segment
|
||||||
|
}
|
||||||
|
|
||||||
func (mc *MachoContext) CollectBindSymbols() []*ImportSymbol {
|
func (mc *MachoContext) CollectBindSymbols() []*ImportSymbol {
|
||||||
if mc.dyldinfo == nil {
|
if mc.dyldinfo == nil {
|
||||||
return mc.CollectBindSymbolsModern()
|
return mc.CollectBindSymbolsModern()
|
||||||
@ -82,7 +93,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
|||||||
// until buf is freed, all pointers are valid
|
// until buf is freed, all pointers are valid
|
||||||
// remember to copy before moving out
|
// remember to copy before moving out
|
||||||
header := (*C.uchar)(unsafe.Pointer(&buf[0]))
|
header := (*C.uchar)(unsafe.Pointer(&buf[0]))
|
||||||
imports_table := C.GetImportsTable(header);
|
imports_table := C.GetImportsTable(header)
|
||||||
|
|
||||||
// for i := 0; i < int(imports_table.size); i++ {
|
// for i := 0; i < int(imports_table.size); i++ {
|
||||||
// s := C.GetImportsAt(&imports_table, C.int(i))
|
// s := C.GetImportsAt(&imports_table, C.int(i))
|
||||||
@ -102,7 +113,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
|||||||
status := int(C.GetSegmentFixAt(header, C.uint(segment_i), fix_ptr))
|
status := int(C.GetSegmentFixAt(header, C.uint(segment_i), fix_ptr))
|
||||||
segment_i += 1
|
segment_i += 1
|
||||||
if status == 2 {
|
if status == 2 {
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
if status == 3 {
|
if status == 3 {
|
||||||
continue
|
continue
|
||||||
@ -110,17 +121,18 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
|||||||
// fmt.Printf("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count)
|
// fmt.Printf("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count)
|
||||||
// fmt.Printf("pages=%x\n", fix.pages)
|
// fmt.Printf("pages=%x\n", fix.pages)
|
||||||
pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count))
|
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++ {
|
for page_i := 0; page_i < int(fix.page_count); page_i++ {
|
||||||
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
||||||
|
|
||||||
address := int64(fix.segment) + int64(pages[page_i])
|
address := int64(fix.segment) + int64(pages[page_i])
|
||||||
mc.file.Seek(address, io.SeekStart)
|
reader.Seek(address, io.SeekStart)
|
||||||
|
|
||||||
code := make([]byte, 8)
|
code := make([]byte, 8)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
mc.file.Read(code)
|
reader.Read(code)
|
||||||
v := binary.LittleEndian.Uint64(code)
|
v := mc.byteorder.Uint64(code)
|
||||||
|
|
||||||
var bind C.int
|
var bind C.int
|
||||||
var ret1 C.ulonglong
|
var ret1 C.ulonglong
|
||||||
@ -133,25 +145,31 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
|||||||
name := C.GoString(s.name)
|
name := C.GoString(s.name)
|
||||||
dylib := string(mc.dylibs[int(s.lib_ordinal)-1].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.address = uint64(address)
|
||||||
sym.name = name
|
sym.name = name
|
||||||
sym.dylib = dylib
|
sym.dylib = dylib
|
||||||
sym.typ = "lazy"
|
sym.typ = "lazy"
|
||||||
|
sym.lib_ordinal = uint32(s.lib_ordinal)
|
||||||
|
|
||||||
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
||||||
sym.file_address = uint64(address)
|
sym.file_address = uint64(address)
|
||||||
|
sym.next = int(next)
|
||||||
new_sym := sym
|
new_sym := sym
|
||||||
syms = append(syms, &new_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 {
|
if int(next) == 0 {
|
||||||
break
|
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)
|
||||||
}
|
}
|
||||||
mc.file.Seek(0, io.SeekStart)
|
reader.Seek(0, io.SeekStart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return syms
|
return syms
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
package macho
|
package macho
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
. "ios-wrapper/pkg/ios"
|
. "ios-wrapper/pkg/ios"
|
||||||
|
"ios-wrapper/pkg/protomodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// #include "fixups.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
func rewriteLoadcommandsWithoutCodesignature(mc *MachoContext) {
|
func rewriteLoadcommandsWithoutCodesignature(mc *MachoContext) {
|
||||||
if mc.Is64bit() {
|
if mc.Is64bit() {
|
||||||
mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
||||||
@ -185,10 +194,6 @@ func (mc *MachoContext) RemoveUnnecessaryInfo() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *MachoContext) RemoveClassicSymbol() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *MachoContext) AddLoadCmd(lcmd LoadCommand) {
|
func (mc *MachoContext) AddLoadCmd(lcmd LoadCommand) {
|
||||||
var offset uint64
|
var offset uint64
|
||||||
payload := lcmd.Serialize(mc)
|
payload := lcmd.Serialize(mc)
|
||||||
@ -273,50 +278,48 @@ func (mc *MachoContext) RemoveBindSymbols() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
if mc.dyldinfo == nil {
|
if mc.dyldinfo == nil {
|
||||||
mc.removeBindSymbolsModern()
|
mc.removeBindSymbolsModern()
|
||||||
} else {
|
} else {
|
||||||
mc.removeBindSymbolsLegacy()
|
mc.removeBindSymbolsLegacy()
|
||||||
}
|
}
|
||||||
|
// Objective-C stub replaces main which can only appears in executable
|
||||||
calculateHash := func(name string) uint32 {
|
if mc.Header().IsExecutable() {
|
||||||
var h uint32 = 0x811c9dc5
|
mc.ReworkForObjc()
|
||||||
for _, s := range name {
|
|
||||||
h ^= uint32(s)
|
|
||||||
h *= 0x01000193
|
|
||||||
}
|
|
||||||
return h
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// due to some limitations when design this tool
|
// due to some limitations when design this tool
|
||||||
// we write the c code to stdout lol
|
// 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() {
|
for _, symbol := range mc.CollectBindSymbols() {
|
||||||
if symbol.Type() != "lazy" {
|
if symbol.Type() != "lazy" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
count += 1
|
|
||||||
dylib_hash := calculateHash(symbol.Dylib())
|
|
||||||
seg := mc.segments[symbol.segment]
|
|
||||||
|
|
||||||
var offset uint64
|
if mc.dyldinfo != nil {
|
||||||
|
// for legacy resolve the opcodes can be rewritten as 0x00
|
||||||
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))
|
mc.file.WriteAt(make([]byte, 8), int64(symbol.file_address))
|
||||||
|
} else {
|
||||||
|
// for modern resolve the opcodes must not be rewritten as 0x00
|
||||||
|
// because it contains 2 types of opcodes, BIND and REBASE
|
||||||
|
// we only fix BIND and leave REBASE intact
|
||||||
|
// However, each opcodes has a *next* field to the next opcode
|
||||||
|
// So if we want to leave the header intact (contains pointers, size)
|
||||||
|
// We should rewrite this as REBASE opcode (so no symbol lookup happens)
|
||||||
|
// and it can continue
|
||||||
|
|
||||||
|
// we can write random values, because the loader just do
|
||||||
|
// (high8 << 56 | target) - mach_header
|
||||||
|
// or something, so no symbol lookup and no error at runtime
|
||||||
|
target := rand.Int()
|
||||||
|
high8 := rand.Int()
|
||||||
|
value := C.MakeRebaseFixupOpcode(C.int(symbol.next), C.ulonglong(target), C.ulonglong(high8))
|
||||||
|
v := make([]byte, 8)
|
||||||
|
mc.byteorder.PutUint64(v, uint64(value))
|
||||||
|
mc.file.WriteAt(v, int64(symbol.file_address))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("};")
|
|
||||||
fmt.Printf("uint32_t nimports = %d;\n", count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *MachoContext) removeBindSymbolsModern() {
|
func (mc *MachoContext) removeBindSymbolsModern() {
|
||||||
@ -356,6 +359,342 @@ func (mc *MachoContext) removeBindSymbolsLegacy() {
|
|||||||
mc.file.WriteAt(make([]byte, size), int64(start))
|
mc.file.WriteAt(make([]byte, size), int64(start))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mc *MachoContext) ReworkForObjc() {
|
||||||
|
text_start := 0
|
||||||
|
data_end := 0
|
||||||
|
lc_main_offset := 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmd := range mc.commands {
|
||||||
|
if cmd.Cmd() == LC_MAIN {
|
||||||
|
lc_main_offset = ptr + 8
|
||||||
|
ptr += int64(cmd.Cmdsize())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cmd.Cmd() != LC_SEGMENT_64 {
|
||||||
|
ptr += int64(cmd.Cmdsize())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var segment = cmd.(*Segment64)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// __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)
|
||||||
|
|
||||||
|
// 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() {
|
func (mc *MachoContext) RemoveSymbolTable() {
|
||||||
// try to remove symtab and dysymtab
|
// try to remove symtab and dysymtab
|
||||||
mc.removeSymtabCommand()
|
mc.removeSymtabCommand()
|
||||||
@ -441,3 +780,130 @@ func (mc *MachoContext) RemoveExportTrie() {
|
|||||||
// should never occur unless this binary is modified
|
// 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) {
|
void ParseFixUps(uint8_t* buffer) {
|
||||||
struct dyld_chained_fixups_header* header = (struct dyld_chained_fixups_header*)buffer;
|
struct dyld_chained_fixups_header* header = (struct dyld_chained_fixups_header*)buffer;
|
||||||
printf("starts=0x%x\n", header->starts_offset);
|
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);
|
struct ImportSymbol GetImportsAt(struct ImportTable* table, int i);
|
||||||
int GetSegmentFixAt(uint8_t* buffer, uint32_t i, struct SegmentFix* fix);
|
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);
|
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
|
#endif
|
||||||
|
@ -37,6 +37,14 @@ func (h *Header) Cpusubtype() uint32 {
|
|||||||
return h.cpusubtype
|
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 {
|
func (h *Header) Serialize(mc *MachoContext) []byte {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
binary.Write(buf, mc.byteorder, h.magic)
|
binary.Write(buf, mc.byteorder, h.magic)
|
||||||
@ -127,6 +135,16 @@ func (lcmd *LoadCmd) Cmdname() string {
|
|||||||
return "LC_DATA_IN_CODE"
|
return "LC_DATA_IN_CODE"
|
||||||
case LC_SYMTAB:
|
case LC_SYMTAB:
|
||||||
return "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:
|
default:
|
||||||
// TODO: Update
|
// TODO: Update
|
||||||
return fmt.Sprintf("LC_DONT_KNOW_0x%x", lcmd.Cmd())
|
return fmt.Sprintf("LC_DONT_KNOW_0x%x", lcmd.Cmd())
|
||||||
@ -406,6 +424,14 @@ type Fixups struct {
|
|||||||
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 {
|
func (lcmd *Fixups) Serialize(mc *MachoContext) []byte {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
binary.Write(buf, mc.byteorder, lcmd.fixups_version)
|
binary.Write(buf, mc.byteorder, lcmd.fixups_version)
|
||||||
|
@ -64,6 +64,18 @@ func (mc *MachoContext) Segments() []Segment {
|
|||||||
return mc.segments
|
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 {
|
func (mc *MachoContext) WriteEnabled() bool {
|
||||||
return mc.file != nil
|
return mc.file != nil
|
||||||
}
|
}
|
||||||
@ -248,12 +260,14 @@ func (mc *MachoContext) Parse(r *bufio.Reader) error {
|
|||||||
lcmd.Deserialize(mc, command_buf)
|
lcmd.Deserialize(mc, command_buf)
|
||||||
mc.commands = append(mc.commands, lcmd)
|
mc.commands = append(mc.commands, lcmd)
|
||||||
mc.symtab = lcmd
|
mc.symtab = lcmd
|
||||||
|
break
|
||||||
|
|
||||||
case LC_DYSYMTAB:
|
case LC_DYSYMTAB:
|
||||||
lcmd := new(DySymtab)
|
lcmd := new(DySymtab)
|
||||||
lcmd.Deserialize(mc, command_buf)
|
lcmd.Deserialize(mc, command_buf)
|
||||||
mc.commands = append(mc.commands, lcmd)
|
mc.commands = append(mc.commands, lcmd)
|
||||||
mc.dysymtab = lcmd
|
mc.dysymtab = lcmd
|
||||||
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
lcmd := new(LoadCmd)
|
lcmd := new(LoadCmd)
|
||||||
|
@ -72,7 +72,6 @@ func (mc *MachoContext) CollectSymbols() []*Symbol {
|
|||||||
var flags uint8
|
var flags uint8
|
||||||
var sect uint8
|
var sect uint8
|
||||||
var desc uint16
|
var desc uint16
|
||||||
var value32 uint32
|
|
||||||
var value64 uint64
|
var value64 uint64
|
||||||
|
|
||||||
binary.Read(symtab_buffer, mc.byteorder, &strx)
|
binary.Read(symtab_buffer, mc.byteorder, &strx)
|
||||||
@ -83,6 +82,7 @@ func (mc *MachoContext) CollectSymbols() []*Symbol {
|
|||||||
binary.Read(symtab_buffer, mc.byteorder, &value64)
|
binary.Read(symtab_buffer, mc.byteorder, &value64)
|
||||||
} else {
|
} else {
|
||||||
// always use value64
|
// always use value64
|
||||||
|
var value32 uint32
|
||||||
binary.Read(symtab_buffer, mc.byteorder, &value32)
|
binary.Read(symtab_buffer, mc.byteorder, &value32)
|
||||||
value64 = uint64(value32)
|
value64 = uint64(value32)
|
||||||
}
|
}
|
||||||
|
@ -17,18 +17,38 @@ message MachoInfo {
|
|||||||
uint64 value = 2; // address of the init function
|
uint64 value = 2; // address of the init function
|
||||||
}
|
}
|
||||||
|
|
||||||
// iOS library rewrite these as opcodes and dyld processes
|
// right now we waste memory to store name/hash for all symbols
|
||||||
// message LazySymbol {
|
// should consider compress them, dyld stores the index in list of LC_DYLIB
|
||||||
// string name = 1;
|
message BindSymbol {
|
||||||
// int32 dylib_ordinal = 2; // could be -1 -2
|
string name = 1;
|
||||||
// uint32 segment = 3;
|
string libname = 2;
|
||||||
// uint32 segment_offset = 4;
|
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;
|
PointerSize pointer_size = 1;
|
||||||
uint64 image_base = 2;
|
uint64 image_base = 2;
|
||||||
repeated InitPointer init_pointers = 3;
|
uint64 main = 3;
|
||||||
// saved or read from header -> dyld_info -> lazyoff
|
repeated InitPointer init_pointers = 4;
|
||||||
// uint64 lazy_symbol_address = 4;
|
// repeated BindSymbol symbols = 5;
|
||||||
// repeated LazySymbol lazy_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}
|
VERSION=${1:-14}
|
||||||
OUT=./out
|
OUT=./out
|
||||||
LOGIC=2
|
LOGIC=${2}
|
||||||
|
|
||||||
mkdir -p $OUT
|
mkdir -p $OUT
|
||||||
|
|
||||||
@ -39,21 +39,80 @@ clang++ -mmacosx-version-min=$VERSION -o $OUT/libc.dylib -shared c.cc
|
|||||||
# create our dummy lib first
|
# create our dummy lib first
|
||||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib dummy.cc
|
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib dummy.cc
|
||||||
# build a references libb
|
# 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
|
# 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
|
# 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
|
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
|
else
|
||||||
|
|
||||||
# dummy test build
|
# remove imports test
|
||||||
|
|
||||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libc.dylib -shared c.cc
|
# test rpath
|
||||||
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/libc.dylib -install_name @rpath/libc.dylib -shared c.cc
|
||||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.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
|
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