ELF Format DIY For Android

Author: ThomasKing

本文只讨论安卓平台ELF格式一些可以DIY的地方。当然,有些DIY有使用价值,有些DIY仅好玩而已。为了完整性,均在下文讨论。

一、Elf32_Ehdr

1. e_ident[16]

这个字段,现ELF标准只使用了前7个字节,后9个字节是未定义的。在linux平台,这9个字节是填0,且不能动。而安卓平台,参看linker源码,对so文件格式只判定前4个字节,即’7f’, ‘45’, ‘4c’, ‘46’。故可DIY如下图:

图 1

2. 平台相关性标识

在Elf32_Ehdr中,于平台性标识有:ELF文件类型e_type;CPU平台属性e_machine;ELF版本号e_version(ELF版本只有1.2,故该值始终为1);文件相关属性e_flag。这些字段都是来说明ELF文件信息,类似产品说明,对SO文件的使用无任何影响。故可DIY如图。

图 2

Readelf查看信息:

图 3

3. e_entry

对于SO文件来说,这个值是无意义的。所以随便怎么都行。

4. 与section相关

与section相关的e_shoff,e_shentsize,e_shnum, e_shstrndx随意DIY,http://bbs.pediy.com/showthread.php?t=192874写得清楚,就不赘述。

5. 其余字段

其余字段由于加载时会被使用,故不能DIY。详见linker源码。

上述DIY无处理时机的限制,即既可对SO作预处理时,也可在代码中。

二、Section

1. 移动section

使用readelf –l查看so文件的Section toSegment mapping:

图 4

总的来说,除了与代码相关受寻址影响的section外,其余section都是可以移动的。受代码访问影响的section有:.plt,.ARM.extab,.ARM.exidx,.rodata,.got,.data,BSS。其余section可以随意移动。为了处理方便,移动到的位置最好选在当前所处LOAD末尾。由于受到segment的属性(RWX)影响,跨LOAD处理稍微繁琐。以移动到LOAD末尾为例,具体移动某section的处理流程如下:

Step1: 选定移动位置。

Step2: 根据对齐属性,计算合适的起始位置。

Step3: 复制section数据到新位置。

Step4: 修订section在.dynamic中的位置信息,即p_offset、p_vaddr和p_paddr。

Step5: 若移动segment,修订对应在segmentheader中的信息。

这里就不给出例子,下文将看到。

2. 增删section

查看section信息可知,LOAD内section之间是紧凑排列的。删除某section的数据,可不移动section。但增加section内容,就需要移动,并且修订section时,需修订p_filesz和p_memsz。有些section可以单独修改,而有些section修改后,需要重新调整与之相关的section。比如往dynsym末尾添加一个符号,并为该符号在dynstr添加一name字符串。便于查找,计算出name的hash值,然后往hash表中添加。如果还涉及到rel,还需修改rel的r_offset和r_info字段。根据上述处理,再移动修改相应的section。这里就不给例子,下文将看到。

3. 修改init_array

对fini_array和init_array类似,以init_array为例,讨论init_array的一些DIY。

3.1 变更执行顺序

通过__attribute__((constructor(num)))声明某一函数(num值越小,越先执行),即指定了在.init_array中的位置,修改其顺序即可实现。例如:

void __attribute__((constructor(101))) kingcoming();

void __attribute__((constructor(200)))soldiercoming();

void kingcoming(){

__android_log_print(ANDROID_LOG_INFO,"init_array", "King is coming!");}

void soldiercoming(){

__android_log_print(ANDROID_LOG_INFO,"init_array", "A soldier is coming!");}

执行结果:

图 5

修改其顺序,即交换图中红色区域中的数据:

图 6

再执行:

图 7

3.2 普通函数——init函数

将普通的函数,升级为init函数。具体操作步骤:

Step1: 查找目标函数的起始位置。

Step2: 移动rel.dyn到LOAD1末尾

Step3: 移动init_array到LOAD2末尾,添加目标函数地址

Step4: 修订rel.dyn和init_array在.dynamic中的位置。

Step5: 修订LOAD1和LOAD2的长度信息

在3.1代码中,添加:

void justHello(){

__android_log_print(ANDROID_LOG_INFO,"JNITag","Hello!");

}

现将其修改,将justHello提升为init函数,且最先执行。实现流程上述已讨论,具体代码就不贴了,参看附件中的updater.c。处理后的so文件,需要重建section才能查看下图(重建工具:http://bbs.pediy.com/showthread.php?t=192874):

图 8

处理后的SO文件执行结果:

图 9

3.3 init函数转普通函数

相对3.2来说,init函数转普通函数算较简单吧,这里就不赘述了

4. GOT表 —— From HOOK to SelfPatch

针对GOT表的HOOK技术屡见不鲜,这里还是啰嗦下一般的HOOK应用场景和流程,以便讨论SO自Patch,实现类似目前基于函数Patch的第二代dex加固技术。下图简单描述了SO函数HOOK的基本原理。

图 10

其中,个人认为有一个很重要的点就是:HOOK的是Import函数,即访问的是外部函数。那么如果HOOK自身的函数,达到替换的函数的目的,就实现了类似DEX的函数patch效果(纯属个人YY)。

在linux平台,PIC模式的SO文件是不区分本地符号还是外部符号,即不管是Import符号还是Export符号,都走GOT过程。采用HOOK原理,即可实现对non-static的变量和函数的替换,达到HOOK和自Patch的效果。

思路清晰,似乎就成功了。但是,随意打开一个ARM架构的SO文件查看GOT表,发现Export函数根本不在GOT中,不过non-static变量还是在的(具体LINUX平台和android在这方面的比较,在附件《Android ELF GOT sectionの不同之处》,此文中仅根据表象,分析了差异点。限于水平,未找到相关资料佐证,其中的原因分析纯属个人YY,难免有错误之处,请各位批评指正)。

一种简单的Patch思路是,通过生成一个空壳libFuncStub.so文件,把需要Patch的函数放在其中。目的是构造一个stub在GOT中。然后SO加载起来之后,通过HOOK GOT自身函数,达到Patch效果。由于linker会将依赖的so也加载起来,libFuncStub.so不能扔掉。

作为SO DIY,不能扔掉是不能忍的。想扔掉libFuncStub.so,即要绕过linker加载机制。一旦绕过linker,让libFuncStub.so不加载,同时函数在执行前被patch掉,就能无缝完成这个过程。Patch利用HOOK原理实现,重点就在如何绕过linker不加载libFuncStub.so。

了解linker加载so大体过程的都知道,linker会将so所依赖的加载起来。具体是:通过DT_NEEDED找到对应在dynstr中的name再加载。同一个so文件不会被加载二次。另外,libdl.so一定会被加载。为了让linker不加载libFuncStub.so,我这里采用修改DT_NEEDED所指向的libFuncStub.so为libdl.so,达到fake的目的。另外还有一个问题就是函数重定向。

还是查看linker源码,发现linker对所有的重定位都采用同样的方法,如图所示:

图 11

似乎可以不用作任何处理。测试发现,加载时报出重定位错误,经过仔细分析linker重定位过程,发现一点:由于_elf_lookup函数寻找符号时,有此if(s->st_shndx == 0) continue;判定,返回NULL,导致relocate错误。st_shndx =0即SHN_UNDEF,故将此设置为非0 即可。那么整个流程的具体步骤就是:

预处理:

Step1: 抹去DT_NEEDED中对应的so文件

Step2: 找到对应的函数符号,修改st_shndx信息

调用函数前的Patch流程:

Step1: 找到SO起始内存

Step2: 找到符号表、字符串表、重定位表

Step3: 找到stub函数并替换

下面,构造一个简单的例子来说明。为了简单期间,不涉及section移动,rel表组合,加密等等。Java调用naïve patchTest,调用getNameStub函数获得字符串并打印。getNameStub定义在libFuncStub.so中。实现目标:通过patch,调用getNameStub即调用SO自身的getName函数。流程上述已说明,代码就不贴了,见附件。patchTest函数:

void patchTest(){

char name[20];

int i = 0;

if(flag) //第一次调用函数时,进行Patch

{

patchName("getNameStub","getName");//Patch函数

flag= 0;

}

getNameStub(name);//获取到字符串”ThomasKing”,并非”Stub!”

__android_log_print(ANDROID_LOG_INFO,"JNITag","Show my name: %s", name);

}

运行效果很简单,就打印一句话:

图 12

当然,PatchName函数不仅仅只Patch,可以把基于函数解密融合在一起(基于函数加解密实现见贴:http://bbs.pediy.com/showthread.php?t=191649)。

这里稍微再YY一下,针对无源码加解密实现。(就起原因是上次做了一个很挫的SO加解密投去ALICTF热身赛)。一种较好的大致思路是,将原SO抹去section,加密添加在壳子SO末尾。把原SO的rel表组合到壳子的rel表,壳子可以自身进行基于SECTION、函数等等加密。执行时,将原SO 匿名映射到内存,修复SO的rel表。当然,如果为了保证JNI入口地址的一致性,再使用NDK HOOK(http://bbs.pediy.com/showthread.php?t=192047)到原SO函数。

这个Patch还算是完美,毕竟不依靠stub.so。不过编译时能否不依靠呢? 即又回到如何在GOT表中构造stub的问题。上述方案是通过HOOK函数符号实现。从原理上,还可以HOOK non-static变量。可能会问,non-static变量本来就是可以访问的,HOOK无非可以改变函数地址而已。从函数指针的间值寻址出发,又可以构造一种Patch方案,可使stub函数存在于自身SO中,在编译时不依靠stub.so文件。具体实现流程和上述差不多,只是修改rel.dyn,限于篇幅,就不贴了吧。相信各位读者都能做到。

三、Segment

由于segment已经包含了一些section,对segment的DIY只是简单的整体移动和长度增长。前面例子已经看到,就不赘述了。

四、参考文献

Linker源码

ELF文件格式

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

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

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

ELF Format DIY For Android相关推荐

  1. 搭积木java,Jimu:像搭积木一样DIY原生Android App

    正如本站之前所报道过的,现在国内国外市场上的App生成工具很多,无需编程就能DIY.目前正在众筹平台Kickstarter上融资的Jimu也是一款App生成工具,顾名思义,Jimu希望将App创建变得 ...

  2. DIY基于android的带GPS的相机遥控器

    相机GPS可以把位置坐标写到照片中,永远记录了你每张照片的拍摄地点,是一种很有价值的摄影附件.尤其对于那些经常旅游的拍摄者来说,更是不可或缺的装备.对于单反相机,很多品牌或型号都必须外接专用的GPS接 ...

  3. DIY 新浪微博Android客户端 ----用Android手机发送来自iPhone的微博

    今天看到一个哥们发布的DIY新浪微博客户端,自己试了下,确实好用,赫赫,给大家分享下 链接 http://hi.baidu.com/aqtata817/blog/item/776246021f81b6 ...

  4. ELF Format 笔记(十三)—— 段权限

    ilocker:关注 Android 安全(新手) QQ: 2597294287 一个可被系统加载的程序至少拥有一个可加载段.当系统创建可加载段的内存映像时,会根据 p_flags 赋予一定的访问权限 ...

  5. ELF Format 笔记(七)—— 符号表

    最是那一低头的温柔,像一朵水莲花不胜凉风的娇羞,道一声珍重,道一声珍重,那一声珍重里有蜜甜的忧愁 -- 徐志摩 ilocker:关注 Android 安全(新手) QQ: 2597294287 符号表 ...

  6. diy一个android手机版下载,Notch DIY

    Notch DIY安卓版app个性化好玩的个性锁屏壁纸应用,十分的炫酷哦,大量不同类型的壁纸都是可以选择的,不管是什么机型的手机都能进行使用,可做锁屏的壁纸或内置的壁纸,自己也能在平台内diy设计起来 ...

  7. diy一个android手机版下载,家居3D设计DIY

    这是一款应用于安卓平台的室内设计软件,大家可以通过它在手机上随意的进行室内家居设计.这款软件功能非常强大,无论是重新设计.布置您的家,改变家居装饰, 更换室内颜色或重新布局,家居3D设计DIY帮助你实 ...

  8. diy一个android手机版下载,居然设计家DIY手机版下载-居然设计家DIY 安卓版v1.3.0.5-PC6安卓网...

    居然设计家DIY手机版是一款DIY家居设计手机软件,有了居然设计家DIY,自己就可以动手设计家居装修图,海量真实品牌家具产品任你挑选,想摆哪里摆哪里,在手机上就能搭配出自己心目中家的样子. 软件介绍 ...

  9. android手机系统怎么刷机包,怎么刷机安卓系统-安卓手机刷机包如何DIY让Android刷机包更好用...

    现在很多用户都喜欢通过网上下载第三方的刷机也来刷自己最喜欢的手机系统 ,但是各类刷机包中往往会捆绑很多垃圾软件和没用的软件,而删除这些软件,一般都要先ROOT手机才能删除.特别是少数软件少数软件非常顽 ...

最新文章

  1. 关于path.join,path.resolve的详细介绍
  2. ai描边工具怎么打开_ai切片工具怎么用?ai切片工具使用教程
  3. 此版本的应用程序不支持其项目类型 (.etp)_适用于Microsoft Dynamics 365商业中心的VPS和VJS版本1.8...
  4. 一步一步教你使用AgileEAS.NET基础类库进行应用开发-基础篇-通过SQL实现特殊业务...
  5. 【多目标优化求解】基于matlab布谷鸟算法多目标(成本+时间+质量)优化求解【含Matlab源码 118期】
  6. 三极管作为电流源时的公式计算
  7. 3dmax模型导入unity
  8. 火车采集器V2010免费版下载
  9. 苹果系统手机调用java线程出错_Java线程面试题
  10. 大象无形11.4.4 实战:创建新的渲染代理 笔记
  11. annaconda 安装 opencv(cv2)
  12. 赵小楼《天道》《遥远的救世主》深度解析(118)女人的客观就那么难么?不难。难的是不想客观的人。
  13. VB中的IIF() 函数
  14. BBEdit 12.6.3 FIXED 特别版 Mac 强大的文本和HTML编辑器及汉化工具
  15. linux添加ip白名单_centOS7 下利用iptables配置IP地址白名单的方法
  16. 基于springboot学生公寓管理系统-计算机毕业设计源码+LW文档
  17. NCMMSC 2021丨希尔贝壳参加第十六届全国人机语音通讯学术会议
  18. 在某点邻域有定义_z = f(x, y) 在点 (x0, y0) 的某一邻域内有定义, 当x从x0 取.PPT...
  19. 决策树(Decision Tree)总结
  20. 中国移动 待遇之我见

热门文章

  1. python:海龟交易法则 画唐奇安通道
  2. FreeRapid v0.9
  3. Windows 64位下载安装My SQL
  4. 串口转以太网模块—WIZ100SR 用户手册(三)
  5. 程代展老师的感悟(前传)
  6. Linux —— tftp 使用
  7. Unity 5.4+版本报错RuntimePlatform.WindowsWebPlayer 或者 RuntimePlatform.OSXWebPlayer 弃用的
  8. 爱情,最幸福的信仰!
  9. 超级计算器 - 网易出品的数学好助手
  10. php码农适合的笔记本,聊一款“码农”比较适合的笔记本