您的位置:首页 > 其它

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

2013-09-24 11:35 435 查看
年初,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 也就是注入代码的二进制。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: