Slide 1

Slide 1 text

SWIFT FUNTIME ! SWIFTSUMMIT, MARCH 2015 BORIS BÜGLING - @NEONACHO

Slide 2

Slide 2 text

COCOAPODS

Slide 3

Slide 3 text

CONTENTFUL

Slide 4

Slide 4 text

!

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

WHAT IS A SWIFT OBJECT EVEN?

Slide 7

Slide 7 text

IS IT A BAR?

Slide 8

Slide 8 text

IT DEPENDS

Slide 9

Slide 9 text

class MyObject : NSObject { }

Slide 10

Slide 10 text

▸ behaves like any old Objective-C object ▸ instance variables are properties ▸ fully interopable with ObjC

Slide 11

Slide 11 text

import ObjectiveC.runtime !

Slide 12

Slide 12 text

class MyObject { }

Slide 13

Slide 13 text

▸ has SwiftObject as superclass ▸ instance variables are ivars only ▸ ivars have no type encoding ▸ methods are not ObjC methods ▸ not interoperable with ObjC

Slide 14

Slide 14 text

SWIFTOBJECT Ivar: magic {SwiftObject_s="isa"^v"refCount"q} Protocol: NSObject NSOBJECT Ivar: isa # Protocol: NSObject

Slide 15

Slide 15 text

HOW DOES BRIDGING WORK, THEN?

Slide 16

Slide 16 text

IT DOESN'T

Slide 17

Slide 17 text

func info(x: T) { println("\(x) is a \(_stdlib_getDemangledTypeName(x))") } let array = [0, 1, 2] // 'as AnyObject' => ! info(array) // is a Swift.Array import Foundation let objc_array: AnyObject = [0, 1, 2] as AnyObject info(objc_array) // is a Swift._NSSwiftArrayImpl // comparing different array types => compiler error as well //let equal = objc_array == array

Slide 18

Slide 18 text

Y DID WE ! THE OBJECTIVE-" RUNTIME? ▸ Dynamic Introspection ! ▸ Change Behaviour "# ▸ Analyse private API $

Slide 19

Slide 19 text

DYNAMIC INTROSPECTION

Slide 20

Slide 20 text

var propertyCount : UInt32 = 0 var properties : UnsafeMutablePointer = class_copyPropertyList(myClass, &propertyCount) for i in 0..

Slide 21

Slide 21 text

IN PURE SWIFT => !

Slide 22

Slide 22 text

THERE IS HOPE // Excerpt from the standard library /// How children of this value should be presented in the IDE. enum MirrorDisposition { case Struct case Class case Enum [...] } /// A protocol that provides a reflection interface to an underlying value. protocol MirrorType { [...] }

Slide 23

Slide 23 text

infix operator --> {} func --> (instance: Any, key: String) -> Any? { let mirror = reflect(instance) for index in 0 ..< mirror.count { let (childKey, childMirror) = mirror[index] if childKey == key { return childMirror.value } } return nil }

Slide 24

Slide 24 text

struct MyPoint { let x: Float let y: Float } let point = MyPoint(x: 1, y: 2) println(point --> "x") // Optional(1.0) println(point --> "y") // Optional(2.0)

Slide 25

Slide 25 text

CHANGE BEHAVIOUR

Slide 26

Slide 26 text

let myString = "foobar" as NSString println(myString.description) // foobar let myBlock : @objc_block (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in "✋" } let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self)) let method = class_getInstanceMethod(myString.dynamicType, "description") method_setImplementation(method, myIMP) println(myString.description) // ✋

Slide 27

Slide 27 text

NSINVOCATION DOES NOT EXIST

Slide 28

Slide 28 text

WHAT ABOUT PURE SWIFT?

Slide 29

Slide 29 text

SWROUTE ▸ PoC of function hooking in Swift ▸ Uses rd_route, a Mach specific injection library for C

Slide 30

Slide 30 text

#include #define kObjectFieldOffset sizeof(uintptr_t) struct swift_func_object { uintptr_t *original_type_ptr; #if defined(__x86_64__) uintptr_t *unknown0; #else uintptr_t *unknown0, *unknown1; #endif uintptr_t function_address; uintptr_t *self; };

Slide 31

Slide 31 text

uintptr_t _rd_get_func_impl(void *func) { struct swift_func_object *obj = (struct swift_func_object *) *(uintptr_t *)(func + kObjectFieldOffset); return obj->function_address; }

Slide 32

Slide 32 text

LET'S DO THAT IN SWIFT

Slide 33

Slide 33 text

MEMORY LAYOUT ▸ 8 bytes => Pointer to _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ▸ 8 bytes => Pointer to struct _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ---> partial apply forwarder for reabstraction thunk helper [...]

Slide 34

Slide 34 text

MEMORY LAYOUT ▸ 16 bytes => Swift object ▸ 8 bytes => Pointer to _TF6memory3addFTSiSi_Si Function pointer !

Slide 35

Slide 35 text

struct f_trampoline { var trampoline_ptr: COpaquePointer var function_obj_ptr: UnsafeMutablePointer } struct function_obj { var some_ptr_0: COpaquePointer var some_ptr_1: COpaquePointer var function_ptr: COpaquePointer }

Slide 36

Slide 36 text

@asmname("floor") func my_floor(dbl: Double) -> Double println(my_floor(6.7)) let handle = dlopen(nil, RTLD_NOW) let pointer = COpaquePointer(dlsym(handle, "ceil")) typealias FunctionType = (Double) -> Double

Slide 37

Slide 37 text

struct f_trampoline { [...] } struct function_obj { [...] } let orig = unsafeBitCast(my_floor, f_trampoline.self) let new = f_trampoline(prototype: orig, new_fp: pointer) let my_ceil = unsafeBitCast(new, FunctionType.self) println(my_ceil(6.7))

Slide 38

Slide 38 text

$ xcrun swift -Onone hook.swift 6.0 7.0

Slide 39

Slide 39 text

CAN WE DO THIS THE OTHER WAY AROUND?

Slide 40

Slide 40 text

void executeFunction(void(*f)(void)) { f(); } @asmname("executeFunction") func executeFunction(fp: CFunctionPointer<()->()>)

Slide 41

Slide 41 text

func greeting() { println("Hello from Swift") } let t = unsafeBitCast(greeting, f_trampoline.self) let fp = CFunctionPointer<()->()> (t.function_obj_ptr.memory.function_ptr) executeFunction(fp) Hello from Swift Program ended with exit code: 0

Slide 42

Slide 42 text

ANALYSE PRIVATE API

Slide 43

Slide 43 text

class MyClass { var someVar = 1 func someFuncWithAReallyLongNameLol() { } }

Slide 44

Slide 44 text

$ xcrun swiftc f.swift $ ./swift-dump.rb f // Code generated from `f` import Foundation class MyClass { var someVar: Int = 0 func someFuncWithAReallyLongNameLol() -> () {} }

Slide 45

Slide 45 text

$ xcrun nm -g f|grep TFC 0000000100000c50 T __TFC1f7MyClass30someFuncWithAReallyLongNameLolfS0_FT_T_ 0000000100000d30 T __TFC1f7MyClassCfMS0_FT_S0_ 0000000100000c70 T __TFC1f7MyClassD 0000000100000d10 T __TFC1f7MyClasscfMS0_FT_S0_ 0000000100000c60 T __TFC1f7MyClassd 0000000100000ca0 T __TFC1f7MyClassg7someVarSi 0000000100000ce0 T __TFC1f7MyClassm7someVarSi 0000000100000cc0 T __TFC1f7MyClasss7someVarSi

Slide 46

Slide 46 text

$ xcrun swift-demangle __TFC1f7MyClassg7someVarSi _TFC1f7MyClassg7someVarSi ---> f.MyClass.someVar.getter : Swift.Int

Slide 47

Slide 47 text

HOW ARE EMOJI FORMED? $ echo 'class ! {}'|xcrun swiftc -emit-library -o test - $ nm -g test ... 0000000000000db0 T __TFC4testX4ypIhD ... $ xcrun swift-demangle __TFC4testX4ypIhD _TFC4testX4ypIhD ---> test.!.__deallocating_deinit X4 ypIh ~ xn--yp8h

Slide 48

Slide 48 text

WHAT HAVE WE LEARNED? ▸ import ObjectiveC.runtime ☺ ▸ Introspection somewhat exists " ▸ Changing behaviour is hard # ▸ Reverse engineering is still fine $

Slide 49

Slide 49 text

THANK YOU!

Slide 50

Slide 50 text

▸ https://github.com/mikeash/memorydumper ▸ http://airspeedvelocity.net/ ▸ https://developer.apple.com/swift/blog/ ▸ http://www.russbishop.net/swift-how-did-i-do-horrible-things

Slide 51

Slide 51 text

@NeoNacho [email protected] http://buegling.com/talks http://www.contentful.com