18 Commits

Author SHA1 Message Date
465aed7ba1 benchmark 2024-05-27 11:57:49 +07:00
998d5844e7 Update .gitignore 2024-05-27 11:55:44 +07:00
78a8ca45d5 naive benchmark 2024-05-03 00:38:50 +07:00
3e99eff22d Update .gitignore 2024-05-03 00:38:11 +07:00
8be97742c9 Merge remote-tracking branch 'origin/objc-hooking' into benchmark 2024-05-02 18:39:11 +07:00
06525b8a5e add method 1 hooking for x86_64; method 3 first commit 2024-03-28 01:59:55 +07:00
57b0ae26a7 fix shellcode x86_64 2024-03-28 01:58:54 +07:00
f795e9b99d add simple objc hooking by modifying the method pointer 2024-02-09 14:01:34 +07:00
792316f4ea add rule for go format 2024-01-11 14:32:45 +07:00
62fa58f039 go fmt 2024-01-11 14:32:45 +07:00
62daeb1c52 Update edit.go: keep LC_SYMTAB 2024-01-10 16:18:21 +07:00
37c2f93383 Update objc.go: fix shellcode offset 2024-01-10 16:17:40 +07:00
901f1ed819 add rule for go format 2024-01-10 15:56:55 +07:00
41144ff0dc go fmt 2024-01-10 15:56:32 +07:00
9a8ab15d88 clean code 2024-01-10 14:50:53 +07:00
a8ffae5202 move everything objc to objc.go 2024-01-10 14:50:19 +07:00
9ec2a301b4 add objc critical function assembly 2024-01-10 14:33:06 +07:00
a68bbf2b8f erase objc method names 2024-01-10 14:32:46 +07:00
21 changed files with 5687 additions and 304 deletions

View File

@ -9,3 +9,6 @@ build-linux:
module:
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
format:
go fmt ./...

View File

@ -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
}

View File

@ -299,6 +299,21 @@ func bcell2header(bfile string, header string) {
}
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, "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, "__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()

View File

@ -46,6 +46,8 @@ func (printer *InfoPrinter) Print() {
)
}
mc.CollectObjectiveCClasses()
fmt.Println("======")
}
}

View File

@ -42,6 +42,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
}
@ -150,7 +154,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)))

View File

@ -280,7 +280,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 +293,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 {
@ -359,254 +360,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()
@ -730,10 +483,10 @@ func (mc *MachoContext) removeSymtabCommand() {
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
mc.file.WriteAt(make([]byte, size), start)
symtab_fix.symoff = 0
symtab_fix.nsyms = 0
symtab_fix.stroff = 0
symtab_fix.strsize = 0
// symtab_fix.symoff = 0
// symtab_fix.nsyms = 0
// symtab_fix.stroff = 0
// symtab_fix.strsize = 0
mc.file.Seek(ptr, io.SeekStart)
mc.file.Write(symtab_fix.Serialize(mc))
break

View 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)
}

View File

@ -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;
}

View File

@ -1 +1,3 @@
out/
coreutils-9.1/
*.tar.xz

View File

@ -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;
}

View File

@ -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/*if meta class*/) { 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);

View File

@ -0,0 +1,220 @@
# import unittest
import subprocess, resource
import lief
import os
# import time
import re
PATH = "./coreutils-9.1/src"
class Line:
file = None
@classmethod
def init(cls):
cls.file = open("out.csv", "w")
cls.file.write("Name,File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s),File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s)\n")
def write(self):
out = f"{self.name},{self.norm_size},{self.norm_symbols},{self.norm_imports},0,{self.norm_exe:.3f},{self.obf_size},{self.obf_symbols},{self.obf_imports},{self.restore:.3f},{self.obf_exe:.3f}\n"
self.file.write(out)
def __init__(self, name: str) -> None:
self.name = name
self.norm_path = f"{PATH}/{name}"
self.obf_path = f"{PATH}/{name}-dir/out/{name}-fixed"
def numOfSymbols(sym: list) -> int:
count = 0
for i in sym:
if i.type != 0:
count += 1
return count
def numOfImports(imp: list) -> int:
count = 0
for i in imp:
if i.name != "":
count += 1
return count
def setup(binpath: str, libpath: str = None):
if os.path.isdir("/tmp/test"):
os.system("rm -rf /tmp/test")
os.mkdir("/tmp/test")
# if libpath:
# os.system(f"cp {binpath} {libpath} ./test_file.txt /tmp/test")
# else:
# os.system(f"cp {binpath} ./test_file.txt /tmp/test")
os.system("cp ./test_file.txt /tmp/test")
# class Benchmark(unittest.TestCase):
def info(l: Line):
l.norm_size = int(os.path.getsize(l.norm_path) / 1024)
l.obf_size = int(os.path.getsize(l.obf_path) / 1024)
norm = lief.parse(l.norm_path)
obf = lief.parse(l.obf_path)
l.norm_symbols = numOfSymbols(norm.symbols)
l.obf_symbols = numOfSymbols(obf.symbols)
l.norm_imports = numOfImports(norm.imported_functions)
l.obf_imports = numOfImports(obf.imported_functions)
def run(l, cmd):
print(f"[+] Running benchmark for {l.name} with command \"{cmd}\"")
cmd = cmd.split(" ")
setup(l.norm_path)
start = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime
p1 = subprocess.run([l.norm_path] + cmd, capture_output=True)
end = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime
l.norm_exe = end - start
setup(l.obf_path)
start = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime
p2 = subprocess.run([l.obf_path] + cmd, capture_output=True)
end = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime
if p2.returncode == -11:
print(f"\033[91m[!] Error in {l.name} (segfault)\033[0m")
Line.file.write(f"{l.name},segfault\n")
return
# print(p2.stdout)
match = re.search(b"restoration library time: ([0-9.]+)", p2.stdout)
l.restore = float(match.group(1))
l.obf_exe = end - start
if p2.returncode != p1.returncode:
print(f"\033[91m[!] Error in {l.name} (diff exit code)\033[0m")
Line.file.write(f"{l.name},exit code diff\n")
return
l.write()
# if p1.stdout in p2.stdout:
# l.write()
# else:
# print(f"\033[91m[!] Error in {l.name} (stdout diff)\033[0m")
# print(p1.stdout)
# print("-"*20)
# print(p2.stdout)
# Line.file.write(f"{l.name},stdout diff\n")
def test_basic(name, cmd):
l = Line(name)
info(l)
run(l, cmd)
test_data = [
("md5sum", "/tmp/test/test_file.txt"),
("split", "/tmp/test/test_file.txt /tmp/test/out"),
("cat", "/tmp/test/test_file.txt"),
("mkfifo", "/tmp/test/a"),
("shuf", "--random-source=/tmp/test/test_file.txt /tmp/test/test_file.txt"),
("pathchk", "/tmp/test/test_file.txt"),
("expand", "/tmp/test/test_file.txt"),
("tty", ""),
("basename", "/tmp/test/test_file.txt"),
("nice", ""),
("truncate", "-s 0 /tmp/test/test_file.txt"),
("echo", "hello"),
("du", "-h /tmp"),
("ptx", "/tmp/test/test_file.txt"),
("join", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
("df", "--help"),
("pwd", ""),
("test", "-f /tmp/test_file.txt"),
("csplit", "/tmp/test_file.txt 1"),
("sort", "/tmp/test_file.txt"),
("whoami", ""),
("touch", "/tmp/test/a"),
("unlink", "/tmp/test/test_file.txt"),
("b2sum", "/tmp/test/test_file.txt"),
("sleep", "1"),
("fmt", "/tmp/test/test_file.txt"),
("stty", ""),
("logname", ""),
("chgrp", "root /tmp/test/test_file.txt"),
("printenv", ""),
("seq", "1 10"),
("uname", ""),
("sha224sum", "/tmp/test/test_file.txt"),
("od", "/tmp/test/test_file.txt"),
("date", ""),
("base64", "/tmp/test/test_file.txt"),
("realpath", "/tmp/test/test_file.txt"),
("readlink", "/tmp/test/test_file.txt"),
("dircolors", ""),
("timeout", "1s sleep 2"),
("tac", "/tmp/test/test_file.txt"),
("numfmt", "1000"),
("wc", "/tmp/test/test_file.txt"),
("basenc", "/tmp/test/test_file.txt"),
("comm", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
("nproc", ""),
("expr", "1"),
("cksum", "/tmp/test/test_file.txt"),
("printf", "hello"),
("groups", ""),
("chcon", "-t s0 /tmp/test/test_file.txt"),
("factor", "10"),
("tail", "-n 1 /tmp/test/test_file.txt"),
("env", ""),
("pr", "/tmp/test/test_file.txt"),
("head", "-n 1 /tmp/test/test_file.txt"),
("kill", "$$"),
("uniq", "/tmp/test/test_file.txt"),
("stat", "-f /tmp/test/test_file.txt"),
("link", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
# ("make-prime-list", "10"), # build fail
("sum", "/tmp/test/test_file.txt"),
("tsort", "/tmp/test/test_file.txt"),
# ("extract-magic", "/tmp/test/test_file.txt"), build fail
("mknod", "/tmp/test/test_file.txt"),
("users", ""),
("dd", "--help"),
("who", ""),
("sha1sum", "/tmp/test/test_file.txt"),
("mktemp", ""),
("cut", "-c 1 /tmp/test/test_file.txt"),
("sha256sum", "/tmp/test/test_file.txt"),
("dir", "/tmp/test/test_file.txt"),
("mkdir", "/tmp/test/a"),
("nl", "/tmp/test/test_file.txt"),
("ginstall", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
("shred", "-u /tmp/test/test_file.txt"),
("fold", "-w 10 /tmp/test/test_file.txt"),
("rmdir", "/tmp/test/a"),
("sha384sum", "/tmp/test/test_file.txt"),
("mv", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
("dirname", "/tmp/test/test_file.txt"),
("id", ""),
("base32", "/tmp/test/test_file.txt"),
("pinky", ""),
("ln", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
("hostid", ""),
("chroot", "/tmp/test /tmp/test/test_file.txt"),
("ls", "/tmp/test"),
("true", ""),
("cp", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
("sync", ""),
("yes", "--help"),
("unexpand", "/tmp/test/test_file.txt"),
("chown", "root /tmp/test/test_file.txt"),
("getlimits", ""),
("chmod", "777 /tmp/test/test_file.txt"),
("uptime", ""),
("rm", "/tmp/test/test_file.txt"),
("vdir", "/tmp/test"),
("false", ""),
("sha512sum", "/tmp/test/test_file.txt"),
("tr", "a b /tmp/test/test_file.txt"),
("paste", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
("nohup", "sleep 1")
]
# core="tee md5sum split cat shuf mkfifo pathchk runcon expand tty basename nice truncate echo du ptx join df pwd test csplit sort whoami touch dcgen unlink b2sum sleep fmt stty logname chgrp printenv seq uname sha224sum od date base64 realpath readlink dircolors timeout tac numfmt wc basenc comm nproc expr stdbuf cksum printf groups chcon factor tail env pr head kill uniq stat link make-prime-list sum tsort extract-magic mknod users dd who sha1sum mktemp cut sha256sum dir mkdir nl ginstall shred fold rmdir sha384sum mv dirname id base32 pinky ln hostid chroot ls true cp sync yes unexpand chown getlimits chmod uptime rm vdir false sha512sum tr paste nohup"
if __name__ == "__main__":
Line.init()
for name, cmd in test_data:
test_basic(name, cmd)
# unittest.main()

View File

@ -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

View 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

View File

@ -0,0 +1,11 @@
curl -LO https://ftp.gnu.org/gnu/coreutils/coreutils-9.1.tar.xz
tar -xvf coreutils-9.1.tar.xz
cd coreutils-9.1
./configure
make
rm coreutils-9.1.tar.xz

View File

@ -0,0 +1,27 @@
rm -r coreutils-9.1/src/*-dir
core="tee md5sum split cat shuf mkfifo pathchk runcon expand tty basename nice truncate echo du ptx join df pwd test csplit sort whoami touch dcgen unlink b2sum sleep fmt stty logname chgrp printenv seq uname sha224sum od date base64 realpath readlink dircolors timeout tac numfmt wc basenc comm nproc expr stdbuf cksum printf groups chcon factor tail env pr head kill uniq stat link make-prime-list sum tsort extract-magic mknod users dd who sha1sum mktemp cut sha256sum dir mkdir nl ginstall shred fold rmdir sha384sum mv dirname id base32 pinky ln hostid chroot ls true cp sync yes unexpand chown getlimits chmod uptime rm vdir false sha512sum tr paste nohup"
for i in $core; do
echo "[+] $i"
WD=coreutils-9.1/src/${i}-dir
OUT=$WD/out
mkdir -p $WD
mkdir -p $OUT
cp b.cc $WD
{
clang++ -mmacosx-version-min=14 -o $OUT/libb.dylib -shared dummy.cc
../../macho-go/bin/ios-wrapper pepe -o $OUT/${i}-fixed -b $OUT/b.bcell --dylibs=./$OUT/libb.dylib --remove-imports --remove-exports --remove-symbol-table --remove-others coreutils-9.1/src/${i}
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
clang++ -mmacosx-version-min=14 -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib $WD/b.cc
codesign --force --deep -s - $OUT/${i}-fixed
codesign --force --deep -s - $OUT/libb.dylib
chmod +x $OUT/${i}-fixed
} > /dev/null 2>&1
done

View File

@ -0,0 +1,104 @@
Name,File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s),File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s)
md5sum,segfault
split,150,1321,101,0,0.003,169,0,4,0.002,0.005
cat,124,916,84,0,0.003,143,0,4,0.002,0.005
mkfifo,121,819,78,0,0.003,140,0,4,0.002,0.004
shuf,149,1255,91,0,0.004,168,0,4,0.002,0.005
pathchk,121,791,79,0,0.003,139,0,4,0.001,0.004
expand,123,904,82,0,0.003,142,0,4,0.002,0.005
tty,120,773,77,0,0.003,139,0,4,0.001,0.004
basename,121,805,74,0,0.003,140,0,4,0.001,0.004
nice,121,802,79,0,0.003,139,0,4,0.002,0.004
truncate,123,868,81,0,0.003,141,0,4,0.002,0.005
echo,118,710,67,0,0.003,137,0,4,0.001,0.004
du,321,2323,130,0,0.003,341,0,4,0.003,0.005
ptx,257,1763,111,0,0.031,277,0,4,0.002,0.038
join,146,1164,90,0,0.013,165,0,4,0.002,0.018
df,197,1797,102,0,0.003,217,0,4,0.002,0.005
pwd,122,861,86,0,0.003,141,0,4,0.002,0.004
test,121,826,71,0,0.003,140,0,4,0.002,0.004
csplit,235,1559,106,0,0.003,255,0,4,0.002,0.005
sort,239,2154,146,0,0.004,258,0,5,0.003,0.007
whoami,120,790,77,0,0.003,139,0,4,0.002,0.004
touch,187,1406,108,0,0.003,206,0,4,0.003,0.006
unlink,121,817,77,0,0.003,140,0,4,0.002,0.004
b2sum,141,956,88,0,0.004,160,0,5,0.002,0.005
sleep,123,848,79,0,0.003,141,0,4,0.002,0.005
fmt,139,908,81,0,0.007,158,0,4,0.003,0.010
stty,158,1023,89,0,0.003,177,0,4,0.002,0.005
logname,120,789,76,0,0.003,139,0,4,0.002,0.004
chgrp,171,1433,105,0,0.003,190,0,4,0.003,0.006
printenv,120,769,75,0,0.003,138,0,4,0.001,0.004
seq,139,887,83,0,0.003,158,0,4,0.002,0.004
uname,120,786,76,0,0.003,139,0,4,0.001,0.004
sha224sum,segfault
od,161,1156,90,0,0.022,181,0,4,0.002,0.023
date,195,1178,95,0,0.003,215,0,4,0.002,0.005
base64,122,856,81,0,0.003,141,0,4,0.002,0.005
realpath,145,1078,81,0,0.003,164,0,4,0.002,0.005
readlink,144,1042,81,0,0.003,163,0,4,0.002,0.005
dircolors,159,1012,96,0,0.003,178,0,4,0.002,0.004
timeout,124,888,95,0,0.006,142,0,4,0.002,0.007
tac,216,1387,97,0,0.003,235,0,4,0.002,0.005
numfmt,160,1079,92,0,0.003,179,0,4,0.002,0.005
wc,146,1104,95,0,0.004,165,0,4,0.002,0.006
basenc,145,1163,81,0,0.003,164,0,4,0.003,0.005
comm,126,948,84,0,0.004,144,0,4,0.002,0.006
nproc,121,817,79,0,0.003,140,0,4,0.002,0.004
expr,232,1436,102,0,0.003,252,0,4,0.002,0.004
cksum,187,1436,115,0,0.004,206,0,5,0.002,0.006
printf,122,853,74,0,0.003,141,0,4,0.001,0.004
groups,122,843,82,0,0.003,141,0,4,0.002,0.005
chcon,169,1380,96,0,0.003,188,0,4,0.002,0.005
factor,183,1284,115,0,0.003,202,0,4,0.003,0.006
tail,165,1192,95,0,0.003,184,0,4,0.003,0.006
env,142,1007,92,0,0.003,161,0,4,0.002,0.004
pr,185,1370,101,0,0.005,204,0,4,0.002,0.007
head,142,994,81,0,0.003,161,0,4,0.002,0.004
kill,121,802,81,0,0.003,140,0,4,0.002,0.004
uniq,142,987,86,0,0.003,161,0,4,0.002,0.006
stat,209,1561,108,0,0.003,228,0,4,0.002,0.005
link,121,818,78,0,0.003,140,0,4,0.002,0.005
sum,140,923,86,0,0.003,159,0,4,0.002,0.005
tsort,123,891,83,0,0.008,142,0,4,0.003,0.015
mknod,123,875,81,0,0.003,142,0,4,0.002,0.005
users,121,818,81,0,0.003,140,0,4,0.002,0.004
dd,165,1284,101,0,0.003,184,0,5,0.002,0.005
who,142,972,97,0,0.003,161,0,4,0.002,0.004
sha1sum,segfault
mktemp,126,960,86,0,0.003,145,0,4,0.002,0.004
cut,140,903,88,0,0.003,159,0,4,0.002,0.005
sha256sum,segfault
dir,281,2551,148,0,0.003,301,0,5,0.003,0.005
mkdir,144,1019,92,0,0.003,163,0,4,0.002,0.005
nl,214,1377,92,0,0.004,233,0,4,0.002,0.006
ginstall,239,2490,161,0,0.003,259,0,4,0.004,0.006
shred,151,1215,109,0,0.003,170,0,4,0.002,0.005
fold,122,843,80,0,0.004,141,0,4,0.002,0.005
rmdir,123,863,83,0,0.003,142,0,4,0.002,0.005
sha384sum,segfault
mv,238,2461,149,0,0.003,258,0,4,0.004,0.007
dirname,121,798,74,0,0.003,139,0,4,0.001,0.004
id,141,940,88,0,0.003,160,0,4,0.002,0.005
base32,122,861,81,0,0.003,141,0,4,0.002,0.005
pinky,125,929,98,0,0.003,144,0,4,0.002,0.005
ln,179,1589,111,0,0.003,198,0,4,0.003,0.006
hostid,120,783,75,0,0.003,139,0,4,0.001,0.004
chroot,148,1170,99,0,0.003,167,0,4,0.003,0.006
ls,281,2551,148,0,0.003,301,0,5,0.003,0.006
true,118,708,65,0,0.003,137,0,4,0.001,0.004
cp,233,2273,148,0,0.003,253,0,4,0.003,0.006
sync,121,809,80,0,0.003,140,0,4,0.002,0.004
yes,121,801,75,0,0.003,140,0,4,0.003,0.008
unexpand,123,907,82,0,0.003,142,0,4,0.003,0.009
chown,172,1449,107,0,0.003,191,0,4,0.003,0.006
getlimits,137,808,78,0,0.004,156,0,4,0.003,0.008
chmod,165,1245,92,0,0.003,184,0,4,0.002,0.004
uptime,140,924,93,0,0.003,159,0,5,0.003,0.009
rm,171,1417,98,0,0.003,190,0,5,0.003,0.008
vdir,281,2551,148,0,0.003,301,0,5,0.005,0.011
false,118,708,65,0,0.003,137,0,4,0.003,0.008
sha512sum,segfault
tr,142,1049,83,0,0.003,161,0,4,0.003,0.009
paste,122,880,77,0,0.004,141,0,4,0.003,0.009
nohup,123,856,82,0,0.005,142,0,4,0.003,0.012
1 Name,File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s),File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s)
2 md5sum,segfault
3 split,150,1321,101,0,0.003,169,0,4,0.002,0.005
4 cat,124,916,84,0,0.003,143,0,4,0.002,0.005
5 mkfifo,121,819,78,0,0.003,140,0,4,0.002,0.004
6 shuf,149,1255,91,0,0.004,168,0,4,0.002,0.005
7 pathchk,121,791,79,0,0.003,139,0,4,0.001,0.004
8 expand,123,904,82,0,0.003,142,0,4,0.002,0.005
9 tty,120,773,77,0,0.003,139,0,4,0.001,0.004
10 basename,121,805,74,0,0.003,140,0,4,0.001,0.004
11 nice,121,802,79,0,0.003,139,0,4,0.002,0.004
12 truncate,123,868,81,0,0.003,141,0,4,0.002,0.005
13 echo,118,710,67,0,0.003,137,0,4,0.001,0.004
14 du,321,2323,130,0,0.003,341,0,4,0.003,0.005
15 ptx,257,1763,111,0,0.031,277,0,4,0.002,0.038
16 join,146,1164,90,0,0.013,165,0,4,0.002,0.018
17 df,197,1797,102,0,0.003,217,0,4,0.002,0.005
18 pwd,122,861,86,0,0.003,141,0,4,0.002,0.004
19 test,121,826,71,0,0.003,140,0,4,0.002,0.004
20 csplit,235,1559,106,0,0.003,255,0,4,0.002,0.005
21 sort,239,2154,146,0,0.004,258,0,5,0.003,0.007
22 whoami,120,790,77,0,0.003,139,0,4,0.002,0.004
23 touch,187,1406,108,0,0.003,206,0,4,0.003,0.006
24 unlink,121,817,77,0,0.003,140,0,4,0.002,0.004
25 b2sum,141,956,88,0,0.004,160,0,5,0.002,0.005
26 sleep,123,848,79,0,0.003,141,0,4,0.002,0.005
27 fmt,139,908,81,0,0.007,158,0,4,0.003,0.010
28 stty,158,1023,89,0,0.003,177,0,4,0.002,0.005
29 logname,120,789,76,0,0.003,139,0,4,0.002,0.004
30 chgrp,171,1433,105,0,0.003,190,0,4,0.003,0.006
31 printenv,120,769,75,0,0.003,138,0,4,0.001,0.004
32 seq,139,887,83,0,0.003,158,0,4,0.002,0.004
33 uname,120,786,76,0,0.003,139,0,4,0.001,0.004
34 sha224sum,segfault
35 od,161,1156,90,0,0.022,181,0,4,0.002,0.023
36 date,195,1178,95,0,0.003,215,0,4,0.002,0.005
37 base64,122,856,81,0,0.003,141,0,4,0.002,0.005
38 realpath,145,1078,81,0,0.003,164,0,4,0.002,0.005
39 readlink,144,1042,81,0,0.003,163,0,4,0.002,0.005
40 dircolors,159,1012,96,0,0.003,178,0,4,0.002,0.004
41 timeout,124,888,95,0,0.006,142,0,4,0.002,0.007
42 tac,216,1387,97,0,0.003,235,0,4,0.002,0.005
43 numfmt,160,1079,92,0,0.003,179,0,4,0.002,0.005
44 wc,146,1104,95,0,0.004,165,0,4,0.002,0.006
45 basenc,145,1163,81,0,0.003,164,0,4,0.003,0.005
46 comm,126,948,84,0,0.004,144,0,4,0.002,0.006
47 nproc,121,817,79,0,0.003,140,0,4,0.002,0.004
48 expr,232,1436,102,0,0.003,252,0,4,0.002,0.004
49 cksum,187,1436,115,0,0.004,206,0,5,0.002,0.006
50 printf,122,853,74,0,0.003,141,0,4,0.001,0.004
51 groups,122,843,82,0,0.003,141,0,4,0.002,0.005
52 chcon,169,1380,96,0,0.003,188,0,4,0.002,0.005
53 factor,183,1284,115,0,0.003,202,0,4,0.003,0.006
54 tail,165,1192,95,0,0.003,184,0,4,0.003,0.006
55 env,142,1007,92,0,0.003,161,0,4,0.002,0.004
56 pr,185,1370,101,0,0.005,204,0,4,0.002,0.007
57 head,142,994,81,0,0.003,161,0,4,0.002,0.004
58 kill,121,802,81,0,0.003,140,0,4,0.002,0.004
59 uniq,142,987,86,0,0.003,161,0,4,0.002,0.006
60 stat,209,1561,108,0,0.003,228,0,4,0.002,0.005
61 link,121,818,78,0,0.003,140,0,4,0.002,0.005
62 sum,140,923,86,0,0.003,159,0,4,0.002,0.005
63 tsort,123,891,83,0,0.008,142,0,4,0.003,0.015
64 mknod,123,875,81,0,0.003,142,0,4,0.002,0.005
65 users,121,818,81,0,0.003,140,0,4,0.002,0.004
66 dd,165,1284,101,0,0.003,184,0,5,0.002,0.005
67 who,142,972,97,0,0.003,161,0,4,0.002,0.004
68 sha1sum,segfault
69 mktemp,126,960,86,0,0.003,145,0,4,0.002,0.004
70 cut,140,903,88,0,0.003,159,0,4,0.002,0.005
71 sha256sum,segfault
72 dir,281,2551,148,0,0.003,301,0,5,0.003,0.005
73 mkdir,144,1019,92,0,0.003,163,0,4,0.002,0.005
74 nl,214,1377,92,0,0.004,233,0,4,0.002,0.006
75 ginstall,239,2490,161,0,0.003,259,0,4,0.004,0.006
76 shred,151,1215,109,0,0.003,170,0,4,0.002,0.005
77 fold,122,843,80,0,0.004,141,0,4,0.002,0.005
78 rmdir,123,863,83,0,0.003,142,0,4,0.002,0.005
79 sha384sum,segfault
80 mv,238,2461,149,0,0.003,258,0,4,0.004,0.007
81 dirname,121,798,74,0,0.003,139,0,4,0.001,0.004
82 id,141,940,88,0,0.003,160,0,4,0.002,0.005
83 base32,122,861,81,0,0.003,141,0,4,0.002,0.005
84 pinky,125,929,98,0,0.003,144,0,4,0.002,0.005
85 ln,179,1589,111,0,0.003,198,0,4,0.003,0.006
86 hostid,120,783,75,0,0.003,139,0,4,0.001,0.004
87 chroot,148,1170,99,0,0.003,167,0,4,0.003,0.006
88 ls,281,2551,148,0,0.003,301,0,5,0.003,0.006
89 true,118,708,65,0,0.003,137,0,4,0.001,0.004
90 cp,233,2273,148,0,0.003,253,0,4,0.003,0.006
91 sync,121,809,80,0,0.003,140,0,4,0.002,0.004
92 yes,121,801,75,0,0.003,140,0,4,0.003,0.008
93 unexpand,123,907,82,0,0.003,142,0,4,0.003,0.009
94 chown,172,1449,107,0,0.003,191,0,4,0.003,0.006
95 getlimits,137,808,78,0,0.004,156,0,4,0.003,0.008
96 chmod,165,1245,92,0,0.003,184,0,4,0.002,0.004
97 uptime,140,924,93,0,0.003,159,0,5,0.003,0.009
98 rm,171,1417,98,0,0.003,190,0,5,0.003,0.008
99 vdir,281,2551,148,0,0.003,301,0,5,0.005,0.011
100 false,118,708,65,0,0.003,137,0,4,0.003,0.008
101 sha512sum,segfault
102 tr,142,1049,83,0,0.003,161,0,4,0.003,0.009
103 paste,122,880,77,0,0.004,141,0,4,0.003,0.009
104 nohup,123,856,82,0,0.005,142,0,4,0.003,0.012

View File

@ -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
}
}
}

View 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

View 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

File diff suppressed because it is too large Load Diff