iOS安全–验证函数地址,检测是否被替换,反注入

使用dladdr方法可以获得一个函数所在模块,名称以及地址。

使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <dlfcn.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <stdio.h>
#include <string.h>

int main() {

    Dl_info info;

    IMP imp = class_getMethodImplementation(

    objc_getClass("NSArray"),

    sel_registerName("description"));

    printf("pointer %p\n", imp);

    if (dladdr(imp, &info)) {

        printf("dli_fname: %s\n", info.dli_fname);

        printf("dli_sname: %s\n", info.dli_sname);

        printf("dli_fbase: %p\n", info.dli_fbase);

        printf("dli_saddr: %p\n", info.dli_saddr);

    } else {

        printf("error: can't find that symbol.\n");

    }

}

编译,运行:

Snip20150118_4所以我们可以通过这种方式来判断一个函数是不是被非法修改了。

下面给一个检测一个类下面的所有函数的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
static inline BOOL validate_methods(const char *cls,const char *fname) __attribute__ ((always_inline));

BOOL validate_methods(const char *cls,const char *fname){
    Class aClass = objc_getClass(cls);
    Method *methods;
    unsigned int nMethods;
    Dl_info info;
    IMP imp;
    char buf[128];
    Method m;
   
    if(!aClass)
        return NO;
    methods = class_copyMethodList(aClass, &nMethods);
    while (nMethods--) {
        m = methods[nMethods];
        printf("validating [%s %s]\n",(const char *)class_getName(aClass),(const char *)method_getName(m));
       
        imp = method_getImplementation(m);
        //imp = class_getMethodImplementation(aClass, sel_registerName("allObjects"));
        if(!imp){
            printf("error:method_getImplementation(%s) failed\n",(const char *)method_getName(m));
            free(methods);
            return NO;
        }
       
        if(!dladdr(imp, &info)){
            printf("error:dladdr() failed for %s\n",(const char *)method_getName(m));
            free(methods);
            return NO;
        }
       
        /*Validate image path*/
        if(strcmp(info.dli_fname, fname))
            goto FAIL;
       
        if (info.dli_sname != NULL && strcmp(info.dli_sname, "<redacted>") != 0) {
            /*Validate class name in symbol*/
            snprintf(buf, sizeof(buf), "[%s ",(const char *) class_getName(aClass));
            if(strncmp(info.dli_sname + 1, buf, strlen(buf))){
                snprintf(buf, sizeof(buf),"[%s(",(const char *)class_getName(aClass));
                if(strncmp(info.dli_sname + 1, buf, strlen(buf)))
                    goto FAIL;
            }
           
            /*Validate selector in symbol*/
            snprintf(buf, sizeof(buf), " %s]",(const char*)method_getName(m));
            if(strncmp(info.dli_sname + (strlen(info.dli_sname) - strlen(buf)), buf, strlen(buf))){
                goto FAIL;
            }
        }else{
            printf("<redacted>  \n");
        }
       
    }
   
    return YES;
   
FAIL:
    printf("method %s failed integrity test:\n",
           (const char *)method_getName(m));
    printf("    dli_fname:%s\n",info.dli_fname);
    printf("    dli_sname:%s\n",info.dli_sname);
    printf("    dli_fbase:%p\n",info.dli_fbase);
    printf("    dli_saddr:%p\n",info.dli_saddr);
    free(methods);
    return NO;
}

同样要确保以inline的方式编译,否则很容易被攻击者使用调试工具绕过,如果你是inline的方式,那么攻击者必须定位并修改每一处调用该函数的地方。

下面使用Swizzle把NSArray的lastObject替换成我们自己的xxx_lastObject,检测之:

Snip20150118_5

 

使用的时候要注意一下几点:

1.加密或者混淆函数参数。

2.重命名函数名字,不要使用具体明显意义的名字

3.任何与敏感数据处理的函数都要验证,而不只是在启动的时候

4.检测经常使用的类,如:NSString、NSData、NSMutableDictionary…

5.去掉printf和NSLog方法

6.这种检测只是减少了被攻击的可能性,但不是一定的安全。

 

本文链接:http://www.alonemonkey.com/ioss-validate-address.html

3条评论

  1. dengchaojie

    AloneMonkey,请问”检测一个类下面的所有函数的代码”怎么用,用在哪里?请指教!

    1. 庆哥他老婆

      就知道写文章,还不回家吃饭?搓衣板了哦

  2. sturt2000@qq.com

    如果dladdr本身也被hook了,该怎么检测呢?

Comments are closed.