format code

This commit is contained in:
nganhkhoa 2023-07-12 13:37:54 +07:00
parent 4016abf40d
commit f88861a87e
10 changed files with 505 additions and 452 deletions

View File

@ -4,13 +4,13 @@ import (
. "ios-wrapper/internal/wrapper/ofile" . "ios-wrapper/internal/wrapper/ofile"
) )
type rewriteImports struct{ type rewriteImports struct {
symbols []string symbols []string
} }
func (action *rewriteImports) withMacho(mf *MachoFile) error { func (action *rewriteImports) withMacho(mf *MachoFile) error {
mc := mf.Context() mc := mf.Context()
mc.RewriteImportsTable(action.symbols) mc.RewriteImportsTable(action.symbols)
return nil return nil
} }
@ -20,7 +20,6 @@ func (action *rewriteImports) withFat(ff *FatFile) error {
func NewRewriteImportsWithKeepSymbolsAction(symbols []string) *rewriteImports { func NewRewriteImportsWithKeepSymbolsAction(symbols []string) *rewriteImports {
return &rewriteImports{ return &rewriteImports{
symbols, symbols,
} }
} }

View File

@ -3,24 +3,24 @@ package action
import ( import (
// "fmt" // "fmt"
"sort" "sort"
"strings" "strings"
// log "github.com/sirupsen/logrus" // log "github.com/sirupsen/logrus"
. "ios-wrapper/internal/wrapper/ofile" . "ios-wrapper/internal/wrapper/ofile"
"ios-wrapper/pkg/protomodel" "ios-wrapper/pkg/protomodel"
) )
type saveImports struct{ type saveImports struct {
keepSymbols []string keepSymbols []string
} }
func (action *saveImports) withMacho(mf *MachoFile) error { func (action *saveImports) withMacho(mf *MachoFile) error {
action.saveToInfo(mf) action.saveToInfo(mf)
mc := mf.Context() mc := mf.Context()
if mc.Header().IsDylib() { if mc.Header().IsDylib() {
mc.WriteInfoToData(mf.Info()) mc.WriteInfoToData(mf.Info())
} }
return nil return nil
} }
func (action *saveImports) saveToInfo(mf *MachoFile) error { func (action *saveImports) saveToInfo(mf *MachoFile) error {
@ -64,28 +64,28 @@ func (action *saveImports) saveToInfo(mf *MachoFile) error {
continue continue
} }
skip := false skip := false
for _, keep := range action.keepSymbols { for _, keep := range action.keepSymbols {
name := keep name := keep
lib := "" lib := ""
parts := strings.Split(keep, ",") parts := strings.Split(keep, ",")
if len(parts) == 2 { if len(parts) == 2 {
name = parts[0] name = parts[0]
lib = parts[1] lib = parts[1]
} }
if symbol.Name() != name { if symbol.Name() != name {
continue continue
} }
if lib == "" || lib == symbol.Dylib() { if lib == "" || lib == symbol.Dylib() {
skip = true skip = true
break break
} }
} }
if skip { if skip {
continue continue
} }
// dylib_hash := calculateHash(symbol.Dylib()) // dylib_hash := calculateHash(symbol.Dylib())
seg := mc.Segments()[symbol.Segment()] seg := mc.Segments()[symbol.Segment()]
@ -139,6 +139,6 @@ func (action *saveImports) withFat(ff *FatFile) error {
func NewSaveImportsAction(keepSymbols []string) *saveImports { func NewSaveImportsAction(keepSymbols []string) *saveImports {
return &saveImports{ return &saveImports{
keepSymbols, keepSymbols,
} }
} }

View File

@ -128,7 +128,7 @@ func Cli() {
pc.rpath_to_add = arg.Rpath pc.rpath_to_add = arg.Rpath
pc.outfile = arg.Out pc.outfile = arg.Out
pc.bcellfile = arg.Bcell pc.bcellfile = arg.Bcell
pc.symbols_keep = arg.KeepImports pc.symbols_keep = arg.KeepImports
default: default:
return return

View File

@ -72,7 +72,7 @@ type PepeArgument struct {
RebuildLinkEdit bool `default:"false" negatable:"" help:"(TODO) Rebuild the LINKEDIT section"` RebuildLinkEdit bool `default:"false" negatable:"" help:"(TODO) Rebuild the LINKEDIT section"`
FullRemoval bool `default:"false" help:"Apply every removal possible"` 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 { type BcellToHeaderArgument struct {

View File

@ -46,7 +46,7 @@ type ProgramContext struct {
remove_symbol_table bool remove_symbol_table bool
dylib_to_add []string dylib_to_add []string
rpath_to_add []string rpath_to_add []string
symbols_keep []string symbols_keep []string
outfile string outfile string
@ -98,7 +98,7 @@ func (pc *ProgramContext) Process(ofile OFile) {
if pc.remove_imports { if pc.remove_imports {
pc.AddAction(NewSaveImportsAction(pc.symbols_keep)) pc.AddAction(NewSaveImportsAction(pc.symbols_keep))
pc.AddAction(NewRemoveImportsAction()) pc.AddAction(NewRemoveImportsAction())
pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep)) pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep))
} }
if pc.remove_symbol_table { if pc.remove_symbol_table {
pc.AddAction(NewRemoveClassicSymbolAction()) pc.AddAction(NewRemoveClassicSymbolAction())

View File

@ -25,7 +25,7 @@ type ImportSymbol struct {
segment_offset uint32 segment_offset uint32
address uint64 address uint64
file_address uint64 file_address uint64
lib_ordinal uint32 lib_ordinal uint32
// push number // push number
pnum uint32 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("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count)
// fmt.Printf("pages=%x\n", fix.pages) // fmt.Printf("pages=%x\n", fix.pages)
pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count)) pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count))
reader := bytes.NewReader(mc.buf) reader := bytes.NewReader(mc.buf)
for page_i := 0; page_i < int(fix.page_count); page_i++ { for page_i := 0; page_i < int(fix.page_count); page_i++ {
// fmt.Printf(" page offset=%x\n", pages[page_i]) // fmt.Printf(" page offset=%x\n", pages[page_i])
@ -151,7 +151,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
sym.name = name sym.name = name
sym.dylib = dylib sym.dylib = dylib
sym.typ = "lazy" sym.typ = "lazy"
sym.lib_ordinal = uint32(s.lib_ordinal) sym.lib_ordinal = uint32(s.lib_ordinal)
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address))) sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
sym.file_address = uint64(address) sym.file_address = uint64(address)

View File

@ -2,17 +2,17 @@ package macho
import ( import (
"bytes" "bytes"
"encoding/binary"
"fmt" "fmt"
"io" "io"
"math/rand" "math/rand"
"strings"
"time" "time"
"strings"
"encoding/binary"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"ios-wrapper/pkg/protomodel"
. "ios-wrapper/pkg/ios" . "ios-wrapper/pkg/ios"
"ios-wrapper/pkg/protomodel"
) )
// #include "fixups.h" // #include "fixups.h"
@ -285,10 +285,10 @@ func (mc *MachoContext) RemoveBindSymbols() {
} else { } else {
mc.removeBindSymbolsLegacy() mc.removeBindSymbolsLegacy()
} }
// Objective-C stub replaces main which can only appears in executable // Objective-C stub replaces main which can only appears in executable
if mc.Header().IsExecutable() { if mc.Header().IsExecutable() {
mc.ReworkForObjc() mc.ReworkForObjc()
} }
// due to some limitations when design this tool // due to some limitations when design this tool
// we write the c code to stdout lol // we write the c code to stdout lol
@ -445,27 +445,27 @@ func (mc *MachoContext) ReworkForObjc() {
// arm also has different indexing instruction, so be careful // arm also has different indexing instruction, so be careful
// https://developer.arm.com/documentation/102374/0101/Loads-and-stores---addressing // https://developer.arm.com/documentation/102374/0101/Loads-and-stores---addressing
/* /*
adr x8, 0 adr x8, 0
# x9 = (offset end of __DATA) - (offset shellcode) # x9 = (offset end of __DATA) - (offset shellcode)
movz x9, #0x9999 movz x9, #0x9999
add x8, x8, x9 add x8, x8, x9
stp x30, x8, [sp], #-0x10 stp x30, x8, [sp], #-0x10
stp x3, x2, [sp], #-0x10 stp x3, x2, [sp], #-0x10
stp x1, x0, [sp], #-0x10 stp x1, x0, [sp], #-0x10
# custom intializer # custom intializer
ldr x9, [x8] ldr x9, [x8]
blr x9 blr x9
ldp x1, x0, [sp, #0x10]! ldp x1, x0, [sp, #0x10]!
ldp x3, x2, [sp, #0x10]! ldp x3, x2, [sp, #0x10]!
ldp x30, x8, [sp, #0x10]! ldp x30, x8, [sp, #0x10]!
# original main # original main
# link register is set so jump only # link register is set so jump only
ldr x9, [x8, #8] ldr x9, [x8, #8]
br x9 br x9
*/ */
// TODO: fix to work with offset larger than 0xffff // TODO: fix to work with offset larger than 0xffff
@ -529,72 +529,72 @@ func (mc *MachoContext) ReworkForObjc() {
func (mc *MachoContext) RewriteImportsTable(keepSymbols []string) { func (mc *MachoContext) RewriteImportsTable(keepSymbols []string) {
allSymbols := mc.CollectBindSymbols() allSymbols := mc.CollectBindSymbols()
fixups, fixupsOffset := mc.Fixups() fixups, fixupsOffset := mc.Fixups()
importTable := fixups.ImportsOffset(uint32(fixupsOffset)) importTable := fixups.ImportsOffset(uint32(fixupsOffset))
symbolTable := fixups.SymbolsOffset(uint32(fixupsOffset)) symbolTable := fixups.SymbolsOffset(uint32(fixupsOffset))
symbolTablePtr := symbolTable symbolTablePtr := symbolTable
// in removeBindSymbolsModern, we erase these pointers in file // in removeBindSymbolsModern, we erase these pointers in file
// but because we keep a few symbols, we need to rewrite the pointers // 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 // as well as rebuild the import table and strings table, and bind values
keepCount := uint32(0) keepCount := uint32(0)
for _, symbol := range keepSymbols { for _, symbol := range keepSymbols {
name := symbol name := symbol
lib := "" lib := ""
parts := strings.Split(symbol, ",") parts := strings.Split(symbol, ",")
if len(parts) == 2 { if len(parts) == 2 {
name = parts[0] name = parts[0]
lib = parts[1] lib = parts[1]
} }
symbolInfo := (*ImportSymbol)(nil) symbolInfo := (*ImportSymbol)(nil)
for _, s := range allSymbols { for _, s := range allSymbols {
if s.Name() != name { if s.Name() != name {
continue continue
} }
if lib == "" || lib == s.Dylib() { if lib == "" || lib == s.Dylib() {
symbolInfo = s symbolInfo = s
break break
} }
} }
if symbolInfo == nil { if symbolInfo == nil {
// symbol not found // symbol not found
continue continue
} }
fmt.Printf("keep symbol %s\n", name) fmt.Printf("keep symbol %s\n", name)
fmt.Printf("importTable at 0x%x; stringTable at 0x%x\n", importTable, symbolTablePtr) 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("bind value at 0x%x\n", symbolInfo.file_address)
// write string to string table // write string to string table
mc.file.WriteAt([]byte(name), int64(symbolTablePtr)) mc.file.WriteAt([]byte(name), int64(symbolTablePtr))
// fix bind value // fix bind value
rebaseOpcodeBytes := make([]byte, 8) rebaseOpcodeBytes := make([]byte, 8)
mc.file.ReadAt(rebaseOpcodeBytes, int64(symbolInfo.file_address)) mc.file.ReadAt(rebaseOpcodeBytes, int64(symbolInfo.file_address))
rebaseOpcode := mc.byteorder.Uint64(rebaseOpcodeBytes) rebaseOpcode := mc.byteorder.Uint64(rebaseOpcodeBytes)
bindOpcode := C.MakeBindFixupOpcodeFromRebase(C.uint64_t(rebaseOpcode), C.uint(keepCount)) bindOpcode := C.MakeBindFixupOpcodeFromRebase(C.uint64_t(rebaseOpcode), C.uint(keepCount))
{ {
v := make([]byte, 8) v := make([]byte, 8)
mc.byteorder.PutUint64(v, uint64(bindOpcode)) mc.byteorder.PutUint64(v, uint64(bindOpcode))
mc.file.WriteAt(v, int64(symbolInfo.file_address)) mc.file.WriteAt(v, int64(symbolInfo.file_address))
} }
// write import data to import table // write import data to import table
entry := C.MakeImportTableEntry(C.uint(symbolInfo.LibOrdinal()), C.uint(symbolTablePtr - symbolTable)) entry := C.MakeImportTableEntry(C.uint(symbolInfo.LibOrdinal()), C.uint(symbolTablePtr-symbolTable))
{ {
v := make([]byte, 4) v := make([]byte, 4)
mc.byteorder.PutUint32(v, uint32(entry)) mc.byteorder.PutUint32(v, uint32(entry))
mc.file.WriteAt(v, int64(importTable)) mc.file.WriteAt(v, int64(importTable))
} }
keepCount += 1 keepCount += 1
importTable += 4 importTable += 4
symbolTablePtr += uint32(len(name)) + 1 symbolTablePtr += uint32(len(name)) + 1
} }
fixups.imports_count = keepCount fixups.imports_count = keepCount
mc.file.WriteAt(fixups.Serialize(mc), int64(mc.fixups.dataoff)) 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 { func (mc *MachoContext) AddSection(segname string, name string, size int) Section {
// mc.file.WriteAt(mc.header.Serialize(mc), 0) // mc.file.WriteAt(mc.header.Serialize(mc), 0)
var ret Section var ret Section
var buffer bytes.Buffer var buffer bytes.Buffer
for _, command := range mc.commands { for _, command := range mc.commands {
switch command.(type) { switch command.(type) {
case *Segment64: case *Segment64:
var virtualAddr uint64 var virtualAddr uint64
var fileOffset uint64 var fileOffset uint64
var segment = command.(*Segment64) var segment = command.(*Segment64)
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte(segname)) != 0 { if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte(segname)) != 0 {
buffer.Write(segment.Serialize(mc)) buffer.Write(segment.Serialize(mc))
continue continue
} else { } else {
virtualAddr = segment.Vmaddr() virtualAddr = segment.Vmaddr()
fileOffset = segment.Fileoff() fileOffset = segment.Fileoff()
for _, section := range segment.Sections() { for _, section := range segment.Sections() {
virtualAddr += section.Size() virtualAddr += section.Size()
// if section.Offset() != 0 { // if section.Offset() != 0 {
fileOffset += section.Size() fileOffset += section.Size()
// } // }
} }
align := uint64(4) align := uint64(4)
alignment := align - (fileOffset % align) alignment := align - (fileOffset % align)
fileOffset += alignment fileOffset += alignment
virtualAddr += alignment virtualAddr += alignment
enoughSpace := segment.Fileoff() + segment.Filesize() >= fileOffset + uint64(size) enoughSpace := segment.Fileoff()+segment.Filesize() >= fileOffset+uint64(size)
if !enoughSpace { if !enoughSpace {
fmt.Println("Not enough space to store saved info in __DATA segment, need resize (not supported now)") 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)") panic("Not enough space to store saved info in __DATA segment, need resize (not supported now)")
} }
} }
var section Section64 var section Section64
section.sectname = make([]byte, 16) section.sectname = make([]byte, 16)
copy(section.sectname, name) copy(section.sectname, name)
section.segname = make([]byte, 16) section.segname = make([]byte, 16)
copy(section.segname, segname) copy(section.segname, segname)
section.size = uint64(size) section.size = uint64(size)
section.reloff = 0 section.reloff = 0
section.nreloc = 0 section.nreloc = 0
section.flags = 0 section.flags = 0
section.align = 3 section.align = 3
section.reserved1 = 0 section.reserved1 = 0
section.reserved2 = 0 section.reserved2 = 0
section.reserved3 = 0 section.reserved3 = 0
// addr will increment from the last section // addr will increment from the last section
// offset will increment from the last section + size // offset will increment from the last section + size
// be careful of Virtual section (bss) // be careful of Virtual section (bss)
section.addr = virtualAddr section.addr = virtualAddr
section.offset = uint32(fileOffset) section.offset = uint32(fileOffset)
segment.nsects += 1 segment.nsects += 1
buffer.Write(segment.Serialize(mc)) buffer.Write(segment.Serialize(mc))
buffer.Write(section.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 = &section ret = &section
continue continue
default: default:
buffer.Write(command.Serialize(mc)) buffer.Write(command.Serialize(mc))
continue continue
} }
} }
mc.header.sizeofcmds = uint32(buffer.Len()) mc.header.sizeofcmds = uint32(buffer.Len())
header := mc.header.Serialize(mc) header := mc.header.Serialize(mc)
mc.file.WriteAt(header, 0) mc.file.WriteAt(header, 0)
mc.file.WriteAt(buffer.Bytes(), int64(len(header))) mc.file.WriteAt(buffer.Bytes(), int64(len(header)))
return ret return ret
} }
func (mc *MachoContext) WriteInfoToData(info *protomodel.MachoInfo) { func (mc *MachoContext) WriteInfoToData(info *protomodel.MachoInfo) {
encode := func () []byte { encode := func() []byte {
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
for _, table := range info.Symbols.Tables { for _, table := range info.Symbols.Tables {
binary.Write(buffer, mc.byteorder, table.LibIndex) binary.Write(buffer, mc.byteorder, table.LibIndex)
binary.Write(buffer, mc.byteorder, table.Nsymbols) binary.Write(buffer, mc.byteorder, table.Nsymbols)
for _, symbol := range table.Symbols { for _, symbol := range table.Symbols {
binary.Write(buffer, mc.byteorder, (symbol.SymbolIndex<<8)|symbol.SegmentIndex) binary.Write(buffer, mc.byteorder, (symbol.SymbolIndex<<8)|symbol.SegmentIndex)
binary.Write(buffer, mc.byteorder, symbol.Offset) 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 { for _, lib := range info.Symbols.Libs {
buffer.WriteString(lib) buffer.WriteString(lib)
buffer.WriteByte(0) buffer.WriteByte(0)
} }
liblist := buffer.Bytes() liblist := buffer.Bytes()
buffer = new(bytes.Buffer) buffer = new(bytes.Buffer)
for _, symbol := range info.Symbols.Symbols { for _, symbol := range info.Symbols.Symbols {
buffer.WriteString(symbol) buffer.WriteString(symbol)
buffer.WriteByte(0) buffer.WriteByte(0)
} }
symbollist := buffer.Bytes() symbollist := buffer.Bytes()
buffer = new(bytes.Buffer) buffer = new(bytes.Buffer)
// offset to liblist // offset to liblist
binary.Write(buffer, mc.byteorder, uint32(len(instructions))) binary.Write(buffer, mc.byteorder, uint32(len(instructions)))
// offset to symbollist // offset to symbollist
binary.Write(buffer, mc.byteorder, uint32(len(instructions) + len(liblist))) binary.Write(buffer, mc.byteorder, uint32(len(instructions)+len(liblist)))
buffer.Write(instructions) buffer.Write(instructions)
buffer.Write(liblist) buffer.Write(liblist)
buffer.Write(symbollist) buffer.Write(symbollist)
return buffer.Bytes() return buffer.Bytes()
} }
encoded := encode() encoded := encode()
// encoded := []byte{0x11,0x22,0x33, 0x44} // encoded := []byte{0x11,0x22,0x33, 0x44}
section := mc.AddSection("__DATA", "selfbind", len(encoded)) section := mc.AddSection("__DATA", "selfbind", len(encoded))
if mc.Is64bit() { if mc.Is64bit() {
var s *Section64 = section.(*Section64) var s *Section64 = section.(*Section64)
mc.file.WriteAt(encoded, int64(s.Offset())) mc.file.WriteAt(encoded, int64(s.Offset()))
} else { } else {
var s *Section32 = section.(*Section32) var s *Section32 = section.(*Section32)
mc.file.WriteAt(encoded, int64(s.Offset())) mc.file.WriteAt(encoded, int64(s.Offset()))
} }
} }

View File

@ -38,11 +38,11 @@ func (h *Header) Cpusubtype() uint32 {
} }
func (h *Header) IsExecutable() bool { func (h *Header) IsExecutable() bool {
return h.filetype == 0x2 return h.filetype == 0x2
} }
func (h *Header) IsDylib() bool { func (h *Header) IsDylib() bool {
return h.filetype == 0x6 return h.filetype == 0x6
} }
func (h *Header) Serialize(mc *MachoContext) []byte { func (h *Header) Serialize(mc *MachoContext) []byte {
@ -135,7 +135,7 @@ func (lcmd *LoadCmd) Cmdname() string {
return "LC_DATA_IN_CODE" return "LC_DATA_IN_CODE"
case LC_SYMTAB: case LC_SYMTAB:
return "LC_SYMTAB" return "LC_SYMTAB"
case LC_BUILD_VERSION: case LC_BUILD_VERSION:
return "LC_BUILD_VERSION" return "LC_BUILD_VERSION"
case LC_UUID: case LC_UUID:
return "LC_UUID" return "LC_UUID"
@ -425,11 +425,11 @@ type Fixups struct {
} }
func (lcmd *Fixups) ImportsOffset(fixups_offset uint32) uint32 { 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 { 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 { func (lcmd *Fixups) Serialize(mc *MachoContext) []byte {

View File

@ -73,7 +73,7 @@ func (mc *MachoContext) Fixups() (*Fixups, uint64) {
size := mc.fixups.datasize size := mc.fixups.datasize
fixups := new(Fixups) fixups := new(Fixups)
fixups.Deserialize(mc, mc.buf[start:start+size]) 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 { func (mc *MachoContext) WriteEnabled() bool {

View File

@ -22,19 +22,17 @@ int custom_strcmp(const char *p1, const char *p2) {
return c1 - c2; 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; register unsigned char u1, u2;
while (n-- > 0) while (n-- > 0) {
{ u1 = (unsigned char)*s1++;
u1 = (unsigned char) *s1++; u2 = (unsigned char)*s2++;
u2 = (unsigned char) *s2++; if (u1 != u2)
if (u1 != u2) return u1 - u2;
return u1 - u2; if (u1 == '\0')
if (u1 == '\0') return 0;
return 0; }
}
return 0; return 0;
} }
@ -64,7 +62,7 @@ struct libcache_item {
// pointer to segment address // pointer to segment address
uint32_t nsegment; uint32_t nsegment;
uint64_t* segment; uint64_t *segment;
}; };
struct libcache { struct libcache {
@ -216,7 +214,7 @@ uint64_t get_slide(const void *header) {
return 0; return 0;
} }
void* get_selfbind(const void *header) { void *get_selfbind(const void *header) {
const uint32_t magic = *(uint32_t *)header; const uint32_t magic = *(uint32_t *)header;
char *ptr = (char *)header; char *ptr = (char *)header;
if (magic == magic64) { if (magic == magic64) {
@ -226,7 +224,7 @@ void* get_selfbind(const void *header) {
} }
const uint32_t ncmds = *((uint32_t *)header + 4); const uint32_t ncmds = *((uint32_t *)header + 4);
char* command_ptr = ptr; char *command_ptr = ptr;
uint64_t slide; uint64_t slide;
for (int i = 0; i < ncmds; i++) { for (int i = 0; i < ncmds; i++) {
@ -240,17 +238,17 @@ void* get_selfbind(const void *header) {
if (custom_strcmp(name, "__TEXT") == 0) { if (custom_strcmp(name, "__TEXT") == 0) {
slide = (uint64_t)header - vmaddr; slide = (uint64_t)header - vmaddr;
} else if (custom_strcmp(name, "__DATA") == 0) { } else if (custom_strcmp(name, "__DATA") == 0) {
uint64_t nsect = *((uint32_t *)ptr + 8*2); uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
char* sections_ptr = (char*)((uint32_t*)ptr + 18); char *sections_ptr = (char *)((uint32_t *)ptr + 18);
sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1); sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1);
for (int sec = 0; sec < nsect; sec++) { for (int sec = 0; sec < nsect; sec++) {
char* secname = sections_ptr; char *secname = sections_ptr;
if (custom_strcmp(secname, "selfbind") == 0) { if (custom_strcmp(secname, "selfbind") == 0) {
uint64_t addr = *((uint64_t*)sections_ptr + 4); uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t size = *((uint64_t*)sections_ptr + 5); uint64_t size = *((uint64_t *)sections_ptr + 5);
uint32_t *data_ptr = (uint32_t*)(addr + slide); uint32_t *data_ptr = (uint32_t *)(addr + slide);
return (void*)data_ptr; return (void *)data_ptr;
} }
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8; 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(" vmaddr=0x%llx fileoffset=0x%llx\n", vmaddr, fileoffset);
printf(" vmsize=0x%llx filesize=0x%llx\n", vmsize, filesize); printf(" vmsize=0x%llx filesize=0x%llx\n", vmsize, filesize);
uint64_t nsect = *((uint32_t *)ptr + 8*2); uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
char* sections_ptr = (char*)((uint32_t*)ptr + 18); char *sections_ptr = (char *)((uint32_t *)ptr + 18);
for (int sec = 0; sec < nsect; sec++) { for (int sec = 0; sec < nsect; sec++) {
char* secname = sections_ptr; char *secname = sections_ptr;
uint64_t addr = *((uint64_t*)sections_ptr + 4); uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t size = *((uint64_t*)sections_ptr + 5); uint64_t size = *((uint64_t *)sections_ptr + 5);
uint32_t fileoffset = *((uint32_t*)sections_ptr + 6*2); uint32_t fileoffset = *((uint32_t *)sections_ptr + 6 * 2);
printf(" Section %s\n", sections_ptr); 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) { if (cmd == LC_SYMTAB) {
@ -327,15 +326,18 @@ void print_macho_summary(const void *header) {
uint64_t value; uint64_t value;
}; };
uint64_t symtab_start = (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr; uint64_t symtab_start =
uint64_t stroff_start = (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr; (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); printf(" symtab: offset=0x%x nsym=0x%x\n", symoff, nsym);
for (int j = 0; j < nsym; j++) { 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]; struct symbol_t symbol = symtab[j];
char* name = (char*)stroff_start + symbol.strx; char *name = (char *)stroff_start + symbol.strx;
printf(" %s %llx => %p\n", name, symbol.value, (void*)(symbol.value + slide)); printf(" %s %llx => %p\n", name, symbol.value,
(void *)(symbol.value + slide));
} }
} }
if (cmd == LC_REEXPORT_DYLIB) { 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; char *name = (char *)ptr + name_offset;
uint32_t hash = calculate_libname_hash(cache, name); uint32_t hash = calculate_libname_hash(cache, name);
for (int j = 0; j < cache->size; j++) { 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) { if (reexport->hash != hash) {
continue; 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) { void *custom_dlsym(struct libcache *cache, uint32_t hash, const char *symbol) {
for (size_t i = 0; i < cache->size; i++) { 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) { if (cache_lib->hash == hash) {
return find_in_lib(cache, cache_lib, symbol); 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); return custom_dlsym(cache, hash, symbol);
} }
void bootstrap_libcache_item(struct libcache_item* item, const void* header, const char* name) { void bootstrap_libcache_item(struct libcache_item *item, const void *header,
item->header = (void*)header; const char *name) {
item->header = (void *)header;
item->trie = get_export_trie(header, item->trie_size); item->trie = get_export_trie(header, item->trie_size);
const uint32_t magic = *(uint32_t *)header; 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); const uint32_t ncmds = *((uint32_t *)header + 4);
char* command_ptr = ptr; char *command_ptr = ptr;
for (int i = 0; i < ncmds; i++) { for (int i = 0; i < ncmds; i++) {
const uint32_t cmd = *((uint32_t *)ptr + 0); 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; 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++) { for (int i = 0, segment_i = 0; i < ncmds; i++) {
const uint32_t cmd = *((uint32_t *)ptr + 0); const uint32_t cmd = *((uint32_t *)ptr + 0);
const uint32_t cmdsize = *((uint32_t *)ptr + 1); 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; return;
} }
struct libcache_item* get_libcache_with_name(struct libcache* cache, const char* name) { struct libcache_item *get_libcache_with_name(struct libcache *cache,
void* to_find = 0; const char *name) {
void *to_find = 0;
if (custom_strcmp(name, "main") == 0) { if (custom_strcmp(name, "main") == 0) {
to_find = cache->main; to_find = cache->main;
} else if (custom_strcmp(name, "thislib") == 0) { } 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); uint32_t hash = calculate_libname_hash(cache, name);
for (int i = 0; i < cache->size; i++) { 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 // search by hash or by pointer for special case
if (cache_lib->hash == hash || cache_lib->header == to_find) { if (cache_lib->hash == hash || cache_lib->header == to_find) {
return cache_lib; return cache_lib;
@ -595,13 +599,14 @@ struct libcache_item* get_libcache_with_name(struct libcache* cache, const char*
return 0; 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"); FILE *outfile = fopen(filename, "wb");
fwrite((char*)trie, size, 1, outfile); fwrite((char *)trie, size, 1, outfile);
fclose(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); uint32_t hash = calculate_libname_hash(cache, libname);
for (int i = 0; i < cache->size; i++) { for (int i = 0; i < cache->size; i++) {
struct libcache_item cache_lib = cache->libs[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 *find_in_symtab(const libcache_item *lib, const char *find) {
void* header = lib->header; void *header = lib->header;
const uint32_t magic = *(uint32_t *)header; const uint32_t magic = *(uint32_t *)header;
char *ptr = (char *)header; char *ptr = (char *)header;
if (magic == magic64) { 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); const uint32_t ncmds = *((uint32_t *)header + 4);
char* command_ptr = ptr; char *command_ptr = ptr;
uint64_t linkedit_vmaddr; uint64_t linkedit_vmaddr;
uint64_t linkedit_fileoffset; uint64_t linkedit_fileoffset;
@ -644,15 +649,17 @@ void* find_in_symtab(const libcache_item* lib, const char* find) {
uint64_t value; uint64_t value;
}; };
uint64_t symtab_start = (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr; uint64_t symtab_start =
uint64_t stroff_start = (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr; (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++) { 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]; 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) { if (custom_strcmp(name, find) == 0) {
return (void*)(symbol.value + slide); return (void *)(symbol.value + slide);
} }
} }
break; break;
@ -673,7 +680,8 @@ void* find_in_symtab(const libcache_item* lib, const char* find) {
return 0; 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); uint32_t hash = calculate_libname_hash(cache, libname);
struct libcache_item *cache_lib = 0; struct libcache_item *cache_lib = 0;
for (int i = 0; i < cache->size; i++) { 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 *(*readClass_t)(void *, bool, bool);
typedef void *(*realizeClassWithoutSwift_t)(void *, void*); typedef void *(*realizeClassWithoutSwift_t)(void *, void *);
typedef void *(*remapClass_t)(void *); typedef void *(*remapClass_t)(void *);
typedef void *(*load_method_t)(void*, void*); typedef void *(*load_method_t)(void *, void *);
typedef void *(*sel_lookUpByName_t)(const char*); typedef void *(*sel_lookUpByName_t)(const char *);
typedef void (*addClassTableEntry_t)(void *); typedef void (*addClassTableEntry_t)(void *);
typedef void (*schedule_class_load_t)(void *); typedef void (*schedule_class_load_t)(void *);
@ -709,24 +717,23 @@ typedef void (*objc_autoreleasePoolPop_t)(void *);
struct custom_initializer_t { struct custom_initializer_t {
// used for Objective-C load methods // used for Objective-C load methods
uint64_t* loadable_classes; uint64_t *loadable_classes;
uint32_t* loadable_classes_used; uint32_t *loadable_classes_used;
sel_lookUpByName_t sel_lookUpByName; sel_lookUpByName_t sel_lookUpByName;
objc_autoreleasePoolPush_t objc_autoreleasePoolPush; objc_autoreleasePoolPush_t objc_autoreleasePoolPush;
objc_autoreleasePoolPop_t objc_autoreleasePoolPop; objc_autoreleasePoolPop_t objc_autoreleasePoolPop;
remapClass_t remapClass; remapClass_t remapClass;
schedule_class_load_t schedule_class_load; schedule_class_load_t schedule_class_load;
uint64_t* cls; uint64_t *cls;
size_t ncls; size_t ncls;
// used for constructors // used for constructors
void* programvars; void *programvars;
uint64_t* constructors; uint64_t *constructors;
size_t nconstructors; size_t nconstructors;
}; };
// global variable for PoC // global variable for PoC
struct custom_initializer_t* custom_initializer_i; struct custom_initializer_t *custom_initializer_i;
struct ProgramVars { struct ProgramVars {
void *mh; // mach_header or mach_header64 void *mh; // mach_header or mach_header64
@ -736,10 +743,10 @@ struct ProgramVars {
const char **__prognamePtr; const char **__prognamePtr;
}; };
void build_cache(struct libcache& cache, void* main); void build_cache(struct libcache &cache, void *main);
void fix(struct libcache& cache); void fix(struct libcache &cache);
void test(struct libcache& cache); void test(struct libcache &cache);
__attribute__((constructor)) static void __attribute__((constructor)) static void
bruh(int argc, const char *const argv[], const char *const envp[], 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, // "/usr/lib/libobjc.A.dylib", &cache,
// "../scripts/lib_objc_symtab.bin"); // "../scripts/lib_objc_symtab.bin");
// struct libcache_item* objc = get_libcache_with_name(&cache, "/usr/lib/libobjc.A.dylib"); // struct libcache_item* objc = get_libcache_with_name(&cache,
// print_macho_summary(objc->header); // "/usr/lib/libobjc.A.dylib"); print_macho_summary(objc->header);
// test(cache); // 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**) // (we do not remove them for **our lib**)
// - malloc // - malloc
// - free // - free
custom_initializer_i = (custom_initializer_t*)malloc(sizeof(custom_initializer_t)); custom_initializer_i =
custom_initializer_i->programvars = (void*)vars; (custom_initializer_t *)malloc(sizeof(custom_initializer_t));
custom_initializer_i->programvars = (void *)vars;
custom_initializer_i->cls = 0; custom_initializer_i->cls = 0;
custom_initializer_i->constructors = 0; custom_initializer_i->constructors = 0;
fix(cache); fix(cache);
@ -786,16 +794,16 @@ bruh(int argc, const char *const argv[], const char *const envp[],
printf("=== manual symbol bind completes ===\n"); 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); const uint64_t main_slide = get_slide(main);
// Find our lib (mapped) file // Find our lib (mapped) file
const void *thislib = find_header((void *)bruh); const void *thislib = find_header((void *)bruh);
// Find dyld lib (mapped) file using a no-sus function // Find dyld lib (mapped) file using a no-sus function
const void *libdyld = find_header((void *)dyld_get_sdk_version); const void *libdyld = find_header((void *)dyld_get_sdk_version);
cache.main = (void*)main; cache.main = (void *)main;
cache.thislib = (void*)thislib; cache.thislib = (void *)thislib;
cache.libdyld = (void*)libdyld; cache.libdyld = (void *)libdyld;
uint32_t libsystem_hash = uint32_t libsystem_hash =
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib"); 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, void fix_binds(struct libcache_item *libfixing, struct libcache *cache,
int n_ins, uint32_t* instructions, char* libs, char* symbols) { int n_ins, uint32_t *instructions, char *libs, char *symbols) {
uint32_t libsystem_hash = uint32_t libsystem_hash =
calculate_libname_hash(cache, "/usr/lib/libSystem.B.dylib"); 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 uint64_t page_rw_fixed[10]; // should be dynamic, but works for now
int pc = 0; int pc = 0;
for (;pc != n_ins;) { for (; pc != n_ins;) {
uint32_t libidx = instructions[pc]; uint32_t libidx = instructions[pc];
uint32_t nsym = instructions[pc + 1]; uint32_t nsym = instructions[pc + 1];
pc += 2; pc += 2;
char* lib = libs + libidx; char *lib = libs + libidx;
for (int i = 0; i < nsym; i++) { for (int i = 0; i < nsym; i++) {
uint32_t op = instructions[pc]; uint32_t op = instructions[pc];
uint32_t offset = instructions[pc + 1]; 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 symidx = op >> 8;
uint32_t segment = op & 0xff; uint32_t segment = op & 0xff;
char* sym = symbols + symidx; char *sym = symbols + symidx;
uint64_t fix_at = offset + libfixing->segment[segment]; uint64_t fix_at = offset + libfixing->segment[segment];
// enable WRITE protection for this data segment // enable WRITE protection for this data segment
int need_rw_fix = true; int need_rw_fix = true;
for (int j = 0; j < npage_rw_fixed; j++) { for (int j = 0; j < npage_rw_fixed; j++) {
if (page_rw_fixed[j] <= fix_at && if (page_rw_fixed[j] <= fix_at && page_rw_fixed[j] + 0x1000 > fix_at) {
page_rw_fixed[j] + 0x1000 > fix_at) {
need_rw_fix = false; need_rw_fix = false;
} }
} }
if (need_rw_fix) { if (need_rw_fix) {
uint64_t start_page = fix_at - (fix_at % 0x1000); uint64_t start_page = fix_at - (fix_at % 0x1000);
vm_protect_func(mach_task_self_func(), start_page, 0x1000, 0, 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; page_rw_fixed[npage_rw_fixed++] = start_page;
printf("modify page starts at 0x%llx to RW\n", 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("imports need to fix: %s at 0x%llx\n", sym, fix_at);
printf(" from=%s\n", lib); printf(" from=%s\n", lib);
printf(" segment id=%d; offset=0x%x;", segment, offset); 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_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);
void fix(struct libcache& cache) { void fix(struct libcache &cache) {
// now we have function to find exported symbols // now we have function to find exported symbols
// it supports full name search or hash search // it supports full name search or hash search
// to reserve space, we use the hash search // to reserve space, we use the hash search
@ -960,20 +966,23 @@ void fix(struct libcache& cache) {
// OBJC: // OBJC:
// In Objective-C, the binary is loaded with the Objective-C runtime // In Objective-C, the binary is loaded with the Objective-C runtime
// This runtime (a library) install a hook on dyld for all images // 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 // And because this runtime is a system runtime, the bootstrap step is already
// The details on this runtime will be in a seperated document, below are some basics // 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 // The compiler for Objective-C emits a bunch of details for the runtime in
// These information are stored in sections with prefix name __objc, namely // the binary itself These information are stored in sections with prefix name
// __objc, namely
// - __objc_classlist // - __objc_classlist
// - __objc_clssrefs // - __objc_clssrefs
// - __objc_selref // - __objc_selref
// - __objc_const // - __objc_const
// - __objc_data // - __objc_data
// //
// Objective-C stores the class interface in the binary particulary in __objc_data // Objective-C stores the class interface in the binary particulary in
// This interface contains the superclass, metaclass, and a cache to methods pointers // __objc_data This interface contains the superclass, metaclass, and a cache
// These information are either bound (by dyld) or built (by Objective-C runtime) // 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. // 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 // 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, // Because __objc_data contains to-be-bound values,
// which will be resolved by dyld and referenced by Objective-C runtime later // 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 // if we simply erase this value, reference(s) read by Objective-C runtime
// (through debugging, we know that the crash happens in readClass, realizeClassWithoutSwift) // 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 // However, we can evade this by making the runtime thinks there is no class
// This can be done by changing the __objc_classlist to some other name or remove this section // needs setup This can be done by changing the __objc_classlist to some other
// Because the runtime find the __objc_classlist section by name, and the size of the section // name or remove this section Because the runtime find the __objc_classlist
// is used to iterate through pointers. // section by name, and the size of the section is used to iterate through
// So if we change the name, the runtime will have no class to run setup. // pointers. So if we change the name, the runtime will have no class to run
// Or complete removal and call the setup by ourselves, because we know where the data is // 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 // The setup is done through readClass function, as said above, its address
// This function is pure C function so call into this function is easy // can be found This function is pure C function so call into this function is
// easy
// //
// Important function with their names: // Important function with their names:
// _readClass(objc_class*, bool, bool) // _readClass(objc_class*, bool, bool)
@ -1022,37 +1034,38 @@ void fix(struct libcache& cache) {
// resolve selfbind if exist // resolve selfbind if exist
{ // stored inside __DATA,selfbind { // 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 { struct selfbind_t {
uint32_t liblist_offset; uint32_t liblist_offset;
uint32_t symbollist_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) { if (selfbind) {
char* libs = (char*)(selfbind+1) + selfbind->liblist_offset; char *libs = (char *)(selfbind + 1) + selfbind->liblist_offset;
char* symbols = (char*)(selfbind+1) + selfbind->symbollist_offset; char *symbols = (char *)(selfbind + 1) + selfbind->symbollist_offset;
uint64_t n_instructions = ((uint64_t)libs - (uint64_t)(selfbind + 1)) / 4; 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); printf("[*] performing selfbind (instructions=%p)\n", selfbind);
fix_binds(libfixing, &cache, fix_binds(libfixing, &cache, n_instructions, encoded_table, libs,
n_instructions, encoded_table, symbols);
libs, symbols);
} }
} }
// the rest of the fixes are in main executable // the rest of the fixes are in main executable
printf("[*] performing bind for main executable\n"); printf("[*] performing bind for main executable\n");
struct libcache_item* libfixing = get_libcache_with_name(&cache, "main"); struct libcache_item *libfixing = get_libcache_with_name(&cache, "main");
fix_binds(libfixing, &cache, fix_binds(libfixing, &cache, bshield_data::n_instructions,
bshield_data::n_instructions, bshield_data::encoded_table, bshield_data::encoded_table, bshield_data::libs,
bshield_data::libs, bshield_data::symbols); bshield_data::symbols);
// TODO: Reformat the region as per before, or leave as it // TODO: Reformat the region as per before, or leave as it
// for (int j = 0; j < npage_rw_fixed; j++) { // for (int j = 0; j < npage_rw_fixed; j++) {
// uint64_t start_page = page_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 // Encrypted __TEXT segment
@ -1070,61 +1083,74 @@ void fix(struct libcache& cache) {
fix_initializer(libfixing, cache); fix_initializer(libfixing, cache);
} }
void volatile void volatile custom_initializer(int argc, const char *const argv[],
custom_initializer(int argc, const char *const argv[], const char *const envp[], const char *const apple[]) { const char *const envp[],
const char *const apple[]) {
printf("run custom initializers %p\n", custom_initializer_i); printf("run custom initializers %p\n", custom_initializer_i);
if (custom_initializer_i->cls != 0) { if (custom_initializer_i->cls != 0) {
// for Objective-C load // for Objective-C load
uint64_t* loadable_classes = custom_initializer_i->loadable_classes; uint64_t *loadable_classes = custom_initializer_i->loadable_classes;
uint32_t* loadable_classes_used = custom_initializer_i->loadable_classes_used; uint32_t *loadable_classes_used =
sel_lookUpByName_t sel_lookUpByName = custom_initializer_i->sel_lookUpByName; custom_initializer_i->loadable_classes_used;
objc_autoreleasePoolPop_t objc_autoreleasePoolPop = custom_initializer_i->objc_autoreleasePoolPop; sel_lookUpByName_t sel_lookUpByName =
objc_autoreleasePoolPush_t objc_autoreleasePoolPush = custom_initializer_i->objc_autoreleasePoolPush; 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; 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++) { for (int i = 0; i < custom_initializer_i->ncls; i++) {
void* cls0 = (void*)custom_initializer_i->cls[i]; void *cls0 = (void *)custom_initializer_i->cls[i];
void* cls = remapClass(cls0); void *cls = remapClass(cls0);
if (!cls) continue; if (!cls)
continue;
schedule_class_load(cls); 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 { struct loadable_class_t {
void* cls; void *cls;
void* method; 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; int used = *loadable_classes_used;
*loadable_classes = 0; *loadable_classes = 0;
// *loadable_classes_allocated = 0; // *loadable_classes_allocated = 0;
*loadable_classes_used = 0; *loadable_classes_used = 0;
void* sel = sel_lookUpByName("load"); void *sel = sel_lookUpByName("load");
// Call all +loads for the detached list. // Call all +loads for the detached list.
void* pool = objc_autoreleasePoolPush(); void *pool = objc_autoreleasePoolPush();
for (int i = 0; i < used; i++) { 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; load_method_t load_method = (load_method_t)classes[i].method;
printf("call load of class %p %p\n", cls, load_method); printf("call load of class %p %p\n", cls, load_method);
if (!cls) continue; if (!cls)
continue;
(load_method)(cls, sel); (load_method)(cls, sel);
} }
// Destroy the detached list. // Destroy the detached list.
if (classes) free(classes); if (classes)
free(classes);
objc_autoreleasePoolPop(pool); objc_autoreleasePoolPop(pool);
} }
// for constructors // for constructors
if (custom_initializer_i->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; uint32_t nconst = custom_initializer_i->nconstructors;
for (int i = 0; i < nconst; i++) { 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); 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); 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); 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 // 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;" // "mov rcx, 123;"
// "call r12;"); // "call r12;");
void* header = libfixing->header; void *header = libfixing->header;
const uint32_t magic = *(uint32_t *)header; const uint32_t magic = *(uint32_t *)header;
char *ptr = (char *)header; char *ptr = (char *)header;
if (magic == magic64) { 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); const uint32_t ncmds = *((uint32_t *)header + 4);
char* command_ptr = ptr; char *command_ptr = ptr;
uint64_t linkedit_vmaddr; uint64_t linkedit_vmaddr;
uint64_t linkedit_fileoffset; uint64_t linkedit_fileoffset;
@ -1173,62 +1199,80 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
if (custom_strcmp(name, "__TEXT") == 0) { if (custom_strcmp(name, "__TEXT") == 0) {
slide = (uint64_t)header - vmaddr; slide = (uint64_t)header - vmaddr;
} else if (custom_strcmp(name, "__DATA_CONST") == 0) { } else if (custom_strcmp(name, "__DATA_CONST") == 0) {
uint64_t nsect = *((uint32_t *)ptr + 8*2); uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
char* sections_ptr = (char*)((uint32_t*)ptr + 18); char *sections_ptr = (char *)((uint32_t *)ptr + 18);
for (int sec = 0; sec < nsect; sec++) { for (int sec = 0; sec < nsect; sec++) {
char* secname = sections_ptr; char *secname = sections_ptr;
printf("section %s\n", secname); printf("section %s\n", secname);
if (custom_strncmp(secname, "__objc_classbruh", 16) == 0) { if (custom_strncmp(secname, "__objc_classbruh", 16) == 0) {
uint64_t addr = *((uint64_t*)sections_ptr + 4); uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t size = *((uint64_t*)sections_ptr + 5); uint64_t size = *((uint64_t *)sections_ptr + 5);
uint64_t *data_ptr = (uint64_t*)(addr + slide); uint64_t *data_ptr = (uint64_t *)(addr + slide);
readClass_t readClass = (readClass_t)find_in_symtab( readClass_t readClass =
"/usr/lib/libobjc.A.dylib", &cache, "__ZL9readClassP10objc_classbb"); (readClass_t)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
realizeClassWithoutSwift_t realizeClassWithoutSwift = (realizeClassWithoutSwift_t)find_in_symtab( "__ZL9readClassP10objc_classbb");
"/usr/lib/libobjc.A.dylib", &cache, "__ZL24realizeClassWithoutSwiftP10objc_classS0_"); 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++) { for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) {
// this pointer is rebased by dyld and points to the correct class interface // this pointer is rebased by dyld and points to the correct class
// for some reason, we can skip this and it should still work // interface for some reason, we can skip this and it should still
void* newCls = readClass((void*)data_ptr[ptr_i], false, false); // work
if (newCls != (void*)data_ptr[ptr_i]) { void *newCls = readClass((void *)data_ptr[ptr_i], false, false);
if (newCls != (void *)data_ptr[ptr_i]) {
realizeClassWithoutSwift(newCls, 0); realizeClassWithoutSwift(newCls, 0);
} }
printf("add class init (%llx)%p\n", data_ptr[ptr_i], newCls); printf("add class init (%llx)%p\n", data_ptr[ptr_i], newCls);
} }
} } else if (custom_strncmp(secname, "__objc_nlclsbruh", 16) == 0) {
else if (custom_strncmp(secname, "__objc_nlclsbruh", 16) == 0) { uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t addr = *((uint64_t*)sections_ptr + 4); uint64_t size = *((uint64_t *)sections_ptr + 5);
uint64_t size = *((uint64_t*)sections_ptr + 5); uint64_t *data_ptr = (uint64_t *)(addr + slide);
uint64_t *data_ptr = (uint64_t*)(addr + slide);
uint64_t* loadable_classes = (uint64_t*)find_in_symtab( uint64_t *loadable_classes = (uint64_t *)find_in_symtab(
"/usr/lib/libobjc.A.dylib", &cache, "__ZL16loadable_classes"); "/usr/lib/libobjc.A.dylib", &cache, "__ZL16loadable_classes");
uint32_t* loadable_classes_allocated = (uint32_t*)find_in_symtab( uint32_t *loadable_classes_allocated =
"/usr/lib/libobjc.A.dylib", &cache, "__ZL26loadable_classes_allocated"); (uint32_t *)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
uint32_t* loadable_classes_used = (uint32_t*)find_in_symtab( "__ZL26loadable_classes_allocated");
"/usr/lib/libobjc.A.dylib", &cache, "__ZL21loadable_classes_used"); 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( remapClass_t remapClass =
"/usr/lib/libobjc.A.dylib", &cache, "__ZL10remapClassP10objc_class"); (remapClass_t)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
schedule_class_load_t schedule_class_load = (schedule_class_load_t)find_in_symtab( "__ZL10remapClassP10objc_class");
"/usr/lib/libobjc.A.dylib", &cache, "__ZL19schedule_class_loadP10objc_class"); schedule_class_load_t schedule_class_load =
realizeClassWithoutSwift_t realizeClassWithoutSwift = (realizeClassWithoutSwift_t)find_in_symtab( (schedule_class_load_t)find_in_symtab(
"/usr/lib/libobjc.A.dylib", &cache, "__ZL24realizeClassWithoutSwiftP10objc_classS0_"); "/usr/lib/libobjc.A.dylib", &cache,
addClassTableEntry_t addClassTableEntry = (addClassTableEntry_t)find_in_symtab( "__ZL19schedule_class_loadP10objc_class");
"/usr/lib/libobjc.A.dylib", &cache, "__ZL18addClassTableEntryP10objc_classb"); realizeClassWithoutSwift_t realizeClassWithoutSwift =
sel_lookUpByName_t sel_lookUpByName = (sel_lookUpByName_t)find_in_symtab( (realizeClassWithoutSwift_t)find_in_symtab(
"/usr/lib/libobjc.A.dylib", &cache, "_sel_lookUpByName"); "/usr/lib/libobjc.A.dylib", &cache,
objc_autoreleasePoolPush_t objc_autoreleasePoolPush = (objc_autoreleasePoolPush_t)find_in_symtab( "__ZL24realizeClassWithoutSwiftP10objc_classS0_");
"/usr/lib/libobjc.A.dylib", &cache, "__objc_autoreleasePoolPush"); addClassTableEntry_t addClassTableEntry =
objc_autoreleasePoolPop_t objc_autoreleasePoolPop = (objc_autoreleasePoolPop_t)find_in_symtab( (addClassTableEntry_t)find_in_symtab(
"/usr/lib/libobjc.A.dylib", &cache, "__objc_autoreleasePoolPop"); "/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 // 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++) { for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) {
void* cls = remapClass((void*)data_ptr[ptr_i]); void *cls = remapClass((void *)data_ptr[ptr_i]);
if (!cls) continue; if (!cls)
continue;
addClassTableEntry(cls); addClassTableEntry(cls);
realizeClassWithoutSwift(cls, 0); realizeClassWithoutSwift(cls, 0);
printf("build nonlazy class at (%llx)%p\n", data_ptr[ptr_i], cls); 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->sel_lookUpByName = sel_lookUpByName;
custom_initializer_i->loadable_classes = loadable_classes; custom_initializer_i->loadable_classes = loadable_classes;
custom_initializer_i->loadable_classes_used = loadable_classes_used; custom_initializer_i->loadable_classes_used = loadable_classes_used;
custom_initializer_i->objc_autoreleasePoolPush = objc_autoreleasePoolPush; custom_initializer_i->objc_autoreleasePoolPush =
custom_initializer_i->objc_autoreleasePoolPop = objc_autoreleasePoolPop; objc_autoreleasePoolPush;
custom_initializer_i->objc_autoreleasePoolPop =
objc_autoreleasePoolPop;
custom_initializer_i->schedule_class_load = schedule_class_load; custom_initializer_i->schedule_class_load = schedule_class_load;
custom_initializer_i->remapClass = remapClass; custom_initializer_i->remapClass = remapClass;
custom_initializer_i->cls = data_ptr; 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 // fix the initializers
// The Objective-C runtime loads the NSObject after this lib booted // 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 // So we will fix the main initializers, which runs after all Objective-C
// The initializers will run these Objective-C classes' load methods // setup The initializers will run these Objective-C classes' load methods
// //
// (THIS IDEA IS TESTED AND WILL NOT WORK) // (THIS IDEA IS TESTED AND WILL NOT WORK)
// As of now, we assume the main executable has a __mod_init_func section // 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. // 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. // The idea is to hijack the main function to do the rest of the
// By fixing the LC_MAIN command, we can make dyld jump to anywhere we want as main. // initalizations. By fixing the LC_MAIN command, we can make dyld jump to
// But the command can't be edited at runtime. // anywhere we want as main. But the command can't be edited at runtime. And
// And pointing to the function we want needs a workaround. // pointing to the function we want needs a workaround.
// //
// So we will write a shellcode in the binary and point main to that shellcode. // So we will write a shellcode in the binary and point main to that
// The shellcode basically loads the address of the initalization function, // shellcode. The shellcode basically loads the address of the initalization
// call it, then call main, and return. // function, call it, then call main, and return.
// //
// The shellcode must be able ot get the current pc address to correctly calculate // The shellcode must be able ot get the current pc address to correctly
// address from any offset. In arm64, we can use `adr x8, 0`. // calculate address from any offset. In arm64, we can use `adr x8, 0`. If we
// If we know where the shellcode is, we can effectively calculate the header of main. // know where the shellcode is, we can effectively calculate the header of
// Now, everything is easy, just need offsets to anywhere we want and we can get it. // 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, // Now the address of the initalization function can be fetched using many
// but it resides inside this library. To reduce redundance work, we can write the // methods, but it resides inside this library. To reduce redundance work, we
// address of this function somewhere inside main, which will then be easily found. // 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, // As a result, we choose the space before __text to write the shellcode,
// the space after __DATA to write the address for initalization function. // the space after __DATA to write the address for initalization function.
// Because all segment is allocated/pre-allocated with page alignement, // Because all segment is allocated/pre-allocated with page alignement,
// we can be pretty sure that there are free space. // 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. // Below is the shellcode.
/* /*
@ -1330,7 +1380,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
br x9 br x9
*/ */
void* header = libfixing->header; void *header = libfixing->header;
const uint32_t magic = *(uint32_t *)header; const uint32_t magic = *(uint32_t *)header;
char *ptr = (char *)header; char *ptr = (char *)header;
if (magic == magic64) { 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); const uint32_t ncmds = *((uint32_t *)header + 4);
char* command_ptr = ptr; char *command_ptr = ptr;
uint64_t linkedit_vmaddr; uint64_t linkedit_vmaddr;
uint64_t linkedit_fileoffset; uint64_t linkedit_fileoffset;
@ -1356,23 +1406,26 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
printf("segment %s\n", name); printf("segment %s\n", name);
if (custom_strcmp(name, "__TEXT") == 0) { if (custom_strcmp(name, "__TEXT") == 0) {
slide = (uint64_t)header - vmaddr; slide = (uint64_t)header - vmaddr;
uint64_t nsect = *((uint32_t *)ptr + 8*2); uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
char* sections_ptr = (char*)((uint32_t*)ptr + 18); char *sections_ptr = (char *)((uint32_t *)ptr + 18);
for (int sec = 0; sec < nsect; sec++) { for (int sec = 0; sec < nsect; sec++) {
char* secname = sections_ptr; char *secname = sections_ptr;
printf("section %s\n", secname); printf("section %s\n", secname);
if (custom_strcmp(secname, "__init_offsets") == 0) { if (custom_strcmp(secname, "__init_offsets") == 0) {
uint64_t addr = *((uint64_t*)sections_ptr + 4); uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t size = *((uint64_t*)sections_ptr + 5); uint64_t size = *((uint64_t *)sections_ptr + 5);
uint32_t *data_ptr = (uint32_t*)(addr + slide); uint32_t *data_ptr = (uint32_t *)(addr + slide);
printf("found initializer at %p\n", data_ptr); printf("found initializer at %p\n", data_ptr);
custom_initializer_i->nconstructors = size / 4; 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++) { for (int j = 0; j < size / 4; j++) {
custom_initializer_i->constructors[j] = (uint64_t)header + data_ptr[j]; custom_initializer_i->constructors[j] =
printf("registered initializer at %llx\n", 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) { 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; sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
} }
} else if (custom_strcmp(name, "__DATA") == 0) { } else if (custom_strcmp(name, "__DATA") == 0) {
uint64_t nsect = *((uint32_t *)ptr + 8*2); uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
char* sections_ptr = (char*)((uint32_t*)ptr + 18); char *sections_ptr = (char *)((uint32_t *)ptr + 18);
sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1); sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1);
uint64_t addr = *((uint64_t*)sections_ptr + 4); uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t size = *((uint64_t*)sections_ptr + 5); 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[0] = (uint64_t)custom_initializer;
dummy[1] = (uint64_t)(header) + bshield_data::main; dummy[1] = (uint64_t)(header) + bshield_data::main;
printf("add custom main-peg at %p\n", dummy); 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 = uint32_t libsystem_hash =
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib"); calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
if (false) { // test search using name 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, printf("Indirect search: Found=%p Expected=%p\n", vm_protect_func,
vm_protect); 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); printf("Indirect search: Found=%p Expected=%p\n", realpath_func, realpath);
} }
} }