<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    原文地址:http://drops.wooyun.org/mobile/15794

    0x00 前言


    目前,在iOS中很多dylib是通過DYLDINSERTLIBRARIES來動態注入的,關于這種注入的防范方式最好的方法是在程序編譯時,添加一個空的restict區段,因為dyld在load動態庫時會檢查區段中是否存在restict,如果存在那么就跳過加載。詳情參見:防止tweak依附,App有高招;破解App保護,tweak留一手

    前段時間,發現自己寫的一個demo加了restict之后,仍然能夠被cycript掛載,順勢分析了cycript的注入原理。

    0x01 cycript簡介


    cycript是大神saurik開發的一個非常強大的工具,可以讓開發者在命令行下和應用交互,在運行時查看和修改應用。其中的底層實現,是通過蘋果的JavaScriptCore.framework來打通iOS與javascript的橋梁。具體詳解,參考

    0x02 cycript結構


    在安裝cycript過后,通過cydia可以看到安裝cycript之后影響的目錄結構

    雖然只提到這幾個文件,但是在/usr/bin下,還有一個cynject的可執行文件和cycript相關,后面分析會發現,這個程序才是注入的核心文件。我這里,通過scp把這些文件都下載到本地,方便分析。

    0x03 cycript主程序


    分析Cycript主程序時,觀察到里面有一個InjectLibrary的函數被調用

    #!c
    void InjectLibrary(int pid, int argc, const char *argv[]) {
        auto cynject(LibraryFor(reinterpret_cast<void *>(&main)));
        auto slash(cynject.rfind('/'));
        _assert(slash != std::string::npos);
        cynject = cynject.substr(0, slash) + "/cynject";
        auto library(LibraryFor(reinterpret_cast<void *>(&MSmain0)));
        bool ios(false);
        for (decltype(header->ncmds) i(0); i != header->ncmds; ++i) {
            if (command->cmd == LC_VERSION_MIN_IPHONEOS)
                ios = true;
            command = shift(command, command->cmdsize);
        }
        _syscall(munmap(map, size)); // XXX: _scope
        auto length(library.size());
        _assert(length >= 6);
        length -= 6;
        _assert(library.substr(length) == ".dylib");
        library = library.substr(0, length);
        library += ios ? "-sim" : "-sys";
        library += ".dylib";
    #endif
        std::ostringstream inject;
        inject << cynject << " " << std::dec << pid << " " << library;
        for (decltype(argc) i(0); i != argc; ++i)
            inject << " " << argv[i];   
    
        _assert(system(inject.str().c_str()) == 0);
    }
    

    程序在解析過程中,注入代碼前會對會環境進行檢查,并且進行字符串格式化拼接,構造一個shell命令,用來做調用system函數的參數,來執行cynject注入功能。

    0x04 cynject注入程序


    接下來分析cynject,在函數subb308的位置可以看到,通過調用taskfor_pid獲取到進程句柄結構。通過該結構,可以對進程能夠有訪問權限。

    舉個栗子:

    #!c
    mach_port_t rtask;
    task_for_pid(mach_task_self(), pid, &rtask);
    

    程序為了讓內存中的dylib有執行能力,把dylib通過線程的方式來加載。繼續往下看,就發現進程創建一個被掛起的線程

    拿到句柄結構,在進程的空間中申請內存,將dylib映射之后,寫入到這片申請的空間里面。

    所以,代碼邏輯大概是這樣的:

    #!c
    vm_size_t codeSize = 124;
    vm_address_t rAddress;
    vm_allocate(rtask, &rAddress, codeSize, TRUE);
    vm_write(rtask, rAddress, &code, (mach_msg_type_number_t)codeSize);
    vm_protect(rtask, rAddress, codeSize, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
    

    最后,等dylib加載完全后,為dylib恢復啟動并執行使其開始運行

    梳理一下大體的流程:

    ( 1 )獲取 PID 的進程句柄
    ( 2 )在 PID 中創建一個被掛起的線程
    ( 3 )在 PID 進程中申請一片用于加載 DYLIB 的內存
    ( 4 )調用 RESUME ,恢復線程

    OVER~

    說到這里,流程差不多梳理完了,拿一個dylib測試一下

    通過lldb連上程序,看一下內存加載模塊:

    可以發現,我們惡意的dylib已經注入到進程中了。

    0x05 后記


    cycript里面還有一些比較有意思的東西,大家可以深挖一下。另外,上面的內容不要用到自己企業的app產品中,按照蘋果appStore原則,調用私有api以及動態注入代碼的操作,是會被下架的。

    97av