add old go tooling
This commit is contained in:
33
macho-go/internal/wrapper/action/action.go
Normal file
33
macho-go/internal/wrapper/action/action.go
Normal file
@ -0,0 +1,33 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
// Actions on MachoFile
|
||||
type Action interface {
|
||||
withMacho(mf *MachoFile) error
|
||||
withFat(ff *FatFile) error
|
||||
// TODO: error context
|
||||
}
|
||||
|
||||
func RunAction(action Action, ofile OFile) error {
|
||||
switch ofile.(type) {
|
||||
case *MachoFile:
|
||||
return action.withMacho(ofile.(*MachoFile))
|
||||
case *FatFile:
|
||||
return action.withFat(ofile.(*FatFile))
|
||||
}
|
||||
// not likely
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultWithFat(action Action, ff *FatFile) error {
|
||||
for _, macho := range ff.Machos() {
|
||||
err := action.withMacho(macho)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
31
macho-go/internal/wrapper/action/add_dylib.go
Normal file
31
macho-go/internal/wrapper/action/add_dylib.go
Normal file
@ -0,0 +1,31 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type addDylib struct {
|
||||
dylib_to_add []string
|
||||
}
|
||||
|
||||
func (action *addDylib) withMacho(mf *MachoFile) error {
|
||||
for _, dylib := range action.dylib_to_add {
|
||||
mf.Context().AddDylib(dylib)
|
||||
log.WithFields(log.Fields{
|
||||
"dylib": dylib,
|
||||
}).Info("Add Load Dylib Command")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *addDylib) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewAddDylibAction(dylib_to_add []string) *addDylib {
|
||||
return &addDylib{
|
||||
dylib_to_add,
|
||||
}
|
||||
}
|
31
macho-go/internal/wrapper/action/add_rpath.go
Normal file
31
macho-go/internal/wrapper/action/add_rpath.go
Normal file
@ -0,0 +1,31 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type addRpath struct {
|
||||
rpath []string
|
||||
}
|
||||
|
||||
func (action *addRpath) withMacho(mf *MachoFile) error {
|
||||
for _, rpath := range action.rpath {
|
||||
mf.Context().AddRPath(rpath)
|
||||
log.WithFields(log.Fields{
|
||||
"rpath": rpath,
|
||||
}).Info("Add Rpath Command")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *addRpath) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewAddRpathAction(rpath []string) *addRpath {
|
||||
return &addRpath{
|
||||
rpath,
|
||||
}
|
||||
}
|
20
macho-go/internal/wrapper/action/remove_classic_symbols.go
Normal file
20
macho-go/internal/wrapper/action/remove_classic_symbols.go
Normal file
@ -0,0 +1,20 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type removeClassicSymbol struct{}
|
||||
|
||||
func (action *removeClassicSymbol) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveClassicSymbol()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *removeClassicSymbol) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewRemoveClassicSymbolAction() *removeClassicSymbol {
|
||||
return &removeClassicSymbol{}
|
||||
}
|
20
macho-go/internal/wrapper/action/remove_code.go
Normal file
20
macho-go/internal/wrapper/action/remove_code.go
Normal file
@ -0,0 +1,20 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type removeCodeSignature struct{}
|
||||
|
||||
func (action *removeCodeSignature) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveCodesign()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *removeCodeSignature) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewRemoveCodeSignatureAction() *removeCodeSignature {
|
||||
return &removeCodeSignature{}
|
||||
}
|
20
macho-go/internal/wrapper/action/remove_init_pointer.go
Normal file
20
macho-go/internal/wrapper/action/remove_init_pointer.go
Normal file
@ -0,0 +1,20 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type removeInitPointer struct{}
|
||||
|
||||
func (action *removeInitPointer) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveInitFunctions()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *removeInitPointer) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewRemoveInitPointerAction() *removeInitPointer {
|
||||
return &removeInitPointer{}
|
||||
}
|
20
macho-go/internal/wrapper/action/remove_unncessary_info.go
Normal file
20
macho-go/internal/wrapper/action/remove_unncessary_info.go
Normal file
@ -0,0 +1,20 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type removeUnnecessaryInfo struct{}
|
||||
|
||||
func (action *removeUnnecessaryInfo) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveUnnecessaryInfo()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *removeUnnecessaryInfo) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewRemoveUnnecessaryInfoAction() *removeUnnecessaryInfo {
|
||||
return &removeUnnecessaryInfo{}
|
||||
}
|
68
macho-go/internal/wrapper/action/save_bcell_file.go
Normal file
68
macho-go/internal/wrapper/action/save_bcell_file.go
Normal file
@ -0,0 +1,68 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
type saveBcellFile struct {
|
||||
bcellfile string
|
||||
content *protomodel.BcellFile
|
||||
signed bool
|
||||
}
|
||||
|
||||
func (action *saveBcellFile) addMacho(mf *MachoFile) {
|
||||
action.content.MachoInfos[mf.Context().ArchName()] = mf.Info()
|
||||
}
|
||||
|
||||
func (action *saveBcellFile) writeFile() error {
|
||||
outfile := fmt.Sprintf(action.bcellfile)
|
||||
content, err := proto.Marshal(action.content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if action.signed {
|
||||
blocks := []*protomodel.Block{
|
||||
{
|
||||
Key: 0,
|
||||
Value: content,
|
||||
},
|
||||
}
|
||||
signed_data := protomodel.SignedData{
|
||||
Blocks: blocks,
|
||||
}
|
||||
content, err = proto.Marshal(&signed_data)
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(outfile, content, 0644)
|
||||
}
|
||||
|
||||
func (action *saveBcellFile) withMacho(mf *MachoFile) error {
|
||||
action.addMacho(mf)
|
||||
return action.writeFile()
|
||||
}
|
||||
|
||||
func (action *saveBcellFile) withFat(ff *FatFile) error {
|
||||
for _, macho := range ff.Machos() {
|
||||
action.addMacho(macho)
|
||||
}
|
||||
return action.writeFile()
|
||||
}
|
||||
|
||||
func NewSaveBcellFileAction(userconfig *protomodel.Config, bcellfile string, signed bool) *saveBcellFile {
|
||||
content := &protomodel.BcellFile{
|
||||
BcellConfig: userconfig,
|
||||
MachoInfos: make(map[string]*protomodel.MachoInfo),
|
||||
}
|
||||
return &saveBcellFile{
|
||||
bcellfile,
|
||||
content,
|
||||
signed,
|
||||
}
|
||||
}
|
34
macho-go/internal/wrapper/action/save_init_pointer.go
Normal file
34
macho-go/internal/wrapper/action/save_init_pointer.go
Normal file
@ -0,0 +1,34 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
type saveInitPointer struct{}
|
||||
|
||||
func (action *saveInitPointer) withMacho(mf *MachoFile) error {
|
||||
init_pointers := []*protomodel.MachoInfo_InitPointer{}
|
||||
for _, ptr := range mf.Context().InitFunctions() {
|
||||
init_pointers = append(init_pointers,
|
||||
&protomodel.MachoInfo_InitPointer{
|
||||
Offset: ptr.Offset(),
|
||||
Value: ptr.Value(),
|
||||
})
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"pointers": init_pointers,
|
||||
}).Info("Saved Init Pointers")
|
||||
mf.Info().InitPointers = init_pointers
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *saveInitPointer) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewSaveInitPointerAction() *saveInitPointer {
|
||||
return &saveInitPointer{}
|
||||
}
|
32
macho-go/internal/wrapper/action/write_file.go
Normal file
32
macho-go/internal/wrapper/action/write_file.go
Normal file
@ -0,0 +1,32 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
"ios-wrapper/pkg/ios/fat"
|
||||
)
|
||||
|
||||
type writeFile struct {
|
||||
outfile string
|
||||
}
|
||||
|
||||
func (action *writeFile) withMacho(mf *MachoFile) error {
|
||||
data, _ := ioutil.ReadFile(mf.TmpFile())
|
||||
return ioutil.WriteFile(action.outfile, data, 0644)
|
||||
}
|
||||
|
||||
func (action *writeFile) withFat(ff *FatFile) error {
|
||||
var files []string
|
||||
for _, macho := range ff.Machos() {
|
||||
files = append(files, macho.TmpFile())
|
||||
}
|
||||
fat.FatJoin(files, action.outfile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewWriteFileAction(outfile string) *writeFile {
|
||||
return &writeFile{
|
||||
outfile,
|
||||
}
|
||||
}
|
48
macho-go/internal/wrapper/addr2line/addr2line.go
Normal file
48
macho-go/internal/wrapper/addr2line/addr2line.go
Normal file
@ -0,0 +1,48 @@
|
||||
package addr2line
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
. "ios-wrapper/pkg/ios/macho"
|
||||
)
|
||||
|
||||
// Try to get Image base of a DWARF binary
|
||||
// using __mh_execute_header symbol
|
||||
func TryGetImageBase(mc *MachoContext, symbols []*Symbol) uint64 {
|
||||
try := mc.ImageBase()
|
||||
if try != 0 {
|
||||
return try
|
||||
}
|
||||
for _, symbol := range symbols {
|
||||
if symbol.Name() == "__mh_execute_header" {
|
||||
return symbol.Address()
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// find the symbol name from dysymtab
|
||||
// the address given is somewhere in the function,
|
||||
// assuming that the address is sorted, we find the last symbol
|
||||
// has its address smaller than `tofind`
|
||||
// I'm not sure this would work always, ;)
|
||||
func FindSymbol(symbols []*Symbol, tofind uint64) string {
|
||||
var lastSymbol string = ""
|
||||
for _, symbol := range symbols {
|
||||
if symbol.Address() > tofind {
|
||||
return lastSymbol
|
||||
} else {
|
||||
lastSymbol = symbol.Name()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ParseAddressString(s string) (uint64, error) {
|
||||
s_, err := strconv.ParseInt(s, 0, 64)
|
||||
v := uint64(s_)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
81
macho-go/internal/wrapper/addr2line/resolver.go
Normal file
81
macho-go/internal/wrapper/addr2line/resolver.go
Normal file
@ -0,0 +1,81 @@
|
||||
package addr2line
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
. "ios-wrapper/pkg/ios/macho"
|
||||
)
|
||||
|
||||
type Resolver struct {
|
||||
symbols []*Symbol
|
||||
debuglineInfo *DebuglineInfo
|
||||
aslr uint64
|
||||
addresses []string
|
||||
}
|
||||
|
||||
type Resolved struct {
|
||||
Raw uint64
|
||||
Base uint64
|
||||
Symbol string
|
||||
File string
|
||||
Line uint32
|
||||
}
|
||||
|
||||
func (r *Resolved) Valid() bool {
|
||||
return r.File == "" && r.Line == 0
|
||||
}
|
||||
|
||||
func (resolver *Resolver) HasNext() bool {
|
||||
return len(resolver.addresses) > 0
|
||||
}
|
||||
|
||||
func (resolver *Resolver) Next() (*Resolved, error) {
|
||||
if !resolver.HasNext() {
|
||||
return nil, errors.New("There is no more address to resolve")
|
||||
}
|
||||
|
||||
head, tail := resolver.addresses[0], resolver.addresses[1:]
|
||||
resolver.addresses = tail
|
||||
|
||||
tofind_aslr, err := ParseAddressString(head)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot parse hex address (%s)", head)
|
||||
}
|
||||
|
||||
tofind := tofind_aslr - resolver.aslr
|
||||
symbol := FindSymbol(resolver.symbols, tofind)
|
||||
fileLine := resolver.debuglineInfo.Find(tofind)
|
||||
|
||||
if fileLine == nil {
|
||||
return &Resolved{
|
||||
Raw: tofind_aslr,
|
||||
Base: tofind,
|
||||
Symbol: symbol,
|
||||
File: "",
|
||||
Line: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &Resolved{
|
||||
Raw: tofind_aslr,
|
||||
Base: tofind,
|
||||
Symbol: symbol,
|
||||
File: fileLine.File,
|
||||
Line: fileLine.Line,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewResolver(mc *MachoContext, loadaddr uint64, addresses []string) *Resolver {
|
||||
symbols := mc.CollectSymbols()
|
||||
debuglineInfo := mc.DebugLineInfo()
|
||||
imagebase := TryGetImageBase(mc, symbols)
|
||||
aslr := loadaddr - imagebase
|
||||
|
||||
return &Resolver{
|
||||
symbols,
|
||||
debuglineInfo,
|
||||
aslr,
|
||||
addresses,
|
||||
}
|
||||
}
|
190
macho-go/internal/wrapper/cli.go
Normal file
190
macho-go/internal/wrapper/cli.go
Normal file
@ -0,0 +1,190 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"ios-wrapper/internal/wrapper/addr2line"
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
"ios-wrapper/pkg/ios/fat"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
const envLogLevel = "LOG_LEVEL"
|
||||
const defaultLogLevel = log.InfoLevel
|
||||
|
||||
func getLogLevel() log.Level {
|
||||
levelString, exists := os.LookupEnv(envLogLevel)
|
||||
if !exists {
|
||||
return defaultLogLevel
|
||||
}
|
||||
|
||||
level, err := log.ParseLevel(levelString)
|
||||
if err != nil {
|
||||
return defaultLogLevel
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
func Cli() {
|
||||
var cli Argument
|
||||
ctx := kong.Parse(&cli)
|
||||
command := ctx.Selected().Name
|
||||
|
||||
log.SetLevel(getLogLevel())
|
||||
|
||||
if command == "info" {
|
||||
arg := cli.Info
|
||||
printOFile(arg.OFile)
|
||||
return
|
||||
} else if command == "signed-bcell" {
|
||||
arg := cli.SignedBcell
|
||||
makeSignedBcell(arg.Out, arg.Bcell)
|
||||
return
|
||||
} else if command == "display-bcell" {
|
||||
arg := cli.DisplayBcell
|
||||
displayBcell(arg.Bcell)
|
||||
return
|
||||
} else if command == "addr2line" {
|
||||
arg := cli.Addr2Line
|
||||
resolveAddresses(arg.Dwarf, arg.Load, arg.Addresses)
|
||||
return
|
||||
} else if command == "lipo split" {
|
||||
arg := cli.Lipo.Split
|
||||
fat.FatSplit(arg.Fat)
|
||||
return
|
||||
} else if command == "lipo join" {
|
||||
arg := cli.Lipo.Join
|
||||
fat.FatJoin(arg.Macho, arg.Out)
|
||||
return
|
||||
}
|
||||
|
||||
var pc ProgramContext
|
||||
var ofile OFile = nil
|
||||
|
||||
switch command {
|
||||
case "wrap":
|
||||
arg := cli.Wrap
|
||||
ofile = NewOFile(arg.OFile)
|
||||
pc.remove_codesign = true
|
||||
pc.strip_init_pointers = true
|
||||
pc.dylib_to_add = []string{"@rpath/bcell.framework/bcell"}
|
||||
pc.rpath_to_add = []string{"@executable_path/Frameworks"}
|
||||
pc.outfile = arg.Out
|
||||
pc.bcellfile = arg.Bcell
|
||||
pc.signed = arg.Signed
|
||||
pc.ReadUserConfig(arg.Config)
|
||||
|
||||
case "vltk":
|
||||
arg := cli.Vltk
|
||||
ofile = NewOFile(arg.OFile)
|
||||
pc.remove_codesign = true
|
||||
pc.dylib_to_add = []string{"@rpath/GTJetModule.framework/GTJetModule"}
|
||||
pc.outfile = arg.Out
|
||||
|
||||
case "remove-codesign", "remove-signature":
|
||||
arg := cli.RemoveCodesign
|
||||
ofile = NewOFile(arg.OFile)
|
||||
pc.remove_codesign = true
|
||||
pc.outfile = arg.Out
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
pc.Process(ofile)
|
||||
ofile.Cleanup()
|
||||
}
|
||||
|
||||
func printOFile(ofile string) {
|
||||
f, _ := os.OpenFile(ofile, os.O_RDWR, 0644)
|
||||
printer := InfoPrinterFromFile(f)
|
||||
printer.Print()
|
||||
}
|
||||
|
||||
func makeSignedBcell(outfile string, infile string) {
|
||||
raw_data, err := ioutil.ReadFile(infile)
|
||||
if err != nil {
|
||||
log.Panic("Cannot read bcell.dat")
|
||||
}
|
||||
data := &protomodel.BcellFile{}
|
||||
err = proto.Unmarshal(raw_data, data)
|
||||
if err != nil {
|
||||
log.Panic("Invalid bcell.dat")
|
||||
}
|
||||
|
||||
blocks := []*protomodel.Block{
|
||||
{
|
||||
Key: 0,
|
||||
Value: raw_data,
|
||||
},
|
||||
}
|
||||
signed_data := protomodel.SignedData{
|
||||
Blocks: blocks,
|
||||
}
|
||||
signed_data_b, err := proto.Marshal(&signed_data)
|
||||
err = ioutil.WriteFile(outfile, signed_data_b, 0644)
|
||||
if err != nil {
|
||||
log.Panic("Cannot write SignedData to file")
|
||||
}
|
||||
}
|
||||
|
||||
func displayBcell(bfile string) {
|
||||
raw_data, err := ioutil.ReadFile(bfile)
|
||||
if err != nil {
|
||||
log.Panic("Invalid Protobuf<BcellFile> bcell.dat (1)")
|
||||
}
|
||||
data := &protomodel.BcellFile{}
|
||||
err = proto.Unmarshal(raw_data, data)
|
||||
if err != nil {
|
||||
log.Panic("Invalid Protobuf<BcellFile> bcell.dat (2)")
|
||||
}
|
||||
|
||||
fmt.Printf("[+] User Config: %+v\n", data.BcellConfig)
|
||||
for arch, info := range data.MachoInfos {
|
||||
fmt.Printf("[+] Arch %s:\n", arch)
|
||||
fmt.Printf(" | PointerSize : %+v\n", info.PointerSize)
|
||||
fmt.Printf(" | Image Base : 0x%x\n", info.ImageBase)
|
||||
fmt.Printf(" | Init Pointers:\n")
|
||||
for _, init_ptr := range info.InitPointers {
|
||||
fmt.Printf(
|
||||
" | offset 0x%x => addr 0x%x\n",
|
||||
init_ptr.Offset,
|
||||
init_ptr.Value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resolveAddresses(dwarf string, load string, addresses []string) {
|
||||
mf, valid_macho := NewOFile(dwarf).(*MachoFile)
|
||||
if !valid_macho {
|
||||
log.Error("Input DWARF file is not a valid Macho binary")
|
||||
return
|
||||
}
|
||||
|
||||
loadaddr, err := addr2line.ParseAddressString(load)
|
||||
if err != nil {
|
||||
log.Error("Load address is not a valid hex number (%s)", load)
|
||||
return
|
||||
}
|
||||
|
||||
resolver := addr2line.NewResolver(mf.Context(), loadaddr, addresses)
|
||||
for ; ; resolver.HasNext() {
|
||||
resolved, err := resolver.Next()
|
||||
if err != nil {
|
||||
fmt.Printf("[?] Error: %s\n", err)
|
||||
} else if resolved.Valid() {
|
||||
fmt.Printf("[+] Found 0x%x => 0x%x %s %s:%d\n",
|
||||
resolved.Raw, resolved.Base, resolved.Symbol, resolved.File, resolved.Line)
|
||||
} else {
|
||||
fmt.Printf("[-] Not found 0x%x\n", resolved.Raw)
|
||||
}
|
||||
}
|
||||
}
|
66
macho-go/internal/wrapper/cli_parser.go
Normal file
66
macho-go/internal/wrapper/cli_parser.go
Normal file
@ -0,0 +1,66 @@
|
||||
package wrapper
|
||||
|
||||
type WrapArgument struct {
|
||||
Config string `short:"c" required help:"User config.json" type:"existingfile"`
|
||||
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"`
|
||||
Signed bool `short:"s" required help:"Output Protobuf<SignedData> for bcell.dat"`
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
}
|
||||
|
||||
type VltkArgument struct {
|
||||
Out string `short:"o" required name:"outfile" help:"Modified Mach-O/Fat binary output file" type:"path"`
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
}
|
||||
|
||||
type InfoArgument struct {
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
All bool `short:"a" help:"If print all information"`
|
||||
Arch string `help:"Only print information in this arch"`
|
||||
}
|
||||
|
||||
type RemoveCodesignArgument struct {
|
||||
Out string `short:"o" required name:"outfile" help:"Modified Mach-O/Fat binary output file" type:"path"`
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
}
|
||||
|
||||
type SignedBcellArgument struct {
|
||||
Out string `short:"o" required name:"outfile" help:"bcell.dat.signed output file" type:"existingfile"`
|
||||
Bcell string `arg short:"b" required help:"bcell.dat input file" type:"existingfile"`
|
||||
}
|
||||
|
||||
type DisplayBcellArgument struct {
|
||||
Bcell string `arg short:"b" required help:"Protobuf<BcellFile> bcell.dat input file" type:"existingfile"`
|
||||
}
|
||||
|
||||
type Addr2LineArgument struct {
|
||||
Load string `short:"l" required help:"Load address of binary"`
|
||||
Dwarf string `short:"d" required help:"Path to Mach-O DWARF binary file" type:"existingfile"`
|
||||
Addresses []string `arg help:"Addresses to resolve"`
|
||||
}
|
||||
|
||||
type LipoArgument struct {
|
||||
Join struct {
|
||||
Out string `short:"o" required help:"Output Fat binary file" type:"path"`
|
||||
Macho []string `arg help:"Macho binaries to join" type:"existingfile"`
|
||||
} `cmd help:"Join Macho binaries into Fat binary"`
|
||||
Split struct {
|
||||
Fat string `arg help:"Fat binary to split" type:"existingfile"`
|
||||
} `cmd help:"Split fat binary into files, named <name>_<arch>"`
|
||||
}
|
||||
|
||||
type PepeArgument struct {
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
}
|
||||
|
||||
type Argument struct {
|
||||
Wrap WrapArgument `cmd help:"Modifies Mach-O/Fat binary"`
|
||||
Vltk VltkArgument `cmd help:"Modifies Mach-O/Fat binary"`
|
||||
Info InfoArgument `cmd help:"Show Mach-O/Fat binary information"`
|
||||
RemoveCodesign RemoveCodesignArgument `cmd aliases:"remove-signature" name:"remove-codesign" help:"Remove LC_CODE_SIGNATURE from Mach-O/Fat binary"`
|
||||
SignedBcell SignedBcellArgument `cmd name:"signed-bcell" help:"Change Protobuf<BcellFile> into Protobuf<SignedData>"`
|
||||
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"`
|
||||
}
|
110
macho-go/internal/wrapper/info.go
Normal file
110
macho-go/internal/wrapper/info.go
Normal file
@ -0,0 +1,110 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
. "ios-wrapper/pkg/ios/fat"
|
||||
. "ios-wrapper/pkg/ios/macho"
|
||||
)
|
||||
|
||||
type InfoPrinter struct {
|
||||
contexts []*MachoContext
|
||||
}
|
||||
|
||||
func (printer *InfoPrinter) Print() {
|
||||
for _, mc := range printer.contexts {
|
||||
fmt.Printf("Mach-O arch: %s\n", mc.ArchName())
|
||||
|
||||
for i, cmd := range mc.Commands() {
|
||||
fmt.Printf("%d: %s\n", i, cmd.Cmdname())
|
||||
}
|
||||
|
||||
fmt.Printf("Image Base: 0x%x\n", mc.ImageBase())
|
||||
|
||||
init_funcs := mc.InitFunctions()
|
||||
if len(init_funcs) == 0 {
|
||||
fmt.Println("No Init functions")
|
||||
}
|
||||
for _, fun := range init_funcs {
|
||||
fmt.Printf("Init functions at offset %s\n", &fun)
|
||||
}
|
||||
|
||||
|
||||
symbols := mc.CollectLazyBindSymbols()
|
||||
if len(symbols) > 0 {
|
||||
fmt.Println("Lazy Symbols")
|
||||
for _, sym := range symbols {
|
||||
fmt.Printf(
|
||||
"%s\n\tStub=0x%x Address=0x%x\n\tDylib=%s\n",
|
||||
sym.Name(),
|
||||
sym.Stub(),
|
||||
sym.Address(),
|
||||
sym.Dylib(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("No lazy symbols")
|
||||
}
|
||||
|
||||
fmt.Println("======")
|
||||
}
|
||||
}
|
||||
|
||||
func machoPrinter(file *os.File) *InfoPrinter {
|
||||
var mc MachoContext
|
||||
err := mc.ParseFile(file, 0)
|
||||
if err != nil {
|
||||
return &InfoPrinter{
|
||||
contexts: []*MachoContext{},
|
||||
}
|
||||
} else {
|
||||
return &InfoPrinter{
|
||||
contexts: []*MachoContext{&mc},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fatPrinter(file *os.File) *InfoPrinter {
|
||||
var fat FatContext
|
||||
fat.ParseFile(file)
|
||||
|
||||
var contexts []*MachoContext
|
||||
for _, m := range fat.Machos() {
|
||||
buf := make([]byte, m.Size)
|
||||
file.Seek(int64(m.Offset), io.SeekStart)
|
||||
file.Read(buf)
|
||||
|
||||
var mc MachoContext
|
||||
mc.ParseBuffer(buf)
|
||||
contexts = append(contexts, &mc)
|
||||
}
|
||||
return &InfoPrinter{
|
||||
contexts,
|
||||
}
|
||||
}
|
||||
|
||||
func InfoPrinterFromFile(file *os.File) *InfoPrinter {
|
||||
var magic uint32
|
||||
magic_buf := []byte{0, 0, 0, 0}
|
||||
|
||||
file.Read(magic_buf)
|
||||
magic_r := bytes.NewReader(magic_buf)
|
||||
binary.Read(magic_r, binary.LittleEndian, &magic)
|
||||
file.Seek(0, io.SeekStart)
|
||||
|
||||
if magic == MagicFat || magic == CigamFat {
|
||||
return fatPrinter(file)
|
||||
}
|
||||
|
||||
if magic == Magic32 || magic == Magic64 || magic == Cigam32 ||
|
||||
magic == Cigam64 {
|
||||
return machoPrinter(file)
|
||||
}
|
||||
|
||||
return &InfoPrinter{}
|
||||
}
|
58
macho-go/internal/wrapper/ofile/fatfile.go
Normal file
58
macho-go/internal/wrapper/ofile/fatfile.go
Normal file
@ -0,0 +1,58 @@
|
||||
package ofile
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"ios-wrapper/pkg/ios/fat"
|
||||
)
|
||||
|
||||
type FatFile struct {
|
||||
machos []*MachoFile
|
||||
files []string
|
||||
tmp_file string
|
||||
}
|
||||
|
||||
func (ff *FatFile) Machos() []*MachoFile {
|
||||
return ff.machos
|
||||
}
|
||||
|
||||
func (ff *FatFile) Cleanup() {
|
||||
for _, macho := range ff.machos {
|
||||
macho.Cleanup()
|
||||
}
|
||||
os.Remove(ff.tmp_file)
|
||||
}
|
||||
|
||||
func NewFatFile(f string) *FatFile {
|
||||
// create a tmp working file
|
||||
tmp, _ := ioutil.TempFile("", "bcell_*")
|
||||
data, _ := ioutil.ReadFile(f)
|
||||
ioutil.WriteFile(tmp.Name(), data, 0644)
|
||||
|
||||
var machos []*MachoFile
|
||||
var files []string
|
||||
splitted_files, err := fat.FatSplit(tmp.Name())
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"err": err,
|
||||
"splitted_ok": splitted_files,
|
||||
}).Panic("Cannot split Fat binary")
|
||||
}
|
||||
for _, splitted_file := range splitted_files {
|
||||
macho := NewMachoFile(splitted_file)
|
||||
machos = append(machos, macho)
|
||||
files = append(files, macho.tmp_file)
|
||||
|
||||
// NewMachoFile creates another tmp file, remove this temp splitted file
|
||||
os.Remove(splitted_file)
|
||||
}
|
||||
|
||||
return &FatFile{
|
||||
machos: machos,
|
||||
tmp_file: tmp.Name(),
|
||||
files: files,
|
||||
}
|
||||
}
|
56
macho-go/internal/wrapper/ofile/machofile.go
Normal file
56
macho-go/internal/wrapper/ofile/machofile.go
Normal file
@ -0,0 +1,56 @@
|
||||
package ofile
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
// log "github.com/sirupsen/logrus"
|
||||
|
||||
"ios-wrapper/pkg/ios/macho"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
type MachoFile struct {
|
||||
mc *macho.MachoContext
|
||||
tmp_file string
|
||||
info *protomodel.MachoInfo
|
||||
}
|
||||
|
||||
func (mf *MachoFile) Context() *macho.MachoContext {
|
||||
return mf.mc
|
||||
}
|
||||
|
||||
func (mf *MachoFile) Info() *protomodel.MachoInfo {
|
||||
return mf.info
|
||||
}
|
||||
|
||||
func (mf *MachoFile) TmpFile() string {
|
||||
return mf.tmp_file
|
||||
}
|
||||
|
||||
func (mf *MachoFile) Cleanup() {
|
||||
os.Remove(mf.tmp_file)
|
||||
}
|
||||
|
||||
func NewMachoFile(f string) *MachoFile {
|
||||
// create a tmp working file
|
||||
tmp, _ := ioutil.TempFile("", "bcell_*")
|
||||
data, _ := ioutil.ReadFile(f)
|
||||
ioutil.WriteFile(tmp.Name(), data, 0644)
|
||||
|
||||
var mc macho.MachoContext
|
||||
tmp, _ = os.OpenFile(tmp.Name(), os.O_RDWR, 0644)
|
||||
mc.ParseFile(tmp, 0)
|
||||
|
||||
return &MachoFile{
|
||||
mc: &mc,
|
||||
tmp_file: tmp.Name(),
|
||||
info: &protomodel.MachoInfo{
|
||||
PointerSize: map[bool]protomodel.MachoInfo_PointerSize{
|
||||
false: protomodel.MachoInfo_p32,
|
||||
true: protomodel.MachoInfo_p64,
|
||||
}[mc.Is64bit()],
|
||||
ImageBase: mc.ImageBase(),
|
||||
},
|
||||
}
|
||||
}
|
47
macho-go/internal/wrapper/ofile/ofile.go
Normal file
47
macho-go/internal/wrapper/ofile/ofile.go
Normal file
@ -0,0 +1,47 @@
|
||||
package ofile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
)
|
||||
|
||||
type OFile interface {
|
||||
Cleanup()
|
||||
}
|
||||
|
||||
func NewOFile(f string) OFile {
|
||||
file, err := os.Open(f)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
r := bufio.NewReader(file)
|
||||
|
||||
var magic uint32
|
||||
magic_buf, _ := r.Peek(4)
|
||||
magic_r := bytes.NewReader(magic_buf)
|
||||
binary.Read(magic_r, binary.LittleEndian, &magic)
|
||||
|
||||
if magic != Magic32 && magic != Magic64 && magic != Cigam32 && magic != Cigam64 && magic != MagicFat &&
|
||||
magic != CigamFat {
|
||||
log.Panic("Magic does not match %x", magic)
|
||||
return nil
|
||||
}
|
||||
|
||||
if magic == Magic32 || magic == Magic64 || magic == Cigam32 ||
|
||||
magic == Cigam64 {
|
||||
return NewMachoFile(f)
|
||||
}
|
||||
|
||||
if magic == MagicFat || magic == CigamFat {
|
||||
return NewFatFile(f)
|
||||
}
|
||||
|
||||
// not likely
|
||||
return nil
|
||||
}
|
110
macho-go/internal/wrapper/program_context.go
Normal file
110
macho-go/internal/wrapper/program_context.go
Normal file
@ -0,0 +1,110 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/internal/wrapper/action"
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
type UserConfig struct {
|
||||
Repack bool `json:"repack"`
|
||||
Hook bool `json:"hook"`
|
||||
Emulator bool `json:"emulator"`
|
||||
Debug bool `json:"debug"`
|
||||
Root bool `json:"root"`
|
||||
ServerURL string `json:"server_url"`
|
||||
ReportType int32 `json:"report_type"`
|
||||
RemoteConfigURL string `json:"remote_config_url"`
|
||||
DomainID string `json:"domain_id"`
|
||||
}
|
||||
|
||||
func (uc *UserConfig) Protomodel() *protomodel.Config {
|
||||
return &protomodel.Config{
|
||||
Hook: uc.Hook,
|
||||
Repack: uc.Repack,
|
||||
Emulator: uc.Emulator,
|
||||
Debug: uc.Debug,
|
||||
Root: uc.Root,
|
||||
ServerUrl: uc.ServerURL,
|
||||
ReportType: uc.ReportType,
|
||||
RemoteConfigUrl: uc.RemoteConfigURL,
|
||||
DomainId: uc.DomainID,
|
||||
}
|
||||
}
|
||||
|
||||
type ProgramContext struct {
|
||||
strip_init_pointers bool
|
||||
remove_codesign bool
|
||||
dylib_to_add []string
|
||||
rpath_to_add []string
|
||||
|
||||
outfile string
|
||||
|
||||
actions []Action
|
||||
err error
|
||||
|
||||
user_config UserConfig
|
||||
bcellfile string
|
||||
signed bool // true: Protobuf<SignedData>; false: Protobuf<BcellFile>
|
||||
}
|
||||
|
||||
func (pc *ProgramContext) ExplainError(err error) {
|
||||
log.WithFields(log.Fields{
|
||||
"pc error": pc.err,
|
||||
"ofile error": err,
|
||||
}).Error("OFile Process gone wrong")
|
||||
}
|
||||
|
||||
func (pc *ProgramContext) ReadUserConfig(f string) {
|
||||
json_data, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
log.Panic("User Config file is invalid")
|
||||
}
|
||||
json.Unmarshal(json_data, &pc.user_config)
|
||||
}
|
||||
|
||||
func (pc *ProgramContext) AddAction(action Action) {
|
||||
pc.actions = append(pc.actions, action)
|
||||
}
|
||||
|
||||
func (pc *ProgramContext) dispatchActions(ofile OFile) {
|
||||
for _, action := range pc.actions {
|
||||
err := RunAction(action, ofile)
|
||||
if err != nil {
|
||||
pc.err = err
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *ProgramContext) Process(ofile OFile) {
|
||||
if pc.remove_codesign {
|
||||
pc.AddAction(NewRemoveCodeSignatureAction())
|
||||
}
|
||||
if pc.strip_init_pointers {
|
||||
pc.AddAction(NewSaveInitPointerAction())
|
||||
pc.AddAction(NewRemoveInitPointerAction())
|
||||
}
|
||||
ExperimentalFeature("Remove Unnecessary Info", func() {
|
||||
pc.AddAction(NewRemoveUnnecessaryInfoAction())
|
||||
})
|
||||
ExperimentalFeature("Remove Classic Symbol", func() {
|
||||
pc.AddAction(NewRemoveClassicSymbolAction())
|
||||
})
|
||||
pc.AddAction(NewAddRpathAction(pc.rpath_to_add))
|
||||
pc.AddAction(NewAddDylibAction(pc.dylib_to_add))
|
||||
|
||||
if pc.bcellfile != "" {
|
||||
pc.AddAction(NewSaveBcellFileAction(pc.user_config.Protomodel(), pc.bcellfile, pc.signed))
|
||||
} else {
|
||||
log.Warn("bcellfile is not set, no output bcellfile")
|
||||
}
|
||||
pc.AddAction(NewWriteFileAction(pc.outfile))
|
||||
|
||||
pc.dispatchActions(ofile)
|
||||
}
|
30
macho-go/internal/wrapper/util.go
Normal file
30
macho-go/internal/wrapper/util.go
Normal file
@ -0,0 +1,30 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func FileExist(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
func CheckFileExistence(filename string) {
|
||||
if !FileExist(filename) {
|
||||
fmt.Printf("File %s does not exists\n", filename)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func ExperimentalFeature(feature string, foo func()) {
|
||||
if os.Getenv("EXPERIMENTAL") == "1" {
|
||||
log.Info(fmt.Sprintf("Running Experimental Feature %s", feature))
|
||||
foo()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user