APP测试0基础——APP加解密对抗

2025-03-02 26 0

前言

某APP,访问后为APP下载页面,从而下载到目标APP进行分析。

安装frida,上传frida-server

# frida-16.6.6-cp37-abi3-win_amd64
pip install frida  
pip install frida-tools
pip install Pyro4

下载frida-server,这里需要与你python安装的frida版本一致,并且需要查看模拟器架构

adb shell getprop ro.product.cpu.abi # 查看位数

APP测试0基础——APP加解密对抗插图

上传frida-server(没连接设备记得先adb connect 连接设备,这里不赘述了)

adb push frida-server /data/local/tmp/

APP测试0基础——APP加解密对抗插图1

启动frida-server,(frida-server是我自己进行了一下重命名)

adb root
adb shell
cd /data/local/tmp
chmod +x frida-server
./frida-server

另起一个cmd,查看模拟器进程以验证frida是否连接成功:有进程相关信息即成功连接。

APP测试0基础——APP加解密对抗插图2

frida 实战测试

抓包发现数据包都是加密内容,这怎么搞,下播了!:

APP测试0基础——APP加解密对抗插图3

先查看正在运行的进程以及包名信息

frida-ps -Ua

APP测试0基础——APP加解密对抗插图4

这里我的程序对应的包名是:calm.pjtuep.zzdokmht

接下来编写hook脚本,输出APP在进行数据交互过程中,出现的字符串以及相关的调用栈信息:

Java.perform(function () {
var StringCls = Java.use("java.lang.String"); // 获取 Java String 类
// Hook getBytes() 方法(无参数版本)
StringCls.getBytes.overload().implementation = function () {
var result = this.getBytes(); // 调用原始 getBytes() 方法
// 过滤掉短字符串,只记录长度大于 16 的字符串
if (this.length() > 16) {  
// 获取调用堆栈信息
console.log("[*] Stack trace:==========>\n" +
Java.use("android.util.Log").getStackTraceString(
Java.use("java.lang.Exception").$new()
)
);
console.log("[*] getBytes() called with ==============>: " + this); // 输出当前字符串
}
return result; // 返回原始结果,确保不影响正常执行
};
});

为什么要hook getBytes方法以及代码解释:

APP测试0基础——APP加解密对抗插图5

frida -U -f "calm.pjtuep.zzdokmht" -l hook_key.js # 使用-f参数会重新载入APP,加载hook脚本,退出可输入exit回车即可。

APP测试0基础——APP加解密对抗插图6

可以看到,在响应包中的加密数据,被hook脚本hook到,并打印出了调用栈信息,其中最为可疑也最为明显的文件是:ApiEncryptUtil.java

APP测试0基础——APP加解密对抗插图7

因此使用jadx-gui反编译apk,找到ApiEncryptUtil.java

APP测试0基础——APP加解密对抗插图8

代码分析:AES加解密,a为解密函数,b为加密函数

public static String a(String str) {
try {
byte[] decode = Base64.decode(str, 0); // 先 Base64 解码
byte[] bytes = "0nxG8fD2kqlrEv5M".getBytes(); // AES 密钥
byte[] bytes2 = "u0r3GcsdXsYmAfhT".getBytes(); // AES IV
SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, "AES"); // 生成密钥
IvParameterSpec ivParameterSpec = new IvParameterSpec(bytes2); // 生成 IV
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 使用 AES-CBC
cipher.init(2, secretKeySpec, ivParameterSpec); // 2 = 解密模式
return new String(cipher.doFinal(decode), C.UTF8_NAME); // 解密并转换回字符串
} catch (Exception e2) {
e2.printStackTrace();
return "";
}
}
public static String b(String str) {
try {
byte[] bytes = str.getBytes(); // 获取字符串的字节数组
byte[] bytes2 = "0nxG8fD2kqlrEv5M".getBytes(C.UTF8_NAME); // AES 密钥
byte[] bytes3 = "u0r3GcsdXsYmAfhT".getBytes(C.UTF8_NAME); // AES IV
SecretKeySpec secretKeySpec = new SecretKeySpec(bytes2, "AES"); // 生成密钥
IvParameterSpec ivParameterSpec = new IvParameterSpec(bytes3); // 生成 IV
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 使用 AES-CBC
cipher.init(1, secretKeySpec, ivParameterSpec); // 1 = 加密模式
return new String(Base64.encode(cipher.doFinal(bytes), 0)); // 加密后 Base64 编码
} catch (Exception e2) {
e2.printStackTrace();
return "";
}
}

编写hook脚本,尝试输出加/解密数据:

Java.perform(function(){
var targetClass =Java.use('c.h.a.m.r');   //包路径根据自己电脑反编译的结果填写
// 调用a解密函数,输出内容
targetClass.a.implementation=function(str){
console.log("解密前数据========>: "+ str + "\n\n\n");
var result =this.a(str);
console.log('解密后数据========>: '+ result + "\n\n\n");
return result;
}
// 调用b加密函数,输出内容
targetClass.b.implementation=function(str){
console.log("加密前数据=======>: "+ str + "\n\n\n");
var result =this.b(str);
console.log('加密后数据========>: '+ result + "\n\n\n");
return result;
}
});

APP测试0基础——APP加解密对抗插图9

APP测试0基础——APP加解密对抗插图10

APP测试0基础——APP加解密对抗插图11

APP测试0基础——APP加解密对抗插图12

整个数据传输过程:

APP测试0基础——APP加解密对抗插图13

这样,我们就可以看到解密和加密数据了,但是我们要在burp中测试的话,还是太麻烦了,因此还需要用到更简单的方法。

Burpy + firda 实现burp上自动加解密

接下来使用Burpy插件,实现burp上自动加解密,方便我们测试。

查看设备名,获取设备名方便脚本编写

我的mumu模拟器设备在127.0.0.1:16384上,因此是这个设备:Device(id=”127.0.0.1:16384”, name=”PGBM10”, type=’usb’),这个一会编写脚本用得上

>>> import frida
>>> frida.get_device_manager().enumerate_devices()
[Device(id="local", name="Local System", type='local'), Device(id="socket", name="Local Socket", type='remote'), Device(id="barebone", name="GDB Remote Stub", type='remote'), Device(id="127.0.0.1:16384", name="PGBM10", type='usb')]

APP测试0基础——APP加解密对抗插图14

设置端口转发

adb forward tcp:27043 tcp:27043
adb forward tcp:27042 tcp:27042
# 查看连接情况
adb forward --list
# 帮助
adb --help | findstr "forward"

APP测试0基础——APP加解密对抗插图15

进入模拟器启动frida-server

adb root
adb shell
ali:/data/local/tmp # ./frida-server

编辑rpc hook脚本,方便调用APP上的加解密方法进行加解密:

我的文件名为:decrypt1.js

Java.perform(function () {
var targetClass = Java.use('c.h.a.m.r');
rpc.exports = {
init: function () {
console.log("[Frida] rpc.exports 初始化成功!");
return "rpc.exports 已加载";
},
enc: function (str) {
try {
console.log("************ 开始加密 ***********");
console.log("加密前数据: " + str);
var result = targetClass.b(str);
console.log("加密后数据: " + result);
return result || ""; // **防止返回 null**
} catch (e) {
console.log("[Frida] enc 方法执行出错: " + e);
return ""; // **防止卡住**
}
},
dec: function (str) {
try {
console.log("************ 开始解密 ***********");
console.log("解密前数据: " + str);
var result = targetClass.a(str);
console.log("解密后数据: " + result);
return result || ""; // **防止返回 null**
} catch (e) {
console.log("[Frida] dec 方法执行出错: " + e);
return ""; // **防止卡住**
}
}
};
});

burpy.py脚本编写:

import frida
import time
from urllib.parse import unquote, parse_qs, quote
import json
class Burpy:
# 初始化,获取模拟器进程,启动和连接进程
def __init__(self):
device = self._get_android_usb_device()
pid = device.spawn("calm.pjtuep.zzdokmht") # 进程包名
self.session = device.attach(pid) # 附加到目标进程
device.resume(pid) # 让进程继续运行
self.rpc = self.load_rpc() # 加载 RPC 脚本
# 获取模拟器设备
def _get_android_usb_device(self):
for x in frida.get_device_manager().enumerate_devices():
if "PGBM10" in x.name: # 根据设备名,返回设备相关信息。
print(f"设备信息=====> {x}")
return x
# 加载远程调用rpc的hook脚本文件
def load_rpc(self):
# 这里打开的文件路径貌似只能写绝对路径,用相对路径会报错?
with open("D:\\secTools\\BurpSuite V2023.2.2\\BurpSuite V2023.2.2\\Burpy\\js\\decrypt1.js",encoding="utf-8", errors="ignore") as f:
script = self.session.create_script(f.read())
script.load()
self.rpc = script.exports_sync
time.sleep(3)   # 等待APP加载完全
self.rpc.init() # 检测rpc调用
return self.rpc
# 对body数据处理,只返回data字段的数据
def convert_to_json(self, body):
try:
if body.strip().startswith('{') and body.strip().endswith('}'):
json_body = json.loads(body)
if 'data' in json_body:
return json_body['data']
else:
parsed_data = parse_qs(body)
return parsed_data['data'][0]
except Exception as e:
print(f"转换失败:{str(e)}")
return None
# 重新构建请求和响应body,方便发包和查看响应
def rebuild_body(self, body, data):
try:
if body.strip().startswith('{') and body.strip().endswith('}'):
json_body = json.loads(body)
if 'data' in json_body:
json_body['data'] = data
# print(str(json_body).encode("gbk").decode("gbk"))
return str(json_body).encode("gbk").decode("gbk") # 在repeater中响应内容发生了中文乱码,不清楚为啥?
else:
parsed_data = parse_qs(body)
string_body = "timestamp=" + str(parsed_data['timestamp'][0]) + "&" + "data=" + data + "&" + "sign=" + str(parsed_data['sign'][0])
return string_body
except Exception as e:
print(f"转换失败:{str(e)}")
return None  
# 加密函数
def encrypt(self, header, body):
try:
process_data = self.convert_to_json(body)
enc_data = self.rpc.enc(process_data)
body = self.rebuild_body(body, enc_data)
except Exception as e:
print(f"无法找到 enc 方法:{str(e)}")
return header, body
# 解密函数
def decrypt(self, header, body):
try:
process_data = self.convert_to_json(body)
dec_data = self.rpc.dec(process_data)
body = self.rebuild_body(body, dec_data)
except Exception as e:
print(f"无法找到 dec 方法:{str(e)}")
return header,body

这里编写burpy.py脚本,要根据实际情况进行编写,进程包名和获取模拟器设备的代码要根据自己所查看的信息进行编写。而代码也是一样,因为目前抓的这个包中,发现请求包中还有一个timestamp、sign,而响应包是一个json格式数据,其中也还有其他参数,而分析发现请求和响应中都只对data字段进行了加密处理,因此在发送数据包和解密响应包的时候,需要对请求体和响应体做拼接参数处理和加解密处理,这样才能顺利完成一次自动加解密请求和响应,具体细节分析代码可得知。

APP测试0基础——APP加解密对抗插图16

设置burpy:

enable processor这个勾选上可以方便在Intruder爆破模块中使用。

APP测试0基础——APP加解密对抗插图17

填好python路径以及burpy.py路径后,在编写好burpy.py以及hook脚本,并确认无误后,就可以点击Start server按钮,这个时候模拟器就会自动打开APP,hook脚本以及python脚本也会开始运行,在控制台也能够看到一些输出(如果你写了输出日志的代码的话。)

此时在历史记录中,右键->扩展->Burpy->选择decrypt函数就会弹出解密后内容的窗口:

APP测试0基础——APP加解密对抗插图18

APP测试0基础——APP加解密对抗插图19

控制台也会输出内容,因为我写了对应代码展示其解密过程。

APP测试0基础——APP加解密对抗插图20

将数据包发送到repeater中,先右键将请求包内容解密,然后点击发送,即可实现自动加解密:

APP测试0基础——APP加解密对抗插图21

APP测试0基础——APP加解密对抗插图22

控制台日志:

APP测试0基础——APP加解密对抗插图23

改包后发送:

APP测试0基础——APP加解密对抗插图24

APP测试0基础——APP加解密对抗插图25

也是正常的。

然后如果你想调试你的代码,在改动了burpy.py后,可以点击Reload Script重新加载代码,此时会重新连接模拟器,重新打开APP。

一些奇怪的问题

在用Burpy插件的时候发现:一旦python脚本运行出错,burp就会卡死白屏,这个时候只能关闭burp,重新开一个,然后配置代理重来,感觉这一点有点麻烦,还有点击select file无反应,不知道是什么情况,路径什么的都是手动填的… …

APP测试0基础——APP加解密对抗插图26

响应内容在repeater中出现中文乱码,而用burpy插件打开的窗口下不会乱码 :(

APP测试0基础——APP加解密对抗插图27

APP测试0基础——APP加解密对抗插图28

一些概念解释

什么是frida?

Frida是一款基于Python + JavaScript 的hook框架,本质是一种动态插桩技术。可以用于Android、Windows、iOS等各大平台,其执行脚本基于Python或者Node.js写成,而注入代码用JavaScript写成,这么说太抽象了,简单理解就是有frida这么一个工具,它可以在APP(用APP举例了)执行的过程中,给这个APP注入一些JS代码,方便我们去调试查看APP内部的情况比如数据、调用的函数等等,或者修改一些APP运行过程中的参数,比如拦截APP中的金币值,改成我们想要的金币值等等。

APP测试0基础——APP加解密对抗插图29

什么是hook技术?

Hook 本质上是一种动态代码注入函数劫持技术,允许开发者或攻击者在程序运行时拦截、修改或替换函数的行为。例如:

  • 拦截系统 API 调用(如sendrecvopenread

  • 修改应用逻辑(如篡改游戏内的金币、血量)

  • 监视应用行为(如捕获键盘输入、获取应用内存数据)

实际上就有点像中间人攻击,比如在APP运行过程中,通过hook技术对一些事件进行监听/篡改,比如玩家A了一下野怪,野怪掉血,我们可以通过hook技术把这一事件钩出来,查看玩家血量/野怪血量/玩家这一刀多少攻击力等等(监视应用行为);并且我们可以通过hook技术,修改玩家的一刀的攻击力,实现一刀999等等(修改应用逻辑);并且可以查看玩家A出去这一刀具体调用了哪个函数方法(拦截/查看系统API调用)。

那么frida hook就很好理解了,就是用frida进行hook呗。

APP测试0基础——APP加解密对抗插图30

为什么要进行adb forward转发端口?

adb forward tcp:27042 tcp:27042和类似的命令用于将 Android/模拟器 设备上的端口(如 27042)映射到主机上的相应端口。这对于frida来说是必要的,因为 Frida 通过网络端口进行通信,尤其是当它在远程设备或模拟器上运行时。

解释:

adb forward命令:此命令将设备上的端口转发到主机上的端口。它创建了一条通过 ADB 连接的端口映射路径。

2704227043端口

  • frida-server会在 Android 设备上启动并监听特定的端口(通常是 27042),用于与主机上的frida客户端进行通信。

  • 端口27043也常用来进行附加连接,尤其是如果你在多个进程或服务之间进行交互时。

为什么需要转发端口?

frida-server通信:Frida 客户端需要与frida-server进行通信,通常会通过 TCP/IP 端口进行交互。adb forward命令通过将端口转发到主机上的相应端口,允许主机上的 Frida 客户端与 Android 设备上的frida-server进行通信。

远程通信:当frida-server在设备上运行时,它会监听设备上的端口(例如 27042)。adb forward允许你从主机访问该端口,就像你直接连接到设备一样。通过这种方式,你可以控制设备上的进程,进行 hook 操作,调试等。

端口的必要性

  • frida-server在 Android 设备上运行并监听 27042 端口。

  • frida客户端通过该端口与设备通信。如果没有端口转发,主机就无法访问设备上的frida-server,导致无法进行 hook 操作或与设备进行其他调试任务。

总结:转发端口是为了方便主机上的frida客户端 与 Android/模拟器上的frida-server frida服务端进行通信,方便我们对设备上的进程(如APP)进行调试/hook/连接。

参考

frida-server下载安装:https://blog.csdn.net/yongmonkey/article/details/124123736

frida:https://github.com/frida/frida/releases

https://cloud.tencent.com/developer/article/2348148

jadx-gui下载:https://github.com/skylot/jadx/releases

burpy下载:https://github.com/mr-m0nst3r/Burpy/releases

https://mp.weixin.qq.com/s/Fwq96_VKIduXAbrPp8kshw

https://github.com/hookmaster/frida-all-in-one

https://sunny250.github.io/2021/02/14/hook%E7%A5%9E%E5%99%A8FRIDA/

https://www.kancloud.cn/alex_wsc/android/506821

https://blog.csdn.net/weixin_39190897/article/details/115354102

本文作者:Track-我是大白

更多SRC实战内容、实战经验分享、渗透技巧、漏洞挖掘&漏洞证书获取技巧,关注我,订阅更多精彩内容~


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

[Meachines] [Easy] Luanne Lua RCE+bozoHTTPd LFI+NetBSD-Dec+doas权限提升
[Meachines] [Easy] Toolbox PostgreSQLI-RCE+Docker逃逸boot2docker权限提升
[Meachines] [Easy] ServMon NVMS-LFI+NSCP(NSClient)权限提升+Chameleon反向shell+reg…
塔塔科技遭勒索攻击,1.4TB数据被泄露
GitHub官方展示如何利用Copilot进行日志安全分析
通过物理渗透测试获取内部网络访问权限:案例分析

发布评论