首页
论坛
课程
招聘
[原创]运行时自篡改dalvik字节码delta.apk原理解析(逆向)
2013-8-6 16:32 37783

[原创]运行时自篡改dalvik字节码delta.apk原理解析(逆向)

2013-8-6 16:32
37783
出于好奇并且当做原生程序分析练习,对之前运行时自篡改dalvik字节码的程序delta.apk进行了逆向分析,了解了该程序的自篡改原理,现小结如下,如有错误希望大家指正!

参考链接:http://bbs.pediy.com/showthread.php?t=170381


年初,bluebox 发布一个可以在运行时修改自身 dalvik 字节码的 demo,在论坛的相关信息
http://bbs.pediy.com/showthread.php?t=170381。

该 demo 通过 native 层代码修改自身 dex 从而增加静态分析难度。 Demo 及伪加密解除代码链接如下
https://github.com/blueboxsecurity/DalvikBytecodeTampering

出于好奇,我对其简单逆向,并了解该程序的执行原理,并进行小记。
首先使用 Apktools,发现不能对其进行反编译。使用压缩包工具打开查看
后并尝试解包则遇到如下图 1 情形:

图 1

突然想起这或许就是之前论坛上有人遇到的 zip 伪加密。在 android 解析apk 时,由于忽略了加密 zip,直接跳过了加密头部的解析,因此通过修改 zip格式的加密标识,可以是实现伪加密。bluebox 也提供了 python 代码进行解压
缩。解压缩完成后将所有内容再次通过 zip 压缩成 apk,即可开始使用 VTS 对其分析。
使用 VTS 载入 delta.apk, 找到 activity(Lcom/bluebox/lab/poc/Action;),
看到有类的静态构造函数如图 2,对应于源代码 static{}代码段:

图 2

从该段代码中可以看出,在 delta.apk 主 activity 运行后,会加载自身native so,并执行该 so 下的 readmem()I 函数。由于该代码段在该类加载时最先执行的,因此余下的算法分析可以暂时不予考虑,我们重点关注如何运行时自
修改 dalvik 代码。打开 IDA 载入 libnet.so 进行静态分析,找到 native 对应的readmem 函数,该函数主要执行一个 search 函数,也就是该程序的主要核心。进入 search 函数,进行主要功能分析,代码流程图如下图 3:

图 3

关键点大致解释如下:
(1)sysconf 用于确定当前的系统变量之值,sys/sysconf.h 头文件可以看到#define _SC_PAGESIZE 0x0027。查看看该系统内存页大小,用于内存操作。
(2)findmagic 可知是查找 dex 加载在内存中的位置。
(3)get* 函数功能主要是定位相应代码的位置。
(4)mprotect 设置内存访问权限,以便对相应内存进行修改。
(5)memcpy 修改函数 dalvik 字节码。


接下来我们将对关键函数进行分析, 需要我们对 dex 文件可是有一定的了解,
循环调用 findmagic,该函数用于找到 dex 在内存中的起始地址,用于在内存中
分析 dex 结构
循环体:
.text:000011E4 loc_11E4 ; CODE XREF: search+2Aj
.text:000011E4 ADDS R4, R4, R6 ;R6 存放对齐页大小
.text:000011E6 MOVS R5, R4
.text:000011E8 ADDS R5, #0x28 ;该页 map 偏移 0x28 的地址
.text:000011EA MOVS R0, R5
.text:000011EC BL findmagic ;查找是否存在 dex\n035
.text:000011F0 CMP R0, #0 ;判断是否找到
.text:000011F2 BEQ loc_11E4

Findmagic 函数:
.text:00000FBC EXPORT findmagic
.text:00000FBC findmagic ; CODE XREF: search+24p
.text:00000FBC
.text:00000FBC dest = -0x1C
.text:00000FBC var_14 = -0x14
.text:00000FBC
.text:00000FBC PUSH {R4,R5,LR}
.text:00000FBE LDR R4, =(__stack_chk_guard_ptr - 0xFC8)
.text:00000FC0 LDR R1, =(aDex035 - 0xFCE)
.text:00000FC2 SUB SP, SP, #0x14
.text:00000FC4 ADD R4, PC
.text:00000FC6 LDR R4, [R4]
.text:00000FC8 MOVS R5, R0 ;上层传参(比较的起始偏移)
.text:00000FCA ADD R1, PC ; "dex\n035" DEX 起始标识
.text:00000FCC LDR R3, [R4]
.text:00000FCE MOVS R2, #8 ; n
.text:00000FD0 ADD R0, SP, #0x20+dest ; dest
.text:00000FD2 STR R3, [SP,#0x20+var_14]
.text:00000FD4 BLX memcpy ;参数:dest,起始标识,8;复制标识到缓冲区
.text:00000FD8 MOVS R2, #8 ; n
.text:00000FDA MOVS R0, R5 ; s1
.text:00000FDC ADD R1, SP, #0x20+dest ; s2
.text:00000FDE BLX memcmp ;比较 s1 偏移是否存在标识
.text:00000FE2 LDR R2, [SP,#0x20+var_14]
.text:00000FE4 NEGS R3, R0
.text:00000FE6 ADCS R0, R3
.text:00000FE8 LDR R3, [R4]
.text:00000FEA CMP R2, R3
.text:00000FEC BNE loc_FF2
.text:00000FEE ADD SP, SP, #0x14
.text:00000FF0 POP {R4,R5,PC}


找到之后开始查找需要修改的代码地址,这需要熟悉 dex 文件中各种索引的关系:

我们可以查看相关的头文件来了解各个结构(字符,类型,函数,类等)对
应关系 , 相 关 头 文 件 位 于 源 码 \dalvik\libdex\DexClass.h 和\dalvik\libdex\DexFile.h 中,如果要修改对应类中的方法, 过程大致如下 (具体过程查看相关书籍材料) :

(1) 首先,要修改函数代码肯定对应于 dalvik 指令, 而结构 DexCode 存放了关
dex 代码的信息,我们的目标就是要修改该结构相应的内容
struct DexCode {
u2 registersSize;
u2 insSize;
u2 outsSize;
u2 triesSize;
u4 debugInfoOff; /* file offset to debug info stream */
u4 insnsSize; /* size of the insns array, in u2 units */
u2 insns[1]; /*存放代码位置,也就是我们需要改动的地方*/
……
};


(2)如何找到对应dexcode结构,将由以下结构DexMethod的成员指明了dexcode
结构偏移

struct DexMethod {
u4 methodIdx; /* index to a method_id_item */
u4 accessFlags;
u4 codeOff; /* file offset to a code_item */
};

该结构存放了对应函数有关的信息, 一个确定的函数就存在这么一个结构,确定
了这一个函数,找到这一个结构体,就可以沿着往下修改 dalvik 指令了。
(3)而找到这么一个结构,我们需要一个确定的函数信息,包括所在的类,函
数签名,函数名字,返回类型等,当这些确定了,这个函数也就唯一确定了。

这也就是如下 get*函数的用途:
.text:000011F4 LDR R1, =(aLSavaLangStrin - 0x11FE)
.text:000011F6 MOVS R0, R5
.text:000011F8 MOVS R2, #0x12
.text:000011FA ADD R1, PC ; "L 褬 ava/lang/String;"
.text:000011FC BL getStrIdx
.text:00001200 LDR R1, =(aAdd - 0x120A)
.text:00001202 MOVS R2, #3
.text:00001204 MOVS R4, R0 ;R4 存放其的对应字符串 ID
.text:00001206 ADD R1, PC ; "add"
.text:00001208 MOVS R0, R5
.text:0000120A BL getStrIdx
.text:0000120E MOVS R1, R4
.text:00001210 MOV R8, R0 ;R8 存放了 add 对应的 ID
.text:00001212 MOVS R0, R5
.text:00001214 BL getTypeIdx
.text:00001218 MOVS R4, R0 ;R4 存放了 string 对应的类型 ID
.text:0000121A MOVS R1, R4
.text:0000121C MOVS R0, R5
.text:0000121E BL getClassItem ;找到 string 类的 Item ID
.text:00001222 MOV R1, R8
.text:00001224 MOVS R6, R0 ;R6 存放 string 类 Item ID
.text:00001226 MOVS R2, R4
.text:00001228 MOVS R0, R5
.text:0000122A BL getMethodIdx ;通过 R8,R4 找到;Ljava/lang/String;->add (Ljava/lang/String;)V;
.text:0000122E MOVS R1, R6
.text:00001230 MOVS R2, R0 ;R2 add 函数 ID
.text:00001232 MOVS R0, R5
.text:00001234 BL getCodeItem
.text:00001238 MOVS R4, R0
.text:0000123A ADDS R4, #0x10 ;此时跳过 16 字节的位置找到要修改的位置 insns[1]


1)先找到 Ljava/lang/String;和 add 对应的字符串 ID。
2)在找 Ljava/lang/String;字符串对应的类型。
3)通过该类型找到对应的类 Ljava/lang/String;
4)接着寻找该类函数 ADD 对应的 ID
5)之后就找到了上文提到的 DexMethod 结构体
6)通过结构体找到了存放代码的缓冲区
也就是对应了 VTS 中解析到的类,如图 4:

图 4
也就是说该程序就是修改了 Ljava/lang/String;->add 函数代码
最后:
.text:0000124A MOVS R1, R7 ; len
.text:0000124C MOVS R2, #3 ; prot
.text:0000124E ADDS R0, #0x10 ; addr
.text:00001250 BLX mprotect
.text:00001254 LDR R1, =(inject_ptr - 0x125E)
.text:00001256 MOVS R0, R4 ; dest
.text:00001258 MOVS R2, #0xDE ; n
.text:0000125A ADD R1, PC
.text:0000125C LDR R1, [R1] ; src
.text:0000125E BLX memcpy
.text:00001262 POP {R2}
.text:00001264 MOV R8, R2
.text:00001266 POP {R4-R7,PC}
.text:00001266 ; End of function search


inject_ptr 也就是注入代码的二进制。
至此,有关的分析到此结束,如有错误请大家指正!

第五届安全开发者峰会(SDC 2021)议题征集正式开启!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (29)
雪    币: 184
活跃值: 活跃值 (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
whnet 活跃值 2013-8-6 16:46
2
0
e ,好复杂的样子。 先读。
雪    币: 160
活跃值: 活跃值 (18)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
Xbalien 活跃值 4 2013-8-6 21:15
3
0
有人分析过这个例子吗?感觉直接读取/proc/self/maps中的内容,直接找到对应dex所在的位置更快捷。不知道是否可行?
雪    币: 101
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
suwey 活跃值 2013-8-6 21:19
4
0
高端绑定。。
雪    币: 245
活跃值: 活跃值 (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
八十客车 活跃值 2013-8-7 13:31
5
0
谢谢分享收藏了
雪    币: 111
活跃值: 活跃值 (75)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
netsniffer 活跃值 2013-8-7 13:40
6
0
这是要增加APK破解的难度吧,类似于加壳,加载后动态修改?
这样不会增加什么权限,毕竟核心的权限判断逻辑都在binder服务端,即system server那边,
一个APK又不可能修改system server中的dex内存,只能修改自身的内存,
这样作也就是为了增加APK破解的难度?
雪    币: 30
活跃值: 活跃值 (112)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
malokch 活跃值 2 2013-8-7 14:45
7
0
敢问楼主,VTS是什么工具,度娘上没搜到
雪    币: 207
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kisbuddy 活跃值 2013-8-7 16:31
8
0
前来支持~~~
雪    币: 160
活跃值: 活跃值 (18)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
Xbalien 活跃值 4 2013-8-7 21:37
9
0
http://www.virtuous-ten-studio.com/
雪    币: 160
活跃值: 活跃值 (18)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
Xbalien 活跃值 4 2013-8-7 21:47
10
0
主要还是为了防dalvik静态分析。说到加壳4.0以后可以使用openDexFile方便内存加载了
雪    币: 182
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
justlovemm 活跃值 2013-8-10 14:29
11
0
好帖!
Arm上的SMC啊。
收藏!
雪    币: 182
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
justlovemm 活跃值 2013-8-10 14:31
12
0
再请教一下楼主,android下有没有通用的注入so文件的方法?
雪    币: 1
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
timped 活跃值 2014-10-3 10:17
13
0
memcpy函数那一块有没有细致分的啊?没有造成溢出吗?
雪    币: 40
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hendyj 活跃值 2014-10-6 00:08
14
0
新人学习一下
雪    币: 1
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
guokaiyou 活跃值 2014-10-8 20:12
15
0
学习
雪    币: 9
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
有效啊 活跃值 2015-5-7 15:37
16
0
楼主 关于那个findMagic函数  那个开始地址是怎么获取的啊 而且好像是每一页只比较第0x28个字节是不是“dex\n035” 这样搜索真的可以么 看了一下 dex还真是这么在内存中映射的   还请楼主赐教啊
雪    币: 4082
活跃值: 活跃值 (253)
能力值: ( LV7,RANK:140 )
在线值:
发帖
回帖
粉丝
二当家a 活跃值 2 2015-5-8 10:18
17
0
你好,有个问题想请教一下。

目前我在进行dalvik的插桩,但是apk的每个method对于使用的寄存器数有限制,有的限制在v0 ~v15之间,有的是v0 ~v 255.  这个设置感觉跟编译出来的结果有关,
但是我经常会需要加入比较多的 寄存器, 遇到限制在 v0 ~ v15 之间的限制,就没有办法了, 会重编译出现错误。   
不知道xbalien兄有没有比较好的建议或者方案,谢谢!
雪    币: 42
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
northriver 活跃值 2015-10-13 16:40
18
0
请教楼主,找到了动态加载的那段数据之后,是怎么分析那段数据的
雪    币: 11
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kilybeer 活跃值 2016-3-6 11:33
20
0
search 的开始地址是怎么来的。
SUB     SP, SP, #0xC
.text:00001278                 LDR     R0, [SP,#0x10+arg_1C]
.text:0000127A                 STR     R1, [SP,#0x10+var_C]
.text:0000127C                 BL      search
雪    币: 409
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
coody 活跃值 2016-3-16 09:40
21
0
先mark一下,等待更好的方法!!!
雪    币: 227
活跃值: 活跃值 (62)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
大王叫我挖坟 活跃值 3 2016-5-20 22:37
22
0
哇塞谢谢分享,之前看jack jia 大神的文章就提到这个了可惜找到的demo无法运行,去掉了伪加密还是不能运行,谢谢分享,再这里居然找到
雪    币: 227
活跃值: 活跃值 (62)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
大王叫我挖坟 活跃值 3 2016-5-20 22:41
23
0
哇哈哈谢谢分享,这个用来对加密有帮助,用来隐藏掉system.load函数再好不过了。嘿嘿
雪    币: 227
活跃值: 活跃值 (62)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
大王叫我挖坟 活跃值 3 2016-5-23 11:32
24
0
大哥啊求你的自动篡改dalvik的源码啊!!
雪    币: 245
活跃值: 活跃值 (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
八十客车 活跃值 2016-6-30 14:26
25
0
大哥,你回复错了吧?我不是楼主
游客
登录 | 注册 方可回帖
返回