Compare commits
27 Commits
x86_64
...
remove-str
Author | SHA1 | Date | |
---|---|---|---|
06bbde2612 | |||
1c495989d4 | |||
083556f914 | |||
88b79bccb9 | |||
9cdf9f0ff5 | |||
7fa3ba0b7d | |||
cc34751c9a | |||
c241e78cd8 | |||
0640d38627 | |||
d9024990f9 | |||
04979b0afd | |||
9b2796b2a1 | |||
925429c4a9 | |||
9b85e4938f | |||
5e601eaa4a | |||
d534d62f5e | |||
d11ef20f4a | |||
8e1e176068 | |||
06525b8a5e | |||
57b0ae26a7 | |||
f795e9b99d | |||
901f1ed819 | |||
41144ff0dc | |||
9a8ab15d88 | |||
a8ffae5202 | |||
9ec2a301b4 | |||
a68bbf2b8f |
@ -9,3 +9,6 @@ build-linux:
|
||||
|
||||
module:
|
||||
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
|
||||
|
||||
format:
|
||||
go fmt ./...
|
||||
|
@ -1,6 +1,6 @@
|
||||
module ios-wrapper
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kong v0.2.16
|
||||
|
20
macho-go/internal/wrapper/action/remove_strings.go
Normal file
20
macho-go/internal/wrapper/action/remove_strings.go
Normal file
@ -0,0 +1,20 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type removeStrings struct{}
|
||||
|
||||
func (action *removeStrings) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveStrings()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *removeStrings) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewRemoveStringsAction() *removeStrings {
|
||||
return &removeStrings{}
|
||||
}
|
@ -68,7 +68,7 @@ func (action *saveImports) saveToInfo(mf *MachoFile) error {
|
||||
// now we expect everything is sorted and easier to build strings tables
|
||||
// this is not fully optimized, there can be repeated symbol name in different libraries
|
||||
for _, symbol := range symbols_raw {
|
||||
if symbol.Type() != "lazy" {
|
||||
if !symbol.SafeForRemoval() {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -138,6 +138,16 @@ func (action *saveImports) saveToInfo(mf *MachoFile) error {
|
||||
}
|
||||
|
||||
mf.Info().Main = mc.Main()
|
||||
|
||||
selectors_list := []*protomodel.MachoInfo_Selector{}
|
||||
for _, sel := range mc.CollectSpecialSelectors() {
|
||||
selectors_list = append(selectors_list, &protomodel.MachoInfo_Selector{
|
||||
Idx: uint32(sel.Idx()),
|
||||
Name: sel.Name(),
|
||||
})
|
||||
}
|
||||
mf.Info().SpecialSelectors = selectors_list
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -117,6 +117,7 @@ func Cli() {
|
||||
pc.remove_inits = true
|
||||
pc.remove_codesign = true
|
||||
pc.remove_others = true
|
||||
pc.remove_string = true
|
||||
}
|
||||
pc.remove_imports = arg.RemoveBindSymbols
|
||||
pc.remove_codesign = arg.RemoveCodeSign
|
||||
@ -124,6 +125,7 @@ func Cli() {
|
||||
pc.remove_others = arg.RemoveOthers
|
||||
pc.remove_exports = arg.RemoveExports
|
||||
pc.remove_symbol_table = arg.RemoveSymbolTable
|
||||
pc.remove_string = arg.RemoveStrings
|
||||
pc.dylib_to_add = arg.Dylibs
|
||||
pc.rpath_to_add = arg.Rpath
|
||||
pc.outfile = arg.Out
|
||||
@ -264,41 +266,69 @@ func bcell2header(bfile string, header string) {
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char libs[] =\n")
|
||||
for _, lib := range info.Symbols.Libs {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", lib)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char symbols[] =\n")
|
||||
for _, symbol := range info.Symbols.Symbols {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", symbol)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "// very compact symbol table,\n")
|
||||
fmt.Fprintf(w, "// [lib idx/*4 bytes*/, nsymbol/*4 byte*/]\n")
|
||||
fmt.Fprintf(w, "// repeat nsymbol times [name offset/*3 bytes*/, segment idx/**/, offset /*4 btyes*/]\n")
|
||||
fmt.Fprintf(w, "// name offset is 3 bytes because we don't think we should have a table size > 2^(3 * 8)\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t encoded_table[] = {\n")
|
||||
n_instructions := 0
|
||||
for i, table := range info.Symbols.Tables {
|
||||
fmt.Fprintf(w, " // %s\n", info.Symbols.Libs[i])
|
||||
fmt.Fprintf(w, " %d/*lib offset*/,\n", table.LibIndex)
|
||||
fmt.Fprintf(w, " %d/*nsymbols*/,\n", table.Nsymbols)
|
||||
n_instructions += 2
|
||||
for _, symbol := range table.Symbols {
|
||||
fmt.Fprintf(w, " %d, 0x%x,\n", (symbol.SymbolIndex<<8)|symbol.SegmentIndex, symbol.Offset)
|
||||
n_instructions += 2
|
||||
if info.Symbols != nil {
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char libs[] =\n")
|
||||
for _, lib := range info.Symbols.Libs {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", lib)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char symbols[] =\n")
|
||||
for _, symbol := range info.Symbols.Symbols {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", symbol)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "// very compact symbol table,\n")
|
||||
fmt.Fprintf(w, "// [lib idx/*4 bytes*/, nsymbol/*4 byte*/]\n")
|
||||
fmt.Fprintf(w, "// repeat nsymbol times [name offset/*3 bytes*/, segment idx/**/, offset /*4 btyes*/]\n")
|
||||
fmt.Fprintf(w, "// name offset is 3 bytes because we don't think we should have a table size > 2^(3 * 8)\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t encoded_table[] = {\n")
|
||||
n_instructions := 0
|
||||
for i, table := range info.Symbols.Tables {
|
||||
fmt.Fprintf(w, " // %s\n", info.Symbols.Libs[i])
|
||||
fmt.Fprintf(w, " %d/*lib offset*/,\n", table.LibIndex)
|
||||
fmt.Fprintf(w, " %d/*nsymbols*/,\n", table.Nsymbols)
|
||||
n_instructions += 2
|
||||
for _, symbol := range table.Symbols {
|
||||
fmt.Fprintf(w, " %d, 0x%x,\n", (symbol.SymbolIndex<<8)|symbol.SegmentIndex, symbol.Offset)
|
||||
n_instructions += 2
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
fmt.Fprintf(w, "uint32_t n_instructions = %d;\n", n_instructions)
|
||||
} else {
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char libs[] = {};\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char symbols[] = {};\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t encoded_table[] = {};\n")
|
||||
|
||||
fmt.Fprintf(w, "uint32_t n_instructions = %d;\n", 0)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t special_selectors_idx[] = {\n")
|
||||
for _, selector := range info.GetSpecialSelectors() {
|
||||
fmt.Fprintf(w, "%x,\n", selector.Idx)
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
fmt.Fprintf(w, "uint32_t n_instructions = %d;\n", n_instructions)
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "const char* special_selectors_name[] = {\n")
|
||||
for _, selector := range info.GetSpecialSelectors() {
|
||||
fmt.Fprintf(w, "\"%s\",\n", selector.Name)
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
fmt.Fprintf(w, "uint32_t n_selectors = %d;\n", len(info.GetSpecialSelectors()))
|
||||
}
|
||||
fmt.Fprintf(w, "}// namespace bshield_data\n")
|
||||
w.Flush()
|
||||
|
@ -61,6 +61,7 @@ type PepeArgument struct {
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
Dylibs []string `short:"l" help:"Add more LC_DYLIB"`
|
||||
Rpath []string `short:"r" help:"Add more LC_RPATH"`
|
||||
RemoveStrings bool `default:"false" negatable:"" help:"Remove static strings found in DATA and DATA_CONST section"`
|
||||
RemoveCodeSign bool `default:"false" negatable:"" help:"Remove LC_CODE_SIGNATURE"`
|
||||
RemoveExports bool `default:"false" negatable:"" help:"Clear the export table/trie"`
|
||||
RemoveSymbolTable bool `default:"false" negatable:"" help:"Remove LC_SYMTAB and LC_DYSYMTAB"`
|
||||
|
@ -46,6 +46,8 @@ func (printer *InfoPrinter) Print() {
|
||||
)
|
||||
}
|
||||
|
||||
mc.CollectObjectiveCClasses()
|
||||
|
||||
fmt.Println("======")
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ type ProgramContext struct {
|
||||
remove_others bool
|
||||
remove_exports bool
|
||||
remove_symbol_table bool
|
||||
remove_string bool
|
||||
dylib_to_add []string
|
||||
rpath_to_add []string
|
||||
symbols_keep []string
|
||||
@ -106,6 +107,9 @@ func (pc *ProgramContext) Process(ofile OFile) {
|
||||
if pc.remove_exports {
|
||||
pc.AddAction(NewRemoveExportsAction())
|
||||
}
|
||||
if pc.remove_string {
|
||||
pc.AddAction(NewRemoveStringsAction())
|
||||
}
|
||||
ExperimentalFeature("Remove Unnecessary Info", func() {
|
||||
if pc.remove_others {
|
||||
pc.AddAction(NewRemoveUnnecessaryInfoAction())
|
||||
|
174
macho-go/pkg/ios/macho/arm.c
Normal file
174
macho-go/pkg/ios/macho/arm.c
Normal file
@ -0,0 +1,174 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
|
||||
// this file encodes and decodes arm instructions
|
||||
// only support for ARMv8 or (aarch64) because
|
||||
// it is the default ISA in Apple devices
|
||||
// https://yurichev.com/mirrors/ARMv8-A_Architecture_Reference_Manual_(Issue_A.a).pdf
|
||||
|
||||
// using bit field in little endian is crazy
|
||||
// but clang does not support switching endianess (fuck u llvm)
|
||||
// gcc does, but default on Apple compiler is clang
|
||||
// TODO: default to using big endian for bit field parsing
|
||||
|
||||
// this file assumes everything is LITTLE ENDIAN
|
||||
// the struct is assigned as bit field, and parsed using get_bits
|
||||
// to encode the instruction, can use the bit field and load each bits
|
||||
// through shifting, by default it has 0 padded so should work
|
||||
|
||||
// mask to delete last 12 bits
|
||||
uint32_t ZEROS_12_LOWER = ~0xFFF;
|
||||
|
||||
uint32_t get_bits(uint32_t value, uint32_t from, uint32_t to) {
|
||||
// should assert compiler error
|
||||
if (to < from) return false;
|
||||
if (from == to) {
|
||||
// bit at position is set
|
||||
return (value & (1 << from)) != 0;
|
||||
}
|
||||
|
||||
return (value << (31 - to)) >> (from + (31 - to));
|
||||
}
|
||||
|
||||
int is_bit_set(uint32_t value, uint32_t at) {
|
||||
return (value & (1 << at)) != 0;
|
||||
}
|
||||
|
||||
struct add {
|
||||
uint32_t sf : 1;
|
||||
uint32_t op : 1;
|
||||
uint32_t s : 1;
|
||||
uint32_t sig : 5;
|
||||
uint32_t shift : 2;
|
||||
uint32_t imm : 12;
|
||||
uint32_t rn : 5;
|
||||
uint32_t rd : 5;
|
||||
};
|
||||
|
||||
struct add to_add(uint32_t inst) {
|
||||
struct add parsed;
|
||||
parsed.sf = is_bit_set(inst, 31);
|
||||
parsed.op = is_bit_set(inst, 30);
|
||||
parsed.s = is_bit_set(inst, 29);
|
||||
parsed.sig = get_bits(inst, 24, 28);
|
||||
parsed.shift = get_bits(inst, 22, 23);
|
||||
parsed.imm = get_bits(inst, 10, 21);
|
||||
parsed.rn = get_bits(inst, 5, 9);
|
||||
parsed.rd = get_bits(inst, 0, 4);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
void from_add(struct add parsed, uint32_t *inst) {
|
||||
*inst = 0;
|
||||
*inst |= parsed.sf << 31;
|
||||
*inst |= parsed.op << 30;
|
||||
*inst |= parsed.s << 29;
|
||||
*inst |= parsed.sig << 24;
|
||||
*inst |= parsed.shift << 22;
|
||||
*inst |= parsed.imm << 10;
|
||||
*inst |= parsed.rn << 5;
|
||||
*inst |= parsed.rd;
|
||||
}
|
||||
|
||||
int add_imm_set(uint32_t *inst, uint32_t offset) {
|
||||
struct add parsed = to_add(*inst);
|
||||
if (parsed.op != 0 || parsed.sig != 0b10001) {
|
||||
return false;
|
||||
}
|
||||
parsed.imm = offset; // auto truncate?
|
||||
from_add(parsed, inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t add_imm_get(uint32_t inst) {
|
||||
struct add parsed = to_add(inst);
|
||||
if (parsed.shift != 0) {
|
||||
printf("add instruction shift != 0 is not supported\n");
|
||||
*(char*)0 = 0;
|
||||
}
|
||||
return parsed.imm;
|
||||
}
|
||||
|
||||
int is_add(uint32_t inst) {
|
||||
struct add parsed = to_add(inst);
|
||||
if (parsed.op != 0 || parsed.sig != 0b10001) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct adrp {
|
||||
uint32_t op : 1;
|
||||
uint32_t immlo : 2;
|
||||
uint32_t sig : 5;
|
||||
uint32_t immhi : 19;
|
||||
uint32_t rd : 5;
|
||||
};
|
||||
|
||||
struct adrp to_adrp(uint32_t inst) {
|
||||
struct adrp parsed;
|
||||
parsed.op = is_bit_set(inst, 31);
|
||||
parsed.immlo = get_bits(inst, 29, 30);
|
||||
parsed.sig = get_bits(inst, 24, 28);
|
||||
parsed.immhi = get_bits(inst, 5, 23);
|
||||
parsed.rd = get_bits(inst, 0, 4);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
void from_adrp(struct adrp parsed, uint32_t *inst) {
|
||||
*inst = 0;
|
||||
*inst |= parsed.op << 31;
|
||||
*inst |= parsed.immlo << 29;
|
||||
*inst |= parsed.sig << 24;
|
||||
*inst |= parsed.immhi << 5;
|
||||
*inst |= parsed.rd;
|
||||
}
|
||||
|
||||
int is_adrp(uint32_t inst) {
|
||||
struct adrp parsed = to_adrp(inst);
|
||||
if (parsed.op != 1 || parsed.sig != 0b10000) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// change adrp imm to something else
|
||||
int adrp_imm_set(uint32_t *inst, uint32_t offset) {
|
||||
struct adrp parsed = to_adrp(*inst);
|
||||
if (parsed.op != 1 || parsed.sig != 0b10000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// uint32_t imm = 0;
|
||||
|
||||
// imm = parsed.immhi << 14;
|
||||
// imm |= parsed.immlo << 12;
|
||||
// printf("old adrp is %x\n", imm);
|
||||
// printf(" immlo %x\n", parsed.immlo);
|
||||
// printf(" immhi %x\n", parsed.immhi);
|
||||
|
||||
// adrp: register = (base masked lower 12-bit) + imm
|
||||
parsed.immlo = get_bits(offset >> 12, 0, 1);
|
||||
parsed.immhi = offset >> 14;
|
||||
|
||||
// imm = parsed.immhi << 14;
|
||||
// imm |= parsed.immlo << 12;
|
||||
// printf("new adrp is %x\n", imm);
|
||||
// printf(" immlo %x\n", parsed.immlo);
|
||||
// printf(" immhi %x\n", parsed.immhi);
|
||||
|
||||
from_adrp(parsed, inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t adrp_imm_get(uint32_t inst) {
|
||||
struct adrp parsed = to_adrp(inst);
|
||||
uint32_t imm = 0;
|
||||
imm = parsed.immhi << 14;
|
||||
imm |= parsed.immlo << 12;
|
||||
return imm;
|
||||
}
|
7
macho-go/pkg/ios/macho/arm.h
Normal file
7
macho-go/pkg/ios/macho/arm.h
Normal file
@ -0,0 +1,7 @@
|
||||
int is_add(uint32_t inst);
|
||||
int add_imm_set(uint32_t *inst, uint32_t offset);
|
||||
uint32_t add_imm_get(uint32_t inst);
|
||||
|
||||
int is_adrp(uint32_t inst);
|
||||
int adrp_imm_set(uint32_t *inst, uint32_t offset);
|
||||
uint32_t adrp_imm_get(uint32_t inst);
|
@ -27,6 +27,9 @@ type ImportSymbol struct {
|
||||
file_address uint64
|
||||
lib_ordinal uint32
|
||||
|
||||
target uint32
|
||||
high8 uint32
|
||||
|
||||
// push number
|
||||
pnum uint32
|
||||
stub uint64
|
||||
@ -42,6 +45,10 @@ func (sym *ImportSymbol) Type() string {
|
||||
return sym.typ
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) SafeForRemoval() bool {
|
||||
return sym.typ == "lazy" || sym.typ == "fixups"
|
||||
}
|
||||
|
||||
func (sym *ImportSymbol) Dylib() string {
|
||||
return sym.dylib
|
||||
}
|
||||
@ -118,16 +125,19 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
if status == 3 {
|
||||
continue
|
||||
}
|
||||
// fmt.Printf("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count)
|
||||
// fmt.Printf("pages=%x\n", fix.pages)
|
||||
|
||||
fmt.Printf("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count)
|
||||
|
||||
pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count))
|
||||
reader := bytes.NewReader(mc.buf)
|
||||
for page_i := 0; page_i < int(fix.page_count); page_i++ {
|
||||
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
||||
|
||||
address := int64(fix.segment) + int64(pages[page_i])
|
||||
// loop through each page in segment, each page has size fix.page_size
|
||||
// the first item in page is offset through pages[page_i]
|
||||
address := int64(fix.segment) + int64(page_i)*int64(fix.page_size) + int64(pages[page_i])
|
||||
reader.Seek(address, io.SeekStart)
|
||||
|
||||
fmt.Printf(" page %d offset=%x\n", page_i, address)
|
||||
|
||||
code := make([]byte, 8)
|
||||
|
||||
for {
|
||||
@ -137,6 +147,10 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
var bind C.int
|
||||
var ret1 C.ulonglong
|
||||
var ret2 C.ulonglong
|
||||
if fix.format != 2 && fix.format != 6 {
|
||||
fmt.Printf("format is %d\n", fix.format)
|
||||
}
|
||||
|
||||
next := C.ParseFixValue(C.int(fix.format), C.ulonglong(v),
|
||||
&bind, &ret1, &ret2)
|
||||
|
||||
@ -150,7 +164,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
sym.address = uint64(address)
|
||||
sym.name = name
|
||||
sym.dylib = dylib
|
||||
sym.typ = "lazy"
|
||||
sym.typ = "fixups"
|
||||
sym.lib_ordinal = uint32(s.lib_ordinal)
|
||||
|
||||
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
||||
@ -160,6 +174,14 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
syms = append(syms, &new_sym)
|
||||
} else {
|
||||
fmt.Printf("// 0x%x rebase=%d target=0x%x high8=0x%x\n", address, bind, ret1, ret2)
|
||||
sym.typ = "rebase"
|
||||
sym.target = uint32(ret1)
|
||||
sym.high8 = uint32(ret2)
|
||||
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
||||
sym.file_address = uint64(address)
|
||||
sym.next = int(next)
|
||||
new_sym := sym
|
||||
syms = append(syms, &new_sym)
|
||||
}
|
||||
|
||||
if int(next) == 0 {
|
||||
@ -172,6 +194,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
reader.Seek(0, io.SeekStart)
|
||||
}
|
||||
}
|
||||
fmt.Printf("number of imports %d\n", len(syms))
|
||||
return syms
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"unsafe"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
@ -16,6 +19,7 @@ import (
|
||||
)
|
||||
|
||||
// #include "fixups.h"
|
||||
// #include "arm.h"
|
||||
import "C"
|
||||
|
||||
func rewriteLoadcommandsWithoutCodesignature(mc *MachoContext) {
|
||||
@ -194,6 +198,29 @@ func (mc *MachoContext) RemoveUnnecessaryInfo() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RewriteHeader() {
|
||||
var offset uint64
|
||||
var start uint64
|
||||
|
||||
if mc.Is64bit() {
|
||||
start = Header_size_64
|
||||
} else {
|
||||
start = Header_size
|
||||
}
|
||||
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
|
||||
offset = start
|
||||
for _, cmd := range mc.commands {
|
||||
nwrite, _ := mc.file.WriteAt(cmd.Serialize(mc), int64(offset))
|
||||
offset += uint64(nwrite)
|
||||
}
|
||||
|
||||
mc.header.ncmds = uint32(len(mc.commands))
|
||||
mc.header.sizeofcmds = uint32(offset - start)
|
||||
mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddLoadCmd(lcmd LoadCommand) {
|
||||
var offset uint64
|
||||
payload := lcmd.Serialize(mc)
|
||||
@ -280,7 +307,10 @@ func (mc *MachoContext) RemoveBindSymbols() {
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
if mc.dyldinfo == nil {
|
||||
isModernSymbol := mc.dyldinfo == nil
|
||||
isLegacySymbol := !isModernSymbol
|
||||
|
||||
if isModernSymbol {
|
||||
mc.removeBindSymbolsModern()
|
||||
} else {
|
||||
mc.removeBindSymbolsLegacy()
|
||||
@ -290,14 +320,12 @@ func (mc *MachoContext) RemoveBindSymbols() {
|
||||
mc.ReworkForObjc()
|
||||
}
|
||||
|
||||
// due to some limitations when design this tool
|
||||
// we write the c code to stdout lol
|
||||
for _, symbol := range mc.CollectBindSymbols() {
|
||||
if symbol.Type() != "lazy" {
|
||||
if !symbol.SafeForRemoval() {
|
||||
continue
|
||||
}
|
||||
|
||||
if mc.dyldinfo != nil {
|
||||
if isLegacySymbol {
|
||||
// for legacy resolve the opcodes can be rewritten as 0x00
|
||||
mc.file.WriteAt(make([]byte, 8), int64(symbol.file_address))
|
||||
} else {
|
||||
@ -317,6 +345,7 @@ func (mc *MachoContext) RemoveBindSymbols() {
|
||||
value := C.MakeRebaseFixupOpcode(C.int(symbol.next), C.ulonglong(target), C.ulonglong(high8))
|
||||
v := make([]byte, 8)
|
||||
mc.byteorder.PutUint64(v, uint64(value))
|
||||
fmt.Printf("change to rebase at %x\n", symbol.file_address)
|
||||
mc.file.WriteAt(v, int64(symbol.file_address))
|
||||
}
|
||||
}
|
||||
@ -359,254 +388,6 @@ func (mc *MachoContext) removeBindSymbolsLegacy() {
|
||||
mc.file.WriteAt(make([]byte, size), int64(start))
|
||||
}
|
||||
|
||||
func (mc *MachoContext) ReworkForObjc() {
|
||||
text_start := 0
|
||||
data_end := 0
|
||||
lc_main_offset := int64(0)
|
||||
|
||||
ptr := int64(0)
|
||||
if mc.Is64bit() {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
||||
} else {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size), io.SeekStart)
|
||||
}
|
||||
|
||||
for _, cmd := range mc.commands {
|
||||
if cmd.Cmd() == LC_MAIN {
|
||||
lc_main_offset = ptr + 8
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
continue
|
||||
}
|
||||
if cmd.Cmd() != LC_SEGMENT_64 {
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
continue
|
||||
}
|
||||
var segment = cmd.(*Segment64)
|
||||
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
|
||||
section_ptr := ptr + 0x40 + 8
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__text")) == 0 {
|
||||
text_start = int(section.Offset())
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__init_offsets")) == 0 {
|
||||
// mc.file.WriteAt([]byte("__init_offsetx"), section_ptr)
|
||||
// edit flags to not S_MOD_INIT_FUNC
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0}, section_ptr+0x40)
|
||||
}
|
||||
section_ptr += 16*2 + 8*2 + 4*8
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA_CONST")) == 0 {
|
||||
section_ptr := ptr + 0x40 + 8
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classlist")) == 0 {
|
||||
mc.file.WriteAt([]byte("__objc_classbruh"), section_ptr)
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_nlclslist")) == 0 {
|
||||
mc.file.WriteAt([]byte("__objc_nlclsbruh"), section_ptr)
|
||||
}
|
||||
section_ptr += 16*2 + 8*2 + 4*8
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA")) == 0 {
|
||||
// end of __DATA segment, should have enough space for a pointer
|
||||
|
||||
// __bss section is dynamically allocated at the end to or something, hmmge
|
||||
// assume that it order correctly, which it should if compiled and not modified
|
||||
// each section has their addr field which we can use that with segment virtual address
|
||||
// to calculate the offset of the last section from segment starts
|
||||
// then use the size of section to calculate the end of segment in file
|
||||
sections := segment.Sections()
|
||||
last := sections[len(sections)-1]
|
||||
data_end = int(last.Addr() - segment.Vmaddr() + segment.Fileoff() + last.Size())
|
||||
}
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
}
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
|
||||
// dummy value past the end of __DATA segment (logical size),
|
||||
// its physical size is still a page
|
||||
// mc.file.WriteAt([]byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, int64(0x81d8))
|
||||
|
||||
// we use 2 registers, x8 x9
|
||||
// stack values:
|
||||
// [ return address, header, argc, argv, env, apple ]
|
||||
// we need to store the return address, and parameters passed to main
|
||||
// we also store our header address to not calculate many times
|
||||
|
||||
// must expand stack to store arguments passed
|
||||
// must use newly allocated stack region
|
||||
// must save return address
|
||||
// must recover stack to same value before calling main
|
||||
// must recover link register before calling main
|
||||
|
||||
// we use shorthand store/load multiple
|
||||
// arm also has different indexing instruction, so be careful
|
||||
// https://developer.arm.com/documentation/102374/0101/Loads-and-stores---addressing
|
||||
/*
|
||||
adr x8, 0
|
||||
# x9 = (offset end of __DATA) - (offset shellcode)
|
||||
movz x9, #0x9999
|
||||
add x8, x8, x9
|
||||
|
||||
stp x30, x8, [sp], #-0x10
|
||||
stp x3, x2, [sp], #-0x10
|
||||
stp x1, x0, [sp], #-0x10
|
||||
|
||||
# custom intializer
|
||||
ldr x9, [x8]
|
||||
blr x9
|
||||
|
||||
ldp x1, x0, [sp, #0x10]!
|
||||
ldp x3, x2, [sp, #0x10]!
|
||||
ldp x30, x8, [sp, #0x10]!
|
||||
|
||||
# original main
|
||||
# link register is set so jump only
|
||||
ldr x9, [x8, #8]
|
||||
br x9
|
||||
*/
|
||||
shellcode := []uint32{}
|
||||
ins_size_byte := 4
|
||||
main_offset := int(mc.entryoff)
|
||||
var shellcode_offset int
|
||||
|
||||
isArm := (mc.header.cputype & 0xff) == 12
|
||||
if isArm {
|
||||
shellcode = []uint32{
|
||||
0x10000008,
|
||||
0, // x9 = (offset end of __DATA) - (offset shellcode)
|
||||
0x8B090108,
|
||||
0xA8BF23FE,
|
||||
0xA8BF0BE3,
|
||||
0xA8BF03E1,
|
||||
0xF9400109,
|
||||
0xD63F0120,
|
||||
0xA9C103E1,
|
||||
0xA9C10BE3,
|
||||
0xA9C123FE,
|
||||
0xF9400509,
|
||||
0xD61F0120,
|
||||
}
|
||||
|
||||
shellcode_offset = text_start - (ins_size_byte * len(shellcode))
|
||||
|
||||
encode_movz := func(v int) uint32 {
|
||||
return uint32(uint32(v)<<5 | uint32(0x694)<<21 | uint32(0x09))
|
||||
}
|
||||
|
||||
// movz_shellcode_offset := encode_movz(shellcode_offset)
|
||||
// movz_main_offset := encode_movz(main_offset)
|
||||
// movz_data_end_offset := encode_movz(data_end)
|
||||
movz_calculate_offset := encode_movz(data_end - shellcode_offset)
|
||||
|
||||
shellcode[1] = movz_calculate_offset
|
||||
// shellcode[10] = movz_data_end_offset
|
||||
// shellcode[19] = movz_main_offset
|
||||
|
||||
fmt.Printf("// shellcode_offset=%x\n", shellcode_offset)
|
||||
fmt.Printf("// main_offset=%x\n", main_offset)
|
||||
fmt.Printf("// data_end=%x\n", data_end)
|
||||
fmt.Printf("// movz_calculate_offset=%x\n", movz_calculate_offset)
|
||||
// fmt.Printf("// movz_shellcode_offset=%x\n", movz_shellcode_offset)
|
||||
// fmt.Printf("// movz_main_offset=%x\n", movz_main_offset)
|
||||
// fmt.Printf("// movz_data_end_offset=%x\n", movz_data_end_offset)
|
||||
fmt.Printf("// lc_main_offset=%x\n", lc_main_offset)
|
||||
} else {
|
||||
shellcode_start := []uint8{
|
||||
0x4c, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x49, 0xC7, 0xC1,
|
||||
}
|
||||
|
||||
shellcode_end := []uint8{
|
||||
0x4d, 0x01, 0xc8,
|
||||
0x57,
|
||||
0x56,
|
||||
0x52,
|
||||
0x51,
|
||||
0x41, 0x50,
|
||||
0x4d, 0x8b, 0x08,
|
||||
0x41, 0xff, 0xd1,
|
||||
0x41,
|
||||
0x58,
|
||||
0x59,
|
||||
0x5a,
|
||||
0x5e,
|
||||
0x5f, 0x4d, 0x8b, 0x48, 0x08,
|
||||
0x41, 0xff, 0xe1,
|
||||
// pad to %4
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
offset := []uint8{0x00, 0x00, 0x00, 0x00} // offset
|
||||
shellcode_size := len(shellcode_start) + len(offset) + len(shellcode_end)
|
||||
|
||||
// could use buffer encoding, but for correctness,
|
||||
// we do this by hand
|
||||
encode_movz := func(v int) {
|
||||
for i := 0; i < 4; i++ {
|
||||
offset[i] = uint8(v >> (i * 8))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ┌─────────────────┐
|
||||
// │ │
|
||||
// shellcode starts ────┼─────────────────┼───── │ │instruction
|
||||
// │ │ │ │fetch RIP size
|
||||
// RIP returns ────┼─────────────────┼───── ▲ │ │
|
||||
// │ │ │ │
|
||||
// │ │ │ │ shellcode length
|
||||
// shellcode ends │ │ │ offset │
|
||||
// __text ────┼─────────────────┼───── │ range │
|
||||
// │ │ │ │ __DATA ends - __text
|
||||
// │ │ │ │
|
||||
// __DATA ends ────┼─────────────────┼───── ▼ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// └─────────────────┘
|
||||
encode_movz((data_end - text_start) + (shellcode_size - len(shellcode_start)))
|
||||
|
||||
shellcode_offset = text_start - shellcode_size
|
||||
shellcode_bytes := append(shellcode_start, offset...)
|
||||
shellcode_bytes = append(shellcode_bytes, shellcode_end...)
|
||||
|
||||
for i := 0; i < len(shellcode_bytes); i += 4 {
|
||||
val := 0
|
||||
// little endian
|
||||
val |= int(shellcode_bytes[i+0]) << 0
|
||||
val |= int(shellcode_bytes[i+1]) << 8
|
||||
val |= int(shellcode_bytes[i+2]) << 16
|
||||
val |= int(shellcode_bytes[i+3]) << 24
|
||||
shellcode = append(shellcode, uint32(val))
|
||||
}
|
||||
|
||||
fmt.Printf("// shellcode_offset=%x\n", shellcode_offset)
|
||||
fmt.Printf("// main_offset=%x\n", main_offset)
|
||||
fmt.Printf("// data_end=%x\n", data_end)
|
||||
fmt.Printf("// lc_main_offset=%x\n", lc_main_offset)
|
||||
}
|
||||
|
||||
offset := int64(shellcode_offset)
|
||||
{
|
||||
// fix main to point to our newly created shellcode
|
||||
bs := make([]byte, 8)
|
||||
mc.byteorder.PutUint64(bs, uint64(offset))
|
||||
mc.file.WriteAt(bs, int64(lc_main_offset))
|
||||
}
|
||||
|
||||
bs := make([]byte, 4)
|
||||
for _, ins := range shellcode {
|
||||
mc.byteorder.PutUint32(bs, ins)
|
||||
mc.file.WriteAt(bs, offset)
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RewriteImportsTable(keepSymbols []string) {
|
||||
allSymbols := mc.CollectBindSymbols()
|
||||
fixups, fixupsOffset := mc.Fixups()
|
||||
@ -781,6 +562,328 @@ func (mc *MachoContext) RemoveExportTrie() {
|
||||
}
|
||||
}
|
||||
|
||||
// this function breaks the file by adding another segment at
|
||||
// the end of the file
|
||||
func (mc *MachoContext) RemoveStrings() {
|
||||
// add a new writable segment with a section
|
||||
// loop over the instructions for adrp,add instructions
|
||||
// if the access points to old cstring section, update
|
||||
// with new values from the new segment and section.
|
||||
|
||||
// data references, e.g., pointer to string, are compiled
|
||||
// into Rebase symbol, so actively check for rebase and
|
||||
// rewrite the rebase value to new segment, section offset.
|
||||
|
||||
// save the strings into a file for recovery. should keep linear
|
||||
// format as before, or customized order, if want complex.
|
||||
|
||||
// __LINKEDIT must be at the end of the section for the binary
|
||||
// to be able to resign, so we have to move __LINKEDIT down
|
||||
// by how much? by the page aligned size of the added segment
|
||||
|
||||
// but __LINKEDIT also contains data for link time data
|
||||
// (fixed ups and bytecode chain) so have to modify
|
||||
// their reference down too,
|
||||
// symtab and dysymtab can be ignored, by removing them lmao
|
||||
|
||||
var cstring *Section64
|
||||
for _, command := range mc.commands {
|
||||
switch command.(type) {
|
||||
case *Segment64:
|
||||
var segment = command.(*Segment64)
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__cstring")) == 0 {
|
||||
cstring = section.(*Section64)
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if cstring == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get last segment, as the start point we extend from
|
||||
// this assumes that the segment are ordered correctly,
|
||||
// first segment offset is lower then second segment offset,
|
||||
// and so on, yielding last segment is the last part of the
|
||||
// binary. Our new segment add another part to the binary
|
||||
// at the end.
|
||||
|
||||
// last segment is always the linkedits
|
||||
last_segment := mc.Segments()[len(mc.Segments())-1]
|
||||
fmt.Printf("last segment %v %s\n", last_segment, string(last_segment.SegName()))
|
||||
|
||||
// all data must be inside the segment (or in header)
|
||||
|
||||
// occupy the segment of linkedit and move linkedit down
|
||||
|
||||
segstart := last_segment.Vmaddr()
|
||||
|
||||
// segment must be page aligned, and the size is based on
|
||||
// the section size
|
||||
secstart := segstart
|
||||
secsize := cstring.Size()
|
||||
|
||||
filestart := last_segment.Fileoff()
|
||||
// align to page address, not sure if neccessary, because the
|
||||
// loader can pick up from anywhere and load in memory (mmap)
|
||||
if filestart%0x4000 != 0 {
|
||||
filestart += 0x4000 - (filestart % 0x4000)
|
||||
}
|
||||
|
||||
fmt.Printf("section size %x\n", secsize)
|
||||
secsize_aligned := uint64(0)
|
||||
if secsize%0x4000 == 0 {
|
||||
// very rare, but possible, it occupies whole pages
|
||||
secsize_aligned = secsize
|
||||
} else {
|
||||
secsize_aligned = secsize + (0x4000 - (secsize % 0x4000))
|
||||
}
|
||||
filesize := secsize_aligned
|
||||
|
||||
segname := make([]byte, 16)
|
||||
copy(segname, []byte("__BSHIELD"))
|
||||
secname := make([]byte, 16)
|
||||
copy(secname, []byte("__secrets"))
|
||||
|
||||
fmt.Printf("segstart %x\n", segstart)
|
||||
fmt.Printf("file_start %x\n", filestart)
|
||||
|
||||
// size of the section and segment is defined by the total
|
||||
// space for strings required
|
||||
|
||||
new_cstring_section := Section64{
|
||||
sectname: secname,
|
||||
segname: segname,
|
||||
addr: secstart,
|
||||
size: secsize,
|
||||
offset: uint32(filestart),
|
||||
align: 3, // idk, see AddSection
|
||||
reloff: 0,
|
||||
nreloc: 0,
|
||||
flags: 0,
|
||||
reserved1: 0,
|
||||
reserved2: 0,
|
||||
reserved3: 0,
|
||||
}
|
||||
string_segment := Segment64{
|
||||
c: LoadCmd{cmd: LC_SEGMENT_64, cmdsize: 0},
|
||||
segname: segname,
|
||||
vmaddr: segstart,
|
||||
vmsize: secsize_aligned,
|
||||
fileoff: filestart,
|
||||
filesize: filesize,
|
||||
maxprot: 3, // read/write
|
||||
initprot: 3, // read/write
|
||||
nsects: 1,
|
||||
flags: 0,
|
||||
sections: []*Section64{&new_cstring_section},
|
||||
}
|
||||
|
||||
// rewrite the header to be correct
|
||||
// mc.AddLoadCmd(&string_segment)
|
||||
|
||||
edit_segname := make([]byte, 16)
|
||||
copy(edit_segname, []byte("__LINKEDIT"))
|
||||
edit_segment := Segment64{
|
||||
c: LoadCmd{cmd: LC_SEGMENT_64, cmdsize: 0},
|
||||
segname: edit_segname,
|
||||
vmaddr: segstart + secsize_aligned, // move down
|
||||
vmsize: last_segment.Vmsize(),
|
||||
fileoff: filestart + filesize,
|
||||
filesize: last_segment.Filesize(),
|
||||
maxprot: 1, // read/write
|
||||
initprot: 1, // read/write
|
||||
nsects: 0,
|
||||
flags: 0,
|
||||
sections: []*Section64{},
|
||||
}
|
||||
|
||||
// modify the segment list
|
||||
mc.segments[len(mc.segments)-1] = &string_segment
|
||||
mc.segments = append(mc.segments, &edit_segment)
|
||||
|
||||
// modify the command list
|
||||
for i, cmd := range mc.commands {
|
||||
if cmd.(*Segment64) == last_segment.(*Segment64) {
|
||||
mc.commands = append(mc.commands[:i+1], mc.commands[i:]...)
|
||||
mc.commands[i] = &string_segment
|
||||
mc.commands[i+1] = &edit_segment
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// modify offset in other commands to use new link edit offset
|
||||
|
||||
edit_offset_migrate := func(file_offset uint64) uint64 {
|
||||
// they should keep the old offset,
|
||||
// but the base related to linkedit is modified
|
||||
relative_offset := file_offset - last_segment.Fileoff()
|
||||
return relative_offset + edit_segment.Fileoff()
|
||||
}
|
||||
|
||||
for _, cmd := range mc.commands {
|
||||
if lcmd, ok := cmd.(*LinkEdit); ok {
|
||||
lcmd.dataoff = uint32(edit_offset_migrate(uint64(lcmd.dataoff)))
|
||||
}
|
||||
if lcmd, ok := cmd.(*Symtab); ok {
|
||||
lcmd.stroff = uint32(edit_offset_migrate(uint64(lcmd.stroff)))
|
||||
lcmd.symoff = uint32(edit_offset_migrate(uint64(lcmd.symoff)))
|
||||
}
|
||||
if lcmd, ok := cmd.(*DySymtab); ok {
|
||||
lcmd.indirectsymoff = uint32(edit_offset_migrate(uint64(lcmd.indirectsymoff)))
|
||||
}
|
||||
}
|
||||
|
||||
mc.RewriteHeader()
|
||||
|
||||
tmp_file := mc.file.Name()
|
||||
|
||||
// has to reopen file as append
|
||||
mc.file.Close()
|
||||
mc.file, _ = os.OpenFile(tmp_file, os.O_RDWR|os.O_APPEND, 0644)
|
||||
|
||||
// make extra space
|
||||
expected_end := edit_segment.Fileoff() + edit_segment.Filesize()
|
||||
end, _ := mc.file.Seek(0, io.SeekEnd)
|
||||
if end < int64(expected_end) {
|
||||
mc.file.WriteAt(make([]byte, expected_end-uint64(end)), end)
|
||||
}
|
||||
|
||||
// close and reopen as read/write, the buffer at the end is now empty
|
||||
mc.file.Close()
|
||||
mc.file, _ = os.OpenFile(tmp_file, os.O_RDWR, 0644)
|
||||
|
||||
// peek at old link edit and move down
|
||||
old_linkedit := make([]byte, last_segment.Filesize())
|
||||
mc.file.ReadAt(old_linkedit, int64(last_segment.Fileoff()))
|
||||
mc.file.WriteAt(old_linkedit, int64(edit_segment.Fileoff()))
|
||||
|
||||
// prepare dummy bytes into new string segment, 0 for now
|
||||
// this is a way to divert their effort, writing fake strings
|
||||
// will be written again at runtime
|
||||
dummy := make([]byte, edit_segment.Fileoff()-string_segment.Fileoff())
|
||||
mc.file.ReadAt(dummy, int64(cstring.Offset()))
|
||||
// copy(dummy, []byte("We R BShield\n"))
|
||||
for i := 0; i < len(dummy); i++ {
|
||||
dummy[i] = dummy[i] ^ 0x4f
|
||||
}
|
||||
mc.file.WriteAt(dummy, int64(string_segment.Fileoff()))
|
||||
|
||||
// TODO: erase old strings
|
||||
cstring_start := uint64(cstring.Offset())
|
||||
random := make([]byte, cstring.Size())
|
||||
rand.Read(random)
|
||||
mc.file.WriteAt(random, int64(cstring_start))
|
||||
|
||||
// re-read internal buffer
|
||||
last, _ := mc.file.Seek(0, io.SeekEnd)
|
||||
mc.buf = make([]byte, last)
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
if _, err := io.ReadFull(mc.file, mc.buf); err != nil {
|
||||
// panic?
|
||||
}
|
||||
|
||||
// loop over __TEXT,__text and find all occurances of (adrp, add)
|
||||
// edit the offset to points to new region
|
||||
// because adrp sets the register to the address page at its address
|
||||
// (for page align 0x4000), e.g.,
|
||||
// `adrp x0` instruction at 0x100003f70, yields x0 = 0x100003000
|
||||
// technically, adrp can offset as far as 33-bit, roughly 4GB memory
|
||||
// so we should be very free, because very few program goes this far
|
||||
// but if this happens, god bless you
|
||||
|
||||
// encoding ADRP is actually hard hmmge?
|
||||
|
||||
// this part uses file offsets for calculations
|
||||
|
||||
in_cstring := func(offset uint64) bool {
|
||||
cstring_start := uint64(cstring.Offset())
|
||||
cstring_end := cstring_start + cstring.Size()
|
||||
return (offset >= cstring_start) && (offset < cstring_end)
|
||||
}
|
||||
|
||||
text := mc.segments[1]
|
||||
text_start := text.Fileoff()
|
||||
text_end := text_start + text.Filesize()
|
||||
|
||||
inst := make([]byte, 4)
|
||||
for addr := text_start; addr < text_end; addr = addr + 4 {
|
||||
mc.file.ReadAt(inst, int64(addr))
|
||||
inst_adrp := binary.LittleEndian.Uint32(inst)
|
||||
mc.file.ReadAt(inst, int64(addr+4))
|
||||
inst_add := binary.LittleEndian.Uint32(inst)
|
||||
|
||||
if !(C.is_adrp(C.uint(inst_adrp)) != 0 && C.is_add(C.uint(inst_add)) != 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
base := (addr >> 12) << 12
|
||||
|
||||
// calculate the old string reference
|
||||
ref_base := C.adrp_imm_get(C.uint(inst_adrp))
|
||||
ref_offset := C.add_imm_get(C.uint(inst_add))
|
||||
ref := base + uint64(ref_base+ref_offset)
|
||||
|
||||
if !in_cstring(ref) {
|
||||
continue
|
||||
}
|
||||
|
||||
oldstr := uint64(ref)
|
||||
oldstr_relative := oldstr - uint64(cstring.Offset())
|
||||
|
||||
// find the new string address
|
||||
// using oldstr relative address to cstring section
|
||||
newstr := uint64(new_cstring_section.Offset()) + oldstr_relative
|
||||
newstr_base := (newstr >> 12) << 12 // to calculate new offset in adrp
|
||||
newstr_offset := newstr - newstr_base // to calculate new offset in add
|
||||
|
||||
C.adrp_imm_set((*C.uint32_t)(unsafe.Pointer(&inst_adrp)), C.uint(newstr_base-base))
|
||||
C.add_imm_set((*C.uint32_t)(unsafe.Pointer(&inst_add)), C.uint(newstr_offset))
|
||||
|
||||
binary.LittleEndian.PutUint32(inst, inst_adrp)
|
||||
mc.file.WriteAt(inst, int64(addr))
|
||||
binary.LittleEndian.PutUint32(inst, inst_add)
|
||||
mc.file.WriteAt(inst, int64(addr+4))
|
||||
}
|
||||
|
||||
// modify the rebase table (for both opcode and fixups chain versions)
|
||||
// this is for pointer references
|
||||
|
||||
isModernSymbol := mc.dyldinfo == nil
|
||||
isLegacySymbol := !isModernSymbol
|
||||
for _, symbol := range mc.CollectBindSymbols() {
|
||||
if isLegacySymbol {
|
||||
} else {
|
||||
// (high8 << 56 | target) - mach_header
|
||||
|
||||
ref := uint64(symbol.high8<<56 | symbol.target)
|
||||
if !in_cstring(ref) {
|
||||
continue
|
||||
}
|
||||
|
||||
oldstr := ref
|
||||
oldstr_relative := oldstr - uint64(cstring.Offset())
|
||||
newstr := uint64(new_cstring_section.Offset()) + oldstr_relative
|
||||
|
||||
target := newstr & 0x00FFFFFFFFFFFFFF
|
||||
high8 := newstr >> 56
|
||||
value := C.MakeRebaseFixupOpcode(C.int(symbol.next), C.ulonglong(target), C.ulonglong(high8))
|
||||
v := make([]byte, 8)
|
||||
mc.byteorder.PutUint64(v, uint64(value))
|
||||
fmt.Printf("string rebase change at %x\n", symbol.file_address)
|
||||
mc.file.WriteAt(v, int64(symbol.file_address))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddSection(segname string, name string, size int) Section {
|
||||
// mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||
var ret Section
|
||||
|
@ -54,6 +54,7 @@ int GetSegmentFixAt(uint8_t* buffer, uint32_t i, struct SegmentFix* fix) {
|
||||
fix->format = chain_header->pointer_format;
|
||||
fix->page_count = chain_header->page_count;
|
||||
fix->pages = chain_header->page_start;
|
||||
fix->page_size = chain_header->page_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ struct SegmentFix {
|
||||
uint64_t segment;
|
||||
uint32_t format;
|
||||
uint32_t page_count;
|
||||
uint16_t page_size;
|
||||
uint16_t* pages;
|
||||
};
|
||||
|
||||
|
@ -190,7 +190,7 @@ func (lcmd *Segment64) Vmaddr() uint64 {
|
||||
return lcmd.vmaddr
|
||||
}
|
||||
func (lcmd *Segment64) Vmsize() uint64 {
|
||||
return lcmd.vmaddr
|
||||
return lcmd.vmsize
|
||||
}
|
||||
func (lcmd *Segment64) Fileoff() uint64 {
|
||||
return lcmd.fileoff
|
||||
|
524
macho-go/pkg/ios/macho/objc.go
Normal file
524
macho-go/pkg/ios/macho/objc.go
Normal file
@ -0,0 +1,524 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
)
|
||||
|
||||
// #include "fixups.h"
|
||||
import "C"
|
||||
|
||||
func (mc *MachoContext) CollectObjectiveCClasses() {
|
||||
var objc_const *bytes.Reader
|
||||
var objc_const_start uint64
|
||||
var objc_const_end uint64
|
||||
// var objc_methname []byte
|
||||
|
||||
for _, cmd := range mc.commands {
|
||||
if cmd.Cmd() == LC_MAIN {
|
||||
continue
|
||||
}
|
||||
if cmd.Cmd() != LC_SEGMENT_64 {
|
||||
continue
|
||||
}
|
||||
var segment = cmd.(*Segment64)
|
||||
|
||||
// we assume the binary comes in perfect ordering, that is as laid out below
|
||||
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
|
||||
for _, section := range segment.Sections() {
|
||||
buffer := make([]byte, section.Size())
|
||||
mc.file.ReadAt(buffer, int64(section.Offset()))
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_stubs")) == 0 {
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methlist")) == 0 {
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methname")) == 0 {
|
||||
// objc_methname := buffer
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classname")) == 0 {
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methtype")) == 0 {
|
||||
}
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA_CONST")) == 0 {
|
||||
for _, section := range segment.Sections() {
|
||||
buffer := make([]byte, section.Size())
|
||||
mc.file.ReadAt(buffer, int64(section.Offset()))
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classlist")) == 0 {
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_nlclslist")) == 0 {
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_imageinfo")) == 0 {
|
||||
}
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA")) == 0 {
|
||||
for _, section := range segment.Sections() {
|
||||
buffer := make([]byte, section.Size())
|
||||
mc.file.ReadAt(buffer, int64(section.Offset()))
|
||||
reader := bytes.NewReader(buffer)
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_const")) == 0 {
|
||||
objc_const = reader
|
||||
objc_const_start = uint64(section.Offset())
|
||||
objc_const_end = objc_const_start + section.Size()
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_selrefs")) == 0 {
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classrefs")) == 0 {
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_superrefs")) == 0 {
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_data")) == 0 {
|
||||
// this section contains a series of class_t
|
||||
// struct _class_t {
|
||||
// struct _class_t *isa;
|
||||
// struct _class_t * const superclass;
|
||||
// void *cache;
|
||||
// IMP *vtable;
|
||||
// struct class_ro_t *ro;
|
||||
// };
|
||||
|
||||
for i := uint64(0); i < (section.Size() / uint64(mc.pointersize*5)); i++ {
|
||||
var isa uint64
|
||||
var superclass uint64
|
||||
var cache uint64
|
||||
var vtable uint64
|
||||
var ro uint64
|
||||
binary.Read(reader, mc.byteorder, &isa)
|
||||
binary.Read(reader, mc.byteorder, &superclass)
|
||||
binary.Read(reader, mc.byteorder, &cache)
|
||||
binary.Read(reader, mc.byteorder, &vtable)
|
||||
binary.Read(reader, mc.byteorder, &ro)
|
||||
|
||||
fmt.Printf("at=0x%x\n", section.Offset()+uint32(i)*mc.pointersize*5)
|
||||
fmt.Printf("isa=0x%x superclass=0x%x\n", isa, superclass)
|
||||
fmt.Printf("cache=0x%x vtable=0x%x\n", cache, vtable)
|
||||
fmt.Printf("ro=0x%x\n", ro)
|
||||
|
||||
var bind int
|
||||
var ret1 uint64
|
||||
var ret2 uint64
|
||||
C.ParseFixValue(C.int(2), C.uint64_t(ro),
|
||||
(*C.int)(unsafe.Pointer(&bind)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&ret1)),
|
||||
(*C.uint64_t)(unsafe.Pointer(&ret2)),
|
||||
)
|
||||
|
||||
// is rebase, because ro points to objc_const
|
||||
// and address is in range
|
||||
if bind != 1 && ret1 >= objc_const_start && ret1 < objc_const_end {
|
||||
offset := ret1 - objc_const_start
|
||||
objc_const.Seek(int64(offset), 0)
|
||||
|
||||
// struct _class_ro_t {
|
||||
// uint32_t const flags;
|
||||
// uint32_t const instanceStart;
|
||||
// uint32_t const instanceSize;
|
||||
// uint32_t const reserved; // only when building for 64bit targets
|
||||
// const uint8_t * const ivarLayout;
|
||||
// const char *const name;
|
||||
// const struct _method_list_t * const baseMethods;
|
||||
// const struct _protocol_list_t *const baseProtocols;
|
||||
// const struct _ivar_list_t *const ivars;
|
||||
// const uint8_t * const weakIvarLayout;
|
||||
// const struct _prop_list_t * const properties;
|
||||
// };
|
||||
|
||||
var tmp uint32
|
||||
var ivarLayout uint64 // ptr
|
||||
var name uint64 // ptr
|
||||
var baseMethods uint64 // ptr
|
||||
var baseProtocols uint64 // ptr
|
||||
var ivars uint64 // ptr
|
||||
var weakIvarLayout uint64 // ptr
|
||||
var properties uint64 // ptr
|
||||
binary.Read(objc_const, mc.byteorder, &tmp)
|
||||
binary.Read(objc_const, mc.byteorder, &tmp)
|
||||
binary.Read(objc_const, mc.byteorder, &tmp)
|
||||
binary.Read(objc_const, mc.byteorder, &tmp)
|
||||
binary.Read(objc_const, mc.byteorder, &ivarLayout)
|
||||
binary.Read(objc_const, mc.byteorder, &name)
|
||||
binary.Read(objc_const, mc.byteorder, &baseMethods)
|
||||
binary.Read(objc_const, mc.byteorder, &baseProtocols)
|
||||
binary.Read(objc_const, mc.byteorder, &ivars)
|
||||
binary.Read(objc_const, mc.byteorder, &weakIvarLayout)
|
||||
binary.Read(objc_const, mc.byteorder, &properties)
|
||||
|
||||
fmt.Printf("method list: %x\n", baseMethods)
|
||||
}
|
||||
fmt.Printf("========\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type SpecialSelector struct {
|
||||
idx uint
|
||||
name string
|
||||
}
|
||||
|
||||
func (sel *SpecialSelector) Idx() uint {
|
||||
return sel.idx
|
||||
}
|
||||
|
||||
func (sel *SpecialSelector) Name() string {
|
||||
return sel.name
|
||||
}
|
||||
|
||||
// collect the index and the name in selector list of special method names
|
||||
// these names are resolved by the dyld objc cache
|
||||
// through __dyld_get_objc_selector
|
||||
//
|
||||
// we currently have the following symbols guaranteed to be in this list:
|
||||
// - load
|
||||
// - retain
|
||||
//
|
||||
// besides special selectors, selectors of outside classes must also be
|
||||
// registered through the cache
|
||||
// selectors of outside classes are defined as not being referenced by
|
||||
// internal classes in __objc_data
|
||||
func (mc *MachoContext) CollectSpecialSelectors() []*SpecialSelector {
|
||||
var special_selectors []*SpecialSelector
|
||||
var methods []byte
|
||||
var methname_offset uint32
|
||||
|
||||
for _, cmd := range mc.commands {
|
||||
if cmd.Cmd() == LC_MAIN {
|
||||
continue
|
||||
}
|
||||
if cmd.Cmd() != LC_SEGMENT_64 {
|
||||
continue
|
||||
}
|
||||
var segment = cmd.(*Segment64)
|
||||
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methname")) == 0 {
|
||||
methname_offset = section.Offset()
|
||||
methods = make([]byte, section.Size())
|
||||
mc.file.ReadAt(methods, int64(section.Offset()))
|
||||
}
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA")) == 0 {
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_selrefs")) == 0 {
|
||||
selectors_buffer := make([]byte, section.Size())
|
||||
mc.file.ReadAt(selectors_buffer, int64(section.Offset()))
|
||||
|
||||
buffer := bytes.NewReader(selectors_buffer)
|
||||
|
||||
for i := uint(0); i < uint(section.Size())/8; i++ {
|
||||
// this field is actually a Rebase
|
||||
// we assume that no rebase is needed
|
||||
// so everything sticks to its file offset
|
||||
var offset uint32
|
||||
binary.Read(buffer, mc.byteorder, &offset) // first 4 bytes is offset
|
||||
|
||||
var name_builder strings.Builder
|
||||
for j := uint32(0); ; j++ {
|
||||
c := methods[offset-methname_offset+j]
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
name_builder.WriteByte(c)
|
||||
}
|
||||
name := name_builder.String()
|
||||
if name == "load" {
|
||||
special_selectors = append(special_selectors, &SpecialSelector{
|
||||
idx: i,
|
||||
name: name,
|
||||
})
|
||||
}
|
||||
|
||||
binary.Read(buffer, mc.byteorder, &offset) // ignore rebase arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return special_selectors
|
||||
}
|
||||
|
||||
func (mc *MachoContext) ReworkForObjc() {
|
||||
text_start := 0
|
||||
data_end := 0
|
||||
lc_main_offset := int64(0)
|
||||
|
||||
ptr := int64(0)
|
||||
if mc.Is64bit() {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size_64), io.SeekStart)
|
||||
} else {
|
||||
ptr, _ = mc.file.Seek(int64(Header_size), io.SeekStart)
|
||||
}
|
||||
|
||||
for _, cmd := range mc.commands {
|
||||
if cmd.Cmd() == LC_MAIN {
|
||||
lc_main_offset = ptr + 8
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
continue
|
||||
}
|
||||
if cmd.Cmd() != LC_SEGMENT_64 {
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
continue
|
||||
}
|
||||
var segment = cmd.(*Segment64)
|
||||
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
|
||||
section_ptr := ptr + 0x40 + 8
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__text")) == 0 {
|
||||
text_start = int(section.Offset())
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__init_offsets")) == 0 {
|
||||
// mc.file.WriteAt([]byte("__init_offsetx"), section_ptr)
|
||||
// edit flags to not S_MOD_INIT_FUNC
|
||||
mc.file.WriteAt([]byte{0, 0, 0, 0}, section_ptr+0x40)
|
||||
}
|
||||
|
||||
// erases all objc method names
|
||||
// this should still works because the cache inserts the pointer value not string
|
||||
// but some symbols relies on pre-defined implementations, such as **load** method
|
||||
// load method is the same across all classes and so objc define an implementation
|
||||
// selector should points to this load selector to make objc thinks that it's "load"
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methname")) == 0 {
|
||||
// mc.file.WriteAt([]byte("__objc_methbruh"), section_ptr)
|
||||
// mc.file.WriteAt(make([]byte, section.Size()), int64(section.Offset()))
|
||||
}
|
||||
section_ptr += 16*2 + 8*2 + 4*8
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA_CONST")) == 0 {
|
||||
section_ptr := ptr + 0x40 + 8
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classlist")) == 0 {
|
||||
mc.file.WriteAt([]byte("__objc_classbruh"), section_ptr)
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_nlclslist")) == 0 {
|
||||
mc.file.WriteAt([]byte("__objc_nlclsbruh"), section_ptr)
|
||||
}
|
||||
section_ptr += 16*2 + 8*2 + 4*8
|
||||
}
|
||||
}
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA")) == 0 {
|
||||
// end of __DATA segment, should have enough space for a pointer
|
||||
|
||||
// __bss section is dynamically allocated at the end to or something, hmmge
|
||||
// assume that it order correctly, which it should if compiled and not modified
|
||||
// each section has their addr field which we can use that with segment virtual address
|
||||
// to calculate the offset of the last section from segment starts
|
||||
// then use the size of section to calculate the end of segment in file
|
||||
sections := segment.Sections()
|
||||
last := sections[len(sections)-1]
|
||||
data_end = int(last.Addr() - segment.Vmaddr() + segment.Fileoff() + last.Size())
|
||||
|
||||
// do not register selector and see what happens
|
||||
section_ptr := ptr + 0x40 + 8
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_selrefs")) == 0 {
|
||||
// mc.file.WriteAt([]byte("__objc_selbruh"), section_ptr)
|
||||
}
|
||||
section_ptr += 16*2 + 8*2 + 4*8
|
||||
}
|
||||
}
|
||||
ptr += int64(cmd.Cmdsize())
|
||||
}
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
|
||||
// dummy value past the end of __DATA segment (logical size),
|
||||
// its physical size is still a page
|
||||
// mc.file.WriteAt([]byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, int64(0x81d8))
|
||||
|
||||
// we use 2 registers, x8 x9
|
||||
// stack values:
|
||||
// [ return address, header, argc, argv, env, apple ]
|
||||
// we need to store the return address, and parameters passed to main
|
||||
// we also store our header address to not calculate many times
|
||||
|
||||
// must expand stack to store arguments passed
|
||||
// must use newly allocated stack region
|
||||
// must save return address
|
||||
// must recover stack to same value before calling main
|
||||
// must recover link register before calling main
|
||||
|
||||
// ┌─────────────────┐
|
||||
// │ │
|
||||
// shellcode starts ────┼─────────────────┼───── │ │instruction
|
||||
// │ │ │ │fetch RIP size
|
||||
// RIP returns ────┼─────────────────┼───── ▲ │ │
|
||||
// │ │ │ │
|
||||
// │ │ │ │ shellcode length
|
||||
// shellcode ends │ │ │ offset │
|
||||
// __text ────┼─────────────────┼───── │ range │
|
||||
// │ │ │ │ __DATA ends - __text
|
||||
// │ │ │ │
|
||||
// __DATA ends ────┼─────────────────┼───── ▼ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// │ │
|
||||
// └─────────────────┘
|
||||
|
||||
shellcode := []uint32{}
|
||||
ins_size_byte := 4
|
||||
main_offset := int(mc.entryoff)
|
||||
var shellcode_offset int
|
||||
|
||||
isArm := (mc.header.cputype & 0xff) == 12
|
||||
if isArm {
|
||||
|
||||
// we use shorthand store/load multiple
|
||||
// arm also has different indexing instruction, so be careful
|
||||
// https://developer.arm.com/documentation/102374/0101/Loads-and-stores---addressing
|
||||
/*
|
||||
adr x8, 0
|
||||
# x9 = (offset end of __DATA) - (offset shellcode)
|
||||
movz x9, #0x9999
|
||||
add x8, x8, x9
|
||||
|
||||
stp x30, x8, [sp], #-0x10
|
||||
stp x3, x2, [sp], #-0x10
|
||||
stp x1, x0, [sp], #-0x10
|
||||
|
||||
# custom intializer
|
||||
ldr x9, [x8]
|
||||
blr x9
|
||||
|
||||
ldp x1, x0, [sp, #0x10]!
|
||||
ldp x3, x2, [sp, #0x10]!
|
||||
ldp x30, x8, [sp, #0x10]!
|
||||
|
||||
# original main
|
||||
# link register is set so jump only
|
||||
ldr x9, [x8, #8]
|
||||
br x9
|
||||
*/
|
||||
shellcode = []uint32{
|
||||
0x10000008,
|
||||
0, // x9 = (offset end of __DATA) - (offset shellcode)
|
||||
0x8B090108,
|
||||
0xA8BF23FE,
|
||||
0xA8BF0BE3,
|
||||
0xA8BF03E1,
|
||||
0xF9400109,
|
||||
0xD63F0120,
|
||||
0xA9C103E1,
|
||||
0xA9C10BE3,
|
||||
0xA9C123FE,
|
||||
0xF9400509,
|
||||
0xD61F0120,
|
||||
}
|
||||
|
||||
shellcode_offset = text_start - (ins_size_byte * len(shellcode))
|
||||
|
||||
encode_movz := func(v int) uint32 {
|
||||
return uint32(uint32(v)<<5 | uint32(0x694)<<21 | uint32(0x09))
|
||||
}
|
||||
|
||||
// movz_shellcode_offset := encode_movz(shellcode_offset)
|
||||
// movz_main_offset := encode_movz(main_offset)
|
||||
// movz_data_end_offset := encode_movz(data_end)
|
||||
movz_calculate_offset := encode_movz(data_end - shellcode_offset)
|
||||
|
||||
shellcode[1] = movz_calculate_offset
|
||||
// shellcode[10] = movz_data_end_offset
|
||||
// shellcode[19] = movz_main_offset
|
||||
|
||||
fmt.Printf("// shellcode_offset=%x\n", shellcode_offset)
|
||||
fmt.Printf("// main_offset=%x\n", main_offset)
|
||||
fmt.Printf("// data_end=%x\n", data_end)
|
||||
fmt.Printf("// movz_calculate_offset=%x\n", movz_calculate_offset)
|
||||
// fmt.Printf("// movz_shellcode_offset=%x\n", movz_shellcode_offset)
|
||||
// fmt.Printf("// movz_main_offset=%x\n", movz_main_offset)
|
||||
// fmt.Printf("// movz_data_end_offset=%x\n", movz_data_end_offset)
|
||||
fmt.Printf("// lc_main_offset=%x\n", lc_main_offset)
|
||||
} else {
|
||||
shellcode_start := []uint8{
|
||||
0x4c, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||
0x49, 0xC7, 0xC1,
|
||||
}
|
||||
|
||||
shellcode_end := []uint8{
|
||||
0x4d, 0x01, 0xc8,
|
||||
0x57,
|
||||
0x56,
|
||||
0x52,
|
||||
0x51,
|
||||
0x41, 0x50,
|
||||
0x4d, 0x8b, 0x08,
|
||||
0x41, 0xff, 0xd1,
|
||||
0x41,
|
||||
0x58,
|
||||
0x59,
|
||||
0x5a,
|
||||
0x5e,
|
||||
0x5f, 0x4d, 0x8b, 0x48, 0x08,
|
||||
0x41, 0xff, 0xe1,
|
||||
// pad to %4
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
offset := []uint8{0x00, 0x00, 0x00, 0x00} // offset
|
||||
shellcode_size := len(shellcode_start) + len(offset) + len(shellcode_end)
|
||||
|
||||
// could use buffer encoding, but for correctness,
|
||||
// we do this by hand
|
||||
encode_movz := func(v int) {
|
||||
for i := 0; i < 4; i++ {
|
||||
offset[i] = uint8(v >> (i * 8))
|
||||
}
|
||||
}
|
||||
|
||||
encode_movz((data_end - text_start) + (shellcode_size - len(shellcode_start)) + 3)
|
||||
|
||||
shellcode_offset = text_start - shellcode_size
|
||||
shellcode_bytes := append(shellcode_start, offset...)
|
||||
shellcode_bytes = append(shellcode_bytes, shellcode_end...)
|
||||
|
||||
for i := 0; i < len(shellcode_bytes); i += 4 {
|
||||
val := 0
|
||||
// little endian
|
||||
val |= int(shellcode_bytes[i+0]) << 0
|
||||
val |= int(shellcode_bytes[i+1]) << 8
|
||||
val |= int(shellcode_bytes[i+2]) << 16
|
||||
val |= int(shellcode_bytes[i+3]) << 24
|
||||
shellcode = append(shellcode, uint32(val))
|
||||
}
|
||||
|
||||
fmt.Printf("// shellcode_offset=%x\n", shellcode_offset)
|
||||
fmt.Printf("// main_offset=%x\n", main_offset)
|
||||
fmt.Printf("// data_end=%x\n", data_end)
|
||||
fmt.Printf("// lc_main_offset=%x\n", lc_main_offset)
|
||||
}
|
||||
|
||||
offset := int64(shellcode_offset)
|
||||
{
|
||||
// fix main to point to our newly created shellcode
|
||||
bs := make([]byte, 8)
|
||||
mc.byteorder.PutUint64(bs, uint64(offset))
|
||||
mc.file.WriteAt(bs, int64(lc_main_offset))
|
||||
}
|
||||
|
||||
bs := make([]byte, 4)
|
||||
for _, ins := range shellcode {
|
||||
mc.byteorder.PutUint32(bs, ins)
|
||||
mc.file.WriteAt(bs, offset)
|
||||
offset += 4
|
||||
}
|
||||
|
||||
// make __TEXT writable lol
|
||||
mc.file.Seek(0, 0)
|
||||
mc.file.WriteAt([]byte{0x7}, 0xa0)
|
||||
}
|
@ -44,11 +44,15 @@ message MachoInfo {
|
||||
repeated LibraryImportedSymbols tables = 3;
|
||||
}
|
||||
|
||||
message Selector {
|
||||
uint32 idx = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
PointerSize pointer_size = 1;
|
||||
uint64 image_base = 2;
|
||||
uint64 main = 3;
|
||||
repeated InitPointer init_pointers = 4;
|
||||
// repeated BindSymbol symbols = 5;
|
||||
AllImportedSymbols symbols = 5;
|
||||
|
||||
repeated Selector special_selectors = 6;
|
||||
}
|
||||
|
@ -1,12 +1,20 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <objc/message.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void* sel_lookUpByName(const char* name);
|
||||
|
||||
|
||||
@interface Foo : NSObject
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
- (void)bar {
|
||||
NSLog(@"%@", self);
|
||||
NSLog(@"Invoke instance method original bar in Foo");
|
||||
}
|
||||
|
||||
- (void)tobehijacked:(NSString*)input {
|
||||
NSLog(@"Invoke tobehijacked method %@ from Foo", input);
|
||||
}
|
||||
@end
|
||||
|
||||
@ -17,41 +25,99 @@
|
||||
static int x;
|
||||
|
||||
+ (void)load {
|
||||
NSLog(@"%@", self);
|
||||
// NSLog(@"x=%d", x)
|
||||
printf("printf in [Bar load]\n");
|
||||
x = 1;
|
||||
printf("Invoke +load method\n");
|
||||
}
|
||||
|
||||
- (void)dummy {
|
||||
NSLog(@"dummy bar x=%d", x);
|
||||
NSLog(@"Static value check after +load should be 1: x=%d", x);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface FakeNSDateFormatter : NSDateFormatter {
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FakeNSDateFormatter
|
||||
- (NSDate*)dateFromString:(NSString*)dateString {
|
||||
NSLog(@"Hijacked the NSDateFormatter");
|
||||
return [super dateFromString:dateString];
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
__attribute__((constructor)) static void
|
||||
hmmge(int argc, char** argv) {
|
||||
// create a dummy blank function to be replaced to call OBJC load
|
||||
printf("hmmge=%p\n", hmmge);
|
||||
printf("hmmge argc=%d\n", argc);
|
||||
printf("Invoke C constructor\n");
|
||||
printf("Checking for arguments to be passed correctly\n");
|
||||
printf(" argc=%d\n", argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
printf(" hmmge argv[%d]=%s\n", i, argv[i]);
|
||||
printf(" argv[%d]=%s\n", i, argv[i]);
|
||||
}
|
||||
NSLog(@"hmmge in objc-c");
|
||||
NSLog(@"Using Objective-C in C constructor");
|
||||
NSLog(@"Test static Objective-C class is initialized and +load completed");
|
||||
Bar *bar = [[Bar alloc] init];
|
||||
[bar dummy];
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
int main(int argc, const char * argv[], char* envp[]) {
|
||||
@autoreleasepool {
|
||||
NSLog(@"Hello, World!");
|
||||
NSLog(@"Invoke main()");
|
||||
|
||||
// Foo bar using Objective-C syntax
|
||||
Foo *foo = [[Foo alloc] init];
|
||||
[foo bar];
|
||||
|
||||
// Foo bar with selector and msgSend
|
||||
NSLog(@"Directly call \"bar\" %p through objc_msgSend %p with object foo %p", @selector(bar), objc_msgSend, foo);
|
||||
typedef void (*barfunc)(id, SEL);
|
||||
barfunc bar_ = (barfunc)&objc_msgSend;
|
||||
bar_(foo, @selector(bar));
|
||||
|
||||
NSString *dummyinput = @"dummy input";
|
||||
[foo tobehijacked:dummyinput];
|
||||
NSLog(@"The above invocation should be hijacked with input at %p", dummyinput);
|
||||
|
||||
NSString *dateString = @"2024-01-01T00:00:00.000Z";
|
||||
|
||||
// NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
// [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
|
||||
// NSDate *date = [dateFormatter dateFromString:dateString];
|
||||
|
||||
// this is to test the idea for hooking,
|
||||
// basically, we create a middle-class inherits the class to be used
|
||||
//
|
||||
// example using NSDateFormatter:
|
||||
// - Create a FakeNSDateFormatter inherits NSDateFormatter
|
||||
// - Have an overloaded function that calls [super inherited]
|
||||
// - The internal struct class_t has superclass points to NSDateFormatter
|
||||
// FakeNSDateFormatter *dateFormatter = [[FakeNSDateFormatter alloc] init];
|
||||
// [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
|
||||
// NSDate *date = [dateFormatter dateFromString:dateString];
|
||||
// NSLog(@"Test hijacked/hooked Objective-C result %@", date);
|
||||
|
||||
NSLog(@"Selector \"dateFromString:\" using @selector %p", @selector(dateFromString:));
|
||||
NSLog(@"Selector \"bar:\" using @selector %p", @selector(bar:));
|
||||
NSLog(@"Selector \"dummy\" using @selector %p", @selector(dummy));
|
||||
|
||||
NSLog(@"[Bar dummy] implementation is at %p\n", [foo methodForSelector:@selector(bar:)]);
|
||||
}
|
||||
|
||||
printf("argc=%d\n", argc);
|
||||
printf("Selector lookup 'dateFromString:' addr: %p\n", sel_lookUpByName("dateFromString:"));
|
||||
printf("Selector lookup 'bar:' addr: %p\n", sel_lookUpByName("bar:"));
|
||||
printf("Selector lookup 'dummy' addr: %p\n", sel_lookUpByName("dummy"));
|
||||
|
||||
printf("Test if arguments are passed correctly to main(argc, argv, env)\n");
|
||||
printf(" argc=%d\n", argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
printf(" argv[%d]=%s\n", i, argv[i]);
|
||||
printf(" argv[%d]=%s\n", i, argv[i]);
|
||||
}
|
||||
|
||||
while (*envp) {
|
||||
printf(" env[]=%s\n", *envp);
|
||||
envp++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -3,11 +3,17 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
// #include <Foundation/Foundation.h>
|
||||
#include <objc/objc.h>
|
||||
|
||||
#include "out/b.h"
|
||||
|
||||
char *pwd;
|
||||
uint32_t pwd_len;
|
||||
clock_t start, end;
|
||||
#define ISARM(header) ((*((uint32_t *)(header)+1) & 0xff) == 0xc)
|
||||
|
||||
int custom_strcmp(const char *p1, const char *p2) {
|
||||
const unsigned char *s1 = (const unsigned char *)p1;
|
||||
@ -74,7 +80,7 @@ struct libcache {
|
||||
void *libdyld;
|
||||
|
||||
int nrpath;
|
||||
char** rpaths;
|
||||
char **rpaths;
|
||||
};
|
||||
|
||||
uint32_t fnv_hash_extend(const char *str, uint32_t h) {
|
||||
@ -93,9 +99,7 @@ uint32_t fnv_hash_extend(const char *str, uint32_t h) {
|
||||
|
||||
return h;
|
||||
}
|
||||
uint32_t fnv_hash(const char* str) {
|
||||
return fnv_hash_extend(str, 0x811c9dc5);
|
||||
}
|
||||
uint32_t fnv_hash(const char *str) { return fnv_hash_extend(str, 0x811c9dc5); }
|
||||
|
||||
// try these hashes
|
||||
// https://gist.github.com/sgsfak/9ba382a0049f6ee885f68621ae86079b
|
||||
@ -122,9 +126,9 @@ uint32_t calculate_libname_hash(const libcache *cache, const char *name) {
|
||||
// then resolve the full path for all rpath
|
||||
//
|
||||
// which rpath is correct can be done by checking if the cache has that hash
|
||||
for (int i = 0; i < cache->nrpath; i++){
|
||||
char* rpath = cache->rpaths[i];
|
||||
char* p = realpath(rpath, 0);
|
||||
for (int i = 0; i < cache->nrpath; i++) {
|
||||
char *rpath = cache->rpaths[i];
|
||||
char *p = realpath(rpath, 0);
|
||||
hash = hash_func(p);
|
||||
hash = fnv_hash_extend(&name[6], hash);
|
||||
for (size_t j = 0; j < cache->size; j++) {
|
||||
@ -448,7 +452,7 @@ uint32_t should_follow_symbol(char *&buffer, char *&_find) {
|
||||
return is_prefix;
|
||||
}
|
||||
|
||||
void *find_in_export_trie(const void *header, void *trie, char *& symbol) {
|
||||
void *find_in_export_trie(const void *header, void *trie, char *&symbol) {
|
||||
uint32_t func = 0;
|
||||
|
||||
char *ptr = (char *)trie;
|
||||
@ -512,10 +516,10 @@ void *find_in_export_trie(const void *header, void *trie, char *& symbol) {
|
||||
}
|
||||
|
||||
void *find_in_lib(struct libcache *cache, struct libcache_item *lib,
|
||||
char *& symbol);
|
||||
char *&symbol);
|
||||
|
||||
void *find_in_reexport(struct libcache *cache, struct libcache_item *lib,
|
||||
char *& symbol) {
|
||||
char *&symbol) {
|
||||
void *header = lib->header;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
@ -551,7 +555,7 @@ void *find_in_reexport(struct libcache *cache, struct libcache_item *lib,
|
||||
}
|
||||
|
||||
void *find_in_lib(struct libcache *cache, struct libcache_item *lib,
|
||||
char *& symbol) {
|
||||
char *&symbol) {
|
||||
void *direct = find_in_export_trie(lib->header, lib->trie, symbol);
|
||||
if (direct) {
|
||||
return direct;
|
||||
@ -582,8 +586,8 @@ void *custom_dlsym(struct libcache *cache, uint32_t hash, const char *symbol) {
|
||||
// C has X but it is a re-export from B with the name Y
|
||||
// then we have to perform a search again from the top
|
||||
// but with symbol Y
|
||||
char** symbol_copy = (char**)&symbol;
|
||||
void* func = find_in_lib(cache, cache_lib, *symbol_copy);
|
||||
char **symbol_copy = (char **)&symbol;
|
||||
void *func = find_in_lib(cache, cache_lib, *symbol_copy);
|
||||
if (*symbol_copy != symbol) {
|
||||
func = find_in_lib(cache, cache_lib, *symbol_copy);
|
||||
}
|
||||
@ -815,6 +819,7 @@ void test(struct libcache &cache);
|
||||
__attribute__((constructor)) static void
|
||||
bruh(int argc, const char *const argv[], const char *const envp[],
|
||||
const char *const apple[], const struct ProgramVars *vars) {
|
||||
start = clock();
|
||||
printf("=== manual symbol bind starts ===\n");
|
||||
// set_cwd(envp);
|
||||
|
||||
@ -896,7 +901,7 @@ void build_cache(struct libcache &cache, void *main) {
|
||||
printf("lib header at %p\n", thislib);
|
||||
printf("libdyld header at %p\n", libdyld);
|
||||
|
||||
find_all_rpath(cache ,main);
|
||||
find_all_rpath(cache, main);
|
||||
uint32_t trie_size;
|
||||
void *libdyld_export_trie = get_export_trie(libdyld, trie_size);
|
||||
|
||||
@ -908,16 +913,16 @@ void build_cache(struct libcache &cache, void *main) {
|
||||
typedef char *(*dyld_get_image_name_t)(int);
|
||||
typedef void *(*dyld_get_image_header_t)(int);
|
||||
|
||||
char* dyld_image_count_s = "__dyld_image_count";
|
||||
char *dyld_image_count_s = (char *)"__dyld_image_count";
|
||||
int (*dyld_image_count_func)(void) = (dyld_image_count_t)find_in_export_trie(
|
||||
libdyld, libdyld_export_trie, dyld_image_count_s);
|
||||
|
||||
char* dyld_get_image_header_s = "__dyld_get_image_header";
|
||||
char *dyld_get_image_header_s = (char *)"__dyld_get_image_header";
|
||||
void *(*dyld_get_image_header_func)(int) =
|
||||
(dyld_get_image_header_t)find_in_export_trie(libdyld, libdyld_export_trie,
|
||||
dyld_get_image_header_s);
|
||||
|
||||
char* dyld_get_image_name_s = "__dyld_get_image_name";
|
||||
char *dyld_get_image_name_s = (char *)"__dyld_get_image_name";
|
||||
char *(*dyld_get_image_name_func)(int) =
|
||||
(dyld_get_image_name_t)find_in_export_trie(libdyld, libdyld_export_trie,
|
||||
dyld_get_image_name_s);
|
||||
@ -930,7 +935,7 @@ void build_cache(struct libcache &cache, void *main) {
|
||||
char *name = dyld_get_image_name_func(i);
|
||||
bootstrap_libcache_item(&cache.libs[i], header, name);
|
||||
cache.libs[i].hash = calculate_libname_hash(&cache, name);
|
||||
printf("%p %s\n", header, name);
|
||||
// printf("%p %s\n", header, name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -950,7 +955,8 @@ void find_all_rpath(struct libcache &cache, void *header) {
|
||||
for (uint32_t i = 0; i < ncmds; i++) {
|
||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
||||
if (cmd == LC_RPATH) cache.nrpath++;
|
||||
if (cmd == LC_RPATH)
|
||||
cache.nrpath++;
|
||||
ptr += cmdsize;
|
||||
}
|
||||
uint32_t idx = 0;
|
||||
@ -1182,11 +1188,13 @@ void fix(struct libcache &cache) {
|
||||
// for (int i = 0; i < 0x2ac; i++) {
|
||||
// text_start[0xb8c + i] = text_start[0xb8c + i] ^ 0xcc;
|
||||
// }
|
||||
// vm_protect_func(mach_task_self_func(), (uint64_t)text_start, 0x1000, 0,
|
||||
// VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
|
||||
fix_objc(libfixing, cache);
|
||||
fix_initializer(libfixing, cache);
|
||||
|
||||
// _TEXT must be RX or RW no RWX
|
||||
// vm_protect_func(mach_task_self_func(), (uint64_t)text_start, 0x1000, 0,
|
||||
// VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
}
|
||||
|
||||
void volatile custom_initializer(int argc, const char *const argv[],
|
||||
@ -1263,9 +1271,341 @@ void volatile custom_initializer(int argc, const char *const argv[],
|
||||
|
||||
printf("[+] initializers completed\n");
|
||||
free(custom_initializer_i);
|
||||
end = clock();
|
||||
double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
|
||||
printf("restoration library time: %lf\n", cpu_time_used);
|
||||
}
|
||||
|
||||
void fix_objc_classdata(struct libcache_item *libfixing, struct libcache &cache);
|
||||
void fix_class_refs(struct libcache_item *libfixing, struct libcache &cache);
|
||||
void run_objc_readclass(struct libcache_item *libfixing, struct libcache &cache);
|
||||
|
||||
// method are splited into 3 kinds, but for simplicity, we think of it as
|
||||
// 2 kinds: big and small
|
||||
// our example are small method list, which all pointers are relative and 32-bit
|
||||
// the size should be 0xc == 12 but we have padding 4-byte 0x0 for some reason?
|
||||
union _objc_method{
|
||||
struct {
|
||||
const char* name;
|
||||
const char* types;
|
||||
void* imp;
|
||||
};
|
||||
struct {
|
||||
int32_t sel_offset;
|
||||
int32_t typ_offset;
|
||||
int32_t imp_offset;
|
||||
};
|
||||
};
|
||||
|
||||
struct method_t {
|
||||
const char* name; /* Pointer to name (or selector reference?) */
|
||||
const char* types; /* Pointer to type info */
|
||||
void* imp; /* Pointer to implementation (code) */
|
||||
};
|
||||
|
||||
// entsize & 0x80000000 is small method kind
|
||||
// entsize = kind | sizeof(_objc_method)
|
||||
struct _method_list_t {
|
||||
uint32_t entsize; // sizeof(struct _objc_method)
|
||||
uint32_t method_count;
|
||||
union _objc_method method_list[];
|
||||
};
|
||||
|
||||
struct _class_ro_t {
|
||||
uint32_t flags;
|
||||
uint32_t const instanceStart;
|
||||
uint32_t const instanceSize;
|
||||
uint32_t const reserved; // only when building for 64bit targets
|
||||
const uint8_t * const ivarLayout;
|
||||
const char *const name;
|
||||
struct _method_list_t * baseMethods;
|
||||
const /*struct _protocol_list_t*/void *const baseProtocols;
|
||||
const /*struct _ivar_list_t*/void *const ivars;
|
||||
const uint8_t * const weakIvarLayout;
|
||||
const /*struct _prop_list_t*/void *const properties;
|
||||
};
|
||||
|
||||
struct _class_t {
|
||||
struct _class_t *isa;
|
||||
struct _class_t * superclass;
|
||||
void *cache;
|
||||
void *vtable;
|
||||
struct _class_ro_t *ro;
|
||||
};
|
||||
void fix_objc(struct libcache_item *libfixing, struct libcache &cache) {
|
||||
printf("[+] dealing with Objective-C\n");
|
||||
#ifdef METH1
|
||||
fix_objc_classdata(libfixing, cache);
|
||||
#endif
|
||||
#ifdef METH3
|
||||
printf("METH3\n");
|
||||
fix_class_refs(libfixing, cache);
|
||||
#endif
|
||||
run_objc_readclass(libfixing, cache);
|
||||
}
|
||||
|
||||
void test_objc_hijack(void* self, void* selector, void* input) {
|
||||
printf("[Foo tobehijacked] function is HIJACKED\n");
|
||||
printf("arg1=%p arg2=%p arg3=%p\n", self, selector, input);
|
||||
}
|
||||
|
||||
// a subroutine to perform hooking of fixed-binary classes
|
||||
// by iterating in the __objc_classref which internally points to
|
||||
// __objc_data for a list of _class_t structs
|
||||
// each _classt_t has a _class_ro_t containing pointers to
|
||||
// the components of an instance, including methods, properties, ivars, ...
|
||||
//
|
||||
// in this function, we only work on hooking/hijacking of class methods
|
||||
// by fixing the method list which to be read by Objective-C runtime during readClass
|
||||
// the method list is a list of {selector, type, implementation} (all pointers)
|
||||
// by fixing the implementation (should point to a function) the readClass
|
||||
// thinks that it is the function associated with the method name/selector
|
||||
//
|
||||
// by now, all rebases have been rebased and pointers should be pointing correctly
|
||||
// however, selectors are to be constructed, unless erased
|
||||
void fix_objc_classdata(struct libcache_item *libfixing, struct libcache &cache) {
|
||||
void *header = libfixing->header;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
ptr += 0x20;
|
||||
} else {
|
||||
ptr += 0x20 - 0x4;
|
||||
}
|
||||
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
char *command_ptr = ptr;
|
||||
|
||||
uint64_t linkedit_vmaddr;
|
||||
uint64_t linkedit_fileoffset;
|
||||
uint64_t slide;
|
||||
|
||||
uint64_t methlist_start;
|
||||
uint64_t methlist_size;
|
||||
|
||||
uint32_t libsystem_hash =
|
||||
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
|
||||
typedef void *(*vm_protect_t)(void *, uint64_t, uint64_t, int, int);
|
||||
typedef void *(*mach_task_self_t)();
|
||||
mach_task_self_t mach_task_self_func =
|
||||
(mach_task_self_t)custom_dlsym(&cache, libsystem_hash, "_mach_task_self");
|
||||
vm_protect_t vm_protect_func =
|
||||
(vm_protect_t)custom_dlsym(&cache, libsystem_hash, "_vm_protect");
|
||||
|
||||
for (int i = 0; i < ncmds; i++) {
|
||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
||||
if (cmd == LC_SEGMENT_64) {
|
||||
char *name = (char *)((uint64_t *)ptr + 1);
|
||||
uint64_t vmaddr = *((uint64_t *)ptr + 3);
|
||||
uint64_t fileoffset = *((uint64_t *)ptr + 5);
|
||||
// this assumes that __TEXT comes before __DATA_CONST
|
||||
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);
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char *secname = sections_ptr;
|
||||
// to be able to fix method list for hooking, we need this section
|
||||
// to be writable
|
||||
if (custom_strncmp(secname, "__objc_methlist", 16) == 0) {
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
|
||||
methlist_start = addr + slide;
|
||||
methlist_size = size;
|
||||
|
||||
printf("setting __objc_methlist to RW: addr=%p size=%x\n", addr + slide, size);
|
||||
vm_protect_func(mach_task_self_func(), methlist_start, methlist_size, 0, VM_PROT_READ | VM_PROT_WRITE);
|
||||
}
|
||||
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);
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char *secname = sections_ptr;
|
||||
// we can iterate in the __objc_data rather than __objc_classref
|
||||
// classref can also point to outside classes that are imported
|
||||
if (custom_strncmp(secname, "__objc_data", 16) == 0) {
|
||||
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
struct _class_t *data_ptr = (struct _class_t *)(addr + slide);
|
||||
|
||||
for (int nclass = 0; nclass < size / sizeof(struct _class_t); nclass++, data_ptr++) {
|
||||
// ro can be null for some reasons
|
||||
// baseMethods is null if the class is a metaclass
|
||||
if (!(data_ptr->ro && data_ptr->ro->baseMethods)) {
|
||||
continue;
|
||||
}
|
||||
const char* class_name = data_ptr->ro->name;
|
||||
struct _method_list_t* methods = data_ptr->ro->baseMethods;
|
||||
for (int i_method = 0; i_method < methods->method_count; i_method++) {
|
||||
// have to use reference because the relative offset is calculated with the variable address
|
||||
// if not using reference, then the variable will be a COPY value and the address is localized
|
||||
union _objc_method* method = &methods->method_list[i_method];
|
||||
if (methods->entsize & 0x80000000) {
|
||||
const char* imp = *(char**)((char*)(&method->sel_offset) + method->sel_offset);
|
||||
if (custom_strcmp(class_name, "Foo") == 0 && custom_strcmp(imp, "tobehijacked:") == 0) {
|
||||
// char* current_imp = (char*)(&method->imp_offset) + method->imp_offset;
|
||||
|
||||
// encode the relative pointer
|
||||
uint64_t replace = (uint64_t)test_objc_hijack;
|
||||
uint64_t original = (uint64_t)&method->imp_offset;
|
||||
printf("modify the Objective-C method at %p\n", &method->imp_offset);
|
||||
if (replace > original) {
|
||||
method->imp_offset = (int32_t)(replace - original);
|
||||
} else {
|
||||
method->imp_offset = -(int32_t)(original - replace);
|
||||
}
|
||||
}
|
||||
|
||||
printf(" method=%p\n", method);
|
||||
printf(" sel=%x --> %p\n", method->sel_offset, (char*)(&method->sel_offset) + method->sel_offset);
|
||||
printf(" %s\n", name);
|
||||
printf(" typ=%x --> %s\n", method->typ_offset, (char*)&method->typ_offset + method->typ_offset);
|
||||
printf(" fun=%x --> %p\n", method->imp_offset, (char*)(&method->imp_offset) + method->imp_offset);
|
||||
}
|
||||
else {
|
||||
const char* imp = method->name;
|
||||
if (custom_strcmp(class_name, "Foo") == 0 && custom_strcmp(imp, "tobehijacked:") == 0) {
|
||||
void* replace = (void*)test_objc_hijack;
|
||||
printf("modify the Objective-C method at %p with legacy format.\n", &method->imp);
|
||||
method->imp = replace;
|
||||
}
|
||||
printf(" method=%p\n", method);
|
||||
printf(" sel=%s\n", method->name);
|
||||
printf(" typ=%p\n", method->types);
|
||||
printf(" fun=%p\n", method->imp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||
}
|
||||
} else if (custom_strcmp(name, "__LINKEDIT") == 0) {
|
||||
linkedit_vmaddr = vmaddr;
|
||||
linkedit_fileoffset = fileoffset;
|
||||
}
|
||||
}
|
||||
ptr += cmdsize;
|
||||
}
|
||||
|
||||
// _TEXT must be RX or RW no RWX
|
||||
vm_protect_func(mach_task_self_func(), methlist_start, methlist_size, 0,
|
||||
VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
}
|
||||
|
||||
uint64_t find_replace_cls_refs(struct libcache cache) {
|
||||
void *header = cache.thislib;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
ptr += 0x20;
|
||||
} else {
|
||||
ptr += 0x20 - 0x4;
|
||||
}
|
||||
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
char *command_ptr = ptr;
|
||||
uint64_t slide;
|
||||
for (int i = 0; i < ncmds; i++){
|
||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
||||
if (cmd == LC_SEGMENT_64){
|
||||
char* name = (char*)((uint64_t*)ptr + 1);
|
||||
uint64_t vmaddr = *((uint64_t*)ptr + 3);
|
||||
if (custom_strcmp(name, "__TEXT") == 0)
|
||||
slide = (uint64_t)header - vmaddr;
|
||||
|
||||
if (custom_strcmp(name, "__DATA") == 0){
|
||||
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;
|
||||
if (custom_strncmp(secname, "__objc_data", 11) == 0){
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
struct _class_t *data_ptr = (struct _class_t *)(addr + slide);
|
||||
for (int nclass = 0; nclass < size / sizeof(struct _class_t); nclass++, data_ptr++) {
|
||||
if (!data_ptr->ro)
|
||||
continue;
|
||||
if (data_ptr->ro->flags & 0x01) { continue; }
|
||||
if (custom_strcmp(data_ptr->ro->name, "Hooker") == 0){
|
||||
printf("Found Hooker @ %p\n", data_ptr);
|
||||
return (uint64_t)data_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr += cmdsize;
|
||||
}
|
||||
}
|
||||
|
||||
void fix_class_refs(struct libcache_item *libfixing, struct libcache &cache) {
|
||||
uint64_t replace = find_replace_cls_refs(cache);
|
||||
void *header = libfixing->header;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
ptr += 0x20;
|
||||
} else {
|
||||
ptr += 0x20 - 0x4;
|
||||
}
|
||||
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
char *command_ptr = ptr;
|
||||
uint64_t slide;
|
||||
for (int i = 0; i < ncmds; i++){
|
||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
||||
if (cmd == LC_SEGMENT_64){
|
||||
char* name = (char*)((uint64_t*)ptr + 1);
|
||||
uint64_t vmaddr = *((uint64_t*)ptr + 3);
|
||||
if (custom_strcmp(name, "__TEXT") == 0)
|
||||
slide = (uint64_t)header - vmaddr;
|
||||
|
||||
if (custom_strcmp(name, "__DATA") == 0){
|
||||
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;
|
||||
if (custom_strncmp(secname, "__objc_classrefs", 16) == 0){
|
||||
uint64_t addr = *((uint64_t*)sections_ptr + 4) + slide;
|
||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
||||
struct _class_t* target_clsref = NULL;
|
||||
for (int nclass = 0; nclass < size / sizeof(uint64_t*); nclass++){
|
||||
target_clsref = (_class_t*)(*((uint64_t *)addr + nclass));
|
||||
// printf("Target clasref @ %p: %p\n", (uint64_t *)addr + nclass, target_clsref);
|
||||
if (custom_strcmp(target_clsref->ro->name, "Foo") == 0){
|
||||
// TODO
|
||||
printf("Target clasref @ %p: %p\n", (uint64_t *)addr + nclass, target_clsref);
|
||||
*((uint64_t *)addr + nclass) = replace;
|
||||
printf("New clasref @ %p: %p\n", (uint64_t *)addr + nclass, *((uint64_t *)addr + nclass));
|
||||
struct _class_t* hooker = (struct _class_t*)replace;
|
||||
printf("superclass hooker: %p\n", target_clsref->superclass);
|
||||
hooker->superclass = target_clsref;
|
||||
printf("New superclass hooker: %p\n", hooker->superclass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr += cmdsize;
|
||||
}
|
||||
}
|
||||
|
||||
void run_objc_readclass(struct libcache_item *libfixing, struct libcache &cache) {
|
||||
// Manually run the Objective-C runtime for each class
|
||||
//
|
||||
|
||||
@ -1279,6 +1619,7 @@ void fix_objc(struct libcache_item *libfixing, struct libcache &cache) {
|
||||
// "mov rcx, 123;"
|
||||
// "call r12;");
|
||||
|
||||
printf("fixing objective-c\n");
|
||||
void *header = libfixing->header;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
@ -1305,6 +1646,57 @@ void fix_objc(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);
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char *secname = sections_ptr;
|
||||
printf("section %s\n", secname);
|
||||
if (custom_strncmp(secname, "__objc_methname", 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);
|
||||
// printf("methname addr %p : %s\n", data_ptr, (char*)data_ptr);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char *secname = sections_ptr;
|
||||
printf("section %s\n", secname);
|
||||
if (custom_strncmp(secname, "__objc_selrefs", 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);
|
||||
|
||||
uint32_t trie_size;
|
||||
char *symbol = (char *)"__dyld_get_objc_selector";
|
||||
void *libdyld = cache.libdyld;
|
||||
void *libdyld_export_trie = get_export_trie(libdyld, trie_size);
|
||||
typedef void *(*dyld_get_objc_selector_t)(const char *);
|
||||
dyld_get_objc_selector_t dyld_get_objc_selector_func =
|
||||
(dyld_get_objc_selector_t)find_in_export_trie(
|
||||
libdyld, libdyld_export_trie, symbol);
|
||||
|
||||
// resolve method names that cached in the dyld
|
||||
for (int i = 0; i < bshield_data::n_selectors; i++) {
|
||||
uint32_t idx = bshield_data::special_selectors_idx[i];
|
||||
const char *name = bshield_data::special_selectors_name[i];
|
||||
data_ptr[idx] = (uint64_t)dyld_get_objc_selector_func(name);
|
||||
}
|
||||
|
||||
typedef void *(*sel_lookUpByName_t)(const char *);
|
||||
sel_lookUpByName_t sel_lookUpByName =
|
||||
(sel_lookUpByName_t)custom_dlsym(
|
||||
&cache, "/usr/lib/libobjc.A.dylib", "_sel_lookUpByName");
|
||||
printf("selector gogogo: %p\n",
|
||||
sel_lookUpByName("dateFromString:"));
|
||||
}
|
||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||
}
|
||||
} else if (custom_strcmp(name, "__DATA_CONST") == 0) {
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||
|
@ -1,9 +1,10 @@
|
||||
# set -ex
|
||||
|
||||
set -e
|
||||
clear
|
||||
VERSION=${1:-14}
|
||||
METH=${2}
|
||||
OUT=./out
|
||||
LOGIC=${2}
|
||||
|
||||
LOGIC=3
|
||||
make -C ../../macho-go
|
||||
mkdir -p $OUT
|
||||
|
||||
echo "using mach-o version $VERSION"
|
||||
@ -14,6 +15,21 @@ else
|
||||
echo "Resulting binary uses LEGACY symbol resolver"
|
||||
fi
|
||||
|
||||
cat <<'fly'
|
||||
______
|
||||
_\ _~-\___
|
||||
= = ==(____AA____D
|
||||
\_____\___________________,-~~~~~~~`-.._
|
||||
/ o O o o o o O O o o o o o o O o |\_
|
||||
`~-.__ ___..----.. )
|
||||
`---~~\___________/------------`````
|
||||
= ===(_________D
|
||||
fly
|
||||
|
||||
# this is a joke for those who knows
|
||||
# https://www.blackhat.com/presentations/bh-dc-09/Iozzo/BlackHat-DC-09-Iozzo-let-your-mach0-fly-whitepaper.pdf
|
||||
echo "make your Mach-O fly"
|
||||
|
||||
if [[ $LOGIC -eq 0 ]]
|
||||
then
|
||||
|
||||
@ -60,15 +76,23 @@ clang++ -mmacosx-version-min=$VERSION -o $OUT/libc.dylib -shared c.cc
|
||||
# create our dummy lib first
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib dummy.cc
|
||||
# build a references libb
|
||||
clang -fobjc-arc -ObjC -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.mm
|
||||
clang -fobjc-arc -ObjC -mmacosx-version-min=$VERSION -o $OUT/a a.mm
|
||||
|
||||
# extract symbols from a
|
||||
# ../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports --remove-symbol-table --keep-imports _printf $OUT/a
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports $OUT/a
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --dylibs=./out/libb.dylib --remove-imports --remove-exports --remove-symbol-table --remove-others $OUT/a
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
|
||||
|
||||
if [ "$METH" = "METH1" ]; then
|
||||
# build libb with symbols extracted from a
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc
|
||||
# ../../macho-go/bin/ios-wrapper pepe -o $OUT/libb.dylib -b $OUT/libb.bcell --remove-imports --remove-exports --keep-imports _dyld_get_sdk_version --keep-imports _malloc --keep-imports _printf --keep-imports ___stack_chk_guard $OUT/libb.dylib
|
||||
clang++ -D $METH -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc
|
||||
# ../../macho-go/bin/ios-wrapper pepe -o $OUT/libb.dylib -b $OUT/libb.bcell --remove-imports --remove-exports --remove-symbol-table --remove-others --keep-imports _dyld_get_sdk_version --keep-imports _malloc --keep-imports ___stack_chk_guard --keep-imports _printf $OUT/libb.dylib
|
||||
|
||||
elif [ "$METH" = "METH3" ]; then
|
||||
clang -mmacosx-version-min=$VERSION -fobjc-arc -ObjC -c -o $OUT/hooking.o hooking.mm
|
||||
clang++ -mmacosx-version-min=$VERSION -D $METH -c -o $OUT/b.o b.cc
|
||||
clang++ -fobjc-arc -ObjC -shared -Wl,-reexport_library -o $OUT/libb.dylib $OUT/b.o $OUT/hooking.o
|
||||
fi
|
||||
|
||||
# resign
|
||||
codesign --force --deep -s - $OUT/a-fixed
|
||||
|
31
research/custom_loader/hooking.mm
Normal file
31
research/custom_loader/hooking.mm
Normal file
@ -0,0 +1,31 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <objc/message.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@interface Hehe : NSObject
|
||||
- (void)bar;
|
||||
- (void)tobehijacked:(NSString*)input;
|
||||
@end
|
||||
|
||||
@interface Hooker : Hehe
|
||||
@end
|
||||
|
||||
@implementation Hehe
|
||||
- (void)bar {
|
||||
NSLog(@"Invoke instance method %@", self);
|
||||
}
|
||||
- (void)tobehijacked:(NSString*)input {
|
||||
NSLog(@"Invoke tobehijacked method %@", input);
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation Hooker
|
||||
- (void)tobehijacked:(NSString*)input {
|
||||
NSLog(@"Hijacked tobehijacked method %@ from Hooker", input);
|
||||
}
|
||||
|
||||
- (void)bar {
|
||||
[super bar];
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,57 @@
|
||||
struct class {
|
||||
class* isa;
|
||||
class* super;
|
||||
struct {
|
||||
void* bucket;
|
||||
void* preoptimize;
|
||||
} cache;
|
||||
uint64_t cache_bits;
|
||||
}
|
||||
|
||||
realizeClassWithoutSwift(class* cls, class* previous) {
|
||||
// x0 = cls
|
||||
// x1 = previous
|
||||
|
||||
x19 = x0
|
||||
if (!x0) {
|
||||
return
|
||||
}
|
||||
|
||||
x28 = x1
|
||||
x8 = *x19 - 1
|
||||
|
||||
if (x8 < 0xf) {
|
||||
} else {
|
||||
x8 = x19
|
||||
x16 = *(x8 + 0x20)
|
||||
x9 = x16
|
||||
// strip pointer auth x9
|
||||
x9 = x9 & 0x7ffffffffff8
|
||||
w9 = *x9
|
||||
|
||||
if (w9 & 0x1f) {
|
||||
x17 = x8
|
||||
x17 = 0xc93a << 48
|
||||
// authenticate x16 x17
|
||||
x17 = x16
|
||||
// strip pointer auth x17
|
||||
if (x16 != x17) {
|
||||
throw
|
||||
}
|
||||
x20 = x16 & 0x7ffffffffff8
|
||||
x0 = x20
|
||||
x0 = malloc_size(x0)
|
||||
if (x0 > 0x1f) {
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
w20 = -0x7ff80000
|
||||
x21 = x19 + w20
|
||||
x0 = x21
|
||||
x0 = safe_ro(x0)
|
||||
x8 = *x0 & 0x1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
657
research/custom_loader/realizeClassWithoutSwift.txt
Normal file
657
research/custom_loader/realizeClassWithoutSwift.txt
Normal file
@ -0,0 +1,657 @@
|
||||
libobjc.A.dylib`realizeClassWithoutSwift:
|
||||
0x193755cac <+0>: pacibsp
|
||||
0x193755cb0 <+4>: sub sp, sp, #0xc0
|
||||
0x193755cb4 <+8>: stp x28, x27, [sp, #0x60]
|
||||
0x193755cb8 <+12>: stp x26, x25, [sp, #0x70]
|
||||
0x193755cbc <+16>: stp x24, x23, [sp, #0x80]
|
||||
0x193755cc0 <+20>: stp x22, x21, [sp, #0x90]
|
||||
0x193755cc4 <+24>: stp x20, x19, [sp, #0xa0]
|
||||
0x193755cc8 <+28>: stp x29, x30, [sp, #0xb0]
|
||||
0x193755ccc <+32>: add x29, sp, #0xb0
|
||||
0x193755cd0 <+36>: mov x19, x0
|
||||
0x193755cd4 <+40>: cbz x0, 0x193756550 ; <+2212>
|
||||
0x193755cd8 <+44>: mov x28, x1
|
||||
0x193755cdc <+48>: ldr x8, [x19]
|
||||
0x193755ce0 <+52>: sub x8, x8, #0x1
|
||||
0x193755ce4 <+56>: cmp x8, #0xf
|
||||
0x193755ce8 <+60>: b.lo 0x193755d08 ; <+92>
|
||||
0x193755cec <+64>: mov x8, x19
|
||||
0x193755cf0 <+68>: ldr x16, [x8, #0x20]!
|
||||
0x193755cf4 <+72>: mov x9, x16
|
||||
0x193755cf8 <+76>: xpacd x9
|
||||
0x193755cfc <+80>: and x9, x9, #0x7ffffffffff8
|
||||
0x193755d00 <+84>: ldr w9, [x9]
|
||||
0x193755d04 <+88>: tbnz w9, #0x1f, 0x1937560e8 ; <+1084>
|
||||
0x193755d08 <+92>: mov w20, #-0x7ff80000
|
||||
0x193755d0c <+96>: add x21, x19, #0x20
|
||||
0x193755d10 <+100>: mov x0, x21
|
||||
0x193755d14 <+104>: bl 0x1937753cc ; class_ro_t const* class_data_bits_t::safe_ro<(Authentication)0>() const
|
||||
0x193755d18 <+108>: ldr w8, [x0]
|
||||
0x193755d1c <+112>: and w26, w8, #0x1
|
||||
0x193755d20 <+116>: tbnz w8, #0x1e, 0x193755d58 ; <+172>
|
||||
0x193755d24 <+120>: mov x23, x0
|
||||
0x193755d28 <+124>: mov w0, #0x20
|
||||
0x193755d2c <+128>: mov w1, #0x1
|
||||
0x193755d30 <+132>: bl 0x193786a90 ; symbol stub for: calloc
|
||||
0x193755d34 <+136>: mov x22, x0
|
||||
0x193755d38 <+140>: add x8, x0, #0x8
|
||||
0x193755d3c <+144>: ldr x9, [x0, #0x8]
|
||||
0x193755d40 <+148>: tbnz w9, #0x0, 0x193755dd8 ; <+300>
|
||||
0x193755d44 <+152>: mov x9, x8
|
||||
0x193755d48 <+156>: movk x9, #0x949b, lsl #48
|
||||
0x193755d4c <+160>: mov x17, x23
|
||||
0x193755d50 <+164>: mov x16, x9
|
||||
0x193755d54 <+168>: b 0x193755e08 ; <+348>
|
||||
0x193755d58 <+172>: ldr x16, [x21]
|
||||
0x193755d5c <+176>: mov x17, x21
|
||||
0x193755d60 <+180>: movk x17, #0xc93a, lsl #48
|
||||
0x193755d64 <+184>: autdb x16, x17
|
||||
0x193755d68 <+188>: mov x17, x16
|
||||
0x193755d6c <+192>: xpacd x17
|
||||
0x193755d70 <+196>: cmp x16, x17
|
||||
0x193755d74 <+200>: b.eq 0x193755d7c ; <+208>
|
||||
0x193755d78 <+204>: brk #0xc473
|
||||
0x193755d7c <+208>: and x22, x16, #0x7ffffffffff8
|
||||
0x193755d80 <+212>: mov x0, x22
|
||||
0x193755d84 <+216>: bl 0x193772238 ; class_rw_t::ro() const
|
||||
0x193755d88 <+220>: mov x23, x0
|
||||
0x193755d8c <+224>: ldr x16, [x21]
|
||||
0x193755d90 <+228>: mov x17, x21
|
||||
0x193755d94 <+232>: movk x17, #0xc93a, lsl #48
|
||||
0x193755d98 <+236>: autdb x16, x17
|
||||
0x193755d9c <+240>: mov x17, x16
|
||||
0x193755da0 <+244>: xpacd x17
|
||||
0x193755da4 <+248>: cmp x16, x17
|
||||
0x193755da8 <+252>: b.eq 0x193755db0 ; <+260>
|
||||
0x193755dac <+256>: brk #0xc473
|
||||
0x193755db0 <+260>: and x8, x16, #0x7ffffffffff8
|
||||
0x193755db4 <+264>: mov w9, #0x3ff7ffff
|
||||
0x193755db8 <+268>: ldr w10, [x8]
|
||||
0x193755dbc <+272>: and w11, w10, w9
|
||||
0x193755dc0 <+276>: orr w11, w11, w20
|
||||
0x193755dc4 <+280>: mov x12, x10
|
||||
0x193755dc8 <+284>: casal w12, w11, [x8]
|
||||
0x193755dcc <+288>: cmp w12, w10
|
||||
0x193755dd0 <+292>: b.ne 0x193755db8 ; <+268>
|
||||
0x193755dd4 <+296>: b 0x193755e24 ; <+376>
|
||||
0x193755dd8 <+300>: and x16, x9, #0xfffffffffffffffe
|
||||
0x193755ddc <+304>: mov x17, x8
|
||||
0x193755de0 <+308>: movk x17, #0xe4f, lsl #48
|
||||
0x193755de4 <+312>: autdb x16, x17
|
||||
0x193755de8 <+316>: mov x17, x16
|
||||
0x193755dec <+320>: xpacd x17
|
||||
0x193755df0 <+324>: cmp x16, x17
|
||||
0x193755df4 <+328>: b.eq 0x193755dfc ; <+336>
|
||||
0x193755df8 <+332>: brk #0xc473
|
||||
0x193755dfc <+336>: mov x8, x16
|
||||
0x193755e00 <+340>: movk x16, #0xa0d3, lsl #48
|
||||
0x193755e04 <+344>: mov x17, x23
|
||||
0x193755e08 <+348>: pacdb x17, x16
|
||||
0x193755e0c <+352>: str x17, [x8]
|
||||
0x193755e10 <+356>: orr w8, w26, w20
|
||||
0x193755e14 <+360>: str w8, [x22]
|
||||
0x193755e18 <+364>: mov x0, x21
|
||||
0x193755e1c <+368>: mov x1, x22
|
||||
0x193755e20 <+372>: bl 0x19377576c ; class_data_bits_t::setData(class_rw_t*)
|
||||
0x193755e24 <+376>: adrp x8, 50
|
||||
0x193755e28 <+380>: add x8, x8, #0x5c0 ; _objc_empty_cache
|
||||
0x193755e2c <+384>: str x8, [x19, #0x10]
|
||||
0x193755e30 <+388>: str xzr, [x19, #0x18]
|
||||
0x193755e34 <+392>: cbz w26, 0x193755e44 ; <+408>
|
||||
0x193755e38 <+396>: add x8, x19, #0x1e
|
||||
0x193755e3c <+400>: mov w9, #0x4
|
||||
0x193755e40 <+404>: ldseth w9, w8, [x8]
|
||||
0x193755e44 <+408>: adrp x8, 363074
|
||||
0x193755e48 <+412>: ldr w8, [x8, #0x4f0]
|
||||
0x193755e4c <+416>: cbnz w8, 0x193756574 ; <+2248>
|
||||
0x193755e50 <+420>: mov x27, x19
|
||||
0x193755e54 <+424>: ldr x16, [x27, #0x8]!
|
||||
0x193755e58 <+428>: str x22, [sp, #0x40]
|
||||
0x193755e5c <+432>: cbz x16, 0x193755e88 ; <+476>
|
||||
0x193755e60 <+436>: mov x17, x27
|
||||
0x193755e64 <+440>: movk x17, #0xb5ab, lsl #48
|
||||
0x193755e68 <+444>: autda x16, x17
|
||||
0x193755e6c <+448>: mov x17, x16
|
||||
0x193755e70 <+452>: xpacd x17
|
||||
0x193755e74 <+456>: cmp x16, x17
|
||||
0x193755e78 <+460>: b.eq 0x193755e80 ; <+468>
|
||||
0x193755e7c <+464>: brk #0xc472
|
||||
0x193755e80 <+468>: mov x0, x16
|
||||
0x193755e84 <+472>: b 0x193755e8c ; <+480>
|
||||
0x193755e88 <+476>: mov x0, #0x0
|
||||
0x193755e8c <+480>: bl 0x19375573c ; remapClass(objc_class*)
|
||||
0x193755e90 <+484>: mov x24, x0
|
||||
0x193755e94 <+488>: mov x1, #0x0
|
||||
0x193755e98 <+492>: bl 0x193755cac ; <+0>
|
||||
0x193755e9c <+496>: ldr x8, [x19]
|
||||
0x193755ea0 <+500>: and x0, x8, #0x7ffffffffff8
|
||||
0x193755ea4 <+504>: bl 0x19375573c ; remapClass(objc_class*)
|
||||
0x193755ea8 <+508>: mov x25, x0
|
||||
0x193755eac <+512>: mov x1, #0x0
|
||||
0x193755eb0 <+516>: bl 0x193755cac ; <+0>
|
||||
0x193755eb4 <+520>: adrp x20, 373658
|
||||
0x193755eb8 <+524>: cbz w26, 0x193755ecc ; <+544>
|
||||
0x193755ebc <+528>: add x8, x19, #0x1e
|
||||
0x193755ec0 <+532>: mov w9, #0x2000
|
||||
0x193755ec4 <+536>: ldseth w9, w8, [x8]
|
||||
0x193755ec8 <+540>: b 0x193755ee0 ; <+564>
|
||||
0x193755ecc <+544>: ldr w8, [x20, #0xa64]
|
||||
0x193755ed0 <+548>: cbz w8, 0x193756130 ; <+1156>
|
||||
0x193755ed4 <+552>: mov w1, #0x0
|
||||
0x193755ed8 <+556>: mov x0, x19
|
||||
0x193755edc <+560>: bl 0x193757828 ; objc_class::setInstancesRequireRawIsaRecursively(bool)
|
||||
0x193755ee0 <+564>: mov x17, x24
|
||||
0x193755ee4 <+568>: mov x8, x27
|
||||
0x193755ee8 <+572>: movk x8, #0xb5ab, lsl #48
|
||||
0x193755eec <+576>: mov x16, x8
|
||||
0x193755ef0 <+580>: pacda x17, x16
|
||||
0x193755ef4 <+584>: str x17, [x27]
|
||||
0x193755ef8 <+588>: ldr w8, [x20, #0xa64]
|
||||
0x193755efc <+592>: mov x16, x19
|
||||
0x193755f00 <+596>: movk x16, #0x6ae1, lsl #48
|
||||
0x193755f04 <+600>: cbnz w8, 0x193755f2c ; <+640>
|
||||
0x193755f08 <+604>: ldrh w8, [x25, #0x1e]
|
||||
0x193755f0c <+608>: tbnz w8, #0xd, 0x193755f2c ; <+640>
|
||||
0x193755f10 <+612>: mov x17, x25
|
||||
0x193755f14 <+616>: pacda x17, x16
|
||||
0x193755f18 <+620>: and x8, x17, #0x7ffffffffffff8
|
||||
0x193755f1c <+624>: mov x9, #0x1
|
||||
0x193755f20 <+628>: movk x9, #0x100, lsl #48
|
||||
0x193755f24 <+632>: orr x8, x8, x9
|
||||
0x193755f28 <+636>: b 0x193755f38 ; <+652>
|
||||
0x193755f2c <+640>: mov x17, x25
|
||||
0x193755f30 <+644>: pacda x17, x16
|
||||
0x193755f34 <+648>: and x8, x17, #0x7ffffffffffff8
|
||||
0x193755f38 <+652>: str x8, [x19]
|
||||
0x193755f3c <+656>: cbz x24, 0x193756280 ; <+1492>
|
||||
0x193755f40 <+660>: cbnz w26, 0x193756280 ; <+1492>
|
||||
0x193755f44 <+664>: ldr x16, [x21]
|
||||
0x193755f48 <+668>: mov x17, x21
|
||||
0x193755f4c <+672>: movk x17, #0xc93a, lsl #48
|
||||
0x193755f50 <+676>: autdb x16, x17
|
||||
0x193755f54 <+680>: mov x17, x16
|
||||
0x193755f58 <+684>: xpacd x17
|
||||
0x193755f5c <+688>: cmp x16, x17
|
||||
0x193755f60 <+692>: b.eq 0x193755f68 ; <+700>
|
||||
0x193755f64 <+696>: brk #0xc473
|
||||
0x193755f68 <+700>: and x25, x16, #0x7ffffffffff8
|
||||
0x193755f6c <+704>: mov x8, x24
|
||||
0x193755f70 <+708>: ldr x16, [x8, #0x20]!
|
||||
0x193755f74 <+712>: mov x17, x8
|
||||
0x193755f78 <+716>: movk x17, #0xc93a, lsl #48
|
||||
0x193755f7c <+720>: autdb x16, x17
|
||||
0x193755f80 <+724>: mov x17, x16
|
||||
0x193755f84 <+728>: xpacd x17
|
||||
0x193755f88 <+732>: cmp x16, x17
|
||||
0x193755f8c <+736>: b.eq 0x193755f94 ; <+744>
|
||||
0x193755f90 <+740>: brk #0xc473
|
||||
0x193755f94 <+744>: and x0, x16, #0x7ffffffffff8
|
||||
0x193755f98 <+748>: bl 0x193772238 ; class_rw_t::ro() const
|
||||
0x193755f9c <+752>: mov x26, x0
|
||||
0x193755fa0 <+756>: adrp x8, 363071
|
||||
0x193755fa4 <+760>: ldr w8, [x8, #0x3c4]
|
||||
0x193755fa8 <+764>: cbz w8, 0x193755fc8 ; <+796>
|
||||
0x193755fac <+768>: mov x0, x19
|
||||
0x193755fb0 <+772>: bl 0x193775830 ; objc_class::mangledName()
|
||||
0x193755fb4 <+776>: mov x27, x0
|
||||
0x193755fb8 <+780>: adrp x1, 60
|
||||
0x193755fbc <+784>: add x1, x1, #0x660 ; "NSCF"
|
||||
0x193755fc0 <+788>: bl 0x193786fb0 ; symbol stub for: strstr
|
||||
0x193755fc4 <+792>: cbz x0, 0x1937561b0 ; <+1284>
|
||||
0x193755fc8 <+796>: ldr w8, [x23, #0x4]
|
||||
0x193755fcc <+800>: ldr w9, [x26, #0x8]
|
||||
0x193755fd0 <+804>: cmp w8, w9
|
||||
0x193755fd4 <+808>: b.hs 0x193756280 ; <+1492>
|
||||
0x193755fd8 <+812>: adrp x8, 363071
|
||||
0x193755fdc <+816>: ldr w8, [x8, #0x380]
|
||||
0x193755fe0 <+820>: cbnz w8, 0x193756688 ; <+2524>
|
||||
0x193755fe4 <+824>: mov x0, x25
|
||||
0x193755fe8 <+828>: bl 0x193770194 ; make_ro_writeable(class_rw_t*)
|
||||
0x193755fec <+832>: mov x27, x0
|
||||
0x193755ff0 <+836>: mov x0, x25
|
||||
0x193755ff4 <+840>: bl 0x193772238 ; class_rw_t::ro() const
|
||||
0x193755ff8 <+844>: mov x23, x0
|
||||
0x193755ffc <+848>: ldr w9, [x26, #0x8]
|
||||
0x193756000 <+852>: ldr w8, [x27, #0x4]
|
||||
0x193756004 <+856>: sub w26, w9, w8
|
||||
0x193756008 <+860>: ldr x25, [x27, #0x30]
|
||||
0x19375600c <+864>: cbz x25, 0x193756264 ; <+1464>
|
||||
0x193756010 <+868>: ldp w20, w9, [x25]
|
||||
0x193756014 <+872>: mul w22, w9, w20
|
||||
0x193756018 <+876>: cbz w22, 0x193756264 ; <+1464>
|
||||
0x19375601c <+880>: mov x8, #0x0
|
||||
0x193756020 <+884>: mov w9, #0x1
|
||||
0x193756024 <+888>: mov w11, #0x8
|
||||
0x193756028 <+892>: mov w10, #0x1
|
||||
0x19375602c <+896>: add x12, x25, x8
|
||||
0x193756030 <+900>: ldr x13, [x12, #0x8]
|
||||
0x193756034 <+904>: cbz x13, 0x193756050 ; <+932>
|
||||
0x193756038 <+908>: ldr w12, [x12, #0x20]
|
||||
0x19375603c <+912>: lsl w13, w9, w12
|
||||
0x193756040 <+916>: cmn w12, #0x1
|
||||
0x193756044 <+920>: csel w12, w11, w13, eq
|
||||
0x193756048 <+924>: cmp w12, w10
|
||||
0x19375604c <+928>: csel w10, w12, w10, hi
|
||||
0x193756050 <+932>: add x8, x8, x20
|
||||
0x193756054 <+936>: cmp x22, x8
|
||||
0x193756058 <+940>: b.ne 0x19375602c ; <+896>
|
||||
0x19375605c <+944>: str x28, [sp, #0x38]
|
||||
0x193756060 <+948>: mov x28, #0x0
|
||||
0x193756064 <+952>: add w8, w26, w10
|
||||
0x193756068 <+956>: sub w8, w8, #0x1
|
||||
0x19375606c <+960>: neg w9, w10
|
||||
0x193756070 <+964>: and w26, w8, w9
|
||||
0x193756074 <+968>: adrp x11, 363070
|
||||
0x193756078 <+972>: add x8, x25, x28
|
||||
0x19375607c <+976>: ldr x10, [x8, #0x8]
|
||||
0x193756080 <+980>: cbz x10, 0x193756098 ; <+1004>
|
||||
0x193756084 <+984>: ldr w8, [x10]
|
||||
0x193756088 <+988>: add w9, w8, w26
|
||||
0x19375608c <+992>: str w9, [x10]
|
||||
0x193756090 <+996>: ldr w10, [x11, #0x380]
|
||||
0x193756094 <+1000>: cbnz w10, 0x1937560a8 ; <+1020>
|
||||
0x193756098 <+1004>: add x28, x28, x20
|
||||
0x19375609c <+1008>: cmp x22, x28
|
||||
0x1937560a0 <+1012>: b.ne 0x193756078 ; <+972>
|
||||
0x1937560a4 <+1016>: b 0x19375625c ; <+1456>
|
||||
0x1937560a8 <+1020>: add x10, x25, x28
|
||||
0x1937560ac <+1024>: ldr x11, [x10, #0x10]
|
||||
0x1937560b0 <+1028>: ldp w12, w10, [x10, #0x20]
|
||||
0x1937560b4 <+1032>: mov w13, #0x1
|
||||
0x1937560b8 <+1036>: lsl w13, w13, w12
|
||||
0x1937560bc <+1040>: cmn w12, #0x1
|
||||
0x1937560c0 <+1044>: mov w12, #0x8
|
||||
0x1937560c4 <+1048>: csel w12, w12, w13, eq
|
||||
0x1937560c8 <+1052>: stp x11, x10, [sp, #0x10]
|
||||
0x1937560cc <+1056>: stp x8, x9, [sp]
|
||||
0x1937560d0 <+1060>: str x12, [sp, #0x20]
|
||||
0x1937560d4 <+1064>: adrp x0, 59
|
||||
0x1937560d8 <+1068>: add x0, x0, #0x71e ; "IVARS: offset %u -> %u for %s (size %u, align %u)"
|
||||
0x1937560dc <+1072>: bl 0x19376dd58 ; _objc_inform
|
||||
0x1937560e0 <+1076>: adrp x11, 363070
|
||||
0x1937560e4 <+1080>: b 0x193756098 ; <+1004>
|
||||
0x1937560e8 <+1084>: mov x17, x8
|
||||
0x1937560ec <+1088>: movk x17, #0xc93a, lsl #48
|
||||
0x1937560f0 <+1092>: autdb x16, x17
|
||||
0x1937560f4 <+1096>: mov x17, x16
|
||||
0x1937560f8 <+1100>: xpacd x17
|
||||
0x1937560fc <+1104>: cmp x16, x17
|
||||
0x193756100 <+1108>: b.eq 0x193756108 ; <+1116>
|
||||
0x193756104 <+1112>: brk #0xc473
|
||||
0x193756108 <+1116>: and x20, x16, #0x7ffffffffff8
|
||||
0x19375610c <+1120>: mov x0, x20
|
||||
0x193756110 <+1124>: bl 0x193786c70 ; symbol stub for: malloc_size
|
||||
0x193756114 <+1128>: cmp x0, #0x1f
|
||||
0x193756118 <+1132>: b.hi 0x193756550 ; <+2212>
|
||||
0x19375611c <+1136>: stp x20, x0, [sp, #0x8]
|
||||
0x193756120 <+1140>: str x19, [sp]
|
||||
0x193756124 <+1144>: adrp x0, 59
|
||||
0x193756128 <+1148>: add x0, x0, #0x61e ; "realized class %p has corrupt data pointer: malloc_size(%p) = %zu"
|
||||
0x19375612c <+1152>: bl 0x193785458 ; _objc_fatal(char const*, ...)
|
||||
0x193756130 <+1156>: ldrh w22, [x19, #0x1e]
|
||||
0x193756134 <+1160>: adrp x8, 373657
|
||||
0x193756138 <+1164>: ldrb w8, [x8, #0xa48]
|
||||
0x19375613c <+1168>: tbnz w8, #0x0, 0x193756160 ; <+1204>
|
||||
0x193756140 <+1172>: add x8, x23, #0x18
|
||||
0x193756144 <+1176>: ldapr x0, [x8]
|
||||
0x193756148 <+1180>: cbz x0, 0x193756160 ; <+1204>
|
||||
0x19375614c <+1184>: adrp x1, 59
|
||||
0x193756150 <+1188>: add x1, x1, #0x614 ; "OS_object"
|
||||
0x193756154 <+1192>: bl 0x193786f30 ; symbol stub for: strcmp
|
||||
0x193756158 <+1196>: adrp x9, 373657
|
||||
0x19375615c <+1200>: cbz w0, 0x193756610 ; <+2404>
|
||||
0x193756160 <+1204>: cbz x24, 0x1937561a8 ; <+1276>
|
||||
0x193756164 <+1208>: mov x8, x24
|
||||
0x193756168 <+1212>: ldr x16, [x8, #0x8]!
|
||||
0x19375616c <+1216>: cbz x16, 0x1937561a8 ; <+1276>
|
||||
0x193756170 <+1220>: mov x17, x8
|
||||
0x193756174 <+1224>: movk x17, #0xb5ab, lsl #48
|
||||
0x193756178 <+1228>: autda x16, x17
|
||||
0x19375617c <+1232>: mov x17, x16
|
||||
0x193756180 <+1236>: xpacd x17
|
||||
0x193756184 <+1240>: cmp x16, x17
|
||||
0x193756188 <+1244>: b.eq 0x193756190 ; <+1252>
|
||||
0x19375618c <+1248>: brk #0xc472
|
||||
0x193756190 <+1252>: cbz x16, 0x1937561a8 ; <+1276>
|
||||
0x193756194 <+1256>: ldrh w8, [x24, #0x1e]
|
||||
0x193756198 <+1260>: orr w9, w8, w22
|
||||
0x19375619c <+1264>: tbz w9, #0xd, 0x193755ee0 ; <+564>
|
||||
0x1937561a0 <+1268>: ubfx w1, w8, #13, #1
|
||||
0x1937561a4 <+1272>: b 0x193755ed8 ; <+556>
|
||||
0x1937561a8 <+1276>: tbnz w22, #0xd, 0x193755ed4 ; <+552>
|
||||
0x1937561ac <+1280>: b 0x193755ee0 ; <+564>
|
||||
0x1937561b0 <+1284>: adrp x1, 59
|
||||
0x1937561b4 <+1288>: add x1, x1, #0x665 ; "__CF"
|
||||
0x1937561b8 <+1292>: mov x0, x27
|
||||
0x1937561bc <+1296>: mov w2, #0x4
|
||||
0x1937561c0 <+1300>: bl 0x193786f90 ; symbol stub for: strncmp
|
||||
0x1937561c4 <+1304>: cbz w0, 0x193755fc8 ; <+796>
|
||||
0x1937561c8 <+1308>: adrp x1, 59
|
||||
0x1937561cc <+1312>: add x1, x1, #0x66a ; "NSConstantString"
|
||||
0x1937561d0 <+1316>: mov x0, x27
|
||||
0x1937561d4 <+1320>: bl 0x193786f30 ; symbol stub for: strcmp
|
||||
0x1937561d8 <+1324>: cbz w0, 0x193755fc8 ; <+796>
|
||||
0x1937561dc <+1328>: adrp x1, 59
|
||||
0x1937561e0 <+1332>: add x1, x1, #0x67b ; "NSSimpleCString"
|
||||
0x1937561e4 <+1336>: mov x0, x27
|
||||
0x1937561e8 <+1340>: bl 0x193786f30 ; symbol stub for: strcmp
|
||||
0x1937561ec <+1344>: cbz w0, 0x193755fc8 ; <+796>
|
||||
0x1937561f0 <+1348>: ldr w20, [x23, #0x4]
|
||||
0x1937561f4 <+1352>: mov x0, x25
|
||||
0x1937561f8 <+1356>: bl 0x193770194 ; make_ro_writeable(class_rw_t*)
|
||||
0x1937561fc <+1360>: mov x27, x0
|
||||
0x193756200 <+1364>: mov x0, x25
|
||||
0x193756204 <+1368>: bl 0x193772238 ; class_rw_t::ro() const
|
||||
0x193756208 <+1372>: mov x23, x0
|
||||
0x19375620c <+1376>: ldr x8, [x0, #0x30]
|
||||
0x193756210 <+1380>: cbz x8, 0x193756620 ; <+2420>
|
||||
0x193756214 <+1384>: ldp w9, w10, [x8]
|
||||
0x193756218 <+1388>: mul w10, w10, w9
|
||||
0x19375621c <+1392>: mov w11, #0x8
|
||||
0x193756220 <+1396>: cbz w10, 0x193756620 ; <+2420>
|
||||
0x193756224 <+1400>: mov x13, #0x0
|
||||
0x193756228 <+1404>: add x14, x8, #0x20
|
||||
0x19375622c <+1408>: mov w15, #0x1
|
||||
0x193756230 <+1412>: mov w12, #0x8
|
||||
0x193756234 <+1416>: ldr w16, [x14, x13]
|
||||
0x193756238 <+1420>: lsl w17, w15, w16
|
||||
0x19375623c <+1424>: cmn w16, #0x1
|
||||
0x193756240 <+1428>: csel w16, w11, w17, eq
|
||||
0x193756244 <+1432>: cmp w16, w12
|
||||
0x193756248 <+1436>: csel w12, w16, w12, hi
|
||||
0x19375624c <+1440>: add x13, x13, x9
|
||||
0x193756250 <+1444>: cmp x10, x13
|
||||
0x193756254 <+1448>: b.ne 0x193756234 ; <+1416>
|
||||
0x193756258 <+1452>: b 0x193756624 ; <+2424>
|
||||
0x19375625c <+1456>: ldr w8, [x27, #0x4]
|
||||
0x193756260 <+1460>: ldr x28, [sp, #0x38]
|
||||
0x193756264 <+1464>: add w8, w26, w8
|
||||
0x193756268 <+1468>: ldr w9, [x27, #0x8]
|
||||
0x19375626c <+1472>: add w9, w9, w26
|
||||
0x193756270 <+1476>: stp w8, w9, [x27, #0x4]
|
||||
0x193756274 <+1480>: add x8, x23, #0x18
|
||||
0x193756278 <+1484>: ldapr xzr, [x8]
|
||||
0x19375627c <+1488>: bl 0x1937650d8 ; gdb_objc_class_changed
|
||||
0x193756280 <+1492>: ldr w1, [x23, #0x8]
|
||||
0x193756284 <+1496>: mov x0, x19
|
||||
0x193756288 <+1500>: bl 0x193775884 ; objc_class::setInstanceSize(unsigned int)
|
||||
0x19375628c <+1504>: ldr w8, [x23]
|
||||
0x193756290 <+1508>: tbz w8, #0x2, 0x1937562b4 ; <+1544>
|
||||
0x193756294 <+1512>: add x9, x19, #0x1e
|
||||
0x193756298 <+1516>: mov w8, #0x1
|
||||
0x19375629c <+1520>: ldseth w8, w8, [x9]
|
||||
0x1937562a0 <+1524>: ldr w8, [x23]
|
||||
0x1937562a4 <+1528>: tbnz w8, #0x8, 0x1937562b4 ; <+1544>
|
||||
0x1937562a8 <+1532>: mov w8, #0x2
|
||||
0x1937562ac <+1536>: ldseth w8, w8, [x9]
|
||||
0x1937562b0 <+1540>: ldr w8, [x23]
|
||||
0x1937562b4 <+1544>: tbnz w8, #0xa, 0x193756304 ; <+1624>
|
||||
0x1937562b8 <+1548>: cbz x24, 0x193756358 ; <+1708>
|
||||
0x1937562bc <+1552>: mov x8, x24
|
||||
0x1937562c0 <+1556>: ldr x16, [x8, #0x20]!
|
||||
0x1937562c4 <+1560>: mov x17, x8
|
||||
0x1937562c8 <+1564>: movk x17, #0xc93a, lsl #48
|
||||
0x1937562cc <+1568>: autdb x16, x17
|
||||
0x1937562d0 <+1572>: mov x17, x16
|
||||
0x1937562d4 <+1576>: xpacd x17
|
||||
0x1937562d8 <+1580>: cmp x16, x17
|
||||
0x1937562dc <+1584>: b.eq 0x1937562e4 ; <+1592>
|
||||
0x1937562e0 <+1588>: brk #0xc473
|
||||
0x1937562e4 <+1592>: and x8, x16, #0x7ffffffffff8
|
||||
0x1937562e8 <+1596>: ldrb w8, [x8, #0x2]
|
||||
0x1937562ec <+1600>: tbz w8, #0x4, 0x193756318 ; <+1644>
|
||||
0x1937562f0 <+1604>: ldr x9, [sp, #0x40]
|
||||
0x1937562f4 <+1608>: ldr w8, [x9]
|
||||
0x1937562f8 <+1612>: orr w8, w8, #0x100000
|
||||
0x1937562fc <+1616>: str w8, [x9]
|
||||
0x193756300 <+1620>: b 0x193756318 ; <+1644>
|
||||
0x193756304 <+1624>: ldr x9, [sp, #0x40]
|
||||
0x193756308 <+1628>: ldr w8, [x9]
|
||||
0x19375630c <+1632>: orr w8, w8, #0x100000
|
||||
0x193756310 <+1636>: str w8, [x9]
|
||||
0x193756314 <+1640>: cbz x24, 0x193756358 ; <+1708>
|
||||
0x193756318 <+1644>: mov x0, x24
|
||||
0x19375631c <+1648>: mov x1, x19
|
||||
0x193756320 <+1652>: bl 0x1937566e8 ; addSubclass(objc_class*, objc_class*)
|
||||
0x193756324 <+1656>: ldr x16, [x19, #0x20]
|
||||
0x193756328 <+1660>: mov x20, x21
|
||||
0x19375632c <+1664>: movk x20, #0xc93a, lsl #48
|
||||
0x193756330 <+1668>: mov x17, x21
|
||||
0x193756334 <+1672>: movk x17, #0xc93a, lsl #48
|
||||
0x193756338 <+1676>: autdb x16, x17
|
||||
0x19375633c <+1680>: mov x17, x16
|
||||
0x193756340 <+1684>: xpacd x17
|
||||
0x193756344 <+1688>: cmp x16, x17
|
||||
0x193756348 <+1692>: b.eq 0x193756350 ; <+1700>
|
||||
0x19375634c <+1696>: brk #0xc473
|
||||
0x193756350 <+1700>: and x22, x16, #0x7ffffffffff8
|
||||
0x193756354 <+1704>: b 0x1937563a8 ; <+1788>
|
||||
0x193756358 <+1708>: adrp x8, 373657
|
||||
0x19375635c <+1712>: ldr x9, [x8, #0x868]
|
||||
0x193756360 <+1716>: add x9, x9, #0x1
|
||||
0x193756364 <+1720>: str x9, [x8, #0x868]
|
||||
0x193756368 <+1724>: adrp x8, 373657
|
||||
0x19375636c <+1728>: ldr x9, [x8, #0xa50]
|
||||
0x193756370 <+1732>: ldr x16, [x19, #0x20]
|
||||
0x193756374 <+1736>: mov x20, x21
|
||||
0x193756378 <+1740>: movk x20, #0xc93a, lsl #48
|
||||
0x19375637c <+1744>: mov x17, x21
|
||||
0x193756380 <+1748>: movk x17, #0xc93a, lsl #48
|
||||
0x193756384 <+1752>: autdb x16, x17
|
||||
0x193756388 <+1756>: mov x17, x16
|
||||
0x19375638c <+1760>: xpacd x17
|
||||
0x193756390 <+1764>: cmp x16, x17
|
||||
0x193756394 <+1768>: b.eq 0x19375639c ; <+1776>
|
||||
0x193756398 <+1772>: brk #0xc473
|
||||
0x19375639c <+1776>: and x22, x16, #0x7ffffffffff8
|
||||
0x1937563a0 <+1780>: str x9, [x22, #0x18]
|
||||
0x1937563a4 <+1784>: str x19, [x8, #0xa50]
|
||||
0x1937563a8 <+1788>: ldrh w23, [x19, #0x1e]
|
||||
0x1937563ac <+1792>: mov x0, x22
|
||||
0x1937563b0 <+1796>: bl 0x193772238 ; class_rw_t::ro() const
|
||||
0x1937563b4 <+1800>: mov x21, x0
|
||||
0x1937563b8 <+1804>: ldr x8, [x22, #0x8]
|
||||
0x1937563bc <+1808>: tbz w8, #0x0, 0x1937563f4 ; <+1864>
|
||||
0x1937563c0 <+1812>: ands x16, x8, #0xfffffffffffffffe
|
||||
0x1937563c4 <+1816>: b.eq 0x1937563f4 ; <+1864>
|
||||
0x1937563c8 <+1820>: add x8, x22, #0x8
|
||||
0x1937563cc <+1824>: mov x17, x8
|
||||
0x1937563d0 <+1828>: movk x17, #0xe4f, lsl #48
|
||||
0x1937563d4 <+1832>: autdb x16, x17
|
||||
0x1937563d8 <+1836>: mov x17, x16
|
||||
0x1937563dc <+1840>: xpacd x17
|
||||
0x1937563e0 <+1844>: cmp x16, x17
|
||||
0x1937563e4 <+1848>: b.eq 0x1937563ec ; <+1856>
|
||||
0x1937563e8 <+1852>: brk #0xc473
|
||||
0x1937563ec <+1856>: mov x22, x16
|
||||
0x1937563f0 <+1860>: b 0x1937563f8 ; <+1868>
|
||||
0x1937563f4 <+1864>: mov x22, #0x0
|
||||
0x1937563f8 <+1868>: adrp x8, 363073
|
||||
0x1937563fc <+1872>: ldr w8, [x8, #0x4f0]
|
||||
0x193756400 <+1876>: cbnz w8, 0x1937565dc ; <+2352>
|
||||
0x193756404 <+1880>: mov x8, x21
|
||||
0x193756408 <+1884>: ldr x16, [x8, #0x20]!
|
||||
0x19375640c <+1888>: cbz x16, 0x19375648c ; <+2016>
|
||||
0x193756410 <+1892>: mov x17, x8
|
||||
0x193756414 <+1896>: movk x17, #0xc310, lsl #48
|
||||
0x193756418 <+1900>: autda x16, x17
|
||||
0x19375641c <+1904>: mov x17, x16
|
||||
0x193756420 <+1908>: xpacd x17
|
||||
0x193756424 <+1912>: cmp x16, x17
|
||||
0x193756428 <+1916>: b.eq 0x193756430 ; <+1924>
|
||||
0x19375642c <+1920>: brk #0xc472
|
||||
0x193756430 <+1924>: str x16, [sp, #0x58]
|
||||
0x193756434 <+1928>: cbz x16, 0x193756490 ; <+2020>
|
||||
0x193756438 <+1932>: ldr x16, [x19, #0x20]
|
||||
0x19375643c <+1936>: autdb x16, x20
|
||||
0x193756440 <+1940>: mov x17, x16
|
||||
0x193756444 <+1944>: xpacd x17
|
||||
0x193756448 <+1948>: cmp x16, x17
|
||||
0x19375644c <+1952>: b.eq 0x193756454 ; <+1960>
|
||||
0x193756450 <+1956>: brk #0xc473
|
||||
0x193756454 <+1960>: and x0, x16, #0x7ffffffffff8
|
||||
0x193756458 <+1964>: bl 0x193772238 ; class_rw_t::ro() const
|
||||
0x19375645c <+1968>: ldr w8, [x0]
|
||||
0x193756460 <+1972>: ubfx w3, w8, #29, #1
|
||||
0x193756464 <+1976>: add x1, sp, #0x58
|
||||
0x193756468 <+1980>: mov x0, x19
|
||||
0x19375646c <+1984>: mov w2, #0x1
|
||||
0x193756470 <+1988>: bl 0x193775914 ; prepareMethodLists(objc_class*, method_list_t**, int, bool, bool, char const*)
|
||||
0x193756474 <+1992>: cbz x22, 0x1937564dc ; <+2096>
|
||||
0x193756478 <+1996>: add x0, x22, #0x8
|
||||
0x19375647c <+2000>: add x1, sp, #0x58
|
||||
0x193756480 <+2004>: mov w2, #0x1
|
||||
0x193756484 <+2008>: bl 0x1937749ac ; list_array_tt<method_t, method_list_t, method_list_t_authed_ptr>::attachLists(method_list_t* const*, unsigned int)
|
||||
0x193756488 <+2012>: b 0x193756490 ; <+2020>
|
||||
0x19375648c <+2016>: str xzr, [sp, #0x58]
|
||||
0x193756490 <+2020>: ldr x8, [x21, #0x40]
|
||||
0x193756494 <+2024>: str x8, [sp, #0x50]
|
||||
0x193756498 <+2028>: cmp x22, #0x0
|
||||
0x19375649c <+2032>: cset w20, ne
|
||||
0x1937564a0 <+2036>: cbz x22, 0x1937564bc ; <+2064>
|
||||
0x1937564a4 <+2040>: cbz x8, 0x1937564bc ; <+2064>
|
||||
0x1937564a8 <+2044>: add x0, x22, #0x10
|
||||
0x1937564ac <+2048>: mov w20, #0x1
|
||||
0x1937564b0 <+2052>: add x1, sp, #0x50
|
||||
0x1937564b4 <+2056>: mov w2, #0x1
|
||||
0x1937564b8 <+2060>: bl 0x193774c28 ; list_array_tt<property_t, property_list_t, RawPtr>::attachLists(property_list_t* const*, unsigned int)
|
||||
0x1937564bc <+2064>: ldr x8, [x21, #0x28]
|
||||
0x1937564c0 <+2068>: str x8, [sp, #0x48]
|
||||
0x1937564c4 <+2072>: cbz w20, 0x1937564dc ; <+2096>
|
||||
0x1937564c8 <+2076>: cbz x8, 0x1937564dc ; <+2096>
|
||||
0x1937564cc <+2080>: add x0, x22, #0x18
|
||||
0x1937564d0 <+2084>: add x1, sp, #0x48
|
||||
0x1937564d4 <+2088>: mov w2, #0x1
|
||||
0x1937564d8 <+2092>: bl 0x193774d78 ; list_array_tt<unsigned long, protocol_list_t, RawPtr>::attachLists(protocol_list_t* const*, unsigned int)
|
||||
0x1937564dc <+2096>: ldr x8, [x19]
|
||||
0x1937564e0 <+2100>: and x8, x8, #0x7ffffffffff8
|
||||
0x1937564e4 <+2104>: cmp x8, x19
|
||||
0x1937564e8 <+2108>: b.ne 0x193756518 ; <+2156>
|
||||
0x1937564ec <+2112>: adrp x8, 326768
|
||||
0x1937564f0 <+2116>: add x1, x8, #0x2f
|
||||
0x1937564f4 <+2120>: adrp x3, 61
|
||||
0x1937564f8 <+2124>: add x3, x3, #0xbf3 ; ""
|
||||
0x1937564fc <+2128>: adrp x16, 42
|
||||
0x193756500 <+2132>: add x16, x16, #0xb74 ; objc_noop_imp
|
||||
0x193756504 <+2136>: paciza x16
|
||||
0x193756508 <+2140>: mov x2, x16
|
||||
0x19375650c <+2144>: mov x0, x19
|
||||
0x193756510 <+2148>: mov w4, #0x0
|
||||
0x193756514 <+2152>: bl 0x1937568a8 ; addMethod(objc_class*, objc_selector*, void (*)(), char const*, bool)
|
||||
0x193756518 <+2156>: cbz x28, 0x193756538 ; <+2188>
|
||||
0x19375651c <+2160>: tst w23, #0x4
|
||||
0x193756520 <+2164>: mov w8, #0x2
|
||||
0x193756524 <+2168>: mov w9, #0x4
|
||||
0x193756528 <+2172>: csel w2, w9, w8, eq
|
||||
0x19375652c <+2176>: mov x0, x19
|
||||
0x193756530 <+2180>: mov x1, x28
|
||||
0x193756534 <+2184>: bl 0x19375714c ; objc::UnattachedCategories::attachToClass(objc_class*, objc_class*, int)
|
||||
0x193756538 <+2188>: tst w23, #0x4
|
||||
0x19375653c <+2192>: mov w8, #0x1
|
||||
0x193756540 <+2196>: cinc w2, w8, ne
|
||||
0x193756544 <+2200>: mov x0, x19
|
||||
0x193756548 <+2204>: mov x1, x19
|
||||
0x19375654c <+2208>: bl 0x19375714c ; objc::UnattachedCategories::attachToClass(objc_class*, objc_class*, int)
|
||||
0x193756550 <+2212>: mov x0, x19
|
||||
0x193756554 <+2216>: ldp x29, x30, [sp, #0xb0]
|
||||
0x193756558 <+2220>: ldp x20, x19, [sp, #0xa0]
|
||||
0x19375655c <+2224>: ldp x22, x21, [sp, #0x90]
|
||||
0x193756560 <+2228>: ldp x24, x23, [sp, #0x80]
|
||||
0x193756564 <+2232>: ldp x26, x25, [sp, #0x70]
|
||||
0x193756568 <+2236>: ldp x28, x27, [sp, #0x60]
|
||||
0x19375656c <+2240>: add sp, sp, #0xc0
|
||||
-> 0x193756570 <+2244>: retab
|
||||
0x193756574 <+2248>: mov x0, x19
|
||||
0x193756578 <+2252>: bl 0x193775470 ; objc_class::nameForLogging()
|
||||
0x19375657c <+2256>: adrp x8, 61
|
||||
0x193756580 <+2260>: add x8, x8, #0xbf3 ; ""
|
||||
0x193756584 <+2264>: adrp x9, 59
|
||||
0x193756588 <+2268>: add x9, x9, #0x111 ; " (meta)"
|
||||
0x19375658c <+2272>: cmp w26, #0x0
|
||||
0x193756590 <+2276>: csel x9, x9, x8, ne
|
||||
0x193756594 <+2280>: ldr x10, [x19, #0x20]
|
||||
0x193756598 <+2284>: adrp x11, 59
|
||||
0x19375659c <+2288>: add x11, x11, #0x5f9 ; "(swift)"
|
||||
0x1937565a0 <+2292>: tst x10, #0x2
|
||||
0x1937565a4 <+2296>: csel x11, x8, x11, eq
|
||||
0x1937565a8 <+2300>: adrp x12, 59
|
||||
0x1937565ac <+2304>: add x12, x12, #0x601 ; "(pre-stable swift)"
|
||||
0x1937565b0 <+2308>: tst x10, #0x1
|
||||
0x1937565b4 <+2312>: csel x8, x8, x12, eq
|
||||
0x1937565b8 <+2316>: stp x11, x8, [sp, #0x28]
|
||||
0x1937565bc <+2320>: stp x23, xzr, [sp, #0x18]
|
||||
0x1937565c0 <+2324>: stp x9, x19, [sp, #0x8]
|
||||
0x1937565c4 <+2328>: adrp x8, 59
|
||||
0x1937565c8 <+2332>: add x8, x8, #0x5cc ; "CLASS: realizing class '%s'%s %p %p #%u %s%s"
|
||||
0x1937565cc <+2336>: str x0, [sp]
|
||||
0x1937565d0 <+2340>: mov x0, x8
|
||||
0x1937565d4 <+2344>: bl 0x19376dd58 ; _objc_inform
|
||||
0x1937565d8 <+2348>: b 0x193755e50 ; <+420>
|
||||
0x1937565dc <+2352>: mov x0, x19
|
||||
0x1937565e0 <+2356>: bl 0x193775470 ; objc_class::nameForLogging()
|
||||
0x1937565e4 <+2360>: adrp x8, 59
|
||||
0x1937565e8 <+2364>: add x8, x8, #0x774 ; "(meta)"
|
||||
0x1937565ec <+2368>: adrp x9, 61
|
||||
0x1937565f0 <+2372>: add x9, x9, #0xbf3 ; ""
|
||||
0x1937565f4 <+2376>: tst w23, #0x4
|
||||
0x1937565f8 <+2380>: csel x8, x9, x8, eq
|
||||
0x1937565fc <+2384>: stp x0, x8, [sp]
|
||||
0x193756600 <+2388>: adrp x0, 59
|
||||
0x193756604 <+2392>: add x0, x0, #0x753 ; "CLASS: methodizing class '%s' %s"
|
||||
0x193756608 <+2396>: bl 0x19376dd58 ; _objc_inform
|
||||
0x19375660c <+2400>: b 0x193756404 ; <+1880>
|
||||
0x193756610 <+2404>: mov w1, #0x0
|
||||
0x193756614 <+2408>: mov w8, #0x1
|
||||
0x193756618 <+2412>: strb w8, [x9, #0xa48]
|
||||
0x19375661c <+2416>: b 0x193755ed8 ; <+556>
|
||||
0x193756620 <+2420>: mov w12, #0x8
|
||||
0x193756624 <+2424>: ldr w9, [x23, #0x4]
|
||||
0x193756628 <+2428>: udiv w10, w9, w12
|
||||
0x19375662c <+2432>: mul w22, w10, w12
|
||||
0x193756630 <+2436>: sub w9, w9, w22
|
||||
0x193756634 <+2440>: ldr w10, [x27, #0x8]
|
||||
0x193756638 <+2444>: sub w10, w10, w22
|
||||
0x19375663c <+2448>: stp w9, w10, [x27, #0x4]
|
||||
0x193756640 <+2452>: adrp x9, 363070
|
||||
0x193756644 <+2456>: ldr w9, [x9, #0x380]
|
||||
0x193756648 <+2460>: cbnz w9, 0x1937566b0 ; <+2564>
|
||||
0x19375664c <+2464>: cbz x8, 0x193755fc8 ; <+796>
|
||||
0x193756650 <+2468>: ldp w9, w10, [x8]
|
||||
0x193756654 <+2472>: mul w10, w10, w9
|
||||
0x193756658 <+2476>: cbz w10, 0x193755fc8 ; <+796>
|
||||
0x19375665c <+2480>: mov x11, #0x0
|
||||
0x193756660 <+2484>: add x8, x8, #0x8
|
||||
0x193756664 <+2488>: ldr x12, [x8, x11]
|
||||
0x193756668 <+2492>: cbz x12, 0x193756678 ; <+2508>
|
||||
0x19375666c <+2496>: ldr w13, [x12]
|
||||
0x193756670 <+2500>: sub w13, w13, w22
|
||||
0x193756674 <+2504>: str w13, [x12]
|
||||
0x193756678 <+2508>: add x11, x11, x9
|
||||
0x19375667c <+2512>: cmp x10, x11
|
||||
0x193756680 <+2516>: b.ne 0x193756664 ; <+2488>
|
||||
0x193756684 <+2520>: b 0x193755fc8 ; <+796>
|
||||
0x193756688 <+2524>: mov x0, x19
|
||||
0x19375668c <+2528>: bl 0x193775470 ; objc_class::nameForLogging()
|
||||
0x193756690 <+2532>: ldr w8, [x23, #0x4]
|
||||
0x193756694 <+2536>: ldr w9, [x26, #0x8]
|
||||
0x193756698 <+2540>: stp x8, x9, [sp, #0x8]
|
||||
0x19375669c <+2544>: str x0, [sp]
|
||||
0x1937566a0 <+2548>: adrp x0, 59
|
||||
0x1937566a4 <+2552>: add x0, x0, #0x6da ; "IVARS: sliding ivars for class %s (superclass was %u bytes, now %u)"
|
||||
0x1937566a8 <+2556>: bl 0x19376dd58 ; _objc_inform
|
||||
0x1937566ac <+2560>: b 0x193755fe4 ; <+824>
|
||||
0x1937566b0 <+2564>: mov x0, x19
|
||||
0x1937566b4 <+2568>: bl 0x193775470 ; objc_class::nameForLogging()
|
||||
0x1937566b8 <+2572>: ldr w8, [x23, #0x4]
|
||||
0x1937566bc <+2576>: stp x20, x8, [sp, #0x8]
|
||||
0x1937566c0 <+2580>: str x0, [sp]
|
||||
0x1937566c4 <+2584>: adrp x0, 59
|
||||
0x1937566c8 <+2588>: add x0, x0, #0x68b ; "IVARS: DEBUG: forcing ivars for class '%s' to slide (instanceStart %zu -> %zu)"
|
||||
0x1937566cc <+2592>: bl 0x19376dd58 ; _objc_inform
|
||||
0x1937566d0 <+2596>: ldr x8, [x23, #0x30]
|
||||
0x1937566d4 <+2600>: cbnz x8, 0x193756650 ; <+2468>
|
||||
0x1937566d8 <+2604>: b 0x193755fc8 ; <+796>
|
||||
0x1937566dc <+2608>: udf #0x0
|
||||
0x1937566e0 <+2612>: udf #0x0
|
||||
0x1937566e4 <+2616>: udf #0x0
|
||||
|
93
research/custom_loader/schedule_class_load.txt
Normal file
93
research/custom_loader/schedule_class_load.txt
Normal file
@ -0,0 +1,93 @@
|
||||
libobjc.A.dylib`schedule_class_load:
|
||||
libobjc.A.dylib[0x1800687c8] <+0>: pacibsp
|
||||
libobjc.A.dylib[0x1800687cc] <+4>: sub sp, sp, #0x50
|
||||
libobjc.A.dylib[0x1800687d0] <+8>: stp x24, x23, [sp, #0x10]
|
||||
libobjc.A.dylib[0x1800687d4] <+12>: stp x22, x21, [sp, #0x20]
|
||||
libobjc.A.dylib[0x1800687d8] <+16>: stp x20, x19, [sp, #0x30]
|
||||
libobjc.A.dylib[0x1800687dc] <+20>: stp x29, x30, [sp, #0x40]
|
||||
libobjc.A.dylib[0x1800687e0] <+24>: add x29, sp, #0x40
|
||||
libobjc.A.dylib[0x1800687e4] <+28>: cbz x0, 0x1800688fc ; <+308>
|
||||
libobjc.A.dylib[0x1800687e8] <+32>: mov x19, x0
|
||||
libobjc.A.dylib[0x1800687ec] <+36>: mov x21, x0
|
||||
libobjc.A.dylib[0x1800687f0] <+40>: ldr x16, [x21, #0x20]!
|
||||
libobjc.A.dylib[0x1800687f4] <+44>: mov x17, x21
|
||||
libobjc.A.dylib[0x1800687f8] <+48>: movk x17, #0xc93a, lsl #48
|
||||
libobjc.A.dylib[0x1800687fc] <+52>: autdb x16, x17
|
||||
libobjc.A.dylib[0x180068800] <+56>: mov x17, x16
|
||||
libobjc.A.dylib[0x180068804] <+60>: xpacd x17
|
||||
libobjc.A.dylib[0x180068808] <+64>: cmp x16, x17
|
||||
libobjc.A.dylib[0x18006880c] <+68>: b.eq 0x180068814 ; <+76>
|
||||
libobjc.A.dylib[0x180068810] <+72>: brk #0xc473
|
||||
libobjc.A.dylib[0x180068814] <+76>: and x8, x16, #0x7ffffffffff8
|
||||
libobjc.A.dylib[0x180068818] <+80>: ldrb w8, [x8, #0x2]
|
||||
libobjc.A.dylib[0x18006881c] <+84>: tbnz w8, #0x7, 0x1800688fc ; <+308>
|
||||
libobjc.A.dylib[0x180068820] <+88>: mov x8, x19
|
||||
libobjc.A.dylib[0x180068824] <+92>: ldr x16, [x8, #0x8]!
|
||||
libobjc.A.dylib[0x180068828] <+96>: cbz x16, 0x180068854 ; <+140>
|
||||
libobjc.A.dylib[0x18006882c] <+100>: mov x17, x8
|
||||
libobjc.A.dylib[0x180068830] <+104>: movk x17, #0xb5ab, lsl #48
|
||||
libobjc.A.dylib[0x180068834] <+108>: autda x16, x17
|
||||
libobjc.A.dylib[0x180068838] <+112>: mov x17, x16
|
||||
libobjc.A.dylib[0x18006883c] <+116>: xpacd x17
|
||||
libobjc.A.dylib[0x180068840] <+120>: cmp x16, x17
|
||||
libobjc.A.dylib[0x180068844] <+124>: b.eq 0x18006884c ; <+132>
|
||||
libobjc.A.dylib[0x180068848] <+128>: brk #0xc472
|
||||
libobjc.A.dylib[0x18006884c] <+132>: mov x0, x16
|
||||
libobjc.A.dylib[0x180068850] <+136>: b 0x180068858 ; <+144>
|
||||
libobjc.A.dylib[0x180068854] <+140>: mov x0, #0x0
|
||||
libobjc.A.dylib[0x180068858] <+144>: bl 0x1800687c8 ; <+0>
|
||||
libobjc.A.dylib[0x18006885c] <+148>: ldr x0, [x19]
|
||||
libobjc.A.dylib[0x180068860] <+152>: bl 0x18007b0d8 ; objc_class::getLoadMethod()
|
||||
libobjc.A.dylib[0x180068864] <+156>: cbz x0, 0x1800688cc ; <+260>
|
||||
libobjc.A.dylib[0x180068868] <+160>: mov x20, x0
|
||||
libobjc.A.dylib[0x18006886c] <+164>: adrp x8, 363064
|
||||
libobjc.A.dylib[0x180068870] <+168>: ldr w8, [x8, #0x374]
|
||||
libobjc.A.dylib[0x180068874] <+172>: cbnz w8, 0x180068914 ; <+332>
|
||||
libobjc.A.dylib[0x180068878] <+176>: adrp x22, 363064
|
||||
libobjc.A.dylib[0x18006887c] <+180>: ldrsw x23, [x22, #0x220]
|
||||
libobjc.A.dylib[0x180068880] <+184>: adrp x8, 363064
|
||||
libobjc.A.dylib[0x180068884] <+188>: ldr w9, [x8, #0x224]
|
||||
libobjc.A.dylib[0x180068888] <+192>: cmp w23, w9
|
||||
libobjc.A.dylib[0x18006888c] <+196>: b.ne 0x1800688b4 ; <+236>
|
||||
libobjc.A.dylib[0x180068890] <+200>: lsl w9, w23, #1
|
||||
libobjc.A.dylib[0x180068894] <+204>: add w9, w9, #0x10
|
||||
libobjc.A.dylib[0x180068898] <+208>: str w9, [x8, #0x224]
|
||||
libobjc.A.dylib[0x18006889c] <+212>: adrp x24, 363064
|
||||
libobjc.A.dylib[0x1800688a0] <+216>: ldr x0, [x24, #0x228]
|
||||
libobjc.A.dylib[0x1800688a4] <+220>: sbfiz x1, x9, #4, #32
|
||||
libobjc.A.dylib[0x1800688a8] <+224>: bl 0x180092ec0 ; symbol stub for: realloc
|
||||
libobjc.A.dylib[0x1800688ac] <+228>: str x0, [x24, #0x228]
|
||||
libobjc.A.dylib[0x1800688b0] <+232>: b 0x1800688bc ; <+244>
|
||||
libobjc.A.dylib[0x1800688b4] <+236>: adrp x8, 363064
|
||||
libobjc.A.dylib[0x1800688b8] <+240>: ldr x0, [x8, #0x228]
|
||||
libobjc.A.dylib[0x1800688bc] <+244>: add x8, x0, x23, lsl #4
|
||||
libobjc.A.dylib[0x1800688c0] <+248>: stp x19, x20, [x8]
|
||||
libobjc.A.dylib[0x1800688c4] <+252>: add w8, w23, #0x1
|
||||
libobjc.A.dylib[0x1800688c8] <+256>: str w8, [x22, #0x220]
|
||||
libobjc.A.dylib[0x1800688cc] <+260>: ldr x16, [x21]
|
||||
libobjc.A.dylib[0x1800688d0] <+264>: mov x17, x21
|
||||
libobjc.A.dylib[0x1800688d4] <+268>: movk x17, #0xc93a, lsl #48
|
||||
libobjc.A.dylib[0x1800688d8] <+272>: autdb x16, x17
|
||||
libobjc.A.dylib[0x1800688dc] <+276>: mov x17, x16
|
||||
libobjc.A.dylib[0x1800688e0] <+280>: xpacd x17
|
||||
libobjc.A.dylib[0x1800688e4] <+284>: cmp x16, x17
|
||||
libobjc.A.dylib[0x1800688e8] <+288>: b.eq 0x1800688f0 ; <+296>
|
||||
libobjc.A.dylib[0x1800688ec] <+292>: brk #0xc473
|
||||
libobjc.A.dylib[0x1800688f0] <+296>: and x8, x16, #0x7ffffffffff8
|
||||
libobjc.A.dylib[0x1800688f4] <+300>: mov w9, #0x800000
|
||||
libobjc.A.dylib[0x1800688f8] <+304>: ldset w9, w8, [x8]
|
||||
libobjc.A.dylib[0x1800688fc] <+308>: ldp x29, x30, [sp, #0x40]
|
||||
libobjc.A.dylib[0x180068900] <+312>: ldp x20, x19, [sp, #0x30]
|
||||
libobjc.A.dylib[0x180068904] <+316>: ldp x22, x21, [sp, #0x20]
|
||||
libobjc.A.dylib[0x180068908] <+320>: ldp x24, x23, [sp, #0x10]
|
||||
libobjc.A.dylib[0x18006890c] <+324>: add sp, sp, #0x50
|
||||
libobjc.A.dylib[0x180068910] <+328>: retab
|
||||
libobjc.A.dylib[0x180068914] <+332>: mov x0, x19
|
||||
libobjc.A.dylib[0x180068918] <+336>: bl 0x180081470 ; objc_class::nameForLogging()
|
||||
libobjc.A.dylib[0x18006891c] <+340>: str x0, [sp]
|
||||
libobjc.A.dylib[0x180068920] <+344>: adrp x0, 52
|
||||
libobjc.A.dylib[0x180068924] <+348>: add x0, x0, #0x7e6 ; "LOAD: class '%s' scheduled for +load"
|
||||
libobjc.A.dylib[0x180068928] <+352>: bl 0x180079d58 ; _objc_inform
|
||||
libobjc.A.dylib[0x18006892c] <+356>: b 0x180068878 ; <+176>
|
||||
libobjc.A.dylib[0x180068930] <+360>: udf #0x0
|
||||
|
1
research/strings_empty/.gitignore
vendored
Normal file
1
research/strings_empty/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
out/*
|
75
research/strings_empty/build.sh
Executable file
75
research/strings_empty/build.sh
Executable file
@ -0,0 +1,75 @@
|
||||
set -e
|
||||
clear
|
||||
VERSION=${1:-14}
|
||||
OUT=./out
|
||||
LOGIC=1
|
||||
make -C ../../macho-go
|
||||
mkdir -p $OUT
|
||||
|
||||
echo "using mach-o version $VERSION"
|
||||
if [[ $VERSION -ge 14 ]]
|
||||
then
|
||||
echo "Resulting binary uses MODERN symbol resolver"
|
||||
else
|
||||
echo "Resulting binary uses LEGACY symbol resolver"
|
||||
fi
|
||||
|
||||
cat <<'fly'
|
||||
______
|
||||
_\ _~-\___
|
||||
= = ==(____AA____D
|
||||
\_____\___________________,-~~~~~~~`-.._
|
||||
/ o O o o o o O O o o o o o o O o |\_
|
||||
`~-.__ ___..----.. )
|
||||
`---~~\___________/------------`````
|
||||
= ===(_________D
|
||||
fly
|
||||
|
||||
# this is a joke for those who knows
|
||||
# https://www.blackhat.com/presentations/bh-dc-09/Iozzo/BlackHat-DC-09-Iozzo-let-your-mach0-fly-whitepaper.pdf
|
||||
echo "make your Mach-O fly"
|
||||
|
||||
if [[ $LOGIC -eq 0 ]]
|
||||
then
|
||||
|
||||
clang-format -i -style=llvm *.cc
|
||||
|
||||
elif [[ $LOGIC -eq 1 ]]
|
||||
then
|
||||
|
||||
# build test binaries
|
||||
clang -mmacosx-version-min=$VERSION -o $OUT/c_code tests/c_code.c
|
||||
clang -fobjc-arc -ObjC -mmacosx-version-min=$VERSION -o $OUT/objc_code tests/objc_code.m
|
||||
swiftc -o $OUT/swift_code tests/swift_code.swift
|
||||
|
||||
# c program
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/c_code_fixed -b $OUT/c_code.bcell -l $OUT/librestore_c.dylib --remove-strings $OUT/c_code
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/c_code.bcell -o $OUT/restore.h
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/librestore_c.dylib -shared -Wl,-reexport_library restore.cc
|
||||
|
||||
# objc program
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/objc_code_fixed -b $OUT/objc_code.bcell -l $OUT/librestore_objc.dylib --remove-strings $OUT/objc_code
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/c_code.bcell -o $OUT/restore.h
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/librestore_objc.dylib -shared -Wl,-reexport_library restore.cc
|
||||
|
||||
# swift program
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/swift_code_fixed -b $OUT/swift_code.bcell -l $OUT/librestore_swift.dylib --remove-strings $OUT/swift_code
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/c_code.bcell -o $OUT/restore.h
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/librestore_swift.dylib -shared -Wl,-reexport_library restore.cc
|
||||
|
||||
# executable
|
||||
chmod +x $OUT/c_code_fixed
|
||||
chmod +x $OUT/objc_code_fixed
|
||||
chmod +x $OUT/swift_code_fixed
|
||||
|
||||
# resign
|
||||
codesign --force --deep -s - $OUT/c_code_fixed
|
||||
codesign --force --deep -s - $OUT/objc_code_fixed
|
||||
codesign --force --deep -s - $OUT/swift_code_fixed
|
||||
|
||||
# run
|
||||
$OUT/c_code_fixed
|
||||
$OUT/objc_code_fixed
|
||||
$OUT/swift_code_fixed
|
||||
|
||||
fi
|
167
research/strings_empty/restore.cc
Normal file
167
research/strings_empty/restore.cc
Normal file
@ -0,0 +1,167 @@
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach/mach.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
// #include <Foundation/Foundation.h>
|
||||
#include <objc/objc.h>
|
||||
|
||||
#include "out/restore.h"
|
||||
|
||||
int custom_strcmp(const char *p1, const char *p2) {
|
||||
const unsigned char *s1 = (const unsigned char *)p1;
|
||||
const unsigned char *s2 = (const unsigned char *)p2;
|
||||
unsigned char c1, c2;
|
||||
do {
|
||||
c1 = (unsigned char)*s1++;
|
||||
c2 = (unsigned char)*s2++;
|
||||
if (c1 == '\0')
|
||||
return c1 - c2;
|
||||
} while (c1 == c2);
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint32_t magic64 = 0xfeedfacf;
|
||||
const uint32_t magic32 = 0xfeedface;
|
||||
|
||||
struct ProgramVars {
|
||||
void *mh; // mach_header or mach_header64
|
||||
int *NXArgcPtr;
|
||||
const char ***NXArgvPtr;
|
||||
const char ***environPtr;
|
||||
const char **__prognamePtr;
|
||||
};
|
||||
|
||||
void restore_strings(void* main);
|
||||
|
||||
__attribute__((constructor)) static void
|
||||
bruh(int argc, const char *const argv[], const char *const envp[],
|
||||
const char *const apple[], const struct ProgramVars *vars) {
|
||||
void* main = (void *)(vars->mh);
|
||||
restore_strings(main);
|
||||
}
|
||||
|
||||
/// strings in __TEXT,__cstring has been removed and this
|
||||
/// function tries to recover those strings. Using either
|
||||
/// these methods below.
|
||||
///
|
||||
/// 1. Recover __TEXT,__cstring
|
||||
/// 2. Build a new segment with section for strings
|
||||
///
|
||||
/// (1) might seem reasonable at first, but requires __TEXT
|
||||
/// segment to be writable. Although we can make that, but
|
||||
/// we are not sure if the modification is allowed by Apple.
|
||||
///
|
||||
/// (2) actually require a little bit more work, by defining
|
||||
/// a new segment with a section inside. This segment is
|
||||
/// mounted readable/writable. Not only that, all string
|
||||
/// references must also be updated.
|
||||
/// In code, ARMv8, the sequence `adrp` `add` referencing
|
||||
/// string must now be updated with new parameters as the
|
||||
/// address/offset has now been changed.
|
||||
/// In ARMv8, every instruction is 8 bytes, so looping
|
||||
/// through all the code and change the instruction is easy.
|
||||
///
|
||||
/// It can be seen that opting for method (2) is safer,
|
||||
/// as Apple allows for arbitrary segment. This option
|
||||
/// requires that there is enough space left for a new segment.
|
||||
/// Calculated, it should be around 152 bytes.
|
||||
///
|
||||
/// 4 + 4 + 16 + 8*4 + 4*4 + 16 + 16 + 8*2 + 4*8
|
||||
/// ^~~~^ ^~~~~~~~~~~~~^ ^~~~~~~~~~~~~~~~~~^
|
||||
/// 1 2 3
|
||||
///
|
||||
/// 1: load command header
|
||||
/// 2: segment data
|
||||
/// 3: section
|
||||
///
|
||||
/// However, if we can expand the old section, and remove the
|
||||
/// old section entry, then we only need 72 bytes. Because,
|
||||
/// we only move the section entry.
|
||||
///
|
||||
/// Remember to update the command count in macho header (+1).
|
||||
void restore_strings(void* main) {
|
||||
printf("=== rebuilding the strings ===\n");
|
||||
|
||||
void* header = main;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
ptr += 0x20;
|
||||
} else {
|
||||
ptr += 0x20 - 0x4;
|
||||
}
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
|
||||
uint32_t slide = 0;
|
||||
|
||||
char* secrets = 0;
|
||||
uint64_t secrets_size = 0;
|
||||
|
||||
for (int i = 0; i < ncmds; i++) {
|
||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
||||
if (cmd == LC_SEGMENT_64) {
|
||||
char *name = (char *)((uint64_t *)ptr + 1);
|
||||
uint64_t vmaddr = *((uint64_t *)ptr + 3);
|
||||
uint64_t fileoffset = *((uint64_t *)ptr + 5);
|
||||
// this assumes that __TEXT comes before __DATA_CONST
|
||||
if (custom_strcmp(name, "__TEXT") == 0) {
|
||||
slide = (uint64_t)header - vmaddr;
|
||||
|
||||
} else if (custom_strcmp(name, "__BSHIELD") == 0) {
|
||||
printf("found __BSHIELD segment at %p\n", ptr);
|
||||
|
||||
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;
|
||||
if (custom_strncmp(secname, "__secrets", 16) == 0) {
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
printf("secrets offset 0x%lx\n", addr);
|
||||
|
||||
secrets = (char*)(addr + slide);
|
||||
secrets_size = size;
|
||||
}
|
||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr += cmdsize;
|
||||
}
|
||||
|
||||
printf("secrets %p\n", secrets);
|
||||
printf("secrets_size = 0x%lx\n", secrets_size);
|
||||
for (size_t i = 0; i < 0x4000; i++) {
|
||||
secrets[i] = secrets[i] ^ 0x4f;
|
||||
}
|
||||
// secrets[0] = 'F';
|
||||
// secrets[1] = 'R';
|
||||
// secrets[2] = 'E';
|
||||
// secrets[3] = 'E';
|
||||
// secrets[4] = ' ';
|
||||
// secrets[5] = 'S';
|
||||
// secrets[6] = 'P';
|
||||
// secrets[7] = 'A';
|
||||
// secrets[8] = 'C';
|
||||
// secrets[9] = 'E';
|
||||
// secrets[10] = '\n';
|
||||
// secrets[11] = 0;
|
||||
}
|
6
research/strings_empty/tests/c_code.c
Normal file
6
research/strings_empty/tests/c_code.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
8
research/strings_empty/tests/objc_code.m
Normal file
8
research/strings_empty/tests/objc_code.m
Normal file
@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
int main() {
|
||||
@autoreleasepool {
|
||||
NSLog(@"Hello, World!");
|
||||
}
|
||||
return 0;
|
||||
}
|
1
research/strings_empty/tests/swift_code.swift
Normal file
1
research/strings_empty/tests/swift_code.swift
Normal file
@ -0,0 +1 @@
|
||||
print("Hello, World!")
|
Reference in New Issue
Block a user