2014年6月15日日曜日

#swift "Swift is Objective-C without messages"

(追記)引用元ブログの追加。肝心なものを忘れてた。。

Swiftのランタイムをリバースエンジニアリングした人のブログ。いくつか興味深い調査結果を紹介している。

気になったところを抜き出してみる。

Objects, Classes,...
Swift objects are actually Objective-C objects.
Swift classes have no Objective-C methods.
SwiftObject is a root Objective-C class, similar to NSObject. It has no superclass,...
classes for Swift objects have no methods. Instead, they have been replaced with C++-like functions, mangling and all. This is likely why Swift has been said to be much faster than Objective-C; there is no more need for objc_msgSend to find and call method implementations.

Swiftのメソッドは、Objective-Cのメソッドの代わりに C++ライクな関数で実装されていて、これが速い理由の一つのようだ。C++のような vtableをクラスが持っているとのこと。


Name Mangling

Swiftは関数のメタ情報を保持する。これを Name Mangling と呼ぶらしい。
例えば
class Shape{
    func numberOfSides() -> Int {
        return 5
    }
}
というクラスがあった場合、The mangled nameは
_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si.
となる。それぞれの文字が意味を持っている。Objective-Cのタイプエンコードっぽいが、クラス名とメソッド名も一緒にまとめられている。

Function Hooking

脱獄系ツールのMobileSubstrateを使っての解析。
class Shape {
    var numberOfSides: Int;

    init(){
        numberOfSides = 5;
    }
}
関数フックを使ってメンバ変数 numberOfSidesの値を4に変えてみる。

先ほどの Name Mangling を応用してメンバ変数のget/setをフックしている。
まず getter
int (*numberOfSides)(id self);

MSHook(int, numberOfSides, id self){
    return 4;
}

%ctor{
    numberOfSides = (int (*)(id self)) dlsym(RTLD_DEFAULT, "_TFC9swifttest5Shapeg13numberOfSidesSi");
    MSHookFunction(numberOfSides, MSHake(numberOfSides));
}
これは成功。

次に setter
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));
}
これは失敗。コンストラクタにおけるメンバ変数設定はインラインで行われる(setterを呼ばない)ため。

そこでsetterを経由せず直接設定
void (*setNumberOfSides)(int newNumber, id self);

MSHook(void, setNumberOfSides, int newNumber, id self){
    MSHookIvar(self, "numberOfSides") = 4;
}



%ctor {
    setNumberOfSides = (void (*)(int newNumber, id self)) dlsym(RTLD_DEFAULT, "_TFC9swifttest5Shapes13numberOfSidesSi");
    MSHookFunction(setNumberOfSides, MSHake(setNumberOfSides));
}
これは成功。


参考:dlsym
dlsym() が特別な handle RTLD_DEFAULT を使用して呼び出された場合は、オブジェクトが ロードされる際に未定義のシンボルを解決するために 使用されるアルゴリズムに従ってシンボルを探索します。 探索されるオブジェクトは次のように書かれた順番になります。.....

0 件のコメント:

コメントを投稿