hook

动态链接系统库函数绑定 Fishhook原理模拟

得到符号绑定表,替换绑定的函数地址

@preconcurrency import Darwin import MachO func rebindSymbol<CFunc>(name: String, block: @escaping (_ oldFunc: CFunc) -> CFunc) { vm_protect(mach_task_self_, unsafeBitCast(got.dict["_\(name)"], to: UInt.self), 1, 0, VM_PROT_READ | VM_PROT_WRITE) got.dict["_\(name)"]?.pointee = unsafeBitCast(block(unsafeBitCast(got.dict["_\(name)"]?.pointee, to: CFunc.self)), to: UnsafeRawPointer.self) } func gotInit() { _dyld_register_func_for_add_image { header, slide in let header = header!.withMemoryRebound(to: mach_header_64.self, capacity: 1, { $0 }) // segment var linkEditSegment: UnsafePointer<segment_command_64>! var symbolTableSegment: UnsafePointer<symtab_command>! var dynamicSymbolTableSegment: UnsafePointer<dysymtab_command>! var dataConstSegment: UnsafePointer<segment_command_64>? var segmentRawPtr = UnsafeRawPointer(header.advanced(by: 1)) for _ in 0..<Int(header.pointee.ncmds) { let segmentPtr = segmentRawPtr.assumingMemoryBound(to: segment_command_64.self) if segmentPtr.pointee.cmd == UInt32(LC_SEGMENT_64) && segmentPtr.pointee.segmentName == SEG_LINKEDIT { linkEditSegment = segmentPtr } if segmentPtr.pointee.cmd == UInt32(LC_SYMTAB) { symbolTableSegment = segmentRawPtr.assumingMemoryBound(to: symtab_command.self) } if segmentPtr.pointee.cmd == UInt32(LC_DYSYMTAB) { dynamicSymbolTableSegment = segmentRawPtr.assumingMemoryBound(to: dysymtab_command.self) } if segmentPtr.pointee.cmd == UInt32(LC_SEGMENT_64) && segmentPtr.pointee.segmentName == "__DATA_CONST" { dataConstSegment = segmentPtr } segmentRawPtr = segmentRawPtr.advanced(by: Int(segmentPtr.pointee.cmdsize)) } guard let dataConstSegment else { return } // symbol table let linkEditBase = UInt(slide) + UInt(linkEditSegment.pointee.vmaddr - linkEditSegment.pointee.fileoff) let symbolTable = UnsafePointer<nlist_64>(bitPattern: linkEditBase + UInt(symbolTableSegment.pointee.symoff))! let stringTable = UnsafePointer<CChar>(bitPattern: linkEditBase + UInt(symbolTableSegment.pointee.stroff))! let indirectSymbolTable = UnsafePointer<UInt32>(bitPattern: linkEditBase + UInt(dynamicSymbolTableSegment.pointee.indirectsymoff))! // got let dataConstSectionsPtr = dataConstSegment.advanced(by: 1).withMemoryRebound(to: section_64.self, capacity: 1, { $0 }) let dataConstSections = [section_64](UnsafeBufferPointer(start: dataConstSectionsPtr, count: Int(dataConstSegment.pointee.nsects))) let gotSections = dataConstSections.filter { section in Int32(section.flags) & SECTION_TYPE == S_NON_LAZY_SYMBOL_POINTERS } for section in gotSections { for i in 0..<(Int(section.size) / MemoryLayout<UnsafeRawPointer>.size) { // symbol name let symbolTableIndex = indirectSymbolTable[Int(section.reserved1) + i] if symbolTableIndex == INDIRECT_SYMBOL_LOCAL || symbolTableIndex == (UInt32(INDIRECT_SYMBOL_ABS) | INDIRECT_SYMBOL_LOCAL) { continue } let stringTableIndex = symbolTable[Int(symbolTableIndex)].n_un.n_strx let symbolName = String(cString: stringTable.advanced(by: Int(stringTableIndex))) // binding let indirectBinding = UnsafeMutablePointer<UnsafeRawPointer>(bitPattern: UInt(slide) + UInt(section.addr))!.advanced(by: i) got.dict[symbolName] = indirectBinding } } } } class GOT: @unchecked Sendable { var dict: [String: UnsafeMutablePointer<UnsafeRawPointer>] = [:] } let got = GOT() extension segment_command_64 { var segmentName: String { String(cString: withUnsafePointer(to: segname, { $0.withMemoryRebound(to: CChar.self, capacity: 1, { $0 }) })) } }
gotInit() typealias ExitFunc = @convention(c) (Int32) -> Void var oldExit: (ExitFunc)? rebindSymbol(name: "exit") { oldFunc in oldExit = oldFunc return { code in print("hooked") oldExit?(code + 3) } as ExitFunc } exit(0)

objc Runtime方法交换

import Foundation @objcMembers class A: NSObject { dynamic func a() { } } @objcMembers class B: NSObject { dynamic func b() { } } let a = class_getInstanceMethod(A.self, #selector(A.a))! let b = class_getInstanceMethod(B.self, #selector(B.b))! method_exchangeImplementations(a, b) A().a()

swift dynamicReplacement

dynamic func a() { } @_dynamicReplacement(for:a) func b() { } a()