APP测试0基础——APP加解密对抗
字数 1149 2025-08-29 08:30:30

APP加解密对抗实战教学文档

一、环境准备

1.1 工具安装

  • Frida框架安装

    pip install frida
    pip install frida-tools
    pip install Pyro4
    
  • Frida-server

    • 版本需与Python安装的frida版本一致
    • 获取模拟器架构:
      adb shell getprop ro.product.cpu.abi
      

1.2 部署Frida-server

  1. 上传frida-server到设备:

    adb push frida-server /data/local/tmp/
    
  2. 启动frida-server:

    adb root
    adb shell
    cd /data/local/tmp
    chmod +x frida-server
    ./frida-server
    
  3. 验证连接:

    frida-ps -U
    

二、目标APP分析

2.1 获取目标APP信息

frida-ps -Ua

示例输出:calm.pjtuep.zzdokmht

2.2 初步Hook测试

编写基础Hook脚本hook_key.js

Java.perform(function () {
    var StringCls = Java.use("java.lang.String");
    
    StringCls.getBytes.overload().implementation = function () {
        var result = this.getBytes();
        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:

frida -U -f "calm.pjtuep.zzdokmht" -l hook_key.js

三、加密算法分析

3.1 定位关键加密类

通过Hook输出定位到关键类:ApiEncryptUtil.java

3.2 加密算法分析

// 解密函数
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);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(2, secretKeySpec, ivParameterSpec); // 2=解密模式
        return new String(cipher.doFinal(decode), "UTF-8");
    } catch (Exception e2) {
        e2.printStackTrace();
        return "";
    }
}

// 加密函数
public static String b(String str) {
    try {
        byte[] bytes = str.getBytes();
        byte[] bytes2 = "0nxG8fD2kqlrEv5M".getBytes("UTF-8"); // AES密钥
        byte[] bytes3 = "u0r3GcsdXsYmAfhT".getBytes("UTF-8"); // AES IV
        
        SecretKeySpec secretKeySpec = new SecretKeySpec(bytes2, "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(bytes3);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(1, secretKeySpec, ivParameterSpec); // 1=加密模式
        return new String(Base64.encode(cipher.doFinal(bytes), 0));
    } catch (Exception e2) {
        e2.printStackTrace();
        return "";
    }
}

算法特点:

  • AES-CBC模式
  • PKCS5Padding填充
  • 固定密钥:0nxG8fD2kqlrEv5M
  • 固定IV:u0r3GcsdXsYmAfhT
  • 数据先Base64编码/解码

四、高级Hook实现

4.1 加解密函数Hook

Java.perform(function(){
    var targetClass = Java.use('c.h.a.m.r'); // 根据实际反编译结果填写
    
    // Hook解密函数
    targetClass.a.implementation = function(str){
        console.log("解密前数据: " + str + "\n\n\n");
        var result = this.a(str);
        console.log('解密后数据: ' + result + "\n\n\n");
        return result;
    }
    
    // Hook加密函数
    targetClass.b.implementation = function(str){
        console.log("加密前数据: " + str + "\n\n\n");
        var result = this.b(str);
        console.log('加密后数据: ' + result + "\n\n\n");
        return result;
    }
});

五、Burp自动加解密集成

5.1 设备准备

  1. 获取设备信息:

    import frida
    frida.get_device_manager().enumerate_devices()
    

    示例输出:Device(id="127.0.0.1:16384", name="PGBM10", type='usb')

  2. 端口转发:

    adb forward tcp:27043 tcp:27043
    adb forward tcp:27042 tcp:27042
    

5.2 RPC Hook脚本

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 "防止卡住";
            }
        }
    };
});

5.3 Burpy脚本

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()
    
    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
    
    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)
            self.rpc.init()
            return self.rpc
    
    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
    
    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
                    return str(json_body).encode("gbk").decode("gbk")
            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

5.4 Burp配置

  1. 安装Burpy插件
  2. 配置:
    • Python路径
    • burpy.py脚本路径
    • 勾选"Enable processor"

六、实战测试流程

  1. 启动frida-server
  2. 启动Burpy服务
  3. 在Burp中:
    • 右键请求 -> 扩展 -> Burpy -> decrypt 查看解密内容
    • 在Repeater中修改明文后发送,自动加密
    • 查看响应自动解密

七、关键概念解释

7.1 Frida

  • 动态插桩技术框架
  • 支持多平台:Android、Windows、iOS等
  • 使用Python/Node.js编写执行脚本
  • 使用JavaScript编写注入代码

7.2 Hook技术

  • 本质:动态代码注入或函数劫持技术
  • 应用
    • 拦截系统API调用
    • 修改应用逻辑
    • 监视应用行为

7.3 ADB端口转发

  • 目的:允许主机Frida客户端与设备frida-server通信
  • 常用命令
    adb forward tcp:27042 tcp:27042
    adb forward tcp:27043 tcp:27043
    
  • 必要性:建立主机与设备间的通信通道

八、常见问题解决

  1. Burp卡死

    • Python脚本出错可能导致Burp卡死
    • 解决方案:关闭Burp重新启动
  2. 中文乱码

    • Repeater中可能出现中文乱码
    • 解决方案:使用encode("gbk").decode("gbk")转换
  3. 路径问题

    • Burpy脚本路径需使用绝对路径
    • 确保路径中不包含中文或特殊字符
APP加解密对抗实战教学文档 一、环境准备 1.1 工具安装 Frida框架安装 : Frida-server : 版本需与Python安装的frida版本一致 获取模拟器架构: 1.2 部署Frida-server 上传frida-server到设备: 启动frida-server: 验证连接: 二、目标APP分析 2.1 获取目标APP信息 示例输出: calm.pjtuep.zzdokmht 2.2 初步Hook测试 编写基础Hook脚本 hook_key.js : 执行Hook: 三、加密算法分析 3.1 定位关键加密类 通过Hook输出定位到关键类: ApiEncryptUtil.java 3.2 加密算法分析 算法特点: AES-CBC模式 PKCS5Padding填充 固定密钥: 0nxG8fD2kqlrEv5M 固定IV: u0r3GcsdXsYmAfhT 数据先Base64编码/解码 四、高级Hook实现 4.1 加解密函数Hook 五、Burp自动加解密集成 5.1 设备准备 获取设备信息: 示例输出: Device(id="127.0.0.1:16384", name="PGBM10", type='usb') 端口转发: 5.2 RPC Hook脚本 decrypt1.js : 5.3 Burpy脚本 burpy.py : 5.4 Burp配置 安装Burpy插件 配置: Python路径 burpy.py脚本路径 勾选"Enable processor" 六、实战测试流程 启动frida-server 启动Burpy服务 在Burp中: 右键请求 -> 扩展 -> Burpy -> decrypt 查看解密内容 在Repeater中修改明文后发送,自动加密 查看响应自动解密 七、关键概念解释 7.1 Frida 动态插桩技术框架 支持多平台:Android、Windows、iOS等 使用Python/Node.js编写执行脚本 使用JavaScript编写注入代码 7.2 Hook技术 本质 :动态代码注入或函数劫持技术 应用 : 拦截系统API调用 修改应用逻辑 监视应用行为 7.3 ADB端口转发 目的 :允许主机Frida客户端与设备frida-server通信 常用命令 : 必要性 :建立主机与设备间的通信通道 八、常见问题解决 Burp卡死 : Python脚本出错可能导致Burp卡死 解决方案:关闭Burp重新启动 中文乱码 : Repeater中可能出现中文乱码 解决方案:使用 encode("gbk").decode("gbk") 转换 路径问题 : Burpy脚本路径需使用绝对路径 确保路径中不包含中文或特殊字符