越狱-相关防护

一.通过DYLD的dyld_insert_library环境变量防护

1.原理(查看dyld-519.2.2源码)
// load any inserted libraries
        if  ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
            for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
                loadInsertedDylib(*lib);
        }

源码中通过判断DYLD_INSERT_LIBRARIES,如果不是NULL,就直接调用loadInsertedDylib进行动态库的插入。
在调用以上代码插入动态库之前,DYLD还是有对DYLD_INSERT_LIBRARIES进行限制的:

  if ( gLinkContext.processIsRestricted ) {
        pruneEnvironmentVariables(envp, &apple);
        // set again because envp and apple may have changed or moved
        setContext(mainExecutableMH, argc, argv, envp, apple);
    }

首先调用 gLinkContext.processIsRestricted判断DYLD_INSERT_LIBRARIES,如果是受限制, 就直接移除环境变量。
直接全局搜索 processIsRestricted = true查看什么情况下会受限制.

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 = §ionsStart[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;
}

通过上述DYLD代码,我们知道matchO中需要有segment为__RESTRICT,且段中需要有section为__restrict,即可限制动态库的插入。

2.实现防护

(1)配置环境变量


7C76F593F390673B235597B98893ED5F.png

(2)编译,查看MatchO


0CA90C42DEE61B26A394275784566DD8.png

这样就能防止插入动态库了。但是如果仅仅只是这只环境变量的话,黑客只需要修改MatchO的这个段,就可以破解这个防护了。
为了增加破解难度,我们可以自己检测这个段是否被修改。
(3)检测段是否被修改,增加破解难度。

#import "ViewController.h"
#import 
#import 

@interface ViewController ()

@end

#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

@implementation ViewController
+(void)load
{
    const struct mach_header_64 * header = _dyld_get_image_header(0);
    if (hasRestrictedSegment(header)) {
        NSLog(@"正常");
    }else{
        NSLog(@"Restrict被修改了");
    }
}

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;
                
                printf("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 = §ionsStart[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;
}
@end

(4).总结:支付宝早期的版本也有使用这种方式防护。但是很遗憾,从iOS 10以后,dyld已不在检测__RESCRICT这个段,因此这种防护在iOS 10以后,已经失效。
那么iOS 10以后如何防护呢?

二.白名单检测

(1).获取到APP中需要的所有库,保存下来。
(2).APP启动后,检测所有第三方库,并查找每个库是否在之前保存的库中,如果找到不存在的库,即认为是有风险的。

bool HKCheckWhitelist(){
    int count = _dyld_image_count();//加载了多少数量
    for (int i = 1; i < count; i++) { 
        //遍历拿到库名称!
       const char * imageName = _dyld_get_image_name(i);
        if (!strstr(libraries, imageName)) {
            printf("该库非白名单之内!!\n%s",imageName);
           return NO;
        }
    }
    return YES;
}

判断是否越狱手机的方式(判断DYLD_INSERT_LIBRARY是否有值)

  char * dlname = getenv("DYLD_INSERT_LIBRARY");
    if (dlname) {
        NSLog(@"越狱设备");
    }

注意点:不同系统,不同型号的设备,所依赖的库可能不一样,因此需要进行处理。

三、越狱检测

    char * dlname = getenv("DYLD_INSERT_LIBRARIES");
    if (dlname) {
        NSLog(@"越狱手机,关闭部分功能");
    }else{
        NSLog(@"正常手机!");
    }

你可能感兴趣的:(越狱-相关防护)