Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Code Injection from Scratch

Code Injection from Scratch

Implementing code injection for iOS apps by directly copying swift vTables.

Daniel Haight

March 15, 2016
Tweet

More Decks by Daniel Haight

Other Decks in Technology

Transcript

  1. class ViewController: UIViewController { ... let textView = UITextView() let

    foo = Foo() override func viewDidLoad() { ... self.updateTextViewText() } ... func updateTextViewText() { self.textView.text = self.foo.bar() } }
  2. import ObjectiveC.runtime class Foo { dynamic //we'll address it don't

    worry func bar() -> String { return "try! Swift" } }
  3. import ObjectiveC.runtime class Foo { dynamic func bar() -> String

    { return "try! Swift" } dynamic func new_bar() -> String { return "try! ObjC" } func updateMethod() { ... } }
  4. let originalClass = Foo.self let originalSelector = Selector("bar") let originalMethod

    = class_getInstanceMethod(originalClass, originalSelector) let types = method_getTypeEncoding(originalMethod) let newMethod = class_getInstanceMethod(originalClass, "new_bar") let newImplementation = method_getImplementation(newMethod) class_replaceMethod(originalClass, originalSelector, newImplementation, types)
  5. class ViewController: UIViewController { ... let textView = UITextView() let

    foo = Foo() override func viewDidLoad() { ... self.foo.updateMethod() self.updateTextViewText() } ... }
  6. class ViewController: UIViewController { ... let textView = UITextView() let

    foo = Foo() override func viewDidLoad() { ... self.updateTextViewText() } func updateTextButtonPressed() { self.foo.updateMethod() self.updateTextViewText() } ... }
  7. class Foo { ... func new_bar() -> String { return

    "try! ObjC from bundle" } ... }
  8. func updateBarMethodFromExternal() { let originalClass = Foo.self let originalSelector =

    Selector("bar") let oldMethod = class_getInstanceMethod(Foo.self, originalSelector) let types = method_getTypeEncoding(oldMethod) ...
  9. ... let newClass = TrySwiftInjectable.Foo.self let newMethod = class_getInstanceMethod(newClass, "new_bar")

    let newImplementation = method_getImplementation(newMethod) class_replaceMethod(originalClass, originalSelector, newImplementation, types) }
  10. func method_list() -> [Method] { var outCount: CUnsignedInt = 0

    var injectedMethodsList = class_copyMethodList(ViewController.self, &outCount); var methods = [Method]() for _ in 0..<outCount { methods.append(injectedMethodsList.memory) injectedMethodsList = injectedMethodsList.successor() } return methods }
  11. let classesCount = objc_getClassList(nil, 0) let buffer = UnsafeMutablePointer<AnyClass?> .alloc(Int(classesCount))

    let autoreleasingBuffer = AutoreleasingUnsafeMutablePointer<AnyClass?>(buffer) objc_getClassList(autoreleasingBuffer, classesCount) var classes = [AnyClass]() for i in 0 ..< classesCount { if let currentClass: AnyClass = buffer[Int(i)] { classes.append(currentClass) } } buffer.dealloc(Int(classesCount))
  12. func classesFromBundlePath(path:String) -> [AnyClass] { let oldClasses = allClasses() loadBundle()

    let newClasses = allClasses() let updatedClasses = newClass.minus(oldClasses) return updatedClasses }
  13. func injectNewClass(newClass: AnyClass) { let originalClass = NSClassFromString(class_getName(newClass)) let methods

    = method_list(newClass) methods.forEach { method in let newImplementation = method_getImplementation(method) let selector = method_getName(method) let types = method_getTypeEncoding(method) class_replaceMethod(originalClass, originalSelector, newImplementation, types) } }
  14. struct almost_swift_class { Class isa; Class superclass; void *cache; //cache_t

    uintptr_t data_NEVER_USE; struct swift_data *swiftData; int an_int_we_dont_need_1; int an_int_we_dont_need_2; int an_int_we_dont_need_3; int an_int_we_dont_need_4; int struct_size; int an_int_we_dont_need_5; IMP vtable[20]; }
  15. struct almost_swift_class *newclass = (__bridge struct almost_swift_class *)injectedClass; if (

    (uintptr_t)newclass->swiftData & 0x1 ) { struct almost_swift_class *oldclass = (__bridge struct almost_swift_class *)originalClass; size_t bytes = oldclass->struct_size - offsetof(struct almost_swift_class, vtable); memcpy( oldclass->vtable, newclass->vtable, bytes ); }
  16. LIVE > Trigger for when new methods are available. >

    CallBack in code when new methods are available.
  17. THANKS - John Holdsworth - Erica Sadun - Jay Freeman

    - Mike Ash - Gwynne Raskind - Gwendolyn Weston - The organisers, Natasha and Translators