如何通过Mach-O实现static函数的动态调用(续)

一、前言

首先先跟大家说个抱歉,在上篇《如何通过Mach-O实现static函数的动态调用》中由于调研不够严谨,没考虑到Xcode在打包时会将符号表strip的情况(在这里要感谢@walreal的及时指正)。由于正式版APP中不存在符号表,因此导致整个方案不可行。为了解决这个问题,特地左思右想换了个实现方案。

二、为什么会strip符号表

为什么会剔除掉符号表?如果剔除了符号表,那么程序运行时如何才能找到地址呢?查看walreal的留言及自身验证得知,实际上Mach-O保存了动态链接的符号表,静态链接的符号表已经被剥离到.dSYM文件中。从这点就可以看出,静态链接的函数实际上是不需要符号的,因为一旦编译完成后,地址即已确定,不能确定地址的才会保留符号表。因此可以说,符号表只是程序员的调试工具,并不完全具备函数调用的映射意义。保留符号表不但没有意义而且会造成安全隐患和应用包的增大。

三、.dSYM

大家都知道,在接入bugly等崩溃统计工具时,会要求开发者上传dSYM文件。有了符号表,无论是通过xcrun atos 命令还是dwarfdump 命令,我们都能根据地址获取到相应的符号。dSYM实际上是一个压缩包,其内部包含了一个Mach-O文件,这是一个胖二进制文件,为了方便查看,首先通过lipo命令将其按架构拆分

lipo xxx.app.dSYM/Contents/Resources/DWARF/xxx -thin arm64 -output xxx_arm64

拆分完成后,我们可以清楚地看到这个文件内容。


image.png

在文件的符号表中,我们可以看到静态函数确实存储在符号表中


image.png

除了符号表外,还有一些debug相关的secion也存储在dSYM中。也就是说之前的方案是从当前应用获取符号表,现在可能需要从外界获取到符号表后,将获取到的函数地址以字符串的形式下发到应用中,而不是下发文件名和符号名。
三、实现

首先将拆分后的文件直接加载到内存中,为了读取文件中的符号表,我创建了一个Mac应用,整个应用主要是读取文件并获取符号信息。在有了前一篇文章的基础后,在内存中加载文件的难度并不大,很容易就可以获取到该文件的符号表和字符串表
加载文件并获取文件header

//加载文件并获取文件header
 NSString *path = @"/Users/a58/Desktop/xxx_armv7";
 NSData *data = [NSData dataWithContentsOfFile:path];
 void *pt = (void*)[data bytes];
 mach_header_t *header = (mach_header_t*)pt;

获取符号表LoadCommand

//获取符号表LoadCommand
 pt = pt+sizeof(mach_header_t);
 for (uint i = 0; i < header->ncmds; i++, pt += cur_seg_cmd->cmdsize) {
            cur_seg_cmd = (struct segment_command *)pt;
            if (cur_seg_cmd->cmd == LC_SYMTAB) {
                symtab_cmd = (struct symtab_command*)cur_seg_cmd;
             }
}

获取符号表和字符串表

//获取符号表和字符串表
 struct nlist *symtab = (struct nlist *)((uintptr_t)header+symtab_cmd->symoff);
  char *strtab = (char *)((uintptr_t)header + symtab_cmd->stroff);

之后的流程与前文一致。但是,经过查看符号表发现,dSYM符号表中并没有存储任何文件相关的信息,在前文中,我们通过匹配文件来定位这个符号到底是不是我们所要的文件中的符号(因为static 在不同的文件中符号相同,需要区别同名符号是否是我们所指文件中的符号),这就意味着我们获取到的地址可能并不是我们所需要的地址。比如,在58中,某符号在项目中共存在48处,因此仅靠符号名无法准确获取到地址。

四、debug_info

使用过apple 的crash 分析工具的同学可能会清除,在获取崩溃后apple能帮你自动定位到crash发生的文件类甚至精确到某行代码。这是因为在dSYM文件中,存在着相关调试信息,


image.png

虽然在MachOView工具中我们看不出什么,但是通过

dwarfdump --debug-info /Users/a58/Desktop/OS2JS_Demo.app.dSYM/Contents/Resources/DWARF/OS2JS_Demo 

命令查看其符号化后的形式,

//随意截取的一个函数
0x00001ac5:     TAG_subprogram [47] *
                 AT_low_pc( 0x00000001000063f0 )//函数起始地址
                 AT_high_pc( 0x00000014 )
                 AT_frame_base( reg31 )
                 AT_object_pointer( {0x00001ade} )
                 AT_name( "-[AppDelegate .cxx_destruct]" )
                 AT_decl_file( "/Users/a58/Desktop/OS2JS_Demo/OS2JS_Demo/AppDelegate.m" )//文件信息
                 AT_decl_line( 27 )
                 AT_prototyped( true )
                 AT_artificial( true )
                 AT_APPLE_optimized( true )

因此可以断定,我们所需要的文件信息其实都在debug_info section中。起初,我的第一想法是获取到符号地址后,反查地址所对应的debug_info文件信息,从而判断该符号地址是否是我需要的地址。但是很遗憾,直接解析debug_info的二进制数据对我来说是十分复杂的,这需要对DWARF格式数据有相当的了解才行。因此偷懒借助dwarfdump来实现地址获取,通过

dwarfdump /Users/a58/Desktop/MachOtest.app.dSYM/Contents/Resources/DWARF/MachOtest --name=xxx

可以获取到指定变量/函数名的debug信息,信息中包括文件信息、变量偏移地址等。


image.png

文件信息与符号表的对照关系如下:


image.png

这个方式比我们遍历符号表省力太多,我们不需要关注符号规则,只需要一个命令即可获取到地址。获取到函数地址后,下发到应用中,在应用内计算出其真实运行时的地址即可。
为了验证可行性,我写了个demo,在demo中,我定义了一个static 变量,一个static函数

static int s_idata = 100;
static int printData(){
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:[NSString stringWithFormat:@"%d",s_idata] delegate:[UIApplication sharedApplication].delegate cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
    [alert show];
    return s_idata;
}

然后通过动态输入地址,修改s_idata的值,并动态调用printData。dSYM文件分析如下:


image.png

输入动态参数,模仿脚本调用,将s_idata修改为0xc8(200),并动态调用static 函数


IMG_0057.PNG

s_idata值被修改,并且函数正确执行
IMG_185E3EE6D9F9-1.jpeg

校验计算过程,运行地址计算正确


IMG_F74FC70EEB38-1.jpeg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容