使用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"); } } |
编译,运行:
下面给一个检测一个类下面的所有函数的代码:
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,检测之:
使用的时候要注意一下几点:
1.加密或者混淆函数参数。
2.重命名函数名字,不要使用具体明显意义的名字
3.任何与敏感数据处理的函数都要验证,而不只是在启动的时候
4.检测经常使用的类,如:NSString、NSData、NSMutableDictionary…
5.去掉printf和NSLog方法
6.这种检测只是减少了被攻击的可能性,但不是一定的安全。
Permalink
AloneMonkey,请问”检测一个类下面的所有函数的代码”怎么用,用在哪里?请指教!
Permalink
就知道写文章,还不回家吃饭?搓衣板了哦
Permalink
如果dladdr本身也被hook了,该怎么检测呢?