看雪论坛
发新帖
6

[原创]ELF section修复的一些思考

ThomasKing 2014-9-30 23:09 30989
终于抽出时间整理了。。。
限于本菜水平有限,难免会有很多错误和不足之处,请各位大牛指正,小弟感激不尽。
-------------------------------------------------------------------------------------------
一、        概述
相信各位读者对so分析都采用静态和动态相结合的方式,静态分析常用readelf、objdump、ida等工具,这些工具对so文件的分析都会使用到Section信息。从这篇帖子中http://bbs.pediy.com/showthread.php?t=191649 知道,程序并不需要section信息。现很多so文件对section信息都进行了处理,导致常用分析工具无法使用。以下讨论前段时间对section修复的一些思考,若有不足或错误之处,请各位大大指正,小弟感激不尽!
二、        仅处理so文件头
在上文提到的帖子中,给出了一种section处理的一种简单方式。这里在罗嗦下,即将Elf32_Ehdr中的e_shoff, e_shnum, e_shstrndx, e_shentsize字段处理。修复公式:e_shstrndx = e_shnum -1; e_shnum  =  (file_size  – e_shoff) / sizeof(Elf32_Shdr)。在那篇帖子中作为修复的数字so文件,并未处理e_shoff字段,故用上式修复可行。那如果都处理掉,则上式中存在两个未知数,无法利用。
一种简单的思路是,手动查找so文件中一些稳定且标志性的数据作为参考来修复。这里,我选择shstrtab表,这样比较简单。因为shshtrtab后面就是section头信息,这样就间接找到e_shoff位置,即可利用上式修复。
手动查找当然可行,毕竟麻烦。作为程序猿,应该通过程序来解决问题。借鉴手动修复的思路,只要程序能找到shstrtab即可实现修复。从观察或e_shstrndx知道,shstrtab section为最后一个section,即处于文件末尾。那直接移动到末尾读取到shstrtab section,则e_shoff  = sh_offset + sh_zize(这里还需对e_shoff  4字节对齐处理)。
三、        无section信息
现阶段,我遇到的很多so文件的section修复都采用上述方法,还未遇到无section信息的so,即直接将so文件中的section直接删除,或者替换section内容(比如填充隐藏代码或者垃圾数据之类的)。直接删除section信息,即可节省空间,又可让静态工具“蛋疼”(有点好奇为什么不对so作如此处理)。另外,直接从内存中dump出来的so文件,也是没有section信息的(因为section没有被加载到内存中)。当然,从内存中dump的so文件,由于内存对齐的原因,需要作下简单处理,这里就不赘述。
从内存dump出的so文件已经经过解密,直接拿来分析,能获得事半功倍的效果。但没有section信息,静态分析很是不爽。如果原so文件有section信息,则只需要把原so文件中的section信息复制过来并修复Elf32_Ehdr即可。那如果原so无section信息,我的理解是需要对section信息进行重建(虽然现阶段还没用到,处于问题思考的完整性,讨论这种情况)。
使用readelf –S 查看一个完整的so文件section如下图所示:

使用readelf –l 如图所示:

从segment信息可以看出, 对.dynamic和.arm_exidx的section重建很简单,即读取即可。
通过.dynamic,可以对大部分section进行重建,具体如下:
1.        通过DT_SYMTAB,DT_STRTAB,DT_STRSZ,DT_REL,DT_RELSZ,DT_JMPREL, DT_PLTRELSZ,DT_INIT_ARRAY,DT_INIT_ARRAYSZ,DT_FINI_ARRAY,DT_FINI_ARRAYSZ 得到.dynsym,.dynstr,  rel.dyn,  rel.plt,  init_array,  fini_array 相应的section vaddr 和 size信息,完成对上述section的重建。这里需要注意,处于load2中的section,offset = vaddr – 0x1000
2.        通过DT_HASH得到hash section的vaddr,然后读入前两项得到nbucket和nchain的值,得到hashsz = (nbucket + nchain + 2) * sizeof(int), 完成对hash表重建
3.        Plt的起始位置即为rel.plt的末尾,通过1中的对rel.plt的处理,即可得到plt的offset和vaddr信息。通过plt的结构知道,plt由固定16字节 + 4字节的__global__offset_table变量和n个需要重定位的函数地址构成,函数地址又与rel.plt中的结构一一对应。故size = (20 + 12 * (rel.plt.size) / sizeof(Elf32_Rel)。
4.        从DT_PLTGOT可以得到__global_offset_table的偏移位置。由got表的结构知道,__global_offset_table前是rel.dyn重定位结构,之后为rel.plt重定位结构,都与rel一一对应。则got表的重建具体为:通过已重建的.dynamic得到got起始位置,通过__global_offset_table 偏移 + 4 * (rel.plt.size) / sizeof(Elf32_Rel)(这里还需要添加2个int的填充位置)得到got的末尾,通过首尾位置得到got的size,完成重建
5.        通过got的末尾,得到data的起始位置,再通过load2_vaddr + load2_filesz得到load2的末尾(load2即第二个LOAD),即data的末尾位置,计算长度,完成修正。可能读者会问,bss才是load2的最后一个section。的确,但bss为NOBITS,即可把data看作load2最后一个section。
6.        对bss的修正就很简单,offset和vaddr即为load2末尾。由于未NOBITS类型,长度信息无关紧要。
7.        到这里,读者可能已经发现,还没对text和ARM.extab修正。限于本人水平,还没能找到方法区分开这两个section。现处理是将之合并,作为text & ARM.extab节。具体修正:offset和vaddr通过plt末尾得到,长度通过ARM.exidx的起始位置和plt末尾位置计算得到。
至此,绝大部分section信息已经重建完成。最后,在将shstrtab添加,并修正Elf32_Ehdr,完成section重建。虽然未100%重建,但已经能够帮助分析了。重建后的如图所示,图中红色部分即是未分离的test & ARM.extab section。

使用ida也能正常打开,只是会将ARM.extab的数据转换成错误代码,其他均正常。

---------------------------------------------------------------------------------------
最后,便于理解,给出附件测试。具体详解附件的说明文档,这里就不赘述了。
-------------------------------------------------------------------------------------
2014.10.10:
由于文件操作不当,导致修复后的Elf32头未写入文件,现将附件更新
上传的附件:
本主题帖已收到 0 次赞赏,累计¥0.00
最新回复 (31)
4
我是小三 2014-10-1 10:46
2
感谢分享。沙发是我的..
2
万抽抽 2014-10-1 11:26
3
那我就只要坐板凳了
5
boyliang 2014-10-1 20:26
4
满满的干货,顶一个
1
鬼谷子c 2014-10-2 00:17
5
好文章“!
4楼广告位出租!
学习elf的进阶篇,赞一个!
6
ThomasKing 2014-10-2 09:31
6
多谢大大支持!
6
ThomasKing 2014-10-2 09:32
7
多谢鬼哥支持!
SANCDAYE 2014-10-7 23:41
8
支持一下,good job
SpaceDye 2014-10-10 13:41
9
支持一下,不错的教程。
netsniffer 2014-10-10 15:56
10
LZ看看附件两个elf文件,改了section entry size和number of sections,strsection index,IDA可以正常解析,否则原始的ori文件加载会失败

IDA解开看,貌似有壳,怎么破?
上传的附件:
zrhai 2014-10-10 16:00
11
多谢分享!
测试了下工具,貌似 e_shentzise 好像没强制修改为 0x28
6
ThomasKing 2014-10-10 19:45
12
额,这个应该是被加过密。。。脱壳得靠你自己了。
6
ThomasKing 2014-10-10 19:47
13
多谢反馈。 由于文件操作不当,导致未将修复后的Elf头写入文件。 已更新附件
jeffycf 2014-10-23 09:57
14
技术才是硬道理
Colbert仔 2015-6-25 12:00
15
顶一个!!
Colbert仔 2015-6-25 17:05
16
发现有个问题:

通过 DT_HASH 得到 hash section 的 vaddr,然后读入前两项得到 nbucket 和 nchain
的值,得到 hashsz = (nbucket + nchain + 2) * sizeof(int), 完成对 hash 表重建

我用LZ提供的工具修复so后,hashsz=327F17DCh,导致IDA报错,010editor也解析不出来,是因为算法有误吗?

这so就是MSC第四题:

http://bbs.pediy.com/showthread.php?t=198529

http://bbs.pediy.com/showthread.php?t=197235

LOG:

默认读入文件名:dump_new.so, 输出文件:dump_new_full.so
如需更改,请修改.bat文件输入参数
************Begin*******************
[0] Find e_phnum = 8
[1] Find load1, offset = 0x0, length = 0xd2e6
[2] Find load2, offset = 0x28000, vaddr = 0x39000, length = 0x296b
[3] Start rebuilding section info
[4] Rebuilding success
jieniruyan 2016-2-16 17:11
17
学习了。
frozenrain 2016-4-9 18:00
18
.data的偏移是0x1000对齐的,由此可以修正got的大小。
.bss的大小是load节的实际大小和内存大小之差
.text和exid的大小可由.finit的偏移算出来。
2
大王叫我挖坟 2016-5-2 20:33
19
文章很好,好可惜哦,没有提供源码,不能对elf文件结构做深入了解了,网上关于elf文件的说明也只是简单停留在section层面上,而linux的elf又跟android 的elf有区别,哎,想找份完整的资料都难
2
大王叫我挖坟 2016-5-2 21:56
20
哈哈大大没有加密exe文件,丢ida结合已有的知识,大概可以写出源码来,如果有疑惑的建议先看大大http://bbs.pediy.com/showthread.php?t=193279这篇文件,弄懂了,就可以还原大大的exe啦
古往今来 2016-11-16 16:20
21
dump之后怎么修复对齐问题?
OnlyForU 2016-11-16 16:26
22
学习! 感谢楼主!
古往今来 2016-11-25 21:02
23
感谢楼主大大,学习了楼主的思路,用了大大的程序,.fini_array和.init_array段修复的有些问题,其他的还行。我根据楼主的思路写了个修复工具,修复SO后GOT略有问题
田darren 2017-3-1 14:40
24
init_array段 修复, 有很大帮助.
狂奔的鸡骨架 2017-3-1 18:52
25
mark
无所从来 2017-5-15 14:25
26
restoreSection.bat  中的apkso.so是什么?
bjhrwzh 2017-6-16 10:05
27
今天又用到了,还是mark下。要不每次都得翻。真心不错
茅山小僧 2017-8-4 09:14
28
多年以后看到依然觉得总结的很牛逼
吴下阿蒙丶 2017-8-6 18:55
29
大佬,,就是你的软件在重建Section时读取ELF魔数错误这是怎么回事呢?
kiyaa 2017-9-1 13:41
30
感谢大佬
臭小鸭 2017-9-11 21:43
31
谢谢分享
wx_天国的声音 2天前
32
new_art_dexFile_openMemory这个函数,是在哪个文件里面调用的?
返回



©2000-2017 看雪学院 | Based on Xiuno BBS | 微信公众号:ikanxue
Time: 0.015, SQL: 12 / 京ICP备10040895号-17