iOS应用代码注入防御

在应用开发过程当中,咱们不单单须要完成正常的业务逻辑,考虑应用性能、代码健壮相关的问题,咱们有时还须要考虑到应用安全的问题。面试

那么应用安全的问题涉及到不少方面。好比防止静态分析的,代码混淆、逻辑混淆;防止重签名的,应用ID检测、甚至是代码的HASH检测等等。那么这篇文章我想聊聊关于代码的注入检测,由于发现随着iOS系统的更新,咱们防御的手段发生了一些变化。算法

代码注入的方式安全

代码注入的方式大体分为两种bash

  • 越狱注入:经过修改 DYLD_INSERT_LIBRARIES 环境变量的值,来插入动态库并执行app

  • 非越狱注入:函数

    • 直接将自定义的Framwork或者dylib库打包进入APP并重签名工具

    • 利用yololib修改MachO文件,添加库路径.在应用启动时,dyld会加载并执行.源码分析

早期防御方式

在工程的Build Settings中找到Other Linker Flages 并添加字段性能

-Wl,-sectcreate,__RESTRICT,__raestrict,/dev/null学习

此操做的做用是在可执行文件中添加一个Section.咱们使用MachOView分析以下:

当MachO文件中拥有这个字段,那么咱们经过越狱环境插入动态库的方式就会失效.起到防御的做用.其原理在DYLD源码中能够分析到.

dyld源码分析

首先这里分析的DYLD源码版本是519.2.2版本. 咱们能够经过检索DYLD_INSERT_LIBRARIES定位到_main函数加载插入动态库的代码以下.

if  ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
            for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
                loadInsertedDylib(*lib);
        }
复制代码

可是早在这个环境变量判断以前,dyld已经作了一个判断

pruneEnvironmentVariables(envp, &apple);
        // set again because envp and apple may have changed or moved
        setContext(mainExecutableMH, argc, argv, envp, apple);
    }
复制代码

若是判断出进程是restricted!也就是当前进程是限制插入动态库的!就会调用pruneEnvironmentVariables函数移除相关的环境变量.

那么咱们的processIsRestricted值何时为true呢?

继续分析源码能够发现两个关键函数影响其值.其中 hasRestrictedSegment 函数专门检测RESTRICT段

if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
        gLinkContext.processIsRestricted = true;
    }
复制代码

经过注释也能发现.任意进程的__RESTRICT段设置为restricted动态库插入将被限制. 咱们进入到processIsRestricted函数内,实现以下.

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;
}
复制代码

因此经过添加Other Linker Flags 在MachO中设置RESTRICT段赋值为restricted能够用来防御越狱的代码注入. 可是新版的dyld源码中去掉了__RESTRICT检测.从iOS10开始,这种防御手段已失效

DYLD_INSERT_LIBRARIES 检测

那么既然dyld加载过程再也不检测__RESTRICT段了咱们就手动的检测 DYLD_INSERT_LIBRARIES 环境变量.经过函数可查看当前进程环境变量的值.

char *env = getenv("DYLD_INSERT_LIBRARIES");
NSLog(@"%s",env);
复制代码

在没有插入动态库时,env为null. 那么一旦为本身的应用写入插件时,咱们就能够看到控制台的输出

2019-01-03 19:20:37.285 antiInject[7482:630392] /Library/MobileSubstrate/MobileSubstrate.dylib 
复制代码

白名单检测

那么上面的检测只能够检测越狱环境中的代码注入,在非越狱环境中,逆向工程师能够利用yololib工具注入动态库.因此咱们能够检索一下本身的应用程序所加载的动态库是不是咱们源程序全部

bool HKCheckWhitelist(){
    
    int count = _dyld_image_count();
    for (int i = 0; i < count; i++) {
        //遍历拿到库名称!
       const char * imageName = _dyld_get_image_name(i);
        //判断是否在白名单内,应用自己的路径是不肯定的,因此要除外.
        if (!strstr(libraries, imageName)&&!strstr(imageName, "/var/mobile/Containers/Bundle/Application")) {
            printf("该库非白名单以内!!\n%s",imageName);
            return NO;
        }
    }
    return YES;
}
复制代码

其中libraries变量是“白名单”.

小编推荐一个qq群:551346706这个群挺不错的,我就是在群里看到的学习资料写出的这篇文章,这篇文章有源码和视频资料须要的能够了解一下,群里还有各大厂的面试题和算法。还有不少的大牛平时的工做问题也能解决。

相关文章
相关标签/搜索