iOS逆向之旅(防護(hù)篇) — 防護(hù)Tweak插件

Tweak的原理

要防護(hù)某種技術(shù),首先你得知道這種技術(shù)是通過什么原理實現(xiàn)的

  • Tweak 在Make package的時候,會生成一個deb包,我們解壓縮看看是什么鬼

這里面保護(hù)了一個動態(tài)庫和一個plist文件



這個plist文件里面包含該dylib要注入到進(jìn)程的BundleId

  • 當(dāng)這個deb包安裝到手機(jī)上時,就會把這兩個文件放到/var/Library/MobileSubstrate/DynamicLibraries這個路徑下,通過iFunBox可以看到
  • App啟動時,就會通過DYLD_INSERT_LIBRARIES這種方式將動態(tài)庫注入到進(jìn)程中,從而實現(xiàn)注入

結(jié)論:所以我們防護(hù)Tweak,就是要防止DYLD_INSERT_LIBRARIES注入

dlyd關(guān)于DYLD_INSERT_LIBRARIES 源碼閱讀 【最新一版dyld-635.2】

首先我們可以在蘋果開源代碼下載dyld的源碼,了解DYLD_INSERT_LIBRARIES注入的流程,逆向分析源碼

  • 首先定位關(guān)鍵字DYLD_INSERT_LIBRARIES

注釋寫的很清楚,加載任何注入的動態(tài)庫

  • 好像只需要讓這段代碼不執(zhí)行就可以了,所以接著往回跟,看看如何讓這段代碼不執(zhí)行【看看哪里修改了sEnv.DYLD_INSERT_LIBRARIES
void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainExecutableDir)
{
    // ...
    else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
        sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL);
    // ...
}

繼續(xù)跟蹤 processDyldEnvironmentVariable

static void checkEnvironmentVariables(const char* envp[])
{
      // ..
      processDyldEnvironmentVariable(key, value, NULL);
      //...
}

繼續(xù)跟蹤 checkEnvironmentVariables

static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
{
#if SUPPORT_LC_DYLD_ENVIRONMENT
    checkLoadCommandEnvironmentVariables();
#endif
#if __MAC_OS_X_VERSION_MIN_REQUIRED
    if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
        pruneEnvironmentVariables(envp, &apple);
        // set again because envp and apple may have changed or moved
        setContext(mainExecutableMH, argc, argv, envp, apple);
    }
    else
#endif
    {
        checkEnvironmentVariables(envp);
        defaultUninitializedFallbackPaths(envp);
    }

上述代碼說明,(!gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache)為假的時候才會去加載注入的動態(tài)庫信息,反之就是只要為真app將不會加載各種注入的dylib。

  • 接著跟蹤(!gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache,定位到4896行
static void configureProcessRestrictions(const macho_header* mainExecutableMH)
{
    uint64_t amfiInputFlags = 0;
#if TARGET_IPHONE_SIMULATOR
    amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IN_SIMULATOR;
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
    if ( hasRestrictedSegment(mainExecutableMH) )
        amfiInputFlags |= AMFI_DYLD_INPUT_PROC_HAS_RESTRICT_SEG;
#elif __IPHONE_OS_VERSION_MIN_REQUIRED
    if ( isFairPlayEncrypted(mainExecutableMH) )
        amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IS_ENCRYPTED;
#endif
    uint64_t amfiOutputFlags = 0;
    if ( amfi_check_dyld_policy_self(amfiInputFlags, &amfiOutputFlags) == 0 ) {
        gLinkContext.allowAtPaths               = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_AT_PATH);
        gLinkContext.allowEnvVarsPrint          = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS);
        gLinkContext.allowEnvVarsPath           = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS);
        gLinkContext.allowEnvVarsSharedCache    = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE);
        gLinkContext.allowClassicFallbackPaths  = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS);
        gLinkContext.allowInsertFailures        = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION);
    }
        //...
}

有點亂,我正序串一下

hasRestrictedSegment(mainExecutableMH)
    -> amfiInputFlags
    -> gLinkContext.allowEnvVarsPrint 、 gLinkContext.allowEnvVarsPath、gLinkContext.allowEnvVarsSharedCache
    -> checkEnvironmentVariables(envp)
    -> sEnv.DYLD_INSERT_LIBRARIES
    -> load any insered libraries

所以最終要確認(rèn)的是hasRestrictedSegment方法,到底是怎么判斷的

static bool hasRestrictedSegment(const macho_header* mh)
{
    const uint32_t cmd_count = mh->ncmds;
    const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
    const struct load_command* cmd = cmds;
    for (uint32_t i = 0; i < cmd_count; ++i) {
        switch (cmd->cmd) {
            case LC_SEGMENT_COMMAND:
            {
                const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
                
                //dyld::log("seg name: %s\n", seg->segname);
                if (strcmp(seg->segname, "__RESTRICT") == 0) {
                    const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
                    const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
                    for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
                        if (strcmp(sect->sectname, "__restrict") == 0) 
                            return true;
                    }
                }
            }
            break;
        }
        cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
    }
        
    return false;
}

根據(jù)參數(shù)名的類型可知,傳進(jìn)去的是一個macho文件頭,然后進(jìn)行遍歷,如果有一個段名是“ __RESTRICT”,里面是“ __restrict”,就會返回true,也就最終決定不會去加載注入的動態(tài)庫

防護(hù)手段1

  • 通過在Other Linker Flags 添加 -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null,這樣就能在macho文件頭部添加一個段名是“ __RESTRICT”,里面是“ __restrict”,滿足了hasRestrictedSegment的要求
  • 然后我們用爛蘋果觀察一下生成的macho文件是否有著一個段

防護(hù)手段1的破解方式

直接利用工具,修改app的macho文件把“__RESTRICT,__restrict”名字隨便改動一兩個字符,就可以直接破解了

防護(hù)手段2

把蘋果的代碼直接拿過來用,我們自己判斷我們有沒有這個段【如果被惡意破壞了,我們自己也可以檢測出來】,我就直接上我封裝好的代碼

#import "AntiInsertLibrary.h"
#import <mach-o/loader.h>
#import <mach-o/dyld.h>

#define CPU_SUBTYPES_SUPPORTED  ((__arm__ || __arm64__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR)

#if __LP64__
#define macho_header              mach_header_64
#define LC_SEGMENT_COMMAND        LC_SEGMENT_64
#define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT
#define LC_ENCRYPT_COMMAND        LC_ENCRYPTION_INFO
#define macho_segment_command    segment_command_64
#define macho_section            section_64
#else


#define macho_header              mach_header
#define LC_SEGMENT_COMMAND        LC_SEGMENT
#define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64
#define LC_ENCRYPT_COMMAND        LC_ENCRYPTION_INFO_64
#define macho_segment_command    segment_command
#define macho_section            section
#endif

static bool hasRestrictedSegment(const struct macho_header* mh)
{
    const uint32_t cmd_count = mh->ncmds;
    const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(struct macho_header));
    const struct load_command* cmd = cmds;
    for (uint32_t i = 0; i < cmd_count; ++i) {
        switch (cmd->cmd) {
            case LC_SEGMENT_COMMAND:
            {
                const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
                
                //dyld::log("seg name: %s\n", seg->segname);
                if (strcmp(seg->segname, "__RESTRICT") == 0) {
                    const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
                    const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
                    for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
                        if (strcmp(sect->sectname, "__restrict") == 0)
                            return true;
                    }
                }
            }
                break;
        }
        cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
    }
    
    return false;
}


@implementation AntiInsertLibrary

+ (BOOL) hasRestrictedSegment {
    struct mach_header * header = _dyld_get_image_header(0);
    return hasRestrictedSegment(header);
}

@end

使用方式也很簡單

if ([AntiInsertLibrary hasRestrictedSegment]) {
     NSLog(@"安全著呢");
} else {
     NSLog(@"防護(hù)受到破壞");
     exit(0);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • [TOC] 修改系統(tǒng)應(yīng)用 目標(biāo):消除對手機(jī)桌面的提醒氣泡 通過cycript分析氣泡連接手機(jī)并登陸,通過進(jìn)程列表指...
    _順_1896閱讀 1,890評論 0 3
  • 概敘 越獄防護(hù)是指防止別人修改自己的APP作出的防護(hù)手段。 1.了解代碼注入方式 了解防護(hù)之前需要了解代碼注入的方...
    Hanfank閱讀 1,547評論 4 14
  • 在應(yīng)用開發(fā)過程中,我們不僅僅需要完成正常的業(yè)務(wù)邏輯,考慮應(yīng)用性能、代碼健壯相關(guān)的問題,我們有時還需要考慮到應(yīng)用安全...
    喵喵唔的老巢閱讀 906評論 0 0
  • tweak插件開發(fā) 1、連接手機(jī) usb或者wifi 通過ssh連手機(jī) 2、ps -A查看所有應(yīng)用 3、導(dǎo)出目標(biāo)...
    looha閱讀 1,776評論 0 1
  • 在應(yīng)用開發(fā)過程中,我們不僅僅需要完成正常的業(yè)務(wù)邏輯,考慮應(yīng)用性能、代碼健壯相關(guān)的問題,我們有時還需要考慮到應(yīng)用安全...
    iOS猿_員閱讀 3,086評論 1 4

友情鏈接更多精彩內(nèi)容