修改so导出函数名称

最近有一个需求是将某so(无源码)的某些函数名称改掉。搜遍网络没有直接的解决方案,最后是综合多个地方的资料和建议及类似代码,自己弄出了个不通用的解决方案。

首先,so没有源码,所以不可能直接修改导出函数,一开始的思路就是下面这两种解决方案:

  1. 通过ida将so反编译成源码,然后再手动整理成可编译的代码,再去改。
  2. so是ELF格式的,直接以二进制方式修改这个ELF文件应该可以达到目的。

以上两种方法中,1最彻底,实行完之后代码就是可维护可更新的了,以后加什么新功能或其它改动都可以。但我使用ida及Hex-ray插件生成c伪代码后看了看代码就放弃了。一个264KB的so,反编译出来了795KB的代码,这你敢信。。。 反编译出来的代码中大量的sub_xxx函数,还结合各种汇编代码,各种跳转,除了调用其它so以及自身的导出函数的名称是可见的,其它一概不可见。预计恢复难度较高,至少在一周的工作时间,所以暂时搁置了。

然后就研究第2种修改ELF格式的方法,首先查找了一些资料如下:

以及elf分析的相关工具:

也找到同样尝试修改导出函数名称的人,还有半成品代码:

然后花了点时间熟悉了makefile及linux编译(没有使用Android Studio+NDK来实现,因为毕竟是工具类),再在github上一个elf工具集的基础上增加了个redefine工具。

看到这里,我假设你已经读了上面一些ELF相关资料,所以下面的解释对ELF相关术语不做详述。

一开始很顺利,找到类型为DYNSYM的section,然后找到它对应的类型为STRTAB的section,然后直接取出整个section,遍历里面的所有字符串(都是以\0结尾,和c字符串一样),然后将字符串修改成新的字符串,目前只支持将长字符串替换成短的,然后把\0前未替换的部分前移(如果替换前后一样长就不用前移了),在后面多余的地方补0。

替换完了后,使用dlsym加载发现找不到符号,然后就发现了* ELF格式可执行文件,更改符号名称要注意的地方,所以又改进了代码,加上了rehash的步骤。

然后测试修改用ndk生成的一个hellojni的示例后成功。但是在实际使用中发现了一个奇葩的问题,即比如原导出函数Java_com_example _test_TestJni_mprotect,被我替换成Java_com_abcdef_test_TestJni_mprotect,在加载时提示dlopen failed: cannot locate symbol "protect" referenced by "/data/app/com.xxxx.xxxx-2/lib/arm/libmxxxx_xxx.so"

dlopen failed: cannot locate symbol "protect" referenced by "/data/app/com.xxxx.xxxx-2/lib/arm/libmxxxx_xxx.so"

使用ida反编译修改后的so发现,有一个import function名为mprotect,在修改后变成了protect,再使用readelf查看section信息:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .note.gnu.build-i NOTE            00000114 000114 000024 00   A  0   0  4
  [ 2] .hash             HASH            00000138 000138 002210 04   A  3   0  4
  [ 3] .dynsym           DYNSYM          00002348 002348 0047b0 10   A  4   3  4
  [ 4] .dynstr           STRTAB          00006af8 006af8 012751 00   A  0   0  1
  [ 5] .gnu.version      VERSYM          0001924a 01924a 0008f6 02   A  3   0  2
  [ 6] .gnu.version_r    VERNEED         00019b40 019b40 000040 00   A  4   2  4
  [ 7] .rel.dyn          REL             00019b80 019b80 000250 08   A  3   0  4
  [ 8] .rel.plt          REL             00019dd0 019dd0 000260 08  AI  3   9  4
  [ 9] .plt              PROGBITS        0001a030 01a030 0003a4 04  AX  0   0  4
  [10] .text             PROGBITS        0001a3d4 01a3d4 0180e0 00  AX  0   0  4
  [11] .rodata           PROGBITS        000324b4 0324b4 0021ac 00   A  0   0  4
  [12] .ARM.extab        PROGBITS        00034660 034660 003d24 00   A  0   0  4
  [13] .ARM.exidx        ARM_EXIDX       00038384 038384 0025a0 00  AL 10   0  4
  [14] .init_array       INIT_ARRAY      00040c74 040c74 00000c 00  WA  0   0  4
  [15] .fini_array       FINI_ARRAY      00040c80 040c80 000008 00  WA  0   0  4
  [16] .data.rel.ro      PROGBITS        00040c88 040c88 0000b8 00  WA  0   0  8
  [17] .dynamic          DYNAMIC         00040d40 040d40 000128 08  WA  4   0  4
  [18] .got              PROGBITS        00040e68 040e68 000194 04  WA  0   0  4
  [19] .data             PROGBITS        00041000 041000 000070 00  WA  0   0  4
  [20] .bss              NOBITS          00041070 041070 0029a4 00  WA  0   0  4
  [21] .comment          PROGBITS        00000000 041070 000027 01  MS  0   0  1
  [22] .ARM.attributes   ARM_ATTRIBUTES  00000000 041097 000031 00      0   0  1
  [23] .shstrtab         STRTAB          00000000 0410c8 0000dd 00      0   0  1

其中 [ 3] .dynsym[17] .dynamic都引用的是索引为4的[ 4] .dynstr STRTAB。那么问题很明显了,应该是导入表和导出表共用了字符表,并且共用了同一个字符串,只不过索引不同。在修改函数名称时,因为是将example替换成了abcdef后,将后面的_test_TestJni_mprotect前移了1位,导致mprotect这个符号引用的name指向了protect。

那么如果想解决这个问题,还得在修改字符表后,枚举所有引用了同一个字符表的section,并查看它里面的每一个符号引用的字符串是某是被修改并移位过的,也要修改它的st_name,然后保存。

已经在这个问题上折腾了几天的我不想再继续倒腾了,所以就调整了下替换字符串的长度,把abcdef换成了abcdefg,这样的话所有符号的st_name索引都不会变化了。

如果以后有机会,会把未完成的功能做完:

  1. 支持短字符替换成长字符。
  2. 替换字符串并移位后检查所有引用同一字符串的符号,修改其st_name
  3. 测试对于不同平台上的的so的兼容性
  4. 支持GCC编译出来的

最后附上代码:https://github.com/k1988/ELFkickers/tree/master/redefine

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

推荐阅读更多精彩内容