一次Android APP脱壳记录

一次针对加壳加固的APP的脱壳简单学习,继续深入学习一下Android应用的分析。

Jadx初步分析确认壳

最开始使用一些ApkScan-PKID之类的查壳工具没有检测出壳(后来用Xposed后使用MT管理器也正确检查到了壳),于是就使用Jadx手动看一下情况。

Jadx简介

Jadx仓库Github地址:skylot/jadx: Dex to Java decompiler (github.com)

jadx - Dex 到 Java 反编译器,用于从 Android Dex 和 Apk 文件生成 Java 源代码的命令行和 GUI 工具

从release中下载最新的压缩包:Releases · skylot/jadx (github.com)

解压后打开/bin/目录,可以看到下面的文件:

1
2
3
4
5
6
7
┌──(kali㉿kali)-[~/Desktop/jadx/bin]
└─$ ll
total 40
-rwxr-xr-x 1 kali kali 9698 Apr 20 2023 jadx
-rwxr-xr-x 1 kali kali 4115 Apr 20 2023 jadx.bat
-rwxr-xr-x 1 kali kali 10419 Apr 20 2023 jadx-gui
-rwxr-xr-x 1 kali kali 4871 Apr 20 2023 jadx-gui.bat

.bat批处理文件是Windows平台运行的,Linux可以使用./jadx运行命令行版本或者./jadx-gui运行gui版本,运行gui版本会展示如下界面:

jadx-gui

分析

我们将待分析的app拖进去就可以。可以看到代码很少,只有一个入口包装代理应用,初步怀疑加壳保护:

打开Resource发现里面有一个tencent_stub文件,搜索一下就发现了完全符合腾讯御安全加固的特征,入口类的load的文件目录等也都符合。

Xposed+反射大师脱壳

利用xposed,将loadClass方法劫持住,每当loadClass方法调用完成后,用xposed执行后置方法。获取方法加载的class对象,然后调用getDex方法。拿到对应的dex文件引用,最后将dex文件序列成byte数据,写到自定义保存文件里面去。就能拿到脱壳后的dex文件。

安装

安装Xposed的app链接:https://pan.baidu.com/s/13f_xXnFRSvT-yotaxL7Igg 提取码: kthq

也可以使用夜神安卓模拟器 Android 5中的应用商店直接搜索Xposed安装即可,安装过程详见:夜神模拟器如何安装xposed框架 | 夜神模拟器帮助中心 (yeshen.com)

反射大师下载地址:https://pan.baidu.com/s/1ALN5n9rsfrV1lqWgaXXY2A 提取码:h5nc

下载之后直接拖到模拟器安装,然后打开Xposed选择该模块,然后按照提示重启即可。

脱壳

打开反射大师,选择需要脱壳的APP:

然后打开需要调试的程序,点击红色的芒星:

点击写出DEX,然后确定即可:

随后使用文件查看器在目录/storage/emulated/0/下即可看到导出的classes.dex文件。

随后使用adb或者直接使用夜神模拟器的文件共享将classes文件导出即可。

模拟器与电脑文件如何互传? | 夜神模拟器帮助中心 (yeshen.com)

frida-dexdump脱壳

2023.12.28更新

安装:hluwa/frida-dexdump: A frida tool to dump dex in memory to support security engineers analyzing malware. (github.com)

frida的配置详见:动态钩子工具Frida的入门学习

开启frida-server之后,打开待脱壳的应用,使用frida-ps -U或者frida-ps -U -a查看app的文件描述符,然后使用frida-dexdump -U -f com.xxx.xxxx即可进行内存查找dex文件脱壳,脱壳后有多个dex文件,可以使用下面脚本简单打包一下方便放入jadx分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import os
import zipfile
import argparse

def rename_class(path):
files = os.listdir(path)
dex_index = 0
if path.endswith('/'):
path = path[:-1]
print(path)
for i in range(len(files)):
if files[i].endswith('.dex'):
old_name = path + '/' + files[i]
if dex_index == 0:
new_name = path + '/' + 'classes.dex'
else:
new_name = path + '/' + 'classes%d.dex' % dex_index
dex_index += 1
if os.path.exists(new_name):
continue
os.rename(old_name, new_name)
print('[*] 重命名完毕')

def extract_META_INF_from_apk(apk_path, target_path):
r = zipfile.is_zipfile(apk_path)
if r:
fz = zipfile.ZipFile(apk_path, 'r')
for file in fz.namelist():
if file.startswith('META-INF'):
fz.extract(file, target_path)
else:
print('[-] %s 不是一个APK文件' % apk_path)

def zip_dir(dirname, zipfilename):
filelist = []
if os.path.isfile(dirname):
if dirname.endswith('.dex'):
filelist.append(dirname)
else:
for root, dirs, files in os.walk(dirname):
for dir in dirs:
filelist.append(os.path.join(root, dir))
for name in files:
filelist.append(os.path.join(root, name))

z = zipfile.ZipFile(zipfilename, 'w', zipfile.ZIP_DEFLATED)
for tar in filelist:
arcname = tar[len(dirname):]

if ('META-INF' in arcname or arcname.endswith('.dex')) and '.DS_Store' not in arcname:
# print(tar + " -->rar: " + arcname)
z.write(tar, arcname)
print('[*] APK打包成功,你可以拖入APK进行分析啦!')
z.close()

if __name__ == '__main__':
args = {
'dex_path': './', # 脱壳后的dex文件目录
'apk_path': './ori.apk', # 原始的apk目录
'output': './output.apk' # 输出打包后的apk目录
}

rename_class(args['dex_path'])
extract_META_INF_from_apk(args['apk_path'], args['dex_path'])
zip_dir(args['dex_path'], args['output'])

Jadx分析脱壳后的Class文件

运行Jadx-gui,将导出的Classes.dex文件拖到窗口,如果报错dex校验和校验失败就点击File->Preferences,将checksum选项改为no:

然后就得到了源代码,可以用于审计:

其他

中间其实有尝试使用ida去逆向,参考的某APP加固产品的深入分析 - 知乎 (zhihu.com),但是由于ida用的不熟,混淆的逻辑也比较复杂,暂时没有使用该方法成功脱壳,未来仍需努力


一次Android APP脱壳记录
https://chujian521.github.io/blog/2023/11/25/一次Android-APP脱壳记录/
作者
Encounter
发布于
2023年11月25日
许可协议