format code
This commit is contained in:
parent
4016abf40d
commit
f88861a87e
@ -4,13 +4,13 @@ import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type rewriteImports struct{
|
||||
symbols []string
|
||||
type rewriteImports struct {
|
||||
symbols []string
|
||||
}
|
||||
|
||||
func (action *rewriteImports) withMacho(mf *MachoFile) error {
|
||||
mc := mf.Context()
|
||||
mc.RewriteImportsTable(action.symbols)
|
||||
mc := mf.Context()
|
||||
mc.RewriteImportsTable(action.symbols)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ func (action *rewriteImports) withFat(ff *FatFile) error {
|
||||
|
||||
func NewRewriteImportsWithKeepSymbolsAction(symbols []string) *rewriteImports {
|
||||
return &rewriteImports{
|
||||
symbols,
|
||||
}
|
||||
symbols,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,24 +3,24 @@ package action
|
||||
import (
|
||||
// "fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"strings"
|
||||
// log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
type saveImports struct{
|
||||
keepSymbols []string
|
||||
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
|
||||
action.saveToInfo(mf)
|
||||
mc := mf.Context()
|
||||
if mc.Header().IsDylib() {
|
||||
mc.WriteInfoToData(mf.Info())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *saveImports) saveToInfo(mf *MachoFile) error {
|
||||
@ -64,28 +64,28 @@ func (action *saveImports) saveToInfo(mf *MachoFile) error {
|
||||
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]
|
||||
}
|
||||
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 symbol.Name() != name {
|
||||
continue
|
||||
}
|
||||
if lib == "" || lib == symbol.Dylib() {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
|
||||
// dylib_hash := calculateHash(symbol.Dylib())
|
||||
seg := mc.Segments()[symbol.Segment()]
|
||||
@ -139,6 +139,6 @@ func (action *saveImports) withFat(ff *FatFile) error {
|
||||
|
||||
func NewSaveImportsAction(keepSymbols []string) *saveImports {
|
||||
return &saveImports{
|
||||
keepSymbols,
|
||||
}
|
||||
keepSymbols,
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func Cli() {
|
||||
pc.rpath_to_add = arg.Rpath
|
||||
pc.outfile = arg.Out
|
||||
pc.bcellfile = arg.Bcell
|
||||
pc.symbols_keep = arg.KeepImports
|
||||
pc.symbols_keep = arg.KeepImports
|
||||
|
||||
default:
|
||||
return
|
||||
|
@ -72,7 +72,7 @@ type PepeArgument struct {
|
||||
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"`
|
||||
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 {
|
||||
|
@ -46,7 +46,7 @@ type ProgramContext struct {
|
||||
remove_symbol_table bool
|
||||
dylib_to_add []string
|
||||
rpath_to_add []string
|
||||
symbols_keep []string
|
||||
symbols_keep []string
|
||||
|
||||
outfile string
|
||||
|
||||
@ -98,7 +98,7 @@ func (pc *ProgramContext) Process(ofile OFile) {
|
||||
if pc.remove_imports {
|
||||
pc.AddAction(NewSaveImportsAction(pc.symbols_keep))
|
||||
pc.AddAction(NewRemoveImportsAction())
|
||||
pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep))
|
||||
pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep))
|
||||
}
|
||||
if pc.remove_symbol_table {
|
||||
pc.AddAction(NewRemoveClassicSymbolAction())
|
||||
|
@ -25,7 +25,7 @@ type ImportSymbol struct {
|
||||
segment_offset uint32
|
||||
address uint64
|
||||
file_address uint64
|
||||
lib_ordinal uint32
|
||||
lib_ordinal uint32
|
||||
|
||||
// push number
|
||||
pnum uint32
|
||||
@ -121,7 +121,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
// fmt.Printf("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count)
|
||||
// fmt.Printf("pages=%x\n", fix.pages)
|
||||
pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count))
|
||||
reader := bytes.NewReader(mc.buf)
|
||||
reader := bytes.NewReader(mc.buf)
|
||||
for page_i := 0; page_i < int(fix.page_count); page_i++ {
|
||||
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
||||
|
||||
@ -151,7 +151,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
sym.name = name
|
||||
sym.dylib = dylib
|
||||
sym.typ = "lazy"
|
||||
sym.lib_ordinal = uint32(s.lib_ordinal)
|
||||
sym.lib_ordinal = uint32(s.lib_ordinal)
|
||||
|
||||
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
||||
sym.file_address = uint64(address)
|
||||
|
@ -2,17 +2,17 @@ package macho
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
"strings"
|
||||
"encoding/binary"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
. "ios-wrapper/pkg/ios"
|
||||
"ios-wrapper/pkg/protomodel"
|
||||
)
|
||||
|
||||
// #include "fixups.h"
|
||||
@ -285,10 +285,10 @@ func (mc *MachoContext) RemoveBindSymbols() {
|
||||
} else {
|
||||
mc.removeBindSymbolsLegacy()
|
||||
}
|
||||
// Objective-C stub replaces main which can only appears in executable
|
||||
if mc.Header().IsExecutable() {
|
||||
mc.ReworkForObjc()
|
||||
}
|
||||
// Objective-C stub replaces main which can only appears in executable
|
||||
if mc.Header().IsExecutable() {
|
||||
mc.ReworkForObjc()
|
||||
}
|
||||
|
||||
// due to some limitations when design this tool
|
||||
// we write the c code to stdout lol
|
||||
@ -445,27 +445,27 @@ func (mc *MachoContext) ReworkForObjc() {
|
||||
// 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
|
||||
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
|
||||
stp x30, x8, [sp], #-0x10
|
||||
stp x3, x2, [sp], #-0x10
|
||||
stp x1, x0, [sp], #-0x10
|
||||
|
||||
# custom intializer
|
||||
ldr x9, [x8]
|
||||
blr x9
|
||||
# custom intializer
|
||||
ldr x9, [x8]
|
||||
blr x9
|
||||
|
||||
ldp x1, x0, [sp, #0x10]!
|
||||
ldp x3, x2, [sp, #0x10]!
|
||||
ldp x30, x8, [sp, #0x10]!
|
||||
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
|
||||
# original main
|
||||
# link register is set so jump only
|
||||
ldr x9, [x8, #8]
|
||||
br x9
|
||||
*/
|
||||
|
||||
// TODO: fix to work with offset larger than 0xffff
|
||||
@ -529,72 +529,72 @@ func (mc *MachoContext) ReworkForObjc() {
|
||||
|
||||
func (mc *MachoContext) RewriteImportsTable(keepSymbols []string) {
|
||||
allSymbols := mc.CollectBindSymbols()
|
||||
fixups, fixupsOffset := mc.Fixups()
|
||||
fixups, fixupsOffset := mc.Fixups()
|
||||
|
||||
importTable := fixups.ImportsOffset(uint32(fixupsOffset))
|
||||
symbolTable := fixups.SymbolsOffset(uint32(fixupsOffset))
|
||||
symbolTablePtr := symbolTable
|
||||
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
|
||||
// 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
|
||||
|
||||
keepCount := uint32(0)
|
||||
for _, symbol := range keepSymbols {
|
||||
name := symbol
|
||||
lib := ""
|
||||
parts := strings.Split(symbol, ",")
|
||||
if len(parts) == 2 {
|
||||
name = parts[0]
|
||||
lib = parts[1]
|
||||
}
|
||||
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
|
||||
}
|
||||
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)
|
||||
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))
|
||||
// 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))
|
||||
}
|
||||
{
|
||||
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
|
||||
}
|
||||
keepCount += 1
|
||||
importTable += 4
|
||||
symbolTablePtr += uint32(len(name)) + 1
|
||||
}
|
||||
|
||||
fixups.imports_count = keepCount
|
||||
fixups.imports_count = keepCount
|
||||
mc.file.WriteAt(fixups.Serialize(mc), int64(mc.fixups.dataoff))
|
||||
}
|
||||
|
||||
@ -685,128 +685,128 @@ func (mc *MachoContext) RemoveExportTrie() {
|
||||
}
|
||||
|
||||
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
|
||||
// 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 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()
|
||||
// }
|
||||
}
|
||||
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
|
||||
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)")
|
||||
}
|
||||
}
|
||||
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
|
||||
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))
|
||||
// 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)
|
||||
fmt.Printf("Add a new section with addr=0x%x, fileoffset=0x%x\n", section.addr, section.offset)
|
||||
|
||||
ret = §ion
|
||||
continue
|
||||
ret = §ion
|
||||
continue
|
||||
|
||||
default:
|
||||
buffer.Write(command.Serialize(mc))
|
||||
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
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
binary.Write(buffer, mc.byteorder, (symbol.SymbolIndex<<8)|symbol.SegmentIndex)
|
||||
binary.Write(buffer, mc.byteorder, symbol.Offset)
|
||||
}
|
||||
}
|
||||
instructions := buffer.Bytes()
|
||||
instructions := buffer.Bytes()
|
||||
|
||||
buffer = new(bytes.Buffer)
|
||||
buffer = new(bytes.Buffer)
|
||||
for _, lib := range info.Symbols.Libs {
|
||||
buffer.WriteString(lib)
|
||||
buffer.WriteByte(0)
|
||||
buffer.WriteString(lib)
|
||||
buffer.WriteByte(0)
|
||||
}
|
||||
liblist := buffer.Bytes()
|
||||
liblist := buffer.Bytes()
|
||||
|
||||
buffer = new(bytes.Buffer)
|
||||
buffer = new(bytes.Buffer)
|
||||
for _, symbol := range info.Symbols.Symbols {
|
||||
buffer.WriteString(symbol)
|
||||
buffer.WriteByte(0)
|
||||
buffer.WriteString(symbol)
|
||||
buffer.WriteByte(0)
|
||||
}
|
||||
symbollist := buffer.Bytes()
|
||||
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)
|
||||
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()))
|
||||
}
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
@ -38,11 +38,11 @@ func (h *Header) Cpusubtype() uint32 {
|
||||
}
|
||||
|
||||
func (h *Header) IsExecutable() bool {
|
||||
return h.filetype == 0x2
|
||||
return h.filetype == 0x2
|
||||
}
|
||||
|
||||
func (h *Header) IsDylib() bool {
|
||||
return h.filetype == 0x6
|
||||
return h.filetype == 0x6
|
||||
}
|
||||
|
||||
func (h *Header) Serialize(mc *MachoContext) []byte {
|
||||
@ -135,7 +135,7 @@ func (lcmd *LoadCmd) Cmdname() string {
|
||||
return "LC_DATA_IN_CODE"
|
||||
case LC_SYMTAB:
|
||||
return "LC_SYMTAB"
|
||||
case LC_BUILD_VERSION:
|
||||
case LC_BUILD_VERSION:
|
||||
return "LC_BUILD_VERSION"
|
||||
case LC_UUID:
|
||||
return "LC_UUID"
|
||||
@ -425,11 +425,11 @@ type Fixups struct {
|
||||
}
|
||||
|
||||
func (lcmd *Fixups) ImportsOffset(fixups_offset uint32) uint32 {
|
||||
return lcmd.imports_offset + fixups_offset
|
||||
return lcmd.imports_offset + fixups_offset
|
||||
}
|
||||
|
||||
func (lcmd *Fixups) SymbolsOffset(fixups_offset uint32) uint32 {
|
||||
return lcmd.symbols_offset + fixups_offset
|
||||
return lcmd.symbols_offset + fixups_offset
|
||||
}
|
||||
|
||||
func (lcmd *Fixups) Serialize(mc *MachoContext) []byte {
|
||||
|
@ -73,7 +73,7 @@ func (mc *MachoContext) Fixups() (*Fixups, uint64) {
|
||||
size := mc.fixups.datasize
|
||||
fixups := new(Fixups)
|
||||
fixups.Deserialize(mc, mc.buf[start:start+size])
|
||||
return fixups, uint64(mc.fixups.dataoff)
|
||||
return fixups, uint64(mc.fixups.dataoff)
|
||||
}
|
||||
|
||||
func (mc *MachoContext) WriteEnabled() bool {
|
||||
|
@ -22,19 +22,17 @@ int custom_strcmp(const char *p1, const char *p2) {
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
int custom_strncmp(const char *s1, const char *s2, register size_t n)
|
||||
{
|
||||
int custom_strncmp(const char *s1, const char *s2, register size_t n) {
|
||||
register unsigned char u1, u2;
|
||||
|
||||
while (n-- > 0)
|
||||
{
|
||||
u1 = (unsigned char) *s1++;
|
||||
u2 = (unsigned char) *s2++;
|
||||
if (u1 != u2)
|
||||
return u1 - u2;
|
||||
if (u1 == '\0')
|
||||
return 0;
|
||||
}
|
||||
while (n-- > 0) {
|
||||
u1 = (unsigned char)*s1++;
|
||||
u2 = (unsigned char)*s2++;
|
||||
if (u1 != u2)
|
||||
return u1 - u2;
|
||||
if (u1 == '\0')
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -64,7 +62,7 @@ struct libcache_item {
|
||||
|
||||
// pointer to segment address
|
||||
uint32_t nsegment;
|
||||
uint64_t* segment;
|
||||
uint64_t *segment;
|
||||
};
|
||||
|
||||
struct libcache {
|
||||
@ -216,7 +214,7 @@ uint64_t get_slide(const void *header) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* get_selfbind(const void *header) {
|
||||
void *get_selfbind(const void *header) {
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
@ -226,7 +224,7 @@ void* get_selfbind(const void *header) {
|
||||
}
|
||||
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
char* command_ptr = ptr;
|
||||
char *command_ptr = ptr;
|
||||
|
||||
uint64_t slide;
|
||||
for (int i = 0; i < ncmds; i++) {
|
||||
@ -240,17 +238,17 @@ void* get_selfbind(const void *header) {
|
||||
if (custom_strcmp(name, "__TEXT") == 0) {
|
||||
slide = (uint64_t)header - vmaddr;
|
||||
} else if (custom_strcmp(name, "__DATA") == 0) {
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||
sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1);
|
||||
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char* secname = sections_ptr;
|
||||
char *secname = sections_ptr;
|
||||
if (custom_strcmp(secname, "selfbind") == 0) {
|
||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
||||
uint32_t *data_ptr = (uint32_t*)(addr + slide);
|
||||
return (void*)data_ptr;
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
uint32_t *data_ptr = (uint32_t *)(addr + slide);
|
||||
return (void *)data_ptr;
|
||||
}
|
||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||
}
|
||||
@ -302,15 +300,16 @@ void print_macho_summary(const void *header) {
|
||||
printf(" vmaddr=0x%llx fileoffset=0x%llx\n", vmaddr, fileoffset);
|
||||
printf(" vmsize=0x%llx filesize=0x%llx\n", vmsize, filesize);
|
||||
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char* secname = sections_ptr;
|
||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
||||
uint32_t fileoffset = *((uint32_t*)sections_ptr + 6*2);
|
||||
char *secname = sections_ptr;
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
uint32_t fileoffset = *((uint32_t *)sections_ptr + 6 * 2);
|
||||
printf(" Section %s\n", sections_ptr);
|
||||
printf(" addr=0x%llx size=0x%llx fileoffset=0x%x\n", addr, size, fileoffset);
|
||||
printf(" addr=0x%llx size=0x%llx fileoffset=0x%x\n", addr, size,
|
||||
fileoffset);
|
||||
}
|
||||
}
|
||||
if (cmd == LC_SYMTAB) {
|
||||
@ -327,15 +326,18 @@ void print_macho_summary(const void *header) {
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
uint64_t symtab_start = (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||
uint64_t stroff_start = (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||
uint64_t symtab_start =
|
||||
(uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||
uint64_t stroff_start =
|
||||
(uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||
|
||||
printf(" symtab: offset=0x%x nsym=0x%x\n", symoff, nsym);
|
||||
for (int j = 0; j < nsym; j++) {
|
||||
struct symbol_t* symtab = (struct symbol_t*)symtab_start;
|
||||
struct symbol_t *symtab = (struct symbol_t *)symtab_start;
|
||||
struct symbol_t symbol = symtab[j];
|
||||
char* name = (char*)stroff_start + symbol.strx;
|
||||
printf(" %s %llx => %p\n", name, symbol.value, (void*)(symbol.value + slide));
|
||||
char *name = (char *)stroff_start + symbol.strx;
|
||||
printf(" %s %llx => %p\n", name, symbol.value,
|
||||
(void *)(symbol.value + slide));
|
||||
}
|
||||
}
|
||||
if (cmd == LC_REEXPORT_DYLIB) {
|
||||
@ -495,7 +497,7 @@ void *find_in_reexport(struct libcache *cache, struct libcache_item *lib,
|
||||
char *name = (char *)ptr + name_offset;
|
||||
uint32_t hash = calculate_libname_hash(cache, name);
|
||||
for (int j = 0; j < cache->size; j++) {
|
||||
struct libcache_item* reexport = &cache->libs[j];
|
||||
struct libcache_item *reexport = &cache->libs[j];
|
||||
if (reexport->hash != hash) {
|
||||
continue;
|
||||
}
|
||||
@ -520,7 +522,7 @@ void *find_in_lib(struct libcache *cache, struct libcache_item *lib,
|
||||
|
||||
void *custom_dlsym(struct libcache *cache, uint32_t hash, const char *symbol) {
|
||||
for (size_t i = 0; i < cache->size; i++) {
|
||||
struct libcache_item* cache_lib = &cache->libs[i];
|
||||
struct libcache_item *cache_lib = &cache->libs[i];
|
||||
if (cache_lib->hash == hash) {
|
||||
return find_in_lib(cache, cache_lib, symbol);
|
||||
}
|
||||
@ -534,8 +536,9 @@ void *custom_dlsym(struct libcache *cache, const char *libname,
|
||||
return custom_dlsym(cache, hash, symbol);
|
||||
}
|
||||
|
||||
void bootstrap_libcache_item(struct libcache_item* item, const void* header, const char* name) {
|
||||
item->header = (void*)header;
|
||||
void bootstrap_libcache_item(struct libcache_item *item, const void *header,
|
||||
const char *name) {
|
||||
item->header = (void *)header;
|
||||
item->trie = get_export_trie(header, item->trie_size);
|
||||
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
@ -547,7 +550,7 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con
|
||||
}
|
||||
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
char* command_ptr = ptr;
|
||||
char *command_ptr = ptr;
|
||||
|
||||
for (int i = 0; i < ncmds; i++) {
|
||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||
@ -564,7 +567,7 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con
|
||||
}
|
||||
|
||||
ptr = command_ptr;
|
||||
item->segment = (uint64_t*)malloc(sizeof(uint64_t) * item->nsegment);
|
||||
item->segment = (uint64_t *)malloc(sizeof(uint64_t) * item->nsegment);
|
||||
for (int i = 0, segment_i = 0; i < ncmds; i++) {
|
||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
||||
@ -577,8 +580,9 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con
|
||||
return;
|
||||
}
|
||||
|
||||
struct libcache_item* get_libcache_with_name(struct libcache* cache, const char* name) {
|
||||
void* to_find = 0;
|
||||
struct libcache_item *get_libcache_with_name(struct libcache *cache,
|
||||
const char *name) {
|
||||
void *to_find = 0;
|
||||
if (custom_strcmp(name, "main") == 0) {
|
||||
to_find = cache->main;
|
||||
} else if (custom_strcmp(name, "thislib") == 0) {
|
||||
@ -586,7 +590,7 @@ struct libcache_item* get_libcache_with_name(struct libcache* cache, const char*
|
||||
}
|
||||
uint32_t hash = calculate_libname_hash(cache, name);
|
||||
for (int i = 0; i < cache->size; i++) {
|
||||
struct libcache_item* cache_lib = &cache->libs[i];
|
||||
struct libcache_item *cache_lib = &cache->libs[i];
|
||||
// search by hash or by pointer for special case
|
||||
if (cache_lib->hash == hash || cache_lib->header == to_find) {
|
||||
return cache_lib;
|
||||
@ -595,13 +599,14 @@ struct libcache_item* get_libcache_with_name(struct libcache* cache, const char*
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dump_export_trie(const void* trie, uint32_t size, const char* filename) {
|
||||
void dump_export_trie(const void *trie, uint32_t size, const char *filename) {
|
||||
FILE *outfile = fopen(filename, "wb");
|
||||
fwrite((char*)trie, size, 1, outfile);
|
||||
fwrite((char *)trie, size, 1, outfile);
|
||||
fclose(outfile);
|
||||
}
|
||||
|
||||
void dump_export_trie_of(const char* libname, const libcache* cache, const char* filename) {
|
||||
void dump_export_trie_of(const char *libname, const libcache *cache,
|
||||
const char *filename) {
|
||||
uint32_t hash = calculate_libname_hash(cache, libname);
|
||||
for (int i = 0; i < cache->size; i++) {
|
||||
struct libcache_item cache_lib = cache->libs[i];
|
||||
@ -611,8 +616,8 @@ void dump_export_trie_of(const char* libname, const libcache* cache, const char*
|
||||
}
|
||||
}
|
||||
|
||||
void* find_in_symtab(const libcache_item* lib, const char* find) {
|
||||
void* header = lib->header;
|
||||
void *find_in_symtab(const libcache_item *lib, const char *find) {
|
||||
void *header = lib->header;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
@ -622,7 +627,7 @@ void* find_in_symtab(const libcache_item* lib, const char* find) {
|
||||
}
|
||||
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
char* command_ptr = ptr;
|
||||
char *command_ptr = ptr;
|
||||
|
||||
uint64_t linkedit_vmaddr;
|
||||
uint64_t linkedit_fileoffset;
|
||||
@ -644,15 +649,17 @@ void* find_in_symtab(const libcache_item* lib, const char* find) {
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
uint64_t symtab_start = (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||
uint64_t stroff_start = (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||
uint64_t symtab_start =
|
||||
(uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||
uint64_t stroff_start =
|
||||
(uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||
|
||||
for (int j = 0; j < nsym; j++) {
|
||||
struct symbol_t* symtab = (struct symbol_t*)symtab_start;
|
||||
struct symbol_t *symtab = (struct symbol_t *)symtab_start;
|
||||
struct symbol_t symbol = symtab[j];
|
||||
char* name = (char*)stroff_start + symbol.strx;
|
||||
char *name = (char *)stroff_start + symbol.strx;
|
||||
if (custom_strcmp(name, find) == 0) {
|
||||
return (void*)(symbol.value + slide);
|
||||
return (void *)(symbol.value + slide);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -673,7 +680,8 @@ void* find_in_symtab(const libcache_item* lib, const char* find) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* find_in_symtab(const char* libname, const libcache* cache, const char* find) {
|
||||
void *find_in_symtab(const char *libname, const libcache *cache,
|
||||
const char *find) {
|
||||
uint32_t hash = calculate_libname_hash(cache, libname);
|
||||
struct libcache_item *cache_lib = 0;
|
||||
for (int i = 0; i < cache->size; i++) {
|
||||
@ -697,10 +705,10 @@ int hook_printf(const char *format, ...) {
|
||||
}
|
||||
|
||||
typedef void *(*readClass_t)(void *, bool, bool);
|
||||
typedef void *(*realizeClassWithoutSwift_t)(void *, void*);
|
||||
typedef void *(*realizeClassWithoutSwift_t)(void *, void *);
|
||||
typedef void *(*remapClass_t)(void *);
|
||||
typedef void *(*load_method_t)(void*, void*);
|
||||
typedef void *(*sel_lookUpByName_t)(const char*);
|
||||
typedef void *(*load_method_t)(void *, void *);
|
||||
typedef void *(*sel_lookUpByName_t)(const char *);
|
||||
typedef void (*addClassTableEntry_t)(void *);
|
||||
typedef void (*schedule_class_load_t)(void *);
|
||||
|
||||
@ -709,24 +717,23 @@ typedef void (*objc_autoreleasePoolPop_t)(void *);
|
||||
|
||||
struct custom_initializer_t {
|
||||
// used for Objective-C load methods
|
||||
uint64_t* loadable_classes;
|
||||
uint32_t* loadable_classes_used;
|
||||
uint64_t *loadable_classes;
|
||||
uint32_t *loadable_classes_used;
|
||||
sel_lookUpByName_t sel_lookUpByName;
|
||||
objc_autoreleasePoolPush_t objc_autoreleasePoolPush;
|
||||
objc_autoreleasePoolPop_t objc_autoreleasePoolPop;
|
||||
remapClass_t remapClass;
|
||||
schedule_class_load_t schedule_class_load;
|
||||
uint64_t* cls;
|
||||
uint64_t *cls;
|
||||
size_t ncls;
|
||||
// used for constructors
|
||||
void* programvars;
|
||||
uint64_t* constructors;
|
||||
void *programvars;
|
||||
uint64_t *constructors;
|
||||
size_t nconstructors;
|
||||
};
|
||||
|
||||
// global variable for PoC
|
||||
struct custom_initializer_t* custom_initializer_i;
|
||||
|
||||
struct custom_initializer_t *custom_initializer_i;
|
||||
|
||||
struct ProgramVars {
|
||||
void *mh; // mach_header or mach_header64
|
||||
@ -736,10 +743,10 @@ struct ProgramVars {
|
||||
const char **__prognamePtr;
|
||||
};
|
||||
|
||||
void build_cache(struct libcache& cache, void* main);
|
||||
void fix(struct libcache& cache);
|
||||
void build_cache(struct libcache &cache, void *main);
|
||||
void fix(struct libcache &cache);
|
||||
|
||||
void test(struct libcache& cache);
|
||||
void test(struct libcache &cache);
|
||||
|
||||
__attribute__((constructor)) static void
|
||||
bruh(int argc, const char *const argv[], const char *const envp[],
|
||||
@ -760,8 +767,8 @@ bruh(int argc, const char *const argv[], const char *const envp[],
|
||||
// "/usr/lib/libobjc.A.dylib", &cache,
|
||||
// "../scripts/lib_objc_symtab.bin");
|
||||
|
||||
// struct libcache_item* objc = get_libcache_with_name(&cache, "/usr/lib/libobjc.A.dylib");
|
||||
// print_macho_summary(objc->header);
|
||||
// struct libcache_item* objc = get_libcache_with_name(&cache,
|
||||
// "/usr/lib/libobjc.A.dylib"); print_macho_summary(objc->header);
|
||||
|
||||
// test(cache);
|
||||
|
||||
@ -773,8 +780,9 @@ bruh(int argc, const char *const argv[], const char *const envp[],
|
||||
// (we do not remove them for **our lib**)
|
||||
// - malloc
|
||||
// - free
|
||||
custom_initializer_i = (custom_initializer_t*)malloc(sizeof(custom_initializer_t));
|
||||
custom_initializer_i->programvars = (void*)vars;
|
||||
custom_initializer_i =
|
||||
(custom_initializer_t *)malloc(sizeof(custom_initializer_t));
|
||||
custom_initializer_i->programvars = (void *)vars;
|
||||
custom_initializer_i->cls = 0;
|
||||
custom_initializer_i->constructors = 0;
|
||||
fix(cache);
|
||||
@ -786,16 +794,16 @@ bruh(int argc, const char *const argv[], const char *const envp[],
|
||||
printf("=== manual symbol bind completes ===\n");
|
||||
}
|
||||
|
||||
void build_cache(struct libcache& cache, void* main) {
|
||||
void build_cache(struct libcache &cache, void *main) {
|
||||
const uint64_t main_slide = get_slide(main);
|
||||
// Find our lib (mapped) file
|
||||
const void *thislib = find_header((void *)bruh);
|
||||
// Find dyld lib (mapped) file using a no-sus function
|
||||
const void *libdyld = find_header((void *)dyld_get_sdk_version);
|
||||
|
||||
cache.main = (void*)main;
|
||||
cache.thislib = (void*)thislib;
|
||||
cache.libdyld = (void*)libdyld;
|
||||
cache.main = (void *)main;
|
||||
cache.thislib = (void *)thislib;
|
||||
cache.libdyld = (void *)libdyld;
|
||||
uint32_t libsystem_hash =
|
||||
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
|
||||
|
||||
@ -855,8 +863,8 @@ void build_cache(struct libcache& cache, void* main) {
|
||||
}
|
||||
}
|
||||
|
||||
void fix_binds(struct libcache_item* libfixing, struct libcache* cache,
|
||||
int n_ins, uint32_t* instructions, char* libs, char* symbols) {
|
||||
void fix_binds(struct libcache_item *libfixing, struct libcache *cache,
|
||||
int n_ins, uint32_t *instructions, char *libs, char *symbols) {
|
||||
uint32_t libsystem_hash =
|
||||
calculate_libname_hash(cache, "/usr/lib/libSystem.B.dylib");
|
||||
|
||||
@ -871,12 +879,12 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache,
|
||||
uint64_t page_rw_fixed[10]; // should be dynamic, but works for now
|
||||
|
||||
int pc = 0;
|
||||
for (;pc != n_ins;) {
|
||||
for (; pc != n_ins;) {
|
||||
uint32_t libidx = instructions[pc];
|
||||
uint32_t nsym = instructions[pc + 1];
|
||||
pc += 2;
|
||||
|
||||
char* lib = libs + libidx;
|
||||
char *lib = libs + libidx;
|
||||
for (int i = 0; i < nsym; i++) {
|
||||
uint32_t op = instructions[pc];
|
||||
uint32_t offset = instructions[pc + 1];
|
||||
@ -884,22 +892,21 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache,
|
||||
|
||||
uint32_t symidx = op >> 8;
|
||||
uint32_t segment = op & 0xff;
|
||||
char* sym = symbols + symidx;
|
||||
char *sym = symbols + symidx;
|
||||
|
||||
uint64_t fix_at = offset + libfixing->segment[segment];
|
||||
|
||||
// enable WRITE protection for this data segment
|
||||
int need_rw_fix = true;
|
||||
for (int j = 0; j < npage_rw_fixed; j++) {
|
||||
if (page_rw_fixed[j] <= fix_at &&
|
||||
page_rw_fixed[j] + 0x1000 > fix_at) {
|
||||
if (page_rw_fixed[j] <= fix_at && page_rw_fixed[j] + 0x1000 > fix_at) {
|
||||
need_rw_fix = false;
|
||||
}
|
||||
}
|
||||
if (need_rw_fix) {
|
||||
uint64_t start_page = fix_at - (fix_at % 0x1000);
|
||||
vm_protect_func(mach_task_self_func(), start_page, 0x1000, 0,
|
||||
VM_PROT_READ | VM_PROT_WRITE);
|
||||
VM_PROT_READ | VM_PROT_WRITE);
|
||||
page_rw_fixed[npage_rw_fixed++] = start_page;
|
||||
printf("modify page starts at 0x%llx to RW\n", start_page);
|
||||
}
|
||||
@ -916,15 +923,14 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache,
|
||||
printf("imports need to fix: %s at 0x%llx\n", sym, fix_at);
|
||||
printf(" from=%s\n", lib);
|
||||
printf(" segment id=%d; offset=0x%x;", segment, offset);
|
||||
printf(" resolved=%llx(%p)\n", *(uint64_t*)fix_at, resolved);
|
||||
printf(" resolved=%llx(%p)\n", *(uint64_t *)fix_at, resolved);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void fix_objc(struct libcache_item* libfixing, struct libcache& cache);
|
||||
void fix_initializer(struct libcache_item* libfixing, struct libcache& cache);
|
||||
void fix(struct libcache& cache) {
|
||||
void fix_objc(struct libcache_item *libfixing, struct libcache &cache);
|
||||
void fix_initializer(struct libcache_item *libfixing, struct libcache &cache);
|
||||
void fix(struct libcache &cache) {
|
||||
// now we have function to find exported symbols
|
||||
// it supports full name search or hash search
|
||||
// to reserve space, we use the hash search
|
||||
@ -960,20 +966,23 @@ void fix(struct libcache& cache) {
|
||||
// OBJC:
|
||||
// In Objective-C, the binary is loaded with the Objective-C runtime
|
||||
// This runtime (a library) install a hook on dyld for all images
|
||||
// And because this runtime is a system runtime, the bootstrap step is already prepared
|
||||
// The details on this runtime will be in a seperated document, below are some basics
|
||||
// And because this runtime is a system runtime, the bootstrap step is already
|
||||
// prepared The details on this runtime will be in a seperated document, below
|
||||
// are some basics
|
||||
//
|
||||
// The compiler for Objective-C emits a bunch of details for the runtime in the binary itself
|
||||
// These information are stored in sections with prefix name __objc, namely
|
||||
// The compiler for Objective-C emits a bunch of details for the runtime in
|
||||
// the binary itself These information are stored in sections with prefix name
|
||||
// __objc, namely
|
||||
// - __objc_classlist
|
||||
// - __objc_clssrefs
|
||||
// - __objc_selref
|
||||
// - __objc_const
|
||||
// - __objc_data
|
||||
//
|
||||
// Objective-C stores the class interface in the binary particulary in __objc_data
|
||||
// This interface contains the superclass, metaclass, and a cache to methods pointers
|
||||
// These information are either bound (by dyld) or built (by Objective-C runtime)
|
||||
// Objective-C stores the class interface in the binary particulary in
|
||||
// __objc_data This interface contains the superclass, metaclass, and a cache
|
||||
// to methods pointers These information are either bound (by dyld) or built
|
||||
// (by Objective-C runtime)
|
||||
//
|
||||
// One of the important routine in the Objective-C runtime is readClass.
|
||||
// https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L3385
|
||||
@ -983,18 +992,21 @@ void fix(struct libcache& cache) {
|
||||
//
|
||||
// Because __objc_data contains to-be-bound values,
|
||||
// which will be resolved by dyld and referenced by Objective-C runtime later
|
||||
// if we simply erase this value, reference(s) read by Objective-C runtime ensues a crash
|
||||
// (through debugging, we know that the crash happens in readClass, realizeClassWithoutSwift)
|
||||
// if we simply erase this value, reference(s) read by Objective-C runtime
|
||||
// ensues a crash (through debugging, we know that the crash happens in
|
||||
// readClass, realizeClassWithoutSwift)
|
||||
//
|
||||
// However, we can evade this by making the runtime thinks there is no class needs setup
|
||||
// This can be done by changing the __objc_classlist to some other name or remove this section
|
||||
// Because the runtime find the __objc_classlist section by name, and the size of the section
|
||||
// is used to iterate through pointers.
|
||||
// So if we change the name, the runtime will have no class to run setup.
|
||||
// Or complete removal and call the setup by ourselves, because we know where the data is
|
||||
// However, we can evade this by making the runtime thinks there is no class
|
||||
// needs setup This can be done by changing the __objc_classlist to some other
|
||||
// name or remove this section Because the runtime find the __objc_classlist
|
||||
// section by name, and the size of the section is used to iterate through
|
||||
// pointers. So if we change the name, the runtime will have no class to run
|
||||
// setup. Or complete removal and call the setup by ourselves, because we know
|
||||
// where the data is
|
||||
//
|
||||
// The setup is done through readClass function, as said above, its address can be found
|
||||
// This function is pure C function so call into this function is easy
|
||||
// The setup is done through readClass function, as said above, its address
|
||||
// can be found This function is pure C function so call into this function is
|
||||
// easy
|
||||
//
|
||||
// Important function with their names:
|
||||
// _readClass(objc_class*, bool, bool)
|
||||
@ -1022,37 +1034,38 @@ void fix(struct libcache& cache) {
|
||||
|
||||
// resolve selfbind if exist
|
||||
{ // stored inside __DATA,selfbind
|
||||
struct libcache_item* libfixing = get_libcache_with_name(&cache, "thislib");
|
||||
struct libcache_item *libfixing = get_libcache_with_name(&cache, "thislib");
|
||||
struct selfbind_t {
|
||||
uint32_t liblist_offset;
|
||||
uint32_t symbollist_offset;
|
||||
};
|
||||
struct selfbind_t* selfbind = (struct selfbind_t*)get_selfbind(libfixing->header);
|
||||
struct selfbind_t *selfbind =
|
||||
(struct selfbind_t *)get_selfbind(libfixing->header);
|
||||
|
||||
if (selfbind) {
|
||||
char* libs = (char*)(selfbind+1) + selfbind->liblist_offset;
|
||||
char* symbols = (char*)(selfbind+1) + selfbind->symbollist_offset;
|
||||
char *libs = (char *)(selfbind + 1) + selfbind->liblist_offset;
|
||||
char *symbols = (char *)(selfbind + 1) + selfbind->symbollist_offset;
|
||||
uint64_t n_instructions = ((uint64_t)libs - (uint64_t)(selfbind + 1)) / 4;
|
||||
uint32_t* encoded_table = (uint32_t*)(selfbind+1);
|
||||
uint32_t *encoded_table = (uint32_t *)(selfbind + 1);
|
||||
|
||||
printf("[*] performing selfbind (instructions=%p)\n", selfbind);
|
||||
fix_binds(libfixing, &cache,
|
||||
n_instructions, encoded_table,
|
||||
libs, symbols);
|
||||
fix_binds(libfixing, &cache, n_instructions, encoded_table, libs,
|
||||
symbols);
|
||||
}
|
||||
}
|
||||
|
||||
// the rest of the fixes are in main executable
|
||||
printf("[*] performing bind for main executable\n");
|
||||
struct libcache_item* libfixing = get_libcache_with_name(&cache, "main");
|
||||
fix_binds(libfixing, &cache,
|
||||
bshield_data::n_instructions, bshield_data::encoded_table,
|
||||
bshield_data::libs, bshield_data::symbols);
|
||||
struct libcache_item *libfixing = get_libcache_with_name(&cache, "main");
|
||||
fix_binds(libfixing, &cache, bshield_data::n_instructions,
|
||||
bshield_data::encoded_table, bshield_data::libs,
|
||||
bshield_data::symbols);
|
||||
|
||||
// TODO: Reformat the region as per before, or leave as it
|
||||
// for (int j = 0; j < npage_rw_fixed; j++) {
|
||||
// uint64_t start_page = page_rw_fixed[j];
|
||||
// vm_protect_func(mach_task_self_func(), start_page, 0x4000, 0, VM_PROT_READ);
|
||||
// vm_protect_func(mach_task_self_func(), start_page, 0x4000, 0,
|
||||
// VM_PROT_READ);
|
||||
// }
|
||||
|
||||
// Encrypted __TEXT segment
|
||||
@ -1070,61 +1083,74 @@ void fix(struct libcache& cache) {
|
||||
fix_initializer(libfixing, cache);
|
||||
}
|
||||
|
||||
void volatile
|
||||
custom_initializer(int argc, const char *const argv[], const char *const envp[], const char *const apple[]) {
|
||||
void volatile custom_initializer(int argc, const char *const argv[],
|
||||
const char *const envp[],
|
||||
const char *const apple[]) {
|
||||
printf("run custom initializers %p\n", custom_initializer_i);
|
||||
|
||||
if (custom_initializer_i->cls != 0) {
|
||||
// for Objective-C load
|
||||
uint64_t* loadable_classes = custom_initializer_i->loadable_classes;
|
||||
uint32_t* loadable_classes_used = custom_initializer_i->loadable_classes_used;
|
||||
sel_lookUpByName_t sel_lookUpByName = custom_initializer_i->sel_lookUpByName;
|
||||
objc_autoreleasePoolPop_t objc_autoreleasePoolPop = custom_initializer_i->objc_autoreleasePoolPop;
|
||||
objc_autoreleasePoolPush_t objc_autoreleasePoolPush = custom_initializer_i->objc_autoreleasePoolPush;
|
||||
uint64_t *loadable_classes = custom_initializer_i->loadable_classes;
|
||||
uint32_t *loadable_classes_used =
|
||||
custom_initializer_i->loadable_classes_used;
|
||||
sel_lookUpByName_t sel_lookUpByName =
|
||||
custom_initializer_i->sel_lookUpByName;
|
||||
objc_autoreleasePoolPop_t objc_autoreleasePoolPop =
|
||||
custom_initializer_i->objc_autoreleasePoolPop;
|
||||
objc_autoreleasePoolPush_t objc_autoreleasePoolPush =
|
||||
custom_initializer_i->objc_autoreleasePoolPush;
|
||||
remapClass_t remapClass = custom_initializer_i->remapClass;
|
||||
schedule_class_load_t schedule_class_load = custom_initializer_i->schedule_class_load;
|
||||
schedule_class_load_t schedule_class_load =
|
||||
custom_initializer_i->schedule_class_load;
|
||||
|
||||
for (int i = 0; i < custom_initializer_i->ncls; i++) {
|
||||
void* cls0 = (void*)custom_initializer_i->cls[i];
|
||||
void* cls = remapClass(cls0);
|
||||
if (!cls) continue;
|
||||
void *cls0 = (void *)custom_initializer_i->cls[i];
|
||||
void *cls = remapClass(cls0);
|
||||
if (!cls)
|
||||
continue;
|
||||
schedule_class_load(cls);
|
||||
}
|
||||
|
||||
printf("loadable_classes %llx %x\n", *loadable_classes, *loadable_classes_used);
|
||||
printf("loadable_classes %llx %x\n", *loadable_classes,
|
||||
*loadable_classes_used);
|
||||
|
||||
struct loadable_class_t {
|
||||
void* cls;
|
||||
void* method;
|
||||
void *cls;
|
||||
void *method;
|
||||
};
|
||||
struct loadable_class_t *classes = (struct loadable_class_t*)*loadable_classes;
|
||||
struct loadable_class_t *classes =
|
||||
(struct loadable_class_t *)*loadable_classes;
|
||||
int used = *loadable_classes_used;
|
||||
*loadable_classes = 0;
|
||||
// *loadable_classes_allocated = 0;
|
||||
*loadable_classes_used = 0;
|
||||
void* sel = sel_lookUpByName("load");
|
||||
void *sel = sel_lookUpByName("load");
|
||||
// Call all +loads for the detached list.
|
||||
void* pool = objc_autoreleasePoolPush();
|
||||
void *pool = objc_autoreleasePoolPush();
|
||||
for (int i = 0; i < used; i++) {
|
||||
void* cls = classes[i].cls;
|
||||
void *cls = classes[i].cls;
|
||||
load_method_t load_method = (load_method_t)classes[i].method;
|
||||
printf("call load of class %p %p\n", cls, load_method);
|
||||
if (!cls) continue;
|
||||
if (!cls)
|
||||
continue;
|
||||
(load_method)(cls, sel);
|
||||
}
|
||||
// Destroy the detached list.
|
||||
if (classes) free(classes);
|
||||
if (classes)
|
||||
free(classes);
|
||||
objc_autoreleasePoolPop(pool);
|
||||
}
|
||||
|
||||
// for constructors
|
||||
if (custom_initializer_i->constructors) {
|
||||
typedef void *(*constructors_t)(int, void*, void*, void*, void*);
|
||||
typedef void *(*constructors_t)(int, void *, void *, void *, void *);
|
||||
uint32_t nconst = custom_initializer_i->nconstructors;
|
||||
for (int i = 0; i < nconst; i++) {
|
||||
constructors_t cons = (constructors_t)custom_initializer_i->constructors[i];
|
||||
constructors_t cons =
|
||||
(constructors_t)custom_initializer_i->constructors[i];
|
||||
printf("call initializer at %p\n", cons);
|
||||
cons(argc, (void*)argv, (void*)envp, (void*)apple, custom_initializer_i->programvars);
|
||||
cons(argc, (void *)argv, (void *)envp, (void *)apple,
|
||||
custom_initializer_i->programvars);
|
||||
}
|
||||
free(custom_initializer_i->constructors);
|
||||
}
|
||||
@ -1132,7 +1158,7 @@ custom_initializer(int argc, const char *const argv[], const char *const envp[],
|
||||
free(custom_initializer_i);
|
||||
}
|
||||
|
||||
void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
void fix_objc(struct libcache_item *libfixing, struct libcache &cache) {
|
||||
// Manually run the Objective-C runtime for each class
|
||||
//
|
||||
|
||||
@ -1146,7 +1172,7 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
// "mov rcx, 123;"
|
||||
// "call r12;");
|
||||
|
||||
void* header = libfixing->header;
|
||||
void *header = libfixing->header;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
@ -1156,7 +1182,7 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
}
|
||||
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
char* command_ptr = ptr;
|
||||
char *command_ptr = ptr;
|
||||
|
||||
uint64_t linkedit_vmaddr;
|
||||
uint64_t linkedit_fileoffset;
|
||||
@ -1173,62 +1199,80 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
if (custom_strcmp(name, "__TEXT") == 0) {
|
||||
slide = (uint64_t)header - vmaddr;
|
||||
} else if (custom_strcmp(name, "__DATA_CONST") == 0) {
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char* secname = sections_ptr;
|
||||
char *secname = sections_ptr;
|
||||
printf("section %s\n", secname);
|
||||
if (custom_strncmp(secname, "__objc_classbruh", 16) == 0) {
|
||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
||||
uint64_t *data_ptr = (uint64_t*)(addr + slide);
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
uint64_t *data_ptr = (uint64_t *)(addr + slide);
|
||||
|
||||
readClass_t readClass = (readClass_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL9readClassP10objc_classbb");
|
||||
realizeClassWithoutSwift_t realizeClassWithoutSwift = (realizeClassWithoutSwift_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL24realizeClassWithoutSwiftP10objc_classS0_");
|
||||
readClass_t readClass =
|
||||
(readClass_t)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__ZL9readClassP10objc_classbb");
|
||||
realizeClassWithoutSwift_t realizeClassWithoutSwift =
|
||||
(realizeClassWithoutSwift_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__ZL24realizeClassWithoutSwiftP10objc_classS0_");
|
||||
|
||||
for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) {
|
||||
// this pointer is rebased by dyld and points to the correct class interface
|
||||
// for some reason, we can skip this and it should still work
|
||||
void* newCls = readClass((void*)data_ptr[ptr_i], false, false);
|
||||
if (newCls != (void*)data_ptr[ptr_i]) {
|
||||
// this pointer is rebased by dyld and points to the correct class
|
||||
// interface for some reason, we can skip this and it should still
|
||||
// work
|
||||
void *newCls = readClass((void *)data_ptr[ptr_i], false, false);
|
||||
if (newCls != (void *)data_ptr[ptr_i]) {
|
||||
realizeClassWithoutSwift(newCls, 0);
|
||||
}
|
||||
printf("add class init (%llx)%p\n", data_ptr[ptr_i], newCls);
|
||||
}
|
||||
}
|
||||
else if (custom_strncmp(secname, "__objc_nlclsbruh", 16) == 0) {
|
||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
||||
uint64_t *data_ptr = (uint64_t*)(addr + slide);
|
||||
} else if (custom_strncmp(secname, "__objc_nlclsbruh", 16) == 0) {
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
uint64_t *data_ptr = (uint64_t *)(addr + slide);
|
||||
|
||||
uint64_t* loadable_classes = (uint64_t*)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL16loadable_classes");
|
||||
uint32_t* loadable_classes_allocated = (uint32_t*)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL26loadable_classes_allocated");
|
||||
uint32_t* loadable_classes_used = (uint32_t*)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL21loadable_classes_used");
|
||||
uint64_t *loadable_classes = (uint64_t *)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL16loadable_classes");
|
||||
uint32_t *loadable_classes_allocated =
|
||||
(uint32_t *)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__ZL26loadable_classes_allocated");
|
||||
uint32_t *loadable_classes_used =
|
||||
(uint32_t *)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__ZL21loadable_classes_used");
|
||||
|
||||
remapClass_t remapClass = (remapClass_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL10remapClassP10objc_class");
|
||||
schedule_class_load_t schedule_class_load = (schedule_class_load_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL19schedule_class_loadP10objc_class");
|
||||
realizeClassWithoutSwift_t realizeClassWithoutSwift = (realizeClassWithoutSwift_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL24realizeClassWithoutSwiftP10objc_classS0_");
|
||||
addClassTableEntry_t addClassTableEntry = (addClassTableEntry_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL18addClassTableEntryP10objc_classb");
|
||||
sel_lookUpByName_t sel_lookUpByName = (sel_lookUpByName_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "_sel_lookUpByName");
|
||||
objc_autoreleasePoolPush_t objc_autoreleasePoolPush = (objc_autoreleasePoolPush_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__objc_autoreleasePoolPush");
|
||||
objc_autoreleasePoolPop_t objc_autoreleasePoolPop = (objc_autoreleasePoolPop_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache, "__objc_autoreleasePoolPop");
|
||||
remapClass_t remapClass =
|
||||
(remapClass_t)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__ZL10remapClassP10objc_class");
|
||||
schedule_class_load_t schedule_class_load =
|
||||
(schedule_class_load_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__ZL19schedule_class_loadP10objc_class");
|
||||
realizeClassWithoutSwift_t realizeClassWithoutSwift =
|
||||
(realizeClassWithoutSwift_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__ZL24realizeClassWithoutSwiftP10objc_classS0_");
|
||||
addClassTableEntry_t addClassTableEntry =
|
||||
(addClassTableEntry_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__ZL18addClassTableEntryP10objc_classb");
|
||||
sel_lookUpByName_t sel_lookUpByName =
|
||||
(sel_lookUpByName_t)find_in_symtab("/usr/lib/libobjc.A.dylib",
|
||||
&cache, "_sel_lookUpByName");
|
||||
objc_autoreleasePoolPush_t objc_autoreleasePoolPush =
|
||||
(objc_autoreleasePoolPush_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__objc_autoreleasePoolPush");
|
||||
objc_autoreleasePoolPop_t objc_autoreleasePoolPop =
|
||||
(objc_autoreleasePoolPop_t)find_in_symtab(
|
||||
"/usr/lib/libobjc.A.dylib", &cache,
|
||||
"__objc_autoreleasePoolPop");
|
||||
|
||||
// https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L3822
|
||||
for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) {
|
||||
void* cls = remapClass((void*)data_ptr[ptr_i]);
|
||||
if (!cls) continue;
|
||||
void *cls = remapClass((void *)data_ptr[ptr_i]);
|
||||
if (!cls)
|
||||
continue;
|
||||
addClassTableEntry(cls);
|
||||
realizeClassWithoutSwift(cls, 0);
|
||||
printf("build nonlazy class at (%llx)%p\n", data_ptr[ptr_i], cls);
|
||||
@ -1237,8 +1281,10 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
custom_initializer_i->sel_lookUpByName = sel_lookUpByName;
|
||||
custom_initializer_i->loadable_classes = loadable_classes;
|
||||
custom_initializer_i->loadable_classes_used = loadable_classes_used;
|
||||
custom_initializer_i->objc_autoreleasePoolPush = objc_autoreleasePoolPush;
|
||||
custom_initializer_i->objc_autoreleasePoolPop = objc_autoreleasePoolPop;
|
||||
custom_initializer_i->objc_autoreleasePoolPush =
|
||||
objc_autoreleasePoolPush;
|
||||
custom_initializer_i->objc_autoreleasePoolPop =
|
||||
objc_autoreleasePoolPop;
|
||||
custom_initializer_i->schedule_class_load = schedule_class_load;
|
||||
custom_initializer_i->remapClass = remapClass;
|
||||
custom_initializer_i->cls = data_ptr;
|
||||
@ -1256,13 +1302,14 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
}
|
||||
}
|
||||
|
||||
void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
void fix_initializer(struct libcache_item *libfixing, struct libcache &cache) {
|
||||
// fix the initializers
|
||||
// The Objective-C runtime loads the NSObject after this lib booted
|
||||
// So all calls to NSObject (and its children classes) will segfault/throw error
|
||||
// So all calls to NSObject (and its children classes) will segfault/throw
|
||||
// error
|
||||
//
|
||||
// So we will fix the main initializers, which runs after all Objective-C setup
|
||||
// The initializers will run these Objective-C classes' load methods
|
||||
// So we will fix the main initializers, which runs after all Objective-C
|
||||
// setup The initializers will run these Objective-C classes' load methods
|
||||
//
|
||||
// (THIS IDEA IS TESTED AND WILL NOT WORK)
|
||||
// As of now, we assume the main executable has a __mod_init_func section
|
||||
@ -1281,29 +1328,32 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
//
|
||||
// There could be many ways to do this. I discovered 1 method of doing this.
|
||||
//
|
||||
// The idea is to hijack the main function to do the rest of the initalizations.
|
||||
// By fixing the LC_MAIN command, we can make dyld jump to anywhere we want as main.
|
||||
// But the command can't be edited at runtime.
|
||||
// And pointing to the function we want needs a workaround.
|
||||
// The idea is to hijack the main function to do the rest of the
|
||||
// initalizations. By fixing the LC_MAIN command, we can make dyld jump to
|
||||
// anywhere we want as main. But the command can't be edited at runtime. And
|
||||
// pointing to the function we want needs a workaround.
|
||||
//
|
||||
// So we will write a shellcode in the binary and point main to that shellcode.
|
||||
// The shellcode basically loads the address of the initalization function,
|
||||
// call it, then call main, and return.
|
||||
// So we will write a shellcode in the binary and point main to that
|
||||
// shellcode. The shellcode basically loads the address of the initalization
|
||||
// function, call it, then call main, and return.
|
||||
//
|
||||
// The shellcode must be able ot get the current pc address to correctly calculate
|
||||
// address from any offset. In arm64, we can use `adr x8, 0`.
|
||||
// If we know where the shellcode is, we can effectively calculate the header of main.
|
||||
// Now, everything is easy, just need offsets to anywhere we want and we can get it.
|
||||
// The shellcode must be able ot get the current pc address to correctly
|
||||
// calculate address from any offset. In arm64, we can use `adr x8, 0`. If we
|
||||
// know where the shellcode is, we can effectively calculate the header of
|
||||
// main. Now, everything is easy, just need offsets to anywhere we want and we
|
||||
// can get it.
|
||||
//
|
||||
// Now the address of the initalization function can be fetched using many methods,
|
||||
// but it resides inside this library. To reduce redundance work, we can write the
|
||||
// address of this function somewhere inside main, which will then be easily found.
|
||||
// Now the address of the initalization function can be fetched using many
|
||||
// methods, but it resides inside this library. To reduce redundance work, we
|
||||
// can write the address of this function somewhere inside main, which will
|
||||
// then be easily found.
|
||||
//
|
||||
// As a result, we choose the space before __text to write the shellcode,
|
||||
// the space after __DATA to write the address for initalization function.
|
||||
// Because all segment is allocated/pre-allocated with page alignement,
|
||||
// we can be pretty sure that there are free space.
|
||||
// (note: __TEXT segment is aligned to the end of the page, free space in the middle)
|
||||
// (note: __TEXT segment is aligned to the end of the page, free space in the
|
||||
// middle)
|
||||
//
|
||||
// Below is the shellcode.
|
||||
/*
|
||||
@ -1330,7 +1380,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
br x9
|
||||
*/
|
||||
|
||||
void* header = libfixing->header;
|
||||
void *header = libfixing->header;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
@ -1340,7 +1390,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
}
|
||||
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
char* command_ptr = ptr;
|
||||
char *command_ptr = ptr;
|
||||
|
||||
uint64_t linkedit_vmaddr;
|
||||
uint64_t linkedit_fileoffset;
|
||||
@ -1356,23 +1406,26 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
printf("segment %s\n", name);
|
||||
if (custom_strcmp(name, "__TEXT") == 0) {
|
||||
slide = (uint64_t)header - vmaddr;
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char* secname = sections_ptr;
|
||||
char *secname = sections_ptr;
|
||||
printf("section %s\n", secname);
|
||||
if (custom_strcmp(secname, "__init_offsets") == 0) {
|
||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
||||
uint32_t *data_ptr = (uint32_t*)(addr + slide);
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
uint32_t *data_ptr = (uint32_t *)(addr + slide);
|
||||
|
||||
printf("found initializer at %p\n", data_ptr);
|
||||
|
||||
custom_initializer_i->nconstructors = size / 4;
|
||||
custom_initializer_i->constructors = (uint64_t*)malloc(sizeof(uint64_t) * size/4);
|
||||
custom_initializer_i->constructors =
|
||||
(uint64_t *)malloc(sizeof(uint64_t) * size / 4);
|
||||
for (int j = 0; j < size / 4; j++) {
|
||||
custom_initializer_i->constructors[j] = (uint64_t)header + data_ptr[j];
|
||||
printf("registered initializer at %llx\n", custom_initializer_i->constructors[j]);
|
||||
custom_initializer_i->constructors[j] =
|
||||
(uint64_t)header + data_ptr[j];
|
||||
printf("registered initializer at %llx\n",
|
||||
custom_initializer_i->constructors[j]);
|
||||
}
|
||||
}
|
||||
if (custom_strcmp(secname, "__mod_init_func") == 0) {
|
||||
@ -1382,14 +1435,14 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||
}
|
||||
} else if (custom_strcmp(name, "__DATA") == 0) {
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||
sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1);
|
||||
|
||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
|
||||
uint64_t* dummy = (uint64_t*)(addr + slide + size);
|
||||
uint64_t *dummy = (uint64_t *)(addr + slide + size);
|
||||
dummy[0] = (uint64_t)custom_initializer;
|
||||
dummy[1] = (uint64_t)(header) + bshield_data::main;
|
||||
printf("add custom main-peg at %p\n", dummy);
|
||||
@ -1402,7 +1455,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
||||
}
|
||||
}
|
||||
|
||||
void test(struct libcache& cache) {
|
||||
void test(struct libcache &cache) {
|
||||
uint32_t libsystem_hash =
|
||||
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
|
||||
if (false) { // test search using name
|
||||
@ -1436,7 +1489,8 @@ void test(struct libcache& cache) {
|
||||
printf("Indirect search: Found=%p Expected=%p\n", vm_protect_func,
|
||||
vm_protect);
|
||||
|
||||
void *realpath_func = custom_dlsym(&cache, libsystem_hash, "_realpath$DARWIN_EXTSN");
|
||||
void *realpath_func =
|
||||
custom_dlsym(&cache, libsystem_hash, "_realpath$DARWIN_EXTSN");
|
||||
printf("Indirect search: Found=%p Expected=%p\n", realpath_func, realpath);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user