前言
某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 # 查看位数
上传frida-server(没连接设备记得先adb connect 连接设备,这里不赘述了)
adb push frida-server /data/local/tmp/
启动frida-server,(frida-server是我自己进行了一下重命名)
adb root
adb shell
cd /data/local/tmp
chmod +x frida-server
./frida-server
另起一个cmd,查看模拟器进程以验证frida是否连接成功:有进程相关信息即成功连接。
frida 实战测试
frida-ps -Ua
Java.perform(function(){
varStringCls=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;// 返回原始结果,确保不影响正常执行
};
});
frida -U -f "calm.pjtuep.zzdokmht"-l hook_key.js # 使用-f参数会重新载入APP,加载hook脚本,退出可输入exit回车即可。
publicstaticString a(String str){
try{
byte[] decode =Base64.decode(str,0);// 先 Base64 解码
byte[] bytes ="0nxG8fD2kqlrEv5M".getBytes();// AES 密钥
byte[] bytes2 ="u0r3GcsdXsYmAfhT".getBytes();// AES IV
SecretKeySpec secretKeySpec =newSecretKeySpec(bytes,"AES");// 生成密钥
IvParameterSpec ivParameterSpec =newIvParameterSpec(bytes2);// 生成 IV
Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding");// 使用 AES-CBC
cipher.init(2, secretKeySpec, ivParameterSpec);// 2 = 解密模式
returnnewString(cipher.doFinal(decode), C.UTF8_NAME);// 解密并转换回字符串
}catch(Exception e2){
e2.printStackTrace();
return"";
}
}
publicstaticString 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 =newSecretKeySpec(bytes2,"AES");// 生成密钥
IvParameterSpec ivParameterSpec =newIvParameterSpec(bytes3);// 生成 IV
Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding");// 使用 AES-CBC
cipher.init(1, secretKeySpec, ivParameterSpec);// 1 = 加密模式
returnnewString(Base64.encode(cipher.doFinal(bytes),0));// 加密后 Base64 编码
}catch(Exception e2){
e2.printStackTrace();
return"";
}
}
Java.perform(function(){
var targetClass =Java.use('c.h.a.m.r');//包路径根据自己电脑反编译的结果填写
// 调用a解密函数,输出内容
targetClass.a.implementation=function(str){
console.log("解密前数据========>: "+ str +"nnn");
var result =this.a(str);
console.log('解密后数据========>: '+ result +"nnn");
return result;
}
// 调用b加密函数,输出内容
targetClass.b.implementation=function(str){
console.log("加密前数据=======>: "+ str +"nnn");
var result =this.b(str);
console.log('加密后数据========>: '+ result +"nnn");
return result;
}
});
Burpy + firda 实现burp上自动加解密
>>>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')]
adb forward tcp:27043 tcp:27043
adb forward tcp:27042 tcp:27042
# 查看连接情况
adb forward --list
# 帮助
adb --help | findstr "forward"
adb root
adb shell
ali:/data/local/tmp # ./frida-server
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"";// **防止卡住**
}
}
};
});
import frida
import time
from urllib.parse import unquote, parse_qs, quote
import json
classBurpy:
# 初始化,获取模拟器进程,启动和连接进程
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调用
returnself.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]
exceptExceptionas e:
print(f"转换失败:{str(e)}")
returnNone
# 重新构建请求和响应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
exceptExceptionas e:
print(f"转换失败:{str(e)}")
returnNone
# 加密函数
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)
exceptExceptionas 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)
exceptExceptionas e:
print(f"无法找到 dec 方法:{str(e)}")
return header,body
一些奇怪的问题
一些概念解释
什么是frida?
什么是hook技术?
拦截系统 API 调用(如`send`、`recv`、`open`、`read`)
修改应用逻辑(如篡改游戏内的金币、血量)
监视应用行为(如捕获键盘输入、获取应用内存数据)
为什么要进行adb forward转发端口?
adb forward tcp:27042 tcp:27042
和类似的命令用于将 Android/模拟器 设备上的端口(如 27042)映射到主机上的相应端口。这对于 frida
来说是必要的,因为 Frida 通过网络端口进行通信,尤其是当它在远程设备或模拟器上运行时。解释:
adb forward
命令:此命令将设备上的端口转发到主机上的端口。它创建了一条通过 ADB 连接的端口映射路径。27042
和 27043
端口: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 操作或与设备进行其他调试任务。参考
所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.
原文始发于微信公众号(掌控安全EDU):APP测试0基础 - APP加解密对抗
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论