add simple objc hooking by modifying the method pointer
This commit is contained in:
@ -46,6 +46,8 @@ func (printer *InfoPrinter) Print() {
|
||||
)
|
||||
}
|
||||
|
||||
mc.CollectObjectiveCClasses()
|
||||
|
||||
fmt.Println("======")
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,164 @@ import (
|
||||
"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
|
||||
@ -30,8 +184,12 @@ func (sel *SpecialSelector) Name() string {
|
||||
// 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
|
||||
@ -137,7 +295,7 @@ func (mc *MachoContext) ReworkForObjc() {
|
||||
// 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()))
|
||||
// mc.file.WriteAt(make([]byte, section.Size()), int64(section.Offset()))
|
||||
}
|
||||
section_ptr += 16*2 + 8*2 + 4*8
|
||||
}
|
||||
@ -360,4 +518,8 @@ func (mc *MachoContext) ReworkForObjc() {
|
||||
mc.file.WriteAt(bs, offset)
|
||||
offset += 4
|
||||
}
|
||||
|
||||
// make __TEXT writable lol
|
||||
mc.file.Seek(0, 0)
|
||||
mc.file.WriteAt([]byte{0x7}, 0xa0)
|
||||
}
|
||||
|
Reference in New Issue
Block a user