免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
内容参考于:图灵Python学院
工具下载:
提取码:zy89
复制这段内容后打开百度网盘手机App,操作更方便哦
上一个内容:8.苹果ios逆向-安装frida
ios中的app很少有加密的,大厂的app会有加密
苹果系统(ios和macOS)中提供了CCCrypt函数,它是一个用来加解密的函数,它提供了AES、DES、3DES、RC4、MD5等算法,就提供了一个函数可以很方便的,让开发者使用
CCCrypt函数说明
CCCryptorStatus CCCrypt( CCOperation op, // 操作类型:加密/解密 CCAlgorithm alg, // 加密算法(如 AES) CCOptions options, // 加密选项(如填充模式、加密模式) const void *key, // 密钥 size_t keyLength, // 密钥长度 const void *iv, // 初始化向量(IV) const void *dataIn, // 输入数据(明文/密文) size_t dataInLength, // 输入数据长度 void *dataOut, // 输出数据缓冲区(密文/明文) size_t dataOutAvailable, // 输出缓冲区大小 size_t *dataOutMoved // 实际输出数据长度 );
首先使用Charles抓一个包,如下图它有一个sign参数,一般有sign参数和一个时间戳这样组合的sign都是加密的,查找加密参数的方式,再请求一次,对比两次请求不一样的参数,只要不一样然后数据还看不懂,看着像加密数据,那它就是加密数据,如下图红框就是它的值就看不懂,然后看着也想加密数据,然后每次请求它都会变化,这就符合加密参数的特征
然后复制一下curl
然后直接让ai大模型把curl转成Python代码,如下图
然后这里有一个加解密网址:
https://gchq.github.io/CyberChef/
![]()
然后复制一下sign的数据,对它进行Base64解密,有些数据加密完会进行base64加密,如下图红框base64解密后以十六进制展示
使用frida-trace指令 hook CCCrypt算法,它要使用app的进程id
查看进程id
frida-ps -Ua
下图红框是我们需要的进程id
![]()
然后使用 frida-trace -Up 24699 -i CCCrypt,拦截CCCrypt,指令执行后刷一下手机,让app执行加密操作也就是调用CCCrypt函数,如下图有CCCrypt的打印说明,注入成功
然后它还会给我创建一个文件,如下图红框,上图红框的内容也是由下图红框的文件进行打印的
CCCrypt文件默认内容,然后我们需要完善它的内容,来查看真实的算法内容,onEnter用来查看入参,onLeave用来查看加密之后的数据(结果)
下图红框里的就是CCCrypt的入参
也就是下图红框的十个数据,args[0] 一直到 args[9]
效果图:拦截加密入参、加密方式
最终的代码
/**
* 完整的十六进制+文本显示的CCCrypt监控脚本
* 数据同时展示十六进制和可打印文本,不包含二进制格式
*/
{
onEnter(log, args, state) {
// 解析基础参数
const op = args[0].toInt32();
const alg = args[1].toInt32();
const options = args[2].toInt32();
const keyAddr = args[3];
const keyLen = args[4].toInt32();
const ivAddr = args[5];
const dataInAddr = args[6];
const dataInLen = args[7].toInt32();
// 解析算法名称
let algorithmName;
switch (alg) {
case 0: algorithmName = 'AES(kCCAlgorithmAES)'; break;
case 1: algorithmName = 'DES(kCCAlgorithmDES)'; break;
case 2: algorithmName = '3DES(kCCAlgorithm3DES)'; break;
case 3: algorithmName = 'CAST(kCCAlgorithmCAST)'; break;
case 4: algorithmName = 'RC4(kCCAlgorithmRC4)'; break;
case 5: algorithmName = 'RC2(kCCAlgorithmRC2)'; break;
default: algorithmName = `未知算法(${alg})`;
}
// 解析选项参数
const mode = options & 0x0F;
let modeName;
switch (mode) {
case 1: modeName = 'CBC(kCCOptionModeCBC)'; break;
case 2: modeName = 'ECB(kCCOptionModeECB)'; break;
case 3: modeName = 'CFB(kCCOptionModeCFB)'; break;
case 4: modeName = 'OFB(kCCOptionModeOFB)'; break;
case 5: modeName = 'CTR(kCCOptionModeCTR)'; break;
case 6: modeName = 'GCM(kCCOptionModeGCM)'; break;
default: modeName = `未知模式(0x${mode.toString(16)})`;
}
const padding = options & 0xF0;
const paddingName = padding === 0x10
? 'PKCS7填充(kCCPaddingPKCS7)'
: padding === 0x00
? '无填充(kCCPaddingNone)'
: `未知填充(0x${padding.toString(16)})`;
// 打印函数调用基本信息
log('=== CCCrypt 调用开始 ===');
log(`操作类型: ${op === 0 ? '加密(kCCEncrypt)' : '解密(kCCDecrypt)'}`);
log(`算法类型: ${algorithmName}`);
log(`选项参数: 0x${options.toString(16)}`);
log(` 加密模式: ${modeName}`);
log(` 填充方式: ${paddingName}`);
log(`密钥长度: ${keyLen}字节`);
log(`输入长度: ${dataInLen}字节`);
// 打印密钥(十六进制+文本)
if (keyAddr && !keyAddr.isNull() && keyLen > 0) {
log('\n[密钥数据] (十六进制+可打印文本)');
log(hexdump(keyAddr, {
length: keyLen,
header: false,
ansi: true,
format: 'hex+ascii' // 同时显示十六进制和ASCII文本
}));
}
// 打印IV(十六进制+文本)
if (ivAddr && !ivAddr.isNull()) {
log('\n[IV数据] (十六进制+可打印文本)');
log(hexdump(ivAddr, {
length: 16,
header: false,
ansi: true,
format: 'hex+ascii'
}));
}
// 打印输入数据(十六进制+文本)
if (dataInAddr && !dataInAddr.isNull() && dataInLen > 0) {
const showLen = Math.min(dataInLen, 256);
log(`\n[输入数据] (共${dataInLen}字节,显示前${showLen}字节)`);
log(hexdump(dataInAddr, {
length: showLen,
header: false,
ansi: true,
format: 'hex+ascii'
}));
}
// 保存输出参数引用
this.dataOutAddr = args[8];
this.dataOutLenPtr = args[10];
},
onLeave(log, retval, state) {
// 打印执行结果
const status = retval.toInt32();
log(`\n[执行结果] 状态码: ${status} (${status === 0 ? '成功' : '失败'})`);
// 打印输出数据(十六进制+文本)
if (this.dataOutAddr && !this.dataOutAddr.isNull() && this.dataOutLenPtr) {
const outLen = this.dataOutLenPtr.readUInt();
if (outLen > 0) {
const showLen = Math.min(outLen, 256);
log(`\n[输出数据] (共${outLen}字节,显示前${showLen}字节)`);
log(hexdump(this.dataOutAddr, {
length: showLen,
header: false,
ansi: true,
format: 'hex+ascii'
}));
log(`输出长度: ${outLen}字节`);
}
}
log('=== CCCrypt 调用结束 ===\n');
}
}