Swift Class Disassembly

In a previous article, we looked at the disassembly of class creation and program semantics for a simple program that prints a string. It did expose how classes are defined and how objects are reference counted however, as simple as that example was. Now we're going to look at an equivalent Swift program

This swift program is equivalent to the Objective-C program, right down to the method names:

import Foundation

class Printer {

  var str_to_print: String

  init() {
  str_to_print = ""
  }

  func printMsg() {
  print(str_to_print)
  }

  func printString(message: String) {
  str_to_print = message
  printMsg()
  }

}

let printer = Printer()
printer.printString(message: "Hello World!")

Now, when compiled, this will create a main subroutine that looks like this:

_main:
  push rbp
  mov rbp, rsp
  push r13
  sub rsp, 0x28
  mov dword [rbp+var_C], edi
  mov qword [rbp+var_18], rsi
  call __T09swift_cmd7PrinterCMa 
  mov r13, rax
  call __T09swift_cmd7PrinterCACycfC 
  lea rdi, qword [aHelloWorld]
  mov ecx, 0xc
  mov esi, ecx
  mov edx, 0x1
  mov qword [__T09swift_cmd7printerAA7PrinterCv], rax 
  mov rax, qword [__T09swift_cmd7printerAA7PrinterCv] 
  mov r13, qword [rax]
  mov r13, qword [r13+0x78]
  mov qword [rbp+var_20], rax
  mov qword [rbp+var_28], r13
  call __T0S2SBp21_builtinStringLiteral_Bw17utf8CodeUnitCountBi1_7isASCIItcfC 
  mov rdi, rax
  mov rsi, rdx
  mov rdx, rcx
  mov r13, qword [rbp+var_20]
  mov rax, qword [rbp+var_28]
  call rax
  xor eax, eax
  add rsp, 0x28
  pop r13
  pop rbp
  ret

First, notice that the Swift code uses call semantics and not message passing. The functions themselves are decorated, if you'd like to undecorate them you can use the swift-demangle command (i.e. $ xcrun swift-demangle <mangled string>). This has some profound implications - first and foremost, call semantics make message interception, as implemented in Objective-C, impossible. We also have very distinct name mangling going on, all with the string "swift" incorporated into the defined functions. This is actually very useful! It makes determining the provenance of a given program trivial. If you run the command strings over a program and grep for Swift, and you see these kinds of function names, you know that you're out of luck if you're planning on injecting anything into the program the old-fashioned way, using Objective-C tools.

Also, notice the last call:

call rax 

This should be the call to the printer.printString(.) method. Notice it's a dynamic call, where the address was previously moved into the RAX register.

mov rax, qword [__T09swift_cmd7printerAA7PrinterCv]
...
mov r13, qword [rax]
mov r13, qword [r13+0x78]
...
mov qword [rbp+var_28], r13
...
mov r13, qword [rbp+var_20]
mov rax, qword [rbp+var_28]
call rax

In this code, var_28 is -40. So here, we're executing a dynamic call into a function located on the stack, at an offset of -0x40. I've outlined the relevant code here. If you look over the code, we're essentially executing a call into an offset into the Swift object we've just created.

We've started to touch on how classes are allocated in Swift, and how calls into Swift objects are actually implemented. We'll start to look into that next.

 

 

 

 

Top