SoFunction
Updated on 2025-04-11

Simple analysis of some basic features of Swift language

Swift is Apple's latest programming language, which, according to many people, is used to "replace" Objective-C. But there is no definite evidence. I spent some time implementing reverse engineering on Swift binary and runtime environments, and then I found some little about Swift. So far, the conclusion is: Swift is Objective-C without a message mechanism.

Object

Believe it or not, the object in Swift is an object of Objective-C. In Mach-O binary files, __objc_classlist contains the data for the class in each binary file. Its structure is as follows:
 

Copy the codeThe code is as follows:

struct objc_class {
    uint64_t isa;
    uint64_t superclass;
    uint64_t cache;
    uint64_t vtable;
    uint64_t data;
};

(Note: All structures are from the 64-bit version)

Note the data record, which points to a structure in the class that lists the contents of methods, instance variables, and protocols. Normally, data is 8 bytes aligned, but for Swift classes, the last bit of data is only 1 byte.


kind

The real structure of the Swift class is a little strange. The Swift class does not have an Objective-C method. We will implement it later. Variables of Swift class are stored as instance variables. What Swift getter and setter methods really modify the value of the instance variable. Strangely, instance variables of swift class have no type encoding. The pointer to type encoding should usually be NULL. This is probably due to the fact that Objective-C runtime does not support processing of Swift variables themselves.

inherit

Swift inheritance is what you expect. In Swift, Square is a subclass of shape and a subclass of Objective-C class Shape. However, there is no superclass in the Swift class?

For example

Copy the codeThe code is as follows:
class Shape { }

In this example, the Shape class is a subclass of SwiftObject. SwiftObject is a root Objective-C class, similar to NSObject. It has no superclass, meaning that isa points to itself. Its purpose is to use Swift runtime methods such as allocation and deallocation instead of standard Objective-C runtime methods. For example, (void)retain does not call objc_retain, but it calls swift_retain.

Class Methods

As I mentioned before, the class of Swift objects has no methods, instead of C++-like functions, name adaptations and everything. This may be why Swift claims to be faster than Objective-C. There is no longer a need to find and call method implementations for objc_msgSend .

In Objective-C, the method is implemented like this:

Copy the codeThe code is as follows:
type method(id self, SEL _cmd, id arg1, id arg2, ...)

The Swift method is very similar, but uses slightly different parameter arrangements, self is passed as the last parameter, and there is no selector.

Copy the codeThe code is as follows:
type method(id arg1, id arg2, ..., id self)


False table

Similar to C++, the Swift class also has a virtual table for listing methods in the class. It is placed directly after the class data in the binary file and looks like this:
 

Copy the codeThe code is as follows:

struct swift_vtable_header {
    uint32_t vtable_size;
    uint32_t unknown_000;
    uint32_t unknown_001;
    uint32_t unknown_002;
    void* nominalTypeDescriptor;
    // vtable pointers
}

As far as I know, virtual tables in Swift classes are only used when they are visible during compilation. Otherwise, it will look like a mess of symbols.

Naming and reorganization

Swift keeps the metadata of the function in its own symbols, which is called naming reorganization. Metadata treasure trove function name (obvious), attributes, module names, parameter types, return value types, and more data, such as this example
 

Copy the codeThe code is as follows:

class Shape{
    func numberOfSides() -> Int {
        return 5
    }
}

The reorganization name of the simpleDescription method is:

_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si. The following is a detailed description:

_T - Prefixes for all Swift symbols, each symbol starts with _T.

F - Function

C - Functions (methods) of class

9swifttest - module name with length prefix

5Shape - The class to which the function belongs, with a length prefix

17simpleDescription - Function Name

f - Function properties. In this example it is f, which is a normal function.

S0_FT- I'm not particularly sure what this means, but it's a token for the start of the parameter and return type

‘_' - This underscore divides the types of parameters and return values. Because the function does not take parameters, it follows directly after S0_FT

S - The start of the return value. 'S' stands for Swift; the return type is a built-in type of Swift, and the next character determines the type

i - This is the built-in type of Swift. A lowercase "I" represents Int.


Function Properties

Character Type

f

s        setter

g        getter

d

D          Release

c

C             Distributor

Swift internal functions

Character Type

a

b           Boolean

c            Character constant

d

f

i

UInt type

Q          Implicit optional

S          String type

In addition to functions, there are many naming conversion mechanisms, and here I will give a brief overview.

Hook function

Have enough of the semantics part, let's touch on something interesting! Let's say we have a class like this:
 

Copy the codeThe code is as follows:

class Shape {
    var numberOfSides: Int;
 
    init(){
        numberOfSides = 5;
    }
}

For example, if we want to change the value of numberOfSides to 4, there are many ways to do it. We can use MobileSubstrate to hang into the getter method and change the return value, like this:

 

Copy the codeThe code is as follows:

int (*numberOfSides)(id self);
 
MSHook(int, numberOfSides, id self){
    return 4;
}
 
%ctor{
    numberOfSides = (int (*)(id self)) dlsym(RTLD_DEFAULT, "_TFC9swifttest5Shapeg13numberOfSidesSi");
    MSHookFunction(numberOfSides, MSHake(numberOfSides));
}

If we create an instance of the shape and print out the value of numberOfSides, we get 4! It looks good, right? Now, I know you might be wondering, shouldn't we return an object with a non-status 4?


Well, in Swift, many built-in types come in written form. An Int type, for example, is the same as the int type in C (although it can be a long plastic surgery - don't let me encounter this situation). A small tip, the String type is a bit old, which is a low-bit priority UTF-16 string, so there is no literal value of C that can be used.

Let's do the same thing, but this time, we're not on the getter, but on the getter.

 

Copy the codeThe code is as follows:

void (*setNumberOfSides)(int newNumber, id self);
 
MSHook(void, setNumberOfSides, int newNumber, id self){
    _setNumberOfSides(4, self);
}
 
%ctor {
    setNumberOfSides = (void (*)(int newNumber, id self)) dlsym(RTLD_DEFAULT, "_TFC9swifttest5Shapes13numberOfSidesSi");
    MSHookFunction(setNumberOfSides, MSHake(setNumberOfSides));
}

Try it again, and then. . . . . . Still 5. What's going on, you ask? Well, some places in Swift, functions are inlined. The class constructor is one of them. It directly sets numberOfSides to ivar, and the setter will be called only when the value is set by the top-level code again. In that was called, you know, we got 4.

Finally, let's modify numberOfSides by directly setting the value of the instance variable.

 

Copy the codeThe code is as follows:

void (*setNumberOfSides)(int newNumber, id self);
 
MSHook(void, setNumberOfSides, int newNumber, id self){
    MSHookIvar<int>(self, "numberOfSides") = 4;
}
 
%ctor {
    setNumberOfSides = (void (*)(int newNumber, id self)) dlsym(RTLD_DEFAULT, "_TFC9swifttest5Shapes13numberOfSidesSi");
    MSHookFunction(setNumberOfSides, MSHake(setNumberOfSides));
}

This function can implement functions. Although it is not recommended to do so, it does have effect.

This is what I want to write at the moment. Of course, there are many other contents I am reading, including the witness table. Since I don’t know much, I can’t write a summary here. A lot of content has been changed in this post, they are just what I currently get from running and viewing binary files compiled in Swift.

What I found should be very good, which means that MobileSubstrate won't die with Objective-C, and fine-tuning can still be done! I'm really interested in knowing what will happen in the future in the app store in the jailbreak scenario... Can the logo be updated to automatically destroy the naming? Even libraries that deal with common Swift types...

If you find more about how Swift works, don't hesitate, please let me know!