├── .gitmodules ├── README.md ├── altconf-funtime.md ├── code ├── .gitignore ├── Currying.swift ├── FunctionPointer.c ├── FunctionPointer.swift ├── Inspect.swift ├── InspectPure.swift ├── Ivar.m ├── Ivar.swift ├── Mirror.swift ├── PerformSelector.swift ├── Reflect.swift ├── ReplaceMethod.swift ├── Swizzle.swift ├── TableThisForNow │ ├── .gitignore │ ├── Code │ │ ├── AppDelegate.swift │ │ └── Info.plist │ └── TableThisForNow.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── xcschemes │ │ └── TableThisForNow.xcscheme └── magic.swift ├── images ├── 25yuswsw28295.gif ├── 4ovUfVD.gif ├── 5RSgg6y.gif ├── 69H4bMsuQk2S4.gif ├── 799569.gif ├── Apple-logo.png ├── Hopper_v3.png ├── Q24x3MLQYi2Hu.gif ├── WcCXCSZ.gif ├── azMA2znY9euje.gif ├── azb6mBK_460sa_v1.gif ├── bar.jpg ├── bye.gif ├── change.gif ├── cocoapods.jpg ├── cocoapods.png ├── contentful-bg-2.png ├── contentful-bg.png ├── contentful.png ├── cut-throat.gif ├── dance.gif ├── dinosuar-t-rex.jpg ├── explain.gif ├── f-u.gif ├── fall-back.gif ├── get_started.gif ├── hi.gif ├── hook.gif ├── introspection.gif ├── it_depends.gif ├── laptop-hacker.jpg ├── mind_blown.gif ├── noooooo.gif ├── oYK11dXjrkXh6.gif ├── object.gif ├── oh_no.gif ├── pAfqXBzJaboIg.gif ├── party.gif ├── questions.gif ├── rob.gif ├── rxQofqSkFZpNm.gif ├── shia-labeouf-magic-gif.gif ├── swift-2.gif ├── swift-bg-2.jpg ├── swift-bg.jpg ├── swift-logo.png ├── swift-slide.png ├── swift.gif ├── swizzle.jpg ├── taylor-laughing.gif ├── taylor-swift.jpg ├── taylor2.jpg ├── taylor3.jpg ├── taylor4.jpg ├── taylor5.jpg ├── taylor6.jpg ├── taylor7.jpg ├── taylor8.jpg ├── thanks.gif ├── three-hands.gif ├── unsafe-tweet.png ├── veryfurrow.jpg ├── wat.jpg ├── womp.gif ├── work.gif └── yay.gif ├── swift-funtime.md ├── swift-libs ├── swift.berlin-funtime.md └── swiftsummit-funtime.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "memorydumper"] 2 | path = memorydumper 3 | url = git@github.com:mikeash/memorydumper.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift funtime 2 | 3 | Talk given at mobiconf, October 2014. 4 | 5 | -------------------------------------------------------------------------------- /altconf-funtime.md: -------------------------------------------------------------------------------- 1 | # Swift funtime 🎉 2 | 3 | ## AltConf, June 2015 4 | 5 | ### Boris Bügling - @NeoNacho 6 | 7 | ![20%, original, inline](images/contentful.png) 8 | 9 | ![](images/taylor-laughing.gif) 10 | 11 | 12 | 13 | --- 14 | 15 | ![](images/hi.gif) 16 | 17 | --- 18 | 19 | ![inline, 200%](images/cocoapods.png) 20 | 21 | --- 22 | 23 | ## Contentful 24 | 25 | ![](images/contentful-bg.png) 26 | 27 | --- 28 | 29 | ![120%](images/contentful-bg-2.png) 30 | 31 | --- 32 | 33 | # 💖 34 | 35 | ![](images/swift-2.gif) 36 | 37 | --- 38 | 39 | ![](images/questions.gif) 40 | 41 | --- 42 | 43 | # What is a Swift object even? 44 | 45 | ![](images/object.gif) 46 | 47 | --- 48 | 49 | # It depends 50 | 51 | ![](images/it_depends.gif) 52 | 53 | --- 54 | 55 | ``` 56 | class MyObject : NSObject { 57 | } 58 | ``` 59 | 60 | ![](images/taylor5.jpg) 61 | 62 | --- 63 | 64 | - behaves like any old Objective-C object 65 | - instance variables are *properties* 66 | - fully interopable with ObjC 67 | 68 | ![](images/taylor6.jpg) 69 | 70 | --- 71 | 72 | ``` 73 | import ObjectiveC.runtime 74 | ``` 75 | 76 | 🎉 77 | 78 | ![](images/taylor7.jpg) 79 | 80 | --- 81 | 82 | ``` 83 | class MyObject { 84 | } 85 | ``` 86 | 87 | ![](images/taylor-swift.jpg) 88 | 89 | --- 90 | 91 | - has *SwiftObject* as superclass 92 | - instance variables are *ivars* only 93 | - ivars have no type encoding 94 | - methods are **not** ObjC methods 95 | - not interoperable with ObjC 96 | 97 | ![](images/taylor2.jpg) 98 | 99 | --- 100 | 101 | ### SwiftObject 102 | 103 | ``` 104 | Ivar: magic {SwiftObject_s="isa"^v"refCount"q} 105 | Protocol: NSObject 106 | ``` 107 | 108 | ### NSObject 109 | 110 | ``` 111 | Ivar: isa # 112 | Protocol: NSObject 113 | ``` 114 | 115 | ![](images/taylor3.jpg) 116 | 117 | --- 118 | 119 | # How does bridging work, then? 120 | 121 | ![inline](images/work.gif) 122 | 123 | --- 124 | 125 | # It doesn't 126 | 127 | ![](images/mind_blown.gif) 128 | 129 | --- 130 | 131 | ```swift 132 | func info(x: T) { 133 | print("\(x) is a \(_stdlib_getDemangledTypeName(x))") 134 | } 135 | 136 | 137 | let array = [0, 1, 2] // 'as AnyObject' => 💥 138 | info(array) // is a Swift.Array 139 | 140 | import Foundation 141 | 142 | let objc_array: AnyObject = [0, 1, 2] as AnyObject 143 | info(objc_array) // is a Swift._NSSwiftArrayImpl 144 | 145 | // comparing different array types => compiler error as well 146 | //let equal = objc_array == array 147 | ``` 148 | 149 | ![](images/swift-bg-2.jpg) 150 | 151 | --- 152 | 153 | ## 🎉 154 | 155 | ![](images/party.gif) 156 | 157 | --- 158 | 159 | # Swift 2.0 160 | 161 | ```bash 162 | ./foo.swift:3:17: error: 'CFunctionPointer' is unavailable: 163 | use a function type '@convention(c) (T) -> U' 164 | typealias foo = CFunctionPointer<() -> ()> 165 | ``` 166 | 167 | ![](images/swift-bg-2.jpg) 168 | 169 | --- 170 | 171 | ![](images/f-u.gif) 172 | 173 | --- 174 | 175 | ```swift 176 | @convention(swift) 177 | ``` 178 | 179 | > Apply this attribute to the type of the function to indicate its calling conventions. 180 | 181 | ![](images/swift-bg-2.jpg) 182 | 183 | --- 184 | 185 | # C function pointers 186 | 187 | ## Swift 1.x 188 | 189 | ```swift 190 | CFunctionPointer<(UnsafeMutablePointer, Float) -> Int>.self 191 | ``` 192 | 193 | ## Swift 2.x 194 | 195 | ```swift 196 | typealias CFunction = @convention(c) (UnsafeMutablePointer, Float) -> Int 197 | CFunction.self 198 | ``` 199 | 200 | ![](images/swift-bg-2.jpg) 201 | 202 | --- 203 | 204 | # Objective-C blocks 205 | 206 | ## Swift 1.x 207 | 208 | ```swift 209 | @objc_block ... 210 | ``` 211 | 212 | ## Swift 2.x 213 | 214 | ```swift 215 | @convention(block) 216 | ``` 217 | 218 | ![](images/swift-bg-2.jpg) 219 | 220 | --- 221 | 222 | ![](images/yay.gif) 223 | 224 | --- 225 | 226 | # Y did we 💖 the Objective-🐲 runtime? 227 | 228 | - Dynamic Introspection 🔍 229 | - Change Behaviour 🐒🔧 230 | - Analyse private API 👹 231 | 232 | ![](images/taylor2.jpg) 233 | 234 | --- 235 | 236 | ![](images/get_started.gif) 237 | 238 | --- 239 | 240 | # Dynamic introspection 241 | 242 | ![](images/introspection.gif) 243 | 244 | --- 245 | 246 | ```swift 247 | var propertyCount : UInt32 = 0 248 | var properties : UnsafeMutablePointer = 249 | class_copyPropertyList(myClass, &propertyCount) 250 | 251 | for i in 0.. 😭 262 | 263 | ![](images/womp.gif) 264 | 265 | --- 266 | 267 | ## There is hope 268 | 269 | ```swift 270 | // Excerpt from the standard library 271 | 272 | /// How children of this value should be presented in the IDE. 273 | enum MirrorDisposition { 274 | case Struct 275 | case Class 276 | case Enum 277 | [...] 278 | } 279 | 280 | /// A protocol that provides a reflection interface to an underlying value. 281 | protocol MirrorType { 282 | [...] 283 | } 284 | ``` 285 | 286 | ![](images/swift-bg-2.jpg) 287 | 288 | --- 289 | 290 | ```swift 291 | infix operator --> {} 292 | func --> (instance: Any, key: String) -> Any? { 293 | let mirror = reflect(instance) 294 | 295 | for index in 0 ..< mirror.count { 296 | let (childKey, childMirror) = mirror[index] 297 | if childKey == key { 298 | return childMirror.value 299 | } 300 | } 301 | 302 | return nil 303 | } 304 | ``` 305 | 306 | ![](images/swift-bg-2.jpg) 307 | 308 | --- 309 | 310 | ```swift 311 | struct MyPoint { 312 | let x: Float 313 | let y: Float 314 | } 315 | 316 | let point = MyPoint(x: 1, y: 2) 317 | print(point --> "x") // Optional(1.0) 318 | print(point --> "y") // Optional(2.0) 319 | ``` 320 | 321 | ![](images/swift-bg-2.jpg) 322 | 323 | --- 324 | 325 | ![](images/oYK11dXjrkXh6.gif) 326 | 327 | --- 328 | 329 | # Change behaviour 330 | 331 | ![](images/change.gif) 332 | 333 | --- 334 | 335 | ```swift 336 | let myString = "foobar" as NSString 337 | 338 | print(myString.description) // foobar 339 | 340 | let myBlock : @convention(block) (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in 341 | "✋" 342 | } 343 | 344 | let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self)) 345 | let method = class_getInstanceMethod(myString.dynamicType, "description") 346 | method_setImplementation(method, myIMP) 347 | 348 | print(myString.description) // ✋ 349 | ``` 350 | 351 | ![](images/swift-bg-2.jpg) 352 | 353 | --- 354 | 355 | ### NSInvocation does not exist 356 | 357 | ![](images/oh_no.gif) 358 | 359 | --- 360 | 361 | # What about pure Swift? 362 | 363 | ![](images/taylor6.jpg) 364 | 365 | --- 366 | 367 | # SWRoute 368 | 369 | - PoC of function hooking in Swift 370 | - Uses `rd_route`, a Mach specific injection library for C 371 | 372 | ![](images/taylor7.jpg) 373 | 374 | --- 375 | 376 | ```c 377 | #include 378 | 379 | #define kObjectFieldOffset sizeof(uintptr_t) 380 | 381 | struct swift_func_object { 382 | uintptr_t *original_type_ptr; 383 | #if defined(__x86_64__) 384 | uintptr_t *unknown0; 385 | #else 386 | uintptr_t *unknown0, *unknown1; 387 | #endif 388 | uintptr_t function_address; 389 | uintptr_t *self; 390 | }; 391 | ``` 392 | 393 | ![](images/swift-bg-2.jpg) 394 | 395 | --- 396 | 397 | ```c 398 | uintptr_t _rd_get_func_impl(void *func) { 399 | struct swift_func_object *obj = (struct swift_func_object *) 400 | *(uintptr_t *)(func + kObjectFieldOffset); 401 | 402 | return obj->function_address; 403 | } 404 | ``` 405 | 406 | ![](images/swift-bg-2.jpg) 407 | 408 | --- 409 | 410 | ![](images/explain.gif) 411 | 412 | --- 413 | 414 | # Let's do that in Swift 415 | 416 | ![](images/taylor-swift.jpg) 417 | 418 | --- 419 | 420 | # Memory layout 421 | 422 | - 8 bytes => Pointer to `_TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_` 423 | - 8 bytes => Pointer to struct 424 | 425 | ``` 426 | _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ---> 427 | partial apply forwarder for reabstraction thunk helper 428 | [...] 429 | ``` 430 | 431 | ![](images/swift-bg-2.jpg) 432 | 433 | --- 434 | 435 | # Memory layout 436 | 437 | - 16 bytes => Swift object 438 | - 8 bytes => Pointer to `_TF6memory3addFTSiSi_Si` 439 | 440 | __Function pointer__ 🎉 441 | 442 | ![](images/swift-bg-2.jpg) 443 | 444 | --- 445 | 446 | ```swift 447 | struct f_trampoline { 448 | var trampoline_ptr: COpaquePointer 449 | var function_obj_ptr: UnsafeMutablePointer 450 | } 451 | 452 | struct function_obj { 453 | var some_ptr_0: COpaquePointer 454 | var some_ptr_1: COpaquePointer 455 | var function_ptr: COpaquePointer 456 | } 457 | ``` 458 | 459 | ![](images/swift-bg-2.jpg) 460 | 461 | --- 462 | 463 | ```swift 464 | @asmname("floor") func my_floor(dbl: Double) -> Double 465 | print(my_floor(6.7)) 466 | 467 | let handle = dlopen(nil, RTLD_NOW) 468 | let pointer = COpaquePointer(dlsym(handle, "ceil")) 469 | 470 | typealias FunctionType = (Double) -> Double 471 | ``` 472 | 473 | ![](images/swift-bg-2.jpg) 474 | 475 | --- 476 | 477 | ```swift 478 | struct f_trampoline { [...] } 479 | struct function_obj { [...] } 480 | 481 | let orig = unsafeBitCast(my_floor, f_trampoline.self) 482 | let new = f_trampoline(prototype: orig, new_fp: pointer) 483 | let my_ceil = unsafeBitCast(new, FunctionType.self) 484 | print(my_ceil(6.7)) 485 | ``` 486 | 487 | ![](images/swift-bg-2.jpg) 488 | 489 | --- 490 | 491 | ``` 492 | $ xcrun swift -Onone hook.swift 493 | 6.0 494 | 7.0 495 | ``` 496 | 497 | ![](images/swift-bg-2.jpg) 498 | 499 | --- 500 | 501 | ![](images/hook.gif) 502 | 503 | --- 504 | 505 | # Can we do this the other way around? 506 | 507 | ![](images/taylor2.jpg) 508 | 509 | --- 510 | 511 | ```c 512 | void executeFunction(void(*f)(void)) { 513 | f(); 514 | } 515 | ``` 516 | 517 | ```swift 518 | typealias CFunc = @convention(c) ()->() 519 | 520 | @asmname("executeFunction") func 521 | executeFunction(fp: CFunc) 522 | ``` 523 | 524 | ![](images/swift-bg-2.jpg) 525 | 526 | --- 527 | 528 | ```swift 529 | func greeting() { 530 | print("Hello from Swift") 531 | } 532 | 533 | typealias CFunc = @convention(c) ()->() 534 | 535 | let t = unsafeBitCast(greeting, f_trampoline.self) 536 | let fp = unsafeBitCast(t.function_obj_ptr.memory.function_ptr, CFunc.self) 537 | executeFunction(fp) 538 | ``` 539 | 540 | ``` 541 | Hello from Swift 542 | Program ended with exit code: 0 543 | ``` 544 | 545 | ![](images/swift-bg-2.jpg) 546 | 547 | --- 548 | 549 | ![](images/69H4bMsuQk2S4.gif) 550 | 551 | --- 552 | 553 | # Analyse private API 554 | 555 | ![](images/taylor3.jpg) 556 | 557 | --- 558 | 559 | ![](images/cut-throat.gif) 560 | 561 | --- 562 | 563 | ```swift 564 | class MyClass { 565 | var someVar = 1 566 | 567 | func someFuncWithAReallyLongNameLol() { 568 | } 569 | } 570 | ``` 571 | 572 | ![](images/swift-bg-2.jpg) 573 | 574 | --- 575 | 576 | ```bash 577 | $ xcrun swiftc f.swift 578 | $ ./swift-dump.rb f 579 | // Code generated from `f` 580 | import Foundation 581 | 582 | class MyClass { 583 | var someVar: Int = 0 584 | func someFuncWithAReallyLongNameLol() -> () {} 585 | } 586 | ``` 587 | 588 | ![](images/swift-bg-2.jpg) 589 | 590 | ---- 591 | 592 | ```bash 593 | $ xcrun nm -g f|grep TFC 594 | 0000000100000c50 T __TFC1f7MyClass30someFuncWithAReallyLongNameLolfS0_FT_T_ 595 | 0000000100000d30 T __TFC1f7MyClassCfMS0_FT_S0_ 596 | 0000000100000c70 T __TFC1f7MyClassD 597 | 0000000100000d10 T __TFC1f7MyClasscfMS0_FT_S0_ 598 | 0000000100000c60 T __TFC1f7MyClassd 599 | 0000000100000ca0 T __TFC1f7MyClassg7someVarSi 600 | 0000000100000ce0 T __TFC1f7MyClassm7someVarSi 601 | 0000000100000cc0 T __TFC1f7MyClasss7someVarSi 602 | ``` 603 | 604 | ![](images/swift-bg-2.jpg) 605 | 606 | --- 607 | 608 | ```bash 609 | $ xcrun swift-demangle __TFC1f7MyClassg7someVarSi 610 | _TFC1f7MyClassg7someVarSi ---> f.MyClass.someVar.getter : Swift.Int 611 | ``` 612 | 613 | ![](images/swift-bg-2.jpg) 614 | 615 | --- 616 | 617 | ![](images/Q24x3MLQYi2Hu.gif) 618 | 619 | --- 620 | 621 | # `performSelector`? 622 | 623 | ```swift 624 | import Foundation 625 | import ObjectiveC.runtime 626 | 627 | @asmname("objc_msgSend") func sendPerformSelector(NSObject, Selector, Selector) -> NSString 628 | 629 | extension NSObject { 630 | public func performSelector2(selector : Selector) -> NSString { 631 | return sendPerformSelector(self, "performSelector:", selector) 632 | } 633 | } 634 | 635 | let string : NSString = "Fuck yeah, Swift!" 636 | println(string.performSelector2("description")) 637 | ``` 638 | 639 | ![](images/swift-bg-2.jpg) 640 | 641 | --- 642 | 643 | # 😱 644 | 645 | ```bash 646 | 1. While emitting IR SIL function 647 | @_TFE15PerformSelectorCSo8NSObject16performSelector2fS0_FV10ObjectiveC8SelectorCSo8NSString 648 | for 'performSelector2' at ./PerformSelector.swift:13:12 649 | Abort trap: 6 650 | ``` 651 | 652 | ![](images/swift-bg-2.jpg) 653 | 654 | --- 655 | 656 | ![](images/noooooo.gif) 657 | 658 | --- 659 | 660 | # Only works with Swift 1.1 :( 661 | 662 | ```swift 663 | #!/usr/bin/env 664 | DEVELOPER_DIR=/Applications/Xcode-6.2.app/Contents/Developer 665 | xcrun swift 666 | ``` 667 | 668 | ![](images/swift-bg-2.jpg) 669 | 670 | --- 671 | 672 | ```bash 673 | $ ./PerformSelector.swift 674 | Fuck yeah, Swift 675 | ``` 676 | 677 | ![](images/swift-bg-2.jpg) 678 | 679 | --- 680 | 681 | ![](images/azMA2znY9euje.gif) 682 | 683 | --- 684 | 685 | # Why `func performSelector2` ? 686 | 687 | ```bash 688 | $ ./PerformSelector.swift 689 | Segmentation fault: 11 690 | ``` 691 | 692 | ![](images/swift-bg-2.jpg) 693 | 694 | --- 695 | 696 | ![](images/rxQofqSkFZpNm.gif) 697 | 698 | --- 699 | 700 | # How are emoji formed? 701 | 702 | ``` 703 | $ echo 'class 👍 {}'|xcrun swiftc -emit-library -o test - 704 | $ nm -g test 705 | ... 706 | 0000000000000db0 T __TFC4testX4ypIhD 707 | ... 708 | $ xcrun swift-demangle __TFC4testX4ypIhD 709 | _TFC4testX4ypIhD ---> test.👍.__deallocating_deinit 710 | ``` 711 | 712 | *X4 ypIh* ~ *xn--yp8h* 713 | 714 | ![](images/taylor4.jpg) 715 | 716 | --- 717 | 718 | ![](images/pAfqXBzJaboIg.gif) 719 | 720 | --- 721 | 722 | # What have we learned? 723 | 724 | - `import ObjectiveC.runtime` ☺️ 725 | - Introspection somewhat exists 😐 726 | - Changing behaviour is hard 😖 727 | - Reverse engineering is still fine 😅 728 | 729 | ![](images/taylor8.jpg) 730 | 731 | --- 732 | 733 | # Thank you! 734 | 735 | ![](images/thanks.gif) 736 | 737 | --- 738 | 739 | - http://airspeedvelocity.net/ 740 | - http://www.russbishop.net/swift-how-did-i-do-horrible-things 741 | - https://github.com/mikeash/memorydumper 742 | - https://github.com/rodionovd/SWRoute 743 | 744 | ![](images/taylor5.jpg) 745 | 746 | --- 747 | 748 | @NeoNacho 749 | 750 | boris@contentful.com 751 | 752 | http://buegling.com/talks 753 | 754 | http://www.contentful.com 755 | 756 | ![](images/bye.gif) 757 | -------------------------------------------------------------------------------- /code/.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | a.out 3 | -------------------------------------------------------------------------------- /code/Currying.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | class BankAccount { 4 | var balance: Double = 0.0 5 | 6 | func deposit(amount: Double) { 7 | balance += amount 8 | } 9 | } 10 | 11 | let account = BankAccount() 12 | account.deposit(100) 13 | print(account.balance) 14 | 15 | let depositor = BankAccount.deposit 16 | depositor(account)(100) 17 | print(account.balance) 18 | 19 | BankAccount.deposit(account)(100) 20 | print(account.balance) 21 | -------------------------------------------------------------------------------- /code/FunctionPointer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef long (*functionPointer)(); 6 | 7 | int main() { 8 | void* handle = dlopen("/usr/lib/libc.dylib", RTLD_NOW); 9 | 10 | functionPointer my_function; 11 | *(void**)(&my_function) = dlsym(handle, "random"); 12 | printf("%p\n", my_function); 13 | 14 | long result = my_function(); 15 | printf("%li == %li\n", result, random()); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /code/FunctionPointer.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | import Darwin 4 | 5 | // thx Mike Ash (https://github.com/mikeash/memorydumper) 6 | func symbolInfo(address: UInt) -> Dl_info? { 7 | var info = Dl_info(dli_fname: "", dli_fbase: nil, dli_sname: "", dli_saddr: nil) 8 | let ptr: UnsafePointer = unsafeBitCast(address, UnsafePointer.self) 9 | let result = dladdr(ptr, &info) 10 | return (result == 0 ? nil : info) 11 | } 12 | 13 | extension COpaquePointer: CustomStringConvertible { 14 | public var description: String { 15 | let info = symbolInfo(unsafeBitCast(self, UInt.self)) 16 | 17 | if let symInfo = info { 18 | let lib_name = String.fromCString(symInfo.dli_fname)! 19 | let func_name = String.fromCString(symInfo.dli_sname)! 20 | return "Function '\(func_name)' from '\(lib_name)'" 21 | } 22 | 23 | return debugDescription 24 | } 25 | 26 | public init(_ library: String, _ symbol: String) { 27 | let handle = dlopen(library, RTLD_NOW) 28 | let sym = dlsym(handle, symbol) 29 | self.init(sym) 30 | } 31 | } 32 | 33 | // ------------------------------------------------------------------------ 34 | 35 | 36 | 37 | 38 | let handle = dlopen("/usr/lib/libc.dylib", RTLD_NOW) 39 | let sym = dlsym(handle, "random") 40 | //let pointer = COpaquePointer(sym) 41 | 42 | let pointer = COpaquePointer("/usr/lib/libc.dylib", "random") 43 | print(pointer.description) 44 | exit(0) 45 | 46 | 47 | 48 | 49 | 50 | let rawPointer = UnsafeMutablePointer<() -> CLong>(sym) 51 | let opaquePointer = COpaquePointer(rawPointer) 52 | typealias CFunction = @convention(c) () -> CLong 53 | let functionPointer = unsafeBitCast(opaquePointer, CFunction.self) 54 | print(functionPointer) 55 | 56 | /*let result : CLong = rawPointer.memory() 57 | print(result)*/ 58 | 59 | 60 | func callf(f: () -> ()) { 61 | f(); 62 | } 63 | 64 | typealias fpointer = () -> () 65 | print(sizeof(functionPointer.dynamicType)) 66 | print(sizeof(fpointer)) 67 | 68 | /*let functionPointer = unsafeBitCast(rawPointer, fpointer.self) 69 | callf(functionPointer)*/ 70 | 71 | let cstr = symbolInfo(unsafeBitCast(rawPointer, UInt.self))?.dli_sname 72 | print(String.fromCString(cstr!)) 73 | 74 | 75 | 76 | @asmname("random") func random2() -> CLong 77 | @asmname("srandomdev") func srandomdev() -> Void 78 | 79 | print(sizeof(random2.dynamicType)) 80 | print(random2()) 81 | 82 | 83 | struct swift_func_object { 84 | let original_type_ptr : COpaquePointer 85 | let function_address : COpaquePointer 86 | } 87 | 88 | struct bar { 89 | let ptr0: COpaquePointer 90 | let ptr1: COpaquePointer 91 | let function_pointer: COpaquePointer 92 | } 93 | 94 | print(sizeof(swift_func_object)) 95 | 96 | var ptr = unsafeBitCast(random2, swift_func_object.self) 97 | print(ptr.original_type_ptr) 98 | print(ptr.function_address) 99 | 100 | /*ptr = unsafeBitCast(srandomdev, swift_func_object.self) 101 | print(ptr.original_type_ptr) 102 | print(ptr.function_address)*/ 103 | 104 | let aptr = UnsafeMutablePointer(ptr.function_address) 105 | let aptr2 = aptr.memory 106 | print(aptr2.function_pointer) 107 | 108 | 109 | let v = unsafeBitCast(aptr2.function_pointer, UInt.self) 110 | let v2 = COpaquePointer(bitPattern: v) 111 | print(v2) 112 | 113 | let symInfo = symbolInfo(v) 114 | print(String.fromCString((symInfo?.dli_sname)!)) 115 | 116 | 117 | 118 | 119 | 120 | /*let my_ptr = swift_func_object(original_type_ptr: COpaquePointer(bitPattern: 0), 121 | function_address: COpaquePointer(bitPattern: 0x00007fff8e6aac7a)) 122 | let my_fpointer = unsafeBitCast(my_ptr, fpointer.self) 123 | callf(my_fpointer)*/ 124 | -------------------------------------------------------------------------------- /code/Inspect.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | import Foundation 4 | import ObjectiveC.runtime 5 | 6 | func inspectClass(myClass : AnyObject.Type) -> Void { 7 | print("Name: " + NSStringFromClass(myClass)) 8 | 9 | var ivarCount : UInt32 = 0 10 | var ivars : UnsafeMutablePointer = class_copyIvarList(myClass, &ivarCount) 11 | 12 | for i in 0.. = class_copyPropertyList(myClass, 19 | &propertyCount) 20 | 21 | for i in 0.. = class_copyMethodList(myClass, &methodCount) 27 | 28 | for i in 0.. = class_copyProtocolList(myClass, 35 | &protocolCount) 36 | 37 | for i in 0.. Void { 43 | // TODO: Missing in Swift 1.2 44 | //print("Mangled name: \(_stdlib_getTypeName(obj))") 45 | 46 | let myClass: AnyObject.Type = obj.dynamicType 47 | inspectClass(myClass) 48 | 49 | let superClass: AnyObject.Type = class_getSuperclass(myClass) 50 | inspectClass(superClass) 51 | } 52 | 53 | print("# Swift based class") 54 | 55 | class MyObject { 56 | var foo : String = "foo" 57 | 58 | func bar(str : String) -> Bool { 59 | return false 60 | } 61 | 62 | init() { 63 | } 64 | } 65 | 66 | inspect(MyObject()) 67 | 68 | print("\n# Objective-C based class") 69 | 70 | class MyNSObject : NSObject { 71 | var foo : String = "foo" 72 | 73 | func bar(str : String) -> Bool { 74 | return false 75 | } 76 | 77 | override init() { 78 | } 79 | } 80 | 81 | inspect(MyNSObject()) 82 | 83 | /*var foo = ["foo", "bar"] 84 | inspect(foo) 85 | 86 | var bar = ["foo": "bar"] 87 | inspect(bar)*/ 88 | 89 | -------------------------------------------------------------------------------- /code/InspectPure.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | class Foo { 4 | func bar() { 5 | } 6 | } 7 | 8 | let f = Foo() 9 | 10 | import Foundation 11 | 12 | typealias CFunction = @convention(c) () -> () 13 | 14 | print(f.dynamicType) 15 | print(CFunction.self) 16 | print([Int].self) 17 | -------------------------------------------------------------------------------- /code/Ivar.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface MyClass : NSObject 5 | 6 | @property (nonatomic, retain) NSString* foo; 7 | 8 | @end 9 | 10 | #pragma mark - 11 | 12 | @implementation MyClass 13 | 14 | -(instancetype)init { 15 | self = [super init]; 16 | if (self) { 17 | self.foo = @"bar"; 18 | } 19 | return self; 20 | } 21 | 22 | @end 23 | 24 | #pragma mark - 25 | 26 | int main(int argc, char *argv[]) 27 | { 28 | @autoreleasepool { 29 | MyClass* object = [MyClass new]; 30 | Ivar ivar = class_getInstanceVariable(object.class, "_foo"); 31 | id value = object_getIvar(object, ivar); 32 | NSLog(@"%@", value); 33 | return 0; 34 | } 35 | } 36 | 37 | // clang Ivar.m -framework Foundation 38 | -------------------------------------------------------------------------------- /code/Ivar.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | class MySwiftClass /*: NSObject*/ { 4 | var foo = "bar"; 5 | 6 | /*override*/ init() { 7 | } 8 | } 9 | 10 | import Foundation 11 | import ObjectiveC.runtime 12 | 13 | var ivar = class_getInstanceVariable(MySwiftClass().dynamicType, "foo") 14 | 15 | print(NSString(CString: ivar_getName(ivar), encoding: NSUTF8StringEncoding)!) 16 | 17 | var value : AnyObject = object_getIvar(MySwiftClass(), ivar)! 18 | 19 | print(value) 20 | -------------------------------------------------------------------------------- /code/Mirror.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | var mirror = ["a", "b"].getMirror() 4 | 5 | print(mirror.summary) 6 | 7 | for i in 0.. NSString 8 | 9 | extension NSObject { 10 | // Using `performSelector` crashes in 1.1 11 | public func performSelector2(selector : Selector) -> NSString { 12 | return sendPerformSelector(self, "performSelector:", selector) 13 | } 14 | } 15 | 16 | let string : NSString = "Fuck yeah, Swift!" 17 | println(string.performSelector2("description")) 18 | -------------------------------------------------------------------------------- /code/Reflect.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | // From: https://gist.github.com/peebsjs/9288f79322ed3119ece4 4 | 5 | infix operator --> {} 6 | func --> (instance: Any, key: String) -> Any? { 7 | let mirror = reflect(instance) 8 | 9 | for index in 0 ..< mirror.count { 10 | let (childKey, childMirror) = mirror[index] 11 | if childKey == key { 12 | return childMirror.value 13 | } 14 | } 15 | 16 | return nil 17 | } 18 | 19 | //Example 20 | struct MyPoint { 21 | let x: Float 22 | let y: Float 23 | } 24 | 25 | let point = MyPoint(x: 1, y: 2) 26 | print(point --> "x") 27 | print(point --> "y") 28 | -------------------------------------------------------------------------------- /code/ReplaceMethod.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | import Foundation 4 | import ObjectiveC.runtime 5 | 6 | let myString = "foobar" as NSString 7 | 8 | print(myString.description) 9 | 10 | let myBlock : @convention(block) (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in 11 | "✋" 12 | } 13 | 14 | let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self)) 15 | let method = class_getInstanceMethod(myString.dynamicType, "description") 16 | method_setImplementation(method, myIMP) 17 | 18 | print(myString.description) 19 | -------------------------------------------------------------------------------- /code/Swizzle.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | import Foundation 4 | import ObjectiveC.runtime 5 | 6 | extension NSString { 7 | func swizzle_description() -> NSString { 8 | return "💥" 9 | } 10 | } 11 | 12 | var myString = "foobar" as NSString 13 | 14 | print(myString.description) 15 | 16 | var originalMethod = class_getInstanceMethod(NSString.self, "description") 17 | var swizzledMethod = class_getInstanceMethod(NSString.self, "swizzle_description") 18 | 19 | method_exchangeImplementations(originalMethod, swizzledMethod) 20 | 21 | print(myString.description) 22 | -------------------------------------------------------------------------------- /code/TableThisForNow/.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | -------------------------------------------------------------------------------- /code/TableThisForNow/Code/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TableThisForNow 4 | // 5 | // Created by Boris Bügling on 28/09/14. 6 | // Copyright (c) 2014 Boris Bügling. All rights reserved. 7 | // 8 | 9 | import ObjectiveC.runtime 10 | import UIKit 11 | 12 | class ModelObject : NSObject { 13 | var foo : String 14 | var bar : NSDate 15 | 16 | init(foo : String, bar : NSDate) { 17 | self.foo = foo 18 | self.bar = bar 19 | } 20 | } 21 | 22 | class TableViewCell : UITableViewCell { 23 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 24 | super.init(style: UITableViewCellStyle.Value1, reuseIdentifier: reuseIdentifier) 25 | } 26 | 27 | required init(coder aDecoder: NSCoder) { 28 | fatalError("init(coder:) has not been implemented") 29 | } 30 | } 31 | 32 | class DynamicDataSource : NSObject, UITableViewDataSource { 33 | let modelObject : ModelObject 34 | let properties : UnsafeMutablePointer 35 | var propertyCount : UInt32 = 0 36 | 37 | init(modelObject : ModelObject) { 38 | self.modelObject = modelObject 39 | self.properties = class_copyPropertyList(modelObject.dynamicType, &propertyCount) 40 | } 41 | 42 | func numberOfSectionsInTableView(tableView: UITableView) -> Int { 43 | return 1 44 | } 45 | 46 | func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 47 | let cell = tv.dequeueReusableCellWithIdentifier(NSStringFromClass(modelObject.dynamicType), 48 | forIndexPath: indexPath) as UITableViewCell 49 | let property = properties[indexPath.row] 50 | 51 | cell.textLabel!.text = String.fromCString(property_getName(property)) 52 | cell.detailTextLabel!.text = modelObject.valueForKey(cell.textLabel!.text!)!.description 53 | 54 | return cell 55 | } 56 | 57 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 58 | return Int(propertyCount) 59 | } 60 | } 61 | 62 | @UIApplicationMain 63 | class AppDelegate: UIResponder, UIApplicationDelegate { 64 | 65 | var dataSource : DynamicDataSource? 66 | var window: UIWindow? 67 | 68 | func application(application: UIApplication, didFinishLaunchingWithOptions 69 | launchOptions: [NSObject: AnyObject]?) -> Bool { 70 | 71 | let modelObject = ModelObject(foo: "Hello World", 72 | bar: NSDate(timeIntervalSinceNow: 10000)) 73 | 74 | dataSource = DynamicDataSource(modelObject: modelObject) 75 | 76 | let rootVC = UITableViewController() 77 | rootVC.tableView.contentInset = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0) 78 | rootVC.tableView.dataSource = dataSource 79 | rootVC.tableView.registerClass(TableViewCell.classForCoder(), 80 | forCellReuseIdentifier: NSStringFromClass(modelObject.dynamicType)) 81 | 82 | self.window = UIWindow(frame: UIScreen.mainScreen().bounds) 83 | self.window!.backgroundColor = UIColor.whiteColor() 84 | self.window!.rootViewController = rootVC 85 | self.window!.makeKeyAndVisible() 86 | 87 | return true 88 | } 89 | 90 | } 91 | 92 | -------------------------------------------------------------------------------- /code/TableThisForNow/Code/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | foo 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /code/TableThisForNow/TableThisForNow.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A15A38EB19D85BB000AF81A9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A15A38EA19D85BB000AF81A9 /* AppDelegate.swift */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXFileReference section */ 14 | A15A38E519D85BB000AF81A9 /* TableThisForNow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TableThisForNow.app; sourceTree = BUILT_PRODUCTS_DIR; }; 15 | A15A38E919D85BB000AF81A9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 16 | A15A38EA19D85BB000AF81A9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 17 | /* End PBXFileReference section */ 18 | 19 | /* Begin PBXFrameworksBuildPhase section */ 20 | A15A38E219D85BB000AF81A9 /* Frameworks */ = { 21 | isa = PBXFrameworksBuildPhase; 22 | buildActionMask = 2147483647; 23 | files = ( 24 | ); 25 | runOnlyForDeploymentPostprocessing = 0; 26 | }; 27 | /* End PBXFrameworksBuildPhase section */ 28 | 29 | /* Begin PBXGroup section */ 30 | A15A38DC19D85BB000AF81A9 = { 31 | isa = PBXGroup; 32 | children = ( 33 | A15A38E719D85BB000AF81A9 /* Code */, 34 | A15A38E619D85BB000AF81A9 /* Products */, 35 | ); 36 | sourceTree = ""; 37 | }; 38 | A15A38E619D85BB000AF81A9 /* Products */ = { 39 | isa = PBXGroup; 40 | children = ( 41 | A15A38E519D85BB000AF81A9 /* TableThisForNow.app */, 42 | ); 43 | name = Products; 44 | sourceTree = ""; 45 | }; 46 | A15A38E719D85BB000AF81A9 /* Code */ = { 47 | isa = PBXGroup; 48 | children = ( 49 | A15A38EA19D85BB000AF81A9 /* AppDelegate.swift */, 50 | A15A38E919D85BB000AF81A9 /* Info.plist */, 51 | ); 52 | path = Code; 53 | sourceTree = ""; 54 | }; 55 | /* End PBXGroup section */ 56 | 57 | /* Begin PBXNativeTarget section */ 58 | A15A38E419D85BB000AF81A9 /* TableThisForNow */ = { 59 | isa = PBXNativeTarget; 60 | buildConfigurationList = A15A390419D85BB000AF81A9 /* Build configuration list for PBXNativeTarget "TableThisForNow" */; 61 | buildPhases = ( 62 | A15A38E119D85BB000AF81A9 /* Sources */, 63 | A15A38E219D85BB000AF81A9 /* Frameworks */, 64 | A15A38E319D85BB000AF81A9 /* Resources */, 65 | ); 66 | buildRules = ( 67 | ); 68 | dependencies = ( 69 | ); 70 | name = TableThisForNow; 71 | productName = TableThisForNow; 72 | productReference = A15A38E519D85BB000AF81A9 /* TableThisForNow.app */; 73 | productType = "com.apple.product-type.application"; 74 | }; 75 | /* End PBXNativeTarget section */ 76 | 77 | /* Begin PBXProject section */ 78 | A15A38DD19D85BB000AF81A9 /* Project object */ = { 79 | isa = PBXProject; 80 | attributes = { 81 | LastSwiftUpdateCheck = 0700; 82 | LastUpgradeCheck = 0700; 83 | ORGANIZATIONNAME = "Boris Bügling"; 84 | TargetAttributes = { 85 | A15A38E419D85BB000AF81A9 = { 86 | CreatedOnToolsVersion = 6.0.1; 87 | }; 88 | }; 89 | }; 90 | buildConfigurationList = A15A38E019D85BB000AF81A9 /* Build configuration list for PBXProject "TableThisForNow" */; 91 | compatibilityVersion = "Xcode 3.2"; 92 | developmentRegion = English; 93 | hasScannedForEncodings = 0; 94 | knownRegions = ( 95 | en, 96 | Base, 97 | ); 98 | mainGroup = A15A38DC19D85BB000AF81A9; 99 | productRefGroup = A15A38E619D85BB000AF81A9 /* Products */; 100 | projectDirPath = ""; 101 | projectRoot = ""; 102 | targets = ( 103 | A15A38E419D85BB000AF81A9 /* TableThisForNow */, 104 | ); 105 | }; 106 | /* End PBXProject section */ 107 | 108 | /* Begin PBXResourcesBuildPhase section */ 109 | A15A38E319D85BB000AF81A9 /* Resources */ = { 110 | isa = PBXResourcesBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | /* End PBXResourcesBuildPhase section */ 117 | 118 | /* Begin PBXSourcesBuildPhase section */ 119 | A15A38E119D85BB000AF81A9 /* Sources */ = { 120 | isa = PBXSourcesBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | A15A38EB19D85BB000AF81A9 /* AppDelegate.swift in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | A15A390219D85BB000AF81A9 /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 135 | CLANG_CXX_LIBRARY = "libc++"; 136 | CLANG_ENABLE_MODULES = YES; 137 | CLANG_ENABLE_OBJC_ARC = YES; 138 | CLANG_WARN_BOOL_CONVERSION = YES; 139 | CLANG_WARN_CONSTANT_CONVERSION = YES; 140 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 141 | CLANG_WARN_EMPTY_BODY = YES; 142 | CLANG_WARN_ENUM_CONVERSION = YES; 143 | CLANG_WARN_INT_CONVERSION = YES; 144 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 145 | CLANG_WARN_UNREACHABLE_CODE = YES; 146 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 147 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 148 | COPY_PHASE_STRIP = NO; 149 | ENABLE_STRICT_OBJC_MSGSEND = YES; 150 | ENABLE_TESTABILITY = YES; 151 | GCC_C_LANGUAGE_STANDARD = gnu99; 152 | GCC_DYNAMIC_NO_PIC = NO; 153 | GCC_OPTIMIZATION_LEVEL = 0; 154 | GCC_PREPROCESSOR_DEFINITIONS = ( 155 | "DEBUG=1", 156 | "$(inherited)", 157 | ); 158 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 159 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 160 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 161 | GCC_WARN_UNDECLARED_SELECTOR = YES; 162 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 163 | GCC_WARN_UNUSED_FUNCTION = YES; 164 | GCC_WARN_UNUSED_VARIABLE = YES; 165 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 166 | MTL_ENABLE_DEBUG_INFO = YES; 167 | ONLY_ACTIVE_ARCH = YES; 168 | SDKROOT = iphoneos; 169 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 170 | }; 171 | name = Debug; 172 | }; 173 | A15A390319D85BB000AF81A9 /* Release */ = { 174 | isa = XCBuildConfiguration; 175 | buildSettings = { 176 | ALWAYS_SEARCH_USER_PATHS = NO; 177 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 178 | CLANG_CXX_LIBRARY = "libc++"; 179 | CLANG_ENABLE_MODULES = YES; 180 | CLANG_ENABLE_OBJC_ARC = YES; 181 | CLANG_WARN_BOOL_CONVERSION = YES; 182 | CLANG_WARN_CONSTANT_CONVERSION = YES; 183 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 184 | CLANG_WARN_EMPTY_BODY = YES; 185 | CLANG_WARN_ENUM_CONVERSION = YES; 186 | CLANG_WARN_INT_CONVERSION = YES; 187 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 188 | CLANG_WARN_UNREACHABLE_CODE = YES; 189 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 190 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 191 | COPY_PHASE_STRIP = YES; 192 | ENABLE_NS_ASSERTIONS = NO; 193 | ENABLE_STRICT_OBJC_MSGSEND = YES; 194 | GCC_C_LANGUAGE_STANDARD = gnu99; 195 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 196 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 197 | GCC_WARN_UNDECLARED_SELECTOR = YES; 198 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 199 | GCC_WARN_UNUSED_FUNCTION = YES; 200 | GCC_WARN_UNUSED_VARIABLE = YES; 201 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 202 | MTL_ENABLE_DEBUG_INFO = NO; 203 | SDKROOT = iphoneos; 204 | VALIDATE_PRODUCT = YES; 205 | }; 206 | name = Release; 207 | }; 208 | A15A390519D85BB000AF81A9 /* Debug */ = { 209 | isa = XCBuildConfiguration; 210 | buildSettings = { 211 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 212 | INFOPLIST_FILE = Code/Info.plist; 213 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 214 | PRODUCT_BUNDLE_IDENTIFIER = "org.vu0.$(PRODUCT_NAME:rfc1034identifier)"; 215 | PRODUCT_NAME = "$(TARGET_NAME)"; 216 | }; 217 | name = Debug; 218 | }; 219 | A15A390619D85BB000AF81A9 /* Release */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 223 | INFOPLIST_FILE = Code/Info.plist; 224 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 225 | PRODUCT_BUNDLE_IDENTIFIER = "org.vu0.$(PRODUCT_NAME:rfc1034identifier)"; 226 | PRODUCT_NAME = "$(TARGET_NAME)"; 227 | }; 228 | name = Release; 229 | }; 230 | /* End XCBuildConfiguration section */ 231 | 232 | /* Begin XCConfigurationList section */ 233 | A15A38E019D85BB000AF81A9 /* Build configuration list for PBXProject "TableThisForNow" */ = { 234 | isa = XCConfigurationList; 235 | buildConfigurations = ( 236 | A15A390219D85BB000AF81A9 /* Debug */, 237 | A15A390319D85BB000AF81A9 /* Release */, 238 | ); 239 | defaultConfigurationIsVisible = 0; 240 | defaultConfigurationName = Release; 241 | }; 242 | A15A390419D85BB000AF81A9 /* Build configuration list for PBXNativeTarget "TableThisForNow" */ = { 243 | isa = XCConfigurationList; 244 | buildConfigurations = ( 245 | A15A390519D85BB000AF81A9 /* Debug */, 246 | A15A390619D85BB000AF81A9 /* Release */, 247 | ); 248 | defaultConfigurationIsVisible = 0; 249 | defaultConfigurationName = Release; 250 | }; 251 | /* End XCConfigurationList section */ 252 | }; 253 | rootObject = A15A38DD19D85BB000AF81A9 /* Project object */; 254 | } 255 | -------------------------------------------------------------------------------- /code/TableThisForNow/TableThisForNow.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /code/TableThisForNow/TableThisForNow.xcodeproj/xcshareddata/xcschemes/TableThisForNow.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 68 | 70 | 76 | 77 | 78 | 79 | 80 | 81 | 87 | 89 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /code/magic.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/xcrun swift 2 | 3 | import Foundation 4 | import ObjectiveC.runtime 5 | 6 | class MyObject {} 7 | 8 | var ivarCount : UInt32 = 0 9 | var ivars : UnsafeMutablePointer = class_copyIvarList(class_getSuperclass(MyObject.self), &ivarCount) 10 | 11 | for i in 0..> “Swift’s clean slate [...] is an opportunity to reimagine how software development works.” 44 | 45 | ![](images/swift-bg.jpg) 46 | 47 | --- 48 | 49 | - Optionals 50 | - Tuples 51 | - Generics 52 | - Pattern matching 53 | - Operator overloading 54 | - Namespaces 55 | - Type inference 56 | - ... 57 | 58 | ![](images/swift-bg.jpg) 59 | 60 | --- 61 | 62 | # emoji identifiers! 🎉 63 | 64 | ```swift 65 | class 🍷 { 66 | func 💥() { 67 | } 68 | } 69 | ``` 70 | 71 | ![](images/swift-bg.jpg) 72 | 73 | --- 74 | 75 | # Interfaces with C, at full speed 76 | 77 | ## Watch *"Swift and C"* by Mike Ash 78 | 79 | ![left, fit](images/unsafe-tweet.png) 80 | 81 | --- 82 | 83 | ## Drops C++ interoperability 84 | 85 | ![](images/swift-bg.jpg) 86 | 87 | --- 88 | 89 | >> Everyone is a Beginner 90 | 91 | ![](images/veryfurrow.jpg) 92 | 93 | --- 94 | 95 | - The Swift Programming Language *by * 96 | 97 | ### and also 98 | 99 | - Swift by Tutorials: A Hands-On Approach 100 | - Your First Swift App 101 | - Functional Programming in Swift 102 | 103 | ![](images/swift-bg.jpg) 104 | 105 | --- 106 | 107 | # Swift command line tools 108 | 109 | ``` 110 | $ xcrun swift 111 | Welcome to Swift! Type :help for assistance. 112 | 113 | $ xcrun swiftc Foo.swift 114 | ## will compile an executable 115 | 116 | $ xcrun swift-stdlib-tool 117 | ## assembles libraries for an application bundle 118 | 119 | $ xcrun swift-demangle _TtCSs29_NativeDictionaryStorageOwner 120 | _TtCSs29_NativeDictionaryStorageOwner ---> Swift._NativeDictionaryStorageOwner 121 | ``` 122 | 123 | ![](images/laptop-hacker.jpg) 124 | 125 | --- 126 | 127 | ### *Let's drop close to the metal* 128 | 129 | ![](images/rob.gif) 130 | 131 | --- 132 | 133 | # What is a Swift object? 134 | 135 | ![](images/swift-bg.jpg) 136 | 137 | --- 138 | 139 | # It depends 140 | 141 | ![](images/swift-bg.jpg) 142 | 143 | --- 144 | 145 | ```swift 146 | class MyObject : NSObject { 147 | } 148 | ``` 149 | 150 | ![](images/swift-bg.jpg) 151 | 152 | --- 153 | 154 | - behaves like any old Objective-C object 155 | - instance variables are *properties* 156 | - fully interopable with ObjC 157 | 158 | ![](images/swift-bg.jpg) 159 | 160 | --- 161 | 162 | 163 | ```swift 164 | class MyObject { 165 | } 166 | ``` 167 | 168 | ![](images/swift-bg.jpg) 169 | 170 | --- 171 | 172 | - has *SwiftObject* as superclass 173 | - instance variables are *ivars* 174 | - ivars have no type encoding 175 | - methods are **not** ObjC methods 176 | - not interoperable with ObjC 177 | 178 | ![](images/swift-bg.jpg) 179 | 180 | --- 181 | 182 | ### Playground! 183 | 184 | ``` 185 | import ObjectiveC.runtime 186 | ``` 187 | 188 | but 189 | 190 | ``` 191 | Playground execution failed: Error in auto-import: 192 | failed to get module 'runtime' from AST context 193 | ``` 194 | 195 | (rdar://problem/18482380) 196 | 197 | ![](images/swift-bg.jpg) 198 | 199 | --- 200 | 201 | # 😢🐼 202 | 203 | ![](images/swift-bg.jpg) 204 | 205 | --- 206 | 207 | ### Demo: Inspect objects 208 | 209 | ![](images/swift-bg.jpg) 210 | 211 | --- 212 | 213 | ### SwiftObject 214 | 215 | Ivar: magic {SwiftObject_s="isa"^v"refCount"q} 216 | Protocol: NSObject 217 | 218 | ### NSObject 219 | 220 | Ivar: isa # 221 | Protocol: NSObject 222 | 223 | ![](images/swift-bg.jpg) 224 | 225 | --- 226 | 227 | ```swift 228 | class MySwiftClass { 229 | var foo = "bar"; 230 | 231 | init() { 232 | } 233 | } 234 | 235 | import Foundation 236 | import ObjectiveC.runtime 237 | 238 | var ivar = class_getInstanceVariable(MySwiftClass().dynamicType, "foo") 239 | var value : AnyObject = object_getIvar(MySwiftClass(), ivar)! 240 | ``` 241 | 242 | Segmentation fault: 11 243 | 244 | ![](images/swift-bg.jpg) 245 | 246 | --- 247 | 248 | ```objc 249 | #import 250 | #import 251 | 252 | @interface MyClass : NSObject 253 | 254 | @property (nonatomic, retain) NSString* foo; 255 | 256 | @end 257 | 258 | #pragma mark - 259 | 260 | @implementation MyClass 261 | 262 | -(instancetype)init { 263 | self = [super init]; 264 | if (self) { 265 | self.foo = @"bar"; 266 | } 267 | return self; 268 | } 269 | 270 | @end 271 | 272 | #pragma mark - 273 | 274 | int main(int argc, char *argv[]) 275 | { 276 | @autoreleasepool { 277 | MyClass* object = [MyClass new]; 278 | Ivar ivar = class_getInstanceVariable(object.class, "_foo"); 279 | id value = object_getIvar(object, ivar); 280 | NSLog(@"%@", value); 281 | return 0; 282 | } 283 | } 284 | ``` 285 | 286 | ![left](images/dinosuar-t-rex.jpg) 287 | 288 | --- 289 | 290 | # value types should be *structs* 291 | 292 | ```swift 293 | struct MyObject { 294 | var a : String 295 | var b : Array 296 | } 297 | ``` 298 | 299 | ![](images/swift-bg.jpg) 300 | 301 | --- 302 | 303 | ## In pure Swift, there's no introspection 😭 304 | 305 | ![](images/swift-bg.jpg) 306 | 307 | --- 308 | 309 | ## There is hope 310 | 311 | ```swift 312 | /// How children of this value should be presented in the IDE. 313 | enum MirrorDisposition { 314 | case Struct 315 | case Class 316 | case Enum 317 | case Tuple 318 | [...] 319 | } 320 | 321 | /// A protocol that provides a reflection interface to an underlying value. 322 | protocol MirrorType { 323 | [...] 324 | 325 | /// Get the number of logical children this value has. 326 | var count: Int { get } 327 | subscript (i: Int) -> (String, MirrorType) { get } 328 | 329 | /// Get a string description of this value. 330 | var summary: String { get } 331 | 332 | [...] 333 | } 334 | ``` 335 | 336 | ![](images/swift-bg.jpg) 337 | 338 | --- 339 | 340 | ```swift 341 | // From: https://gist.github.com/peebsjs/9288f79322ed3119ece4 342 | 343 | infix operator --> {} 344 | func --> (instance: Any, key: String) -> Any? { 345 | let mirror = reflect(instance) 346 | 347 | for index in 0 ..< mirror.count { 348 | let (childKey, childMirror) = mirror[index] 349 | if childKey == key { 350 | return childMirror.value 351 | } 352 | } 353 | 354 | return nil 355 | } 356 | 357 | //Example 358 | struct MyPoint { 359 | let x: Float 360 | let y: Float 361 | } 362 | 363 | let point = MyPoint(x: 1, y: 2) 364 | println(point --> "x") 365 | println(point --> "y") 366 | ``` 367 | 368 | ![](images/swift-bg.jpg) 369 | 370 | --- 371 | 372 | # Objective-C runtime in the age of Swift 373 | 374 | ![](images/dinosuar-t-rex.jpg) 375 | 376 | --- 377 | 378 | ## Inherit from `NSObject` and it just works! 379 | 380 | ![](images/dinosuar-t-rex.jpg) 381 | 382 | --- 383 | 384 | # Even swizzling 😱 385 | 386 | ![](images/dinosuar-t-rex.jpg) 387 | 388 | --- 389 | 390 | ```swift 391 | import Foundation 392 | import ObjectiveC.runtime 393 | 394 | extension NSString { 395 | func swizzle_description() -> NSString { 396 | return "💥" 397 | } 398 | } 399 | 400 | var myString = "foobar" as NSString 401 | 402 | println(myString.description) 403 | 404 | var originalMethod = class_getInstanceMethod(NSString.self, "description") 405 | var swizzledMethod = class_getInstanceMethod(NSString.self, "swizzle_description") 406 | 407 | method_exchangeImplementations(originalMethod, swizzledMethod) 408 | 409 | println(myString.description) 410 | ``` 411 | 412 | ![](images/swift-bg.jpg) 413 | 414 | --- 415 | 416 | ![](images/wat.jpg) 417 | 418 | --- 419 | 420 | ![250%](images/swizzle.jpg) 421 | 422 | --- 423 | 424 | # Or replacing methods 425 | 426 | ``` 427 | import Foundation 428 | import ObjectiveC.runtime 429 | 430 | let myString = "foobar" as NSString 431 | 432 | println(myString.description) 433 | 434 | let myBlock : @objc_block (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in 435 | "✋" 436 | } 437 | 438 | let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self)) 439 | let method = class_getInstanceMethod(myString.dynamicType, "description") 440 | method_setImplementation(method, myIMP) 441 | 442 | println(myString.description) 443 | ``` 444 | 445 | ![](images/swift-bg.jpg) 446 | 447 | --- 448 | 449 | ## *Let's take a step back* 450 | 451 | ![](images/fall-back.gif) 452 | 453 | --- 454 | 455 | # Objects 456 | 457 | ```c 458 | typedef struct objc_object { 459 | Class isa; 460 | } *id; 461 | ``` 462 | 463 | ![](images/dinosuar-t-rex.jpg) 464 | 465 | --- 466 | 467 | # Classes 468 | 469 | ```c 470 | struct objc_class { 471 | Class isa; 472 | 473 | #if !__OBJC2__ 474 | Class super_class OBJC2_UNAVAILABLE; 475 | const char *name OBJC2_UNAVAILABLE; 476 | long version OBJC2_UNAVAILABLE; 477 | long info OBJC2_UNAVAILABLE; 478 | long instance_size OBJC2_UNAVAILABLE; 479 | struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; 480 | struct objc_method_list **methodLists OBJC2_UNAVAILABLE; 481 | struct objc_cache *cache OBJC2_UNAVAILABLE; 482 | struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; 483 | #endif 484 | } OBJC2_UNAVAILABLE; 485 | ``` 486 | 487 | ![](images/dinosuar-t-rex.jpg) 488 | 489 | --- 490 | 491 | ## Objects 492 | 493 | - struct `magic` 494 | - contains `refCount` and `isa` 495 | - methods are in virtual table, like in C++ 496 | 497 | ## Classes 498 | 499 | - have mangled names, which contain the module name 500 | 501 | ![](images/swift-bg.jpg) 502 | 503 | --- 504 | 505 | # Name mangling 506 | 507 | another C++ concept 508 | 509 | ``` 510 | _TFV4test1eCfMS0_FT_S0_ ---> test.e.init (test.e.Type)() -> test.e 511 | _TMLCCC4test1a1b1c ---> lazy cache variable for type metadata for test.a.b.c 512 | _TMmCCC4test1a1b1c ---> metaclass for test.a.b.c 513 | _TMnCC4test1a1b ---> nominal type descriptor for test.a.b 514 | 515 | _TTWOV4test1e1fSs9EquatableFS2_oi2eeUS2___fMQPS2_FTS3_S3__Sb ---> protocol witness for Swift. 516 | Equatable.== infix (Swift.Equatable.Self.Type) 517 | (Swift.Equatable.Self, Swift.Equatable.Self) -> Swift.Bool in conformance test.e.f : Swift.Equatable 518 | 519 | _TWoFC4test1aCfMS0_FT_S0_ ---> witness table offset for 520 | test.a.__allocating_init (test.a.Type)() -> test.a 521 | 522 | _TWoFCCC4test1a1b1c1dfS2_FT1zS0_1xS1_1vFT1xSi_Si_OVS_1e1f ---> witness table offset 523 | for test.a.b.c.d (test.a.b.c)(z : test.a, x : test.a.b, v : 524 | (x : Swift.Int) -> Swift.Int) -> test.e.f 525 | ``` 526 | 527 | --- 528 | 529 | # How are emoji formed? 530 | 531 | ``` 532 | $ echo 'class 👍 {}'|xcrun swiftc -emit-library -o test - 533 | $ nm -g test 534 | ... 535 | 0000000000000db0 T __TFC4testX4ypIhD 536 | ... 537 | $ xcrun swift-demangle __TFC4testX4ypIhD 538 | _TFC4testX4ypIhD ---> test.👍.__deallocating_deinit 539 | ``` 540 | 541 | *X4 ypIh* ~ *xn--yp8h* 542 | 543 | --- 544 | 545 | # Methods 546 | 547 | ```c 548 | struct objc_method { 549 | SEL method_name OBJC2_UNAVAILABLE; 550 | char *method_types OBJC2_UNAVAILABLE; 551 | IMP method_imp OBJC2_UNAVAILABLE; 552 | } OBJC2_UNAVAILABLE; 553 | ``` 554 | 555 | ![](images/dinosuar-t-rex.jpg) 556 | 557 | --- 558 | 559 | # Method Implementations 560 | 561 | ```c 562 | typedef struct objc_selector *SEL; 563 | 564 | typedef id (*IMP)(id self, SEL _cmd ,...); 565 | ``` 566 | 567 | ![](images/dinosuar-t-rex.jpg) 568 | 569 | --- 570 | 571 | # Message forwarding 572 | 573 | ```c 574 | +(BOOL)resolveInstanceMethod:(SEL)aSEL; 575 | 576 | -(void)forwardInvocation:(NSInvocation*)anInvocation; 577 | 578 | -(NSMethodSignature*)methodSignatureForSelector:(SEL)selector; 579 | 580 | -(BOOL)respondsToSelector:(SEL)aSelector; 581 | ``` 582 | 583 | ![](images/dinosuar-t-rex.jpg) 584 | 585 | --- 586 | 587 | # From *UIViewController.h* 588 | 589 | ```c 590 | - (void)attentionClassDumpUser:(id)arg1 591 | yesItsUsAgain:(id)arg2 592 | althoughSwizzlingAndOverridingPrivateMethodsIsFun:(id)arg3 593 | itWasntMuchFunWhenYourAppStoppedWorking:(id)arg4 594 | pleaseRefrainFromDoingSoInTheFutureOkayThanksBye:(id)arg5; 595 | ``` 596 | 597 | ![](images/Apple-logo.png) 598 | 599 | --- 600 | 601 | # Change classes at runtime 602 | 603 | - method_setImplementation() 604 | 605 | - class_addMethod() 606 | 607 | - ... 608 | 609 | ![](images/dinosuar-t-rex.jpg) 610 | 611 | --- 612 | 613 | ### Demo: dynamic table view 614 | 615 | ![](images/swift-bg.jpg) 616 | 617 | --- 618 | 619 | ### NSInvocation does not exist 620 | 621 | ![180%](images/WcCXCSZ.gif) 622 | 623 | --- 624 | 625 | ## But what can we do about pure Swift? 626 | 627 | ![](images/swift-bg.jpg) 628 | 629 | --- 630 | 631 | # SWRoute 632 | 633 | - PoC of function hooking in Swift 634 | - Uses `rd_route`, a Mach specific injection library for C 635 | 636 | ![](images/swift-bg.jpg) 637 | 638 | --- 639 | 640 | ```c 641 | #include 642 | 643 | #define kObjectFieldOffset sizeof(uintptr_t) 644 | 645 | struct swift_func_object { 646 | uintptr_t *original_type_ptr; 647 | #if defined(__x86_64__) 648 | uintptr_t *unknown0; 649 | #else 650 | uintptr_t *unknown0, *unknown1; 651 | #endif 652 | uintptr_t function_address; 653 | uintptr_t *self; 654 | }; 655 | 656 | uintptr_t _rd_get_func_impl(void *func) { 657 | struct swift_func_object *obj = (struct swift_func_object *)*(uintptr_t *)(func + kObjectFieldOffset); 658 | 659 | return obj->function_address; 660 | } 661 | ``` 662 | 663 | ![](images/swift-bg.jpg) 664 | 665 | --- 666 | 667 | # Swift runtime 668 | 669 | ![40%](images/swift-logo.png) 670 | 671 | --- 672 | 673 | - libswiftCore.dylib 674 | 675 | implementations of `NSSwiftArray`, etc. 676 | 677 | - libswiftRuntime.a 678 | 679 | low-level primitives like `swift_release` 680 | 681 | ![](images/swift-bg.jpg) 682 | 683 | --- 684 | 685 | # Hopper 686 | 687 | ![](images/Hopper_v3.png) 688 | 689 | --- 690 | 691 | ## Compatibility 692 | 693 | - App Compatibility ✅ 694 | - Binary Compatibility ⛔️ 695 | - Source Compatibility ⛔️ 696 | 697 | ![](images/swift-bg.jpg) 698 | 699 | --- 700 | 701 | ``` 702 | Foo.app boris$ find . -type f 703 | ./Frameworks/libswiftCore.dylib 704 | ./Frameworks/libswiftCoreGraphics.dylib 705 | ./Frameworks/libswiftCoreImage.dylib 706 | ./Frameworks/libswiftDarwin.dylib 707 | ./Frameworks/libswiftDispatch.dylib 708 | ./Frameworks/libswiftFoundation.dylib 709 | ./Frameworks/libswiftObjectiveC.dylib 710 | ./Frameworks/libswiftUIKit.dylib 711 | ./Info.plist 712 | ./PkgInfo 713 | ./Foo 714 | ``` 715 | 716 | ![](images/swift-bg.jpg) 717 | 718 | --- 719 | 720 | # Two final tidbits 721 | 722 | ![](images/swift-bg.jpg) 723 | 724 | --- 725 | 726 | # Speed 727 | 728 | - less dynamic dispatch 729 | - omits `_cmd` - freeing one register 730 | - usually no pointer aliasing 731 | 732 | ``` 733 | int *ptrA = malloc(100 * sizeof(*ptrA)); 734 | int *ptrB = ptrA; 735 | ``` 736 | 737 | ![](images/swift-bg.jpg) 738 | 739 | --- 740 | 741 | ``` 742 | class BankAccount { 743 | var balance: Double = 0.0 744 | 745 | func deposit(amount: Double) { 746 | balance += amount 747 | } 748 | } 749 | 750 | let account = BankAccount() 751 | account.deposit(100) 752 | 753 | let depositor = BankAccount.deposit 754 | depositor(account)(100) 755 | 756 | BankAccount.deposit(account)(100) 757 | ``` 758 | 759 | ![](images/swift-bg.jpg) 760 | 761 | --- 762 | 763 | ## Methods are curried functions 764 | 765 | ![](images/azb6mBK_460sa_v1.gif) 766 | 767 | --- 768 | 769 | ![](images/799569.gif) 770 | 771 | --- 772 | 773 | # You don't need Objective-C anymore 774 | 775 | ![](images/25yuswsw28295.gif) 776 | 777 | --- 778 | 779 | ## Unless you build frameworks or need to work with C++ 780 | 781 | ![left](images/5RSgg6y.gif) 782 | 783 | --- 784 | 785 | ## But the ObjC runtime is still strong 786 | 787 | ![right](images/shia-labeouf-magic-gif.gif) 788 | 789 | --- 790 | 791 | # Thank you! 792 | 793 | ![](images/three-hands.gif) 794 | 795 | --- 796 | 797 | - https://www.mikeash.com/pyblog/ 798 | - http://airspeedvelocity.net/ 799 | - https://developer.apple.com/swift/blog/ 800 | - http://www.russbishop.net/swift-how-did-i-do-horrible-things 801 | 802 | --- 803 | 804 | @NeoNacho 805 | 806 | boris@contentful.com 807 | 808 | http://buegling.com/talks 809 | 810 | ![](images/cocoapods.jpg) 811 | 812 | -------------------------------------------------------------------------------- /swift-libs: -------------------------------------------------------------------------------- 1 | /Applications/Xcode-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -------------------------------------------------------------------------------- /swift.berlin-funtime.md: -------------------------------------------------------------------------------- 1 | # Swift funtime 2 | 3 | ## swift.berlin #7 4 | 5 | ### Boris Bügling - @NeoNacho 6 | 7 | ![20%, original, inline](images/contentful.png) 8 | 9 | ![](images/swift-slide.png) 10 | 11 | --- 12 | 13 | ## CocoaPods 14 | 15 | ![](images/cocoapods.jpg) 16 | 17 | --- 18 | 19 | ## Contentful 20 | 21 | ![](images/contentful-bg.png) 22 | 23 | --- 24 | 25 | >> “Swift’s clean slate [...] is an opportunity to reimagine how software development works.” 26 | 27 | ![](images/taylor-swift.jpg) 28 | 29 | --- 30 | 31 | # Agenda 32 | 33 | - What is a Swift object? 34 | - Objective-C runtime in the age of Swift 35 | - Swift runtime 36 | 37 | ![](images/swift-bg.jpg) 38 | 39 | --- 40 | 41 | # What is a Swift object? 42 | 43 | ![](images/swift-bg.jpg) 44 | 45 | --- 46 | 47 | # It depends 48 | 49 | ![](images/swift-bg.jpg) 50 | 51 | --- 52 | 53 | ```swift 54 | class MyObject : NSObject { 55 | } 56 | ``` 57 | 58 | ![](images/swift-bg.jpg) 59 | 60 | --- 61 | 62 | - behaves like any old Objective-C object 63 | - instance variables are *properties* 64 | - fully interopable with ObjC 65 | 66 | ![](images/swift-bg.jpg) 67 | 68 | --- 69 | 70 | 71 | ```swift 72 | class MyObject { 73 | } 74 | ``` 75 | 76 | ![](images/swift-bg.jpg) 77 | 78 | --- 79 | 80 | - has *SwiftObject* as superclass 81 | - instance variables are *ivars* 82 | - ivars have no type encoding 83 | - methods are **not** ObjC methods 84 | - not interoperable with ObjC 85 | 86 | ![](images/swift-bg.jpg) 87 | 88 | --- 89 | 90 | ### Playground! 91 | 92 | ``` 93 | import ObjectiveC.runtime 94 | ``` 95 | 96 | but 97 | 98 | ``` 99 | Playground execution failed: Error in auto-import: 100 | failed to get module 'runtime' from AST context 101 | ``` 102 | 103 | (rdar://problem/18482380) 104 | 105 | ![](images/swift-bg.jpg) 106 | 107 | --- 108 | 109 | # 😢🐼 110 | 111 | ![](images/swift-bg.jpg) 112 | 113 | --- 114 | 115 | ### SwiftObject 116 | 117 | Ivar: magic {SwiftObject_s="isa"^v"refCount"q} 118 | Protocol: NSObject 119 | 120 | ### NSObject 121 | 122 | Ivar: isa # 123 | Protocol: NSObject 124 | 125 | ![](images/swift-bg.jpg) 126 | 127 | --- 128 | 129 | ```swift 130 | class MySwiftClass { 131 | var foo = "bar"; 132 | 133 | init() { 134 | } 135 | } 136 | 137 | import Foundation 138 | import ObjectiveC.runtime 139 | 140 | var ivar = class_getInstanceVariable(MySwiftClass().dynamicType, "foo") 141 | var value : AnyObject = object_getIvar(MySwiftClass(), ivar)! 142 | ``` 143 | 144 | Segmentation fault: 11 145 | 146 | ![](images/swift-bg.jpg) 147 | 148 | --- 149 | 150 | ```objc 151 | #import 152 | #import 153 | 154 | @interface MyClass : NSObject 155 | 156 | @property (nonatomic, retain) NSString* foo; 157 | 158 | @end 159 | 160 | #pragma mark - 161 | 162 | @implementation MyClass 163 | 164 | -(instancetype)init { 165 | self = [super init]; 166 | if (self) { 167 | self.foo = @"bar"; 168 | } 169 | return self; 170 | } 171 | 172 | @end 173 | 174 | #pragma mark - 175 | 176 | int main(int argc, char *argv[]) 177 | { 178 | @autoreleasepool { 179 | MyClass* object = [MyClass new]; 180 | Ivar ivar = class_getInstanceVariable(object.class, "_foo"); 181 | id value = object_getIvar(object, ivar); 182 | NSLog(@"%@", value); 183 | return 0; 184 | } 185 | } 186 | ``` 187 | 188 | ![left](images/dinosuar-t-rex.jpg) 189 | 190 | --- 191 | 192 | # value types should be *structs* 193 | 194 | ```swift 195 | struct MyObject { 196 | var a : String 197 | var b : Array 198 | } 199 | ``` 200 | 201 | ![](images/swift-bg.jpg) 202 | 203 | --- 204 | 205 | ## In pure Swift, there's no introspection 😭 206 | 207 | ![](images/swift-bg.jpg) 208 | 209 | --- 210 | 211 | ## There is hope 212 | 213 | ```swift 214 | /// How children of this value should be presented in the IDE. 215 | enum MirrorDisposition { 216 | case Struct 217 | case Class 218 | case Enum 219 | case Tuple 220 | [...] 221 | } 222 | 223 | /// A protocol that provides a reflection interface to an underlying value. 224 | protocol MirrorType { 225 | [...] 226 | 227 | /// Get the number of logical children this value has. 228 | var count: Int { get } 229 | subscript (i: Int) -> (String, MirrorType) { get } 230 | 231 | /// Get a string description of this value. 232 | var summary: String { get } 233 | 234 | [...] 235 | } 236 | ``` 237 | 238 | ![](images/swift-bg.jpg) 239 | 240 | --- 241 | 242 | ```swift 243 | // From: https://gist.github.com/peebsjs/9288f79322ed3119ece4 244 | 245 | infix operator --> {} 246 | func --> (instance: Any, key: String) -> Any? { 247 | let mirror = reflect(instance) 248 | 249 | for index in 0 ..< mirror.count { 250 | let (childKey, childMirror) = mirror[index] 251 | if childKey == key { 252 | return childMirror.value 253 | } 254 | } 255 | 256 | return nil 257 | } 258 | 259 | //Example 260 | struct MyPoint { 261 | let x: Float 262 | let y: Float 263 | } 264 | 265 | let point = MyPoint(x: 1, y: 2) 266 | println(point --> "x") 267 | println(point --> "y") 268 | ``` 269 | 270 | ![](images/swift-bg.jpg) 271 | 272 | --- 273 | 274 | ```swift 275 | #!/usr/bin/env xcrun swift 276 | 277 | func info(x: T) { 278 | println("\(x) is a \(_stdlib_getDemangledTypeName(x))") 279 | } 280 | 281 | 282 | let array = [0, 1, 2] // appending 'as AnyObject' here yields a compiler error 283 | info(array) 284 | 285 | import Foundation 286 | 287 | let objc_array: AnyObject = [0, 1, 2] as AnyObject 288 | info(objc_array) 289 | 290 | // comparing different array types => compiler error as well 291 | //let equal = objc_array == array 292 | ``` 293 | 294 | ![](images/swift-bg.jpg) 295 | 296 | --- 297 | 298 | # Objective-C runtime in the age of Swift 299 | 300 | ![](images/dinosuar-t-rex.jpg) 301 | 302 | --- 303 | 304 | ## Inherit from `NSObject` and it just works! 305 | 306 | ![](images/dinosuar-t-rex.jpg) 307 | 308 | --- 309 | 310 | # Even swizzling 😱 311 | 312 | ![](images/dinosuar-t-rex.jpg) 313 | 314 | --- 315 | 316 | ```swift 317 | import Foundation 318 | import ObjectiveC.runtime 319 | 320 | extension NSString { 321 | func swizzle_description() -> NSString { 322 | return "💥" 323 | } 324 | } 325 | 326 | var myString = "foobar" as NSString 327 | 328 | println(myString.description) 329 | 330 | var originalMethod = class_getInstanceMethod(NSString.self, "description") 331 | var swizzledMethod = class_getInstanceMethod(NSString.self, "swizzle_description") 332 | 333 | method_exchangeImplementations(originalMethod, swizzledMethod) 334 | 335 | println(myString.description) 336 | ``` 337 | 338 | ![](images/swift-bg.jpg) 339 | 340 | --- 341 | 342 | ![](images/wat.jpg) 343 | 344 | --- 345 | 346 | ![250%](images/swizzle.jpg) 347 | 348 | --- 349 | 350 | # Or replacing methods 351 | 352 | ``` 353 | import Foundation 354 | import ObjectiveC.runtime 355 | 356 | let myString = "foobar" as NSString 357 | 358 | println(myString.description) 359 | 360 | let myBlock : @objc_block (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in 361 | "✋" 362 | } 363 | 364 | let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self)) 365 | let method = class_getInstanceMethod(myString.dynamicType, "description") 366 | method_setImplementation(method, myIMP) 367 | 368 | println(myString.description) 369 | ``` 370 | 371 | ![](images/swift-bg.jpg) 372 | 373 | --- 374 | 375 | ## *Let's take a step back* 376 | 377 | ![](images/fall-back.gif) 378 | 379 | --- 380 | 381 | # Objects 382 | 383 | ```c 384 | typedef struct objc_object { 385 | Class isa; 386 | } *id; 387 | ``` 388 | 389 | ![](images/dinosuar-t-rex.jpg) 390 | 391 | --- 392 | 393 | # Classes 394 | 395 | ```c 396 | struct objc_class { 397 | Class isa; 398 | 399 | #if !__OBJC2__ 400 | Class super_class OBJC2_UNAVAILABLE; 401 | const char *name OBJC2_UNAVAILABLE; 402 | long version OBJC2_UNAVAILABLE; 403 | long info OBJC2_UNAVAILABLE; 404 | long instance_size OBJC2_UNAVAILABLE; 405 | struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; 406 | struct objc_method_list **methodLists OBJC2_UNAVAILABLE; 407 | struct objc_cache *cache OBJC2_UNAVAILABLE; 408 | struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; 409 | #endif 410 | } OBJC2_UNAVAILABLE; 411 | ``` 412 | 413 | ![](images/dinosuar-t-rex.jpg) 414 | 415 | --- 416 | 417 | ## Objects 418 | 419 | - struct `magic` 420 | - contains `refCount` and `isa` 421 | - methods are in virtual table, like in C++ 422 | 423 | ## Classes 424 | 425 | - have mangled names, which contain the module name 426 | 427 | ![](images/swift-bg.jpg) 428 | 429 | --- 430 | 431 | # Name mangling 432 | 433 | another C++ concept 434 | 435 | ``` 436 | _TFV4test1eCfMS0_FT_S0_ ---> test.e.init (test.e.Type)() -> test.e 437 | _TMLCCC4test1a1b1c ---> lazy cache variable for type metadata for test.a.b.c 438 | _TMmCCC4test1a1b1c ---> metaclass for test.a.b.c 439 | _TMnCC4test1a1b ---> nominal type descriptor for test.a.b 440 | 441 | _TTWOV4test1e1fSs9EquatableFS2_oi2eeUS2___fMQPS2_FTS3_S3__Sb ---> protocol witness for Swift. 442 | Equatable.== infix (Swift.Equatable.Self.Type) 443 | (Swift.Equatable.Self, Swift.Equatable.Self) -> Swift.Bool in conformance test.e.f : Swift.Equatable 444 | 445 | _TWoFC4test1aCfMS0_FT_S0_ ---> witness table offset for 446 | test.a.__allocating_init (test.a.Type)() -> test.a 447 | 448 | _TWoFCCC4test1a1b1c1dfS2_FT1zS0_1xS1_1vFT1xSi_Si_OVS_1e1f ---> witness table offset 449 | for test.a.b.c.d (test.a.b.c)(z : test.a, x : test.a.b, v : 450 | (x : Swift.Int) -> Swift.Int) -> test.e.f 451 | ``` 452 | 453 | --- 454 | 455 | # How are emoji formed? 456 | 457 | ``` 458 | $ echo 'class 👍 {}'|xcrun swiftc -emit-library -o test - 459 | $ nm -g test 460 | ... 461 | 0000000000000db0 T __TFC4testX4ypIhD 462 | ... 463 | $ xcrun swift-demangle __TFC4testX4ypIhD 464 | _TFC4testX4ypIhD ---> test.👍.__deallocating_deinit 465 | ``` 466 | 467 | *X4 ypIh* ~ *xn--yp8h* 468 | 469 | --- 470 | 471 | # Methods 472 | 473 | ```c 474 | struct objc_method { 475 | SEL method_name OBJC2_UNAVAILABLE; 476 | char *method_types OBJC2_UNAVAILABLE; 477 | IMP method_imp OBJC2_UNAVAILABLE; 478 | } OBJC2_UNAVAILABLE; 479 | ``` 480 | 481 | ![](images/dinosuar-t-rex.jpg) 482 | 483 | --- 484 | 485 | # Method Implementations 486 | 487 | ```c 488 | typedef struct objc_selector *SEL; 489 | 490 | typedef id (*IMP)(id self, SEL _cmd ,...); 491 | ``` 492 | 493 | ![](images/dinosuar-t-rex.jpg) 494 | 495 | --- 496 | 497 | # Message forwarding 498 | 499 | ```c 500 | +(BOOL)resolveInstanceMethod:(SEL)aSEL; 501 | 502 | -(void)forwardInvocation:(NSInvocation*)anInvocation; 503 | 504 | -(NSMethodSignature*)methodSignatureForSelector:(SEL)selector; 505 | 506 | -(BOOL)respondsToSelector:(SEL)aSelector; 507 | ``` 508 | 509 | ![](images/dinosuar-t-rex.jpg) 510 | 511 | --- 512 | 513 | # From *UIViewController.h* 514 | 515 | ```c 516 | - (void)attentionClassDumpUser:(id)arg1 517 | yesItsUsAgain:(id)arg2 518 | althoughSwizzlingAndOverridingPrivateMethodsIsFun:(id)arg3 519 | itWasntMuchFunWhenYourAppStoppedWorking:(id)arg4 520 | pleaseRefrainFromDoingSoInTheFutureOkayThanksBye:(id)arg5; 521 | ``` 522 | 523 | ![](images/Apple-logo.png) 524 | 525 | --- 526 | 527 | # Change classes at runtime 528 | 529 | - method_setImplementation() 530 | 531 | - class_addMethod() 532 | 533 | - ... 534 | 535 | ![](images/dinosuar-t-rex.jpg) 536 | 537 | --- 538 | 539 | ### NSInvocation does not exist 540 | 541 | ![180%](images/WcCXCSZ.gif) 542 | 543 | --- 544 | 545 | ## But what can we do about pure Swift? 546 | 547 | ![](images/swift-bg.jpg) 548 | 549 | --- 550 | 551 | # SWRoute 552 | 553 | - PoC of function hooking in Swift 554 | - Uses `rd_route`, a Mach specific injection library for C 555 | 556 | ![](images/swift-bg.jpg) 557 | 558 | --- 559 | 560 | ```c 561 | #include 562 | 563 | #define kObjectFieldOffset sizeof(uintptr_t) 564 | 565 | struct swift_func_object { 566 | uintptr_t *original_type_ptr; 567 | #if defined(__x86_64__) 568 | uintptr_t *unknown0; 569 | #else 570 | uintptr_t *unknown0, *unknown1; 571 | #endif 572 | uintptr_t function_address; 573 | uintptr_t *self; 574 | }; 575 | 576 | uintptr_t _rd_get_func_impl(void *func) { 577 | struct swift_func_object *obj = (struct swift_func_object *)*(uintptr_t *)(func + kObjectFieldOffset); 578 | 579 | return obj->function_address; 580 | } 581 | ``` 582 | 583 | ![](images/swift-bg.jpg) 584 | 585 | --- 586 | 587 | # What is a function? 588 | 589 | ```swift 590 | func add(a: Int, b: Int) -> Int { 591 | return a + b 592 | } 593 | 594 | let f = add 595 | 596 | f(1, 2) 597 | // $R0: Int = 3 598 | 599 | println(f) 600 | // (Function) 601 | ``` 602 | 603 | ![](images/swift-bg.jpg) 604 | 605 | --- 606 | 607 | # Name mangling 608 | 609 | ``` 610 | $ xcrun swiftc func.swift 611 | $ nm -g func 612 | 0000000100000f10 T __TF4func3addFTSiSi_Si 613 | [...] 614 | $ xcrun swift-demangle __TF4func3addFTSiSi_Si 615 | _TF4func3addFTSiSi_Si ---> func.add (Swift.Int, Swift.Int) -> Swift.Int 616 | ``` 617 | 618 | ![](images/swift-bg.jpg) 619 | 620 | --- 621 | 622 | # Memory layout 623 | 624 | - 8 bytes => Pointer to `_TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_` 625 | - 8 bytes => Pointer to struct 626 | 627 | ``` 628 | _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ---> 629 | partial apply forwarder for reabstraction thunk helper 630 | [...] 631 | ``` 632 | 633 | ![](images/swift-bg.jpg) 634 | 635 | --- 636 | 637 | # Memory layout 638 | 639 | - 16 bytes => Swift object 640 | - 8 bytes => Pointer to `_TF6memory3addFTSiSi_Si` 641 | 642 | __Function pointer__ 🎉 643 | 644 | ![](images/swift-bg.jpg) 645 | 646 | --- 647 | 648 | ```swift 649 | struct f_trampoline { 650 | var trampoline_ptr: COpaquePointer 651 | var function_obj_ptr: UnsafeMutablePointer 652 | } 653 | 654 | struct function_obj { 655 | var some_ptr_0: COpaquePointer 656 | var some_ptr_1: COpaquePointer 657 | var function_ptr: COpaquePointer 658 | } 659 | ``` 660 | 661 | ![](images/swift-bg.jpg) 662 | 663 | --- 664 | 665 | ```swift 666 | import Darwin 667 | 668 | @asmname("floor") func my_floor(dbl: Double) -> Double 669 | println(my_floor(6.7)) 670 | 671 | let handle = dlopen(nil, RTLD_NOW) 672 | let pointer = COpaquePointer(dlsym(handle, "ceil")) 673 | 674 | typealias FunctionType = (Double) -> Double 675 | ``` 676 | 677 | ![](images/swift-bg.jpg) 678 | 679 | --- 680 | 681 | ```swift 682 | struct f_trampoline { [...] } 683 | struct function_obj { [...] } 684 | 685 | let orig = unsafeBitCast(my_floor, f_trampoline.self) 686 | let new = f_trampoline(prototype: orig, new_fp: pointer) 687 | let my_ceil = unsafeBitCast(new, FunctionType.self) 688 | println(my_ceil(6.7)) 689 | ``` 690 | 691 | ![](images/swift-bg.jpg) 692 | 693 | --- 694 | 695 | ``` 696 | $ xcrun swift -Onone hook.swift 697 | 6.0 698 | 7.0 699 | ``` 700 | 701 | ![](images/swift-bg.jpg) 702 | 703 | --- 704 | 705 | ```c 706 | void executeFunction(void(*f)(void)) { 707 | f(); 708 | } 709 | ``` 710 | 711 | ```swift 712 | @asmname("executeFunction") func 713 | executeFunction(fp: CFunctionPointer<()->()>) 714 | ``` 715 | 716 | ![](images/swift-bg.jpg) 717 | 718 | --- 719 | 720 | ```swift 721 | func greeting() { 722 | println("Hello from Swift") 723 | } 724 | 725 | let t = unsafeBitCast(greeting, f_trampoline.self) 726 | let fp = CFunctionPointer<()->()> 727 | (t.function_obj_ptr.memory.function_ptr) 728 | executeFunction(fp) 729 | ``` 730 | 731 | ``` 732 | Hello from Swift 733 | Program ended with exit code: 0 734 | ``` 735 | 736 | ![](images/swift-bg.jpg) 737 | 738 | --- 739 | 740 | # Swift runtime 741 | 742 | ![40%](images/swift-logo.png) 743 | 744 | --- 745 | 746 | - libswiftCore.dylib 747 | 748 | implementations of `NSSwiftArray`, etc. 749 | 750 | - libswiftRuntime.a 751 | 752 | low-level primitives like `swift_release` 753 | 754 | ![](images/swift-bg.jpg) 755 | 756 | --- 757 | 758 | # Hopper 759 | 760 | ![](images/Hopper_v3.png) 761 | 762 | --- 763 | 764 | ## Compatibility 765 | 766 | - App Compatibility ✅ 767 | - Binary Compatibility ⛔️ 768 | - Source Compatibility ⛔️ 769 | 770 | ![](images/swift-bg.jpg) 771 | 772 | --- 773 | 774 | ``` 775 | Foo.app boris$ find . -type f 776 | ./Frameworks/libswiftCore.dylib 777 | ./Frameworks/libswiftCoreGraphics.dylib 778 | ./Frameworks/libswiftCoreImage.dylib 779 | ./Frameworks/libswiftDarwin.dylib 780 | ./Frameworks/libswiftDispatch.dylib 781 | ./Frameworks/libswiftFoundation.dylib 782 | ./Frameworks/libswiftObjectiveC.dylib 783 | ./Frameworks/libswiftUIKit.dylib 784 | ./Info.plist 785 | ./PkgInfo 786 | ./Foo 787 | ``` 788 | 789 | ![](images/swift-bg.jpg) 790 | 791 | --- 792 | 793 | # Two final tidbits 794 | 795 | ![](images/swift-bg.jpg) 796 | 797 | --- 798 | 799 | # Speed 800 | 801 | - less dynamic dispatch 802 | - omits `_cmd` - freeing one register 803 | - usually no pointer aliasing 804 | 805 | ``` 806 | int *ptrA = malloc(100 * sizeof(*ptrA)); 807 | int *ptrB = ptrA; 808 | ``` 809 | 810 | ![](images/swift-bg.jpg) 811 | 812 | --- 813 | 814 | ``` 815 | class BankAccount { 816 | var balance: Double = 0.0 817 | 818 | func deposit(amount: Double) { 819 | balance += amount 820 | } 821 | } 822 | 823 | let account = BankAccount() 824 | account.deposit(100) 825 | 826 | let depositor = BankAccount.deposit 827 | depositor(account)(100) 828 | 829 | BankAccount.deposit(account)(100) 830 | ``` 831 | 832 | ![](images/swift-bg.jpg) 833 | 834 | --- 835 | 836 | ## Methods are curried functions 837 | 838 | ![](images/azb6mBK_460sa_v1.gif) 839 | 840 | --- 841 | 842 | # Thank you! 843 | 844 | ![](images/4ovUfVD.gif) 845 | 846 | --- 847 | 848 | - https://www.mikeash.com/pyblog/ 849 | - http://airspeedvelocity.net/ 850 | - https://developer.apple.com/swift/blog/ 851 | - http://www.russbishop.net/swift-how-did-i-do-horrible-things 852 | 853 | --- 854 | 855 | @NeoNacho 856 | 857 | boris@contentful.com 858 | 859 | http://buegling.com/talks 860 | 861 | ![](images/cocoapods.jpg) 862 | 863 | -------------------------------------------------------------------------------- /swiftsummit-funtime.md: -------------------------------------------------------------------------------- 1 | # Swift funtime 🎉 2 | 3 | ## SwiftSummit, March 2015 4 | 5 | ### Boris Bügling - @NeoNacho 6 | 7 | ![20%, original, inline](images/contentful.png) 8 | 9 | ![](images/taylor-swift.jpg) 10 | 11 | 12 | 13 | --- 14 | 15 | ## CocoaPods 16 | 17 | ![](images/cocoapods.jpg) 18 | 19 | --- 20 | 21 | ## Contentful 22 | 23 | ![](images/contentful-bg.png) 24 | 25 | --- 26 | 27 | # 💖 28 | 29 | ![](images/swift.gif) 30 | 31 | --- 32 | 33 | ![](images/questions.gif) 34 | 35 | --- 36 | 37 | # What is a Swift object even? 38 | 39 | ![](images/taylor3.jpg) 40 | 41 | --- 42 | 43 | # Is it a bar? 44 | 45 | ![](images/bar.jpg) 46 | 47 | --- 48 | 49 | # It depends 50 | 51 | ![](images/taylor4.jpg) 52 | 53 | --- 54 | 55 | ``` 56 | class MyObject : NSObject { 57 | } 58 | ``` 59 | 60 | ![](images/taylor5.jpg) 61 | 62 | --- 63 | 64 | - behaves like any old Objective-C object 65 | - instance variables are *properties* 66 | - fully interopable with ObjC 67 | 68 | ![](images/taylor6.jpg) 69 | 70 | --- 71 | 72 | ``` 73 | import ObjectiveC.runtime 74 | ``` 75 | 76 | 🎉 77 | 78 | ![](images/taylor7.jpg) 79 | 80 | --- 81 | 82 | ``` 83 | class MyObject { 84 | } 85 | ``` 86 | 87 | ![](images/taylor-swift.jpg) 88 | 89 | --- 90 | 91 | - has *SwiftObject* as superclass 92 | - instance variables are *ivars* only 93 | - ivars have no type encoding 94 | - methods are **not** ObjC methods 95 | - not interoperable with ObjC 96 | 97 | ![](images/taylor2.jpg) 98 | 99 | --- 100 | 101 | ### SwiftObject 102 | 103 | ``` 104 | Ivar: magic {SwiftObject_s="isa"^v"refCount"q} 105 | Protocol: NSObject 106 | ``` 107 | 108 | ### NSObject 109 | 110 | ``` 111 | Ivar: isa # 112 | Protocol: NSObject 113 | ``` 114 | 115 | ![](images/taylor3.jpg) 116 | 117 | --- 118 | 119 | # How does bridging work, then? 120 | 121 | ![inline](images/work.gif) 122 | 123 | --- 124 | 125 | # It doesn't 126 | 127 | ![](images/azb6mBK_460sa_v1.gif) 128 | 129 | --- 130 | 131 | ```swift 132 | func info(x: T) { 133 | println("\(x) is a \(_stdlib_getDemangledTypeName(x))") 134 | } 135 | 136 | 137 | let array = [0, 1, 2] // 'as AnyObject' => 💥 138 | info(array) // is a Swift.Array 139 | 140 | import Foundation 141 | 142 | let objc_array: AnyObject = [0, 1, 2] as AnyObject 143 | info(objc_array) // is a Swift._NSSwiftArrayImpl 144 | 145 | // comparing different array types => compiler error as well 146 | //let equal = objc_array == array 147 | ``` 148 | 149 | ![](images/swift-bg.jpg) 150 | 151 | --- 152 | 153 | # Y did we 💖 the Objective-🐲 runtime? 154 | 155 | - Dynamic Introspection 🔍 156 | - Change Behaviour 🐒🔧 157 | - Analyse private API 👹 158 | 159 | ![](images/taylor2.jpg) 160 | 161 | --- 162 | 163 | # Dynamic introspection 164 | 165 | ![](images/taylor-swift.jpg) 166 | 167 | --- 168 | 169 | ```swift 170 | var propertyCount : UInt32 = 0 171 | var properties : UnsafeMutablePointer = 172 | class_copyPropertyList(myClass, &propertyCount) 173 | 174 | for i in 0.. 😭 185 | 186 | ![](images/taylor4.jpg) 187 | 188 | --- 189 | 190 | ## There is hope 191 | 192 | ```swift 193 | // Excerpt from the standard library 194 | 195 | /// How children of this value should be presented in the IDE. 196 | enum MirrorDisposition { 197 | case Struct 198 | case Class 199 | case Enum 200 | [...] 201 | } 202 | 203 | /// A protocol that provides a reflection interface to an underlying value. 204 | protocol MirrorType { 205 | [...] 206 | } 207 | ``` 208 | 209 | ![](images/swift-bg.jpg) 210 | 211 | --- 212 | 213 | ```swift 214 | infix operator --> {} 215 | func --> (instance: Any, key: String) -> Any? { 216 | let mirror = reflect(instance) 217 | 218 | for index in 0 ..< mirror.count { 219 | let (childKey, childMirror) = mirror[index] 220 | if childKey == key { 221 | return childMirror.value 222 | } 223 | } 224 | 225 | return nil 226 | } 227 | ``` 228 | 229 | ![](images/swift-bg.jpg) 230 | 231 | --- 232 | 233 | ```swift 234 | struct MyPoint { 235 | let x: Float 236 | let y: Float 237 | } 238 | 239 | let point = MyPoint(x: 1, y: 2) 240 | println(point --> "x") // Optional(1.0) 241 | println(point --> "y") // Optional(2.0) 242 | ``` 243 | 244 | ![](images/swift-bg.jpg) 245 | 246 | --- 247 | 248 | # Change behaviour 249 | 250 | ![](images/taylor5.jpg) 251 | 252 | --- 253 | 254 | ```swift 255 | let myString = "foobar" as NSString 256 | 257 | println(myString.description) // foobar 258 | 259 | let myBlock : @objc_block (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in 260 | "✋" 261 | } 262 | 263 | let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self)) 264 | let method = class_getInstanceMethod(myString.dynamicType, "description") 265 | method_setImplementation(method, myIMP) 266 | 267 | println(myString.description) // ✋ 268 | ``` 269 | 270 | ![](images/swift-bg.jpg) 271 | 272 | --- 273 | 274 | ### NSInvocation does not exist 275 | 276 | ![200%](images/WcCXCSZ.gif) 277 | 278 | --- 279 | 280 | # What about pure Swift? 281 | 282 | ![](images/taylor6.jpg) 283 | 284 | --- 285 | 286 | # SWRoute 287 | 288 | - PoC of function hooking in Swift 289 | - Uses `rd_route`, a Mach specific injection library for C 290 | 291 | ![](images/taylor7.jpg) 292 | 293 | --- 294 | 295 | ```c 296 | #include 297 | 298 | #define kObjectFieldOffset sizeof(uintptr_t) 299 | 300 | struct swift_func_object { 301 | uintptr_t *original_type_ptr; 302 | #if defined(__x86_64__) 303 | uintptr_t *unknown0; 304 | #else 305 | uintptr_t *unknown0, *unknown1; 306 | #endif 307 | uintptr_t function_address; 308 | uintptr_t *self; 309 | }; 310 | ``` 311 | 312 | ![](images/swift-bg.jpg) 313 | 314 | --- 315 | 316 | ```c 317 | uintptr_t _rd_get_func_impl(void *func) { 318 | struct swift_func_object *obj = (struct swift_func_object *) 319 | *(uintptr_t *)(func + kObjectFieldOffset); 320 | 321 | return obj->function_address; 322 | } 323 | ``` 324 | 325 | ![](images/swift-bg.jpg) 326 | 327 | --- 328 | 329 | # Let's do that in Swift 330 | 331 | ![](images/taylor-swift.jpg) 332 | 333 | --- 334 | 335 | # Memory layout 336 | 337 | - 8 bytes => Pointer to `_TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_` 338 | - 8 bytes => Pointer to struct 339 | 340 | ``` 341 | _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ---> 342 | partial apply forwarder for reabstraction thunk helper 343 | [...] 344 | ``` 345 | 346 | ![](images/swift-bg.jpg) 347 | 348 | --- 349 | 350 | # Memory layout 351 | 352 | - 16 bytes => Swift object 353 | - 8 bytes => Pointer to `_TF6memory3addFTSiSi_Si` 354 | 355 | __Function pointer__ 🎉 356 | 357 | ![](images/swift-bg.jpg) 358 | 359 | --- 360 | 361 | ```swift 362 | struct f_trampoline { 363 | var trampoline_ptr: COpaquePointer 364 | var function_obj_ptr: UnsafeMutablePointer 365 | } 366 | 367 | struct function_obj { 368 | var some_ptr_0: COpaquePointer 369 | var some_ptr_1: COpaquePointer 370 | var function_ptr: COpaquePointer 371 | } 372 | ``` 373 | 374 | ![](images/swift-bg.jpg) 375 | 376 | --- 377 | 378 | ```swift 379 | @asmname("floor") func my_floor(dbl: Double) -> Double 380 | println(my_floor(6.7)) 381 | 382 | let handle = dlopen(nil, RTLD_NOW) 383 | let pointer = COpaquePointer(dlsym(handle, "ceil")) 384 | 385 | typealias FunctionType = (Double) -> Double 386 | ``` 387 | 388 | ![](images/swift-bg.jpg) 389 | 390 | --- 391 | 392 | ```swift 393 | struct f_trampoline { [...] } 394 | struct function_obj { [...] } 395 | 396 | let orig = unsafeBitCast(my_floor, f_trampoline.self) 397 | let new = f_trampoline(prototype: orig, new_fp: pointer) 398 | let my_ceil = unsafeBitCast(new, FunctionType.self) 399 | println(my_ceil(6.7)) 400 | ``` 401 | 402 | ![](images/swift-bg.jpg) 403 | 404 | --- 405 | 406 | ``` 407 | $ xcrun swift -Onone hook.swift 408 | 6.0 409 | 7.0 410 | ``` 411 | 412 | ![](images/swift-bg.jpg) 413 | 414 | --- 415 | 416 | # Can we do this the other way around? 417 | 418 | ![](images/taylor2.jpg) 419 | 420 | --- 421 | 422 | ```c 423 | void executeFunction(void(*f)(void)) { 424 | f(); 425 | } 426 | ``` 427 | 428 | ```swift 429 | @asmname("executeFunction") func 430 | executeFunction(fp: CFunctionPointer<()->()>) 431 | ``` 432 | 433 | ![](images/swift-bg.jpg) 434 | 435 | --- 436 | 437 | ```swift 438 | func greeting() { 439 | println("Hello from Swift") 440 | } 441 | 442 | let t = unsafeBitCast(greeting, f_trampoline.self) 443 | let fp = CFunctionPointer<()->()> 444 | (t.function_obj_ptr.memory.function_ptr) 445 | executeFunction(fp) 446 | ``` 447 | 448 | ``` 449 | Hello from Swift 450 | Program ended with exit code: 0 451 | ``` 452 | 453 | ![](images/swift-bg.jpg) 454 | 455 | --- 456 | 457 | # Analyse private API 458 | 459 | ![](images/taylor3.jpg) 460 | 461 | --- 462 | 463 | ```swift 464 | class MyClass { 465 | var someVar = 1 466 | 467 | func someFuncWithAReallyLongNameLol() { 468 | } 469 | } 470 | ``` 471 | 472 | ![](images/swift-bg.jpg) 473 | 474 | --- 475 | 476 | ```bash 477 | $ xcrun swiftc f.swift 478 | $ ./swift-dump.rb f 479 | // Code generated from `f` 480 | import Foundation 481 | 482 | class MyClass { 483 | var someVar: Int = 0 484 | func someFuncWithAReallyLongNameLol() -> () {} 485 | } 486 | ``` 487 | 488 | ![](images/swift-bg.jpg) 489 | 490 | ---- 491 | 492 | ```bash 493 | $ xcrun nm -g f|grep TFC 494 | 0000000100000c50 T __TFC1f7MyClass30someFuncWithAReallyLongNameLolfS0_FT_T_ 495 | 0000000100000d30 T __TFC1f7MyClassCfMS0_FT_S0_ 496 | 0000000100000c70 T __TFC1f7MyClassD 497 | 0000000100000d10 T __TFC1f7MyClasscfMS0_FT_S0_ 498 | 0000000100000c60 T __TFC1f7MyClassd 499 | 0000000100000ca0 T __TFC1f7MyClassg7someVarSi 500 | 0000000100000ce0 T __TFC1f7MyClassm7someVarSi 501 | 0000000100000cc0 T __TFC1f7MyClasss7someVarSi 502 | ``` 503 | 504 | ![](images/swift-bg.jpg) 505 | 506 | --- 507 | 508 | ```bash 509 | $ xcrun swift-demangle __TFC1f7MyClassg7someVarSi 510 | _TFC1f7MyClassg7someVarSi ---> f.MyClass.someVar.getter : Swift.Int 511 | ``` 512 | 513 | ![](images/swift-bg.jpg) 514 | 515 | --- 516 | 517 | # How are emoji formed? 518 | 519 | ``` 520 | $ echo 'class 👍 {}'|xcrun swiftc -emit-library -o test - 521 | $ nm -g test 522 | ... 523 | 0000000000000db0 T __TFC4testX4ypIhD 524 | ... 525 | $ xcrun swift-demangle __TFC4testX4ypIhD 526 | _TFC4testX4ypIhD ---> test.👍.__deallocating_deinit 527 | ``` 528 | 529 | *X4 ypIh* ~ *xn--yp8h* 530 | 531 | ![](images/taylor4.jpg) 532 | 533 | --- 534 | 535 | # What have we learned? 536 | 537 | - `import ObjectiveC.runtime` ☺️ 538 | - Introspection somewhat exists 😐 539 | - Changing behaviour is hard 😖 540 | - Reverse engineering is still fine 😅 541 | 542 | ![](images/taylor8.jpg) 543 | 544 | --- 545 | 546 | # Thank you! 547 | 548 | ![](images/dance.gif) 549 | 550 | --- 551 | 552 | - https://github.com/mikeash/memorydumper 553 | - http://airspeedvelocity.net/ 554 | - https://developer.apple.com/swift/blog/ 555 | - http://www.russbishop.net/swift-how-did-i-do-horrible-things 556 | 557 | ![](images/taylor5.jpg) 558 | 559 | --- 560 | 561 | @NeoNacho 562 | 563 | boris@contentful.com 564 | 565 | http://buegling.com/talks 566 | 567 | http://www.contentful.com 568 | 569 | ![](images/taylor6.jpg) 570 | --------------------------------------------------------------------------------