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

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

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

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

从该段代码中可以看出,在 delta.apk 主 activity 运行后,会加载自身native so,并执行该 so 下的 readmem()I 函数。由于该代码段在该类加载时最先执行的,因此余下的算法分析可以暂时不予考虑,我们重点关注如何运行时自
修改 dalvik 代码。打开 IDA 载入 libnet.so 进行静态分析,找到 native 对应的readmem 函数,该函数主要执行一个 search 函数,也就是该程序的主要核心。进入 search 函数,进行主要功能分析,代码流程图如下图 3:
点击图片以查看大图图片名称:	3.jpg查看次数:	3文件大小:	51.1 KB文件 ID :	81457
图 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.jpg查看次数: 0文件大小:  17.6 KB
图 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 也就是注入代码的二进制。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐