IOS逆向 CCCrypt技巧
Posted on Tue 05 January 2021 in SoftwareSecurity • 2 min read
CCCrypt原型接口
由于IOS/OSX相对闭源, 故⼤多数算法都是使⽤ObejectiveC 封装的⼀套Crypto库. 该库拥有⼀套统⼀的调⽤ ⼊⼝ CCCrypt. 其具体定义如下
CCCryptorStatus CCCrypt( CCOperation op,
CCAlgorithm alg,
CCOptions options,
const void *key,
size_t keyLength,
const void *iv,
const void *dataIn,
size_t dataInLength,
void *dataOut,
size_t dataOutAvailable,
size_t *dataOutMoved)
# 参数意义
op:kCCEncrypt(加密)/ kCCDecrypt(解密)
alg:加密算法
options:加密⽅式
key:加密密钥
keyLength:密钥⻓度
iv:初始化向量
dataIn:加密数据
dataInLength:加密数据⻓度
dataOut:加密结果
dataOutAvailable:缓冲区⼤⼩
dataOutMoved:加密结果⼤⼩
# 部分参数Enum列表
# alg
enum {
kCCAlgorithmAES128 = 0,
kCCAlgorithmAES = 0,
kCCAlgorithmDES,
kCCAlgorithm3DES,
kCCAlgorithmCAST,
kCCAlgorithmRC4,
kCCAlgorithmRC2,
kCCAlgorithmBlowfish
};
typedef uint32_t CCAlgorithm;
#options
enum {
/* options for block ciphers */
kCCOptionPKCS7Padding = 0x0001,
kCCOptionECBMode = 0x0002
/* stream ciphers currently have no options */
};
typedef uint32_t CCOptions;
#keyLength
enum {
kCCKeySizeAES128 = 16,
kCCKeySizeAES192 = 24,
kCCKeySizeAES256 = 32,
kCCKeySizeDES = 8,
kCCKeySize3DES = 24,
kCCKeySizeMinCAST = 5,
kCCKeySizeMaxCAST = 16,
kCCKeySizeMinRC4 = 1,
kCCKeySizeMaxRC4 = 512,
kCCKeySizeMinRC2 = 1,
kCCKeySizeMaxRC2 = 128,
kCCKeySizeMinBlowfish = 8,
kCCKeySizeMaxBlowfish = 56,
};
利用Frida 快速返回CCCrypt相关调用信息
首先运行命令
frida-trace -U -p 14534 -i "CCCrypt"
生成一个默认脚本, 随后修改脚本使之能够返回更多信息. 脚本如下
/*
* Auto-generated by Frida. Please modify to match the signature of CCCrypt.
* This stub is currently auto-generated from manpages when available.
*
* For full API reference, see: http://www.frida.re/docs/javascript-api/
*/
{
/**
* Called synchronously when about to call CCCrypt.
*
* @this {object} - Object allowing you to store state for use in onLeave.
* @param {function} log - Call this function with a string to be presented to the user.
* @param {array} args - Function arguments represented as an array of NativePointer objects.
* For example use args[0].readUtf8String() if the first argument is a pointer to a C string encoded as UTF-8.
* It is also possible to modify arguments by assigning a NativePointer object to an element of this array.
* @param {object} state - Object allowing you to keep state across function calls.
* Only one JavaScript function will execute at a time, so do not worry about race-conditions.
* However, do not use this to store function arguments across onEnter/onLeave, but instead
* use "this" which is an object for keeping state local to an invocation.
*/
onEnter: function (log, args, state) {
log('CCCrypt(' +
'op=' + args[0] +
', alg=' + args[1] +
', options=' + args[2] +
', key=' + args[3] +
', keyLength=' + args[4] +
', iv=' + args[5] +
', dataIn=' + args[6] +
', dataInLength=' + args[7] +
', dataOut=' + args[8] +
', dataOutAvailable=' + args[9] +
', dataOutMoved=' + args[10] +
')');
//保存参数
this.operation = args[0]
this.CCAlgorithm = args[1]
this.CCOptions = args[2]
this.keyBytes = args[3]
this.keyLength = args[4]
this.ivBuffer = args[5]
this.inBuffer = args[6]
this.inLength = args[7]
this.outBuffer = args[8]
this.outLength = args[9]
this.outCountPtr = args[10]
//this.operation == 0
if (this.operation == 0) {
//打印加密前的原文
console.log("In buffer:")
console.log(hexdump(ptr(this.inBuffer), {
length: this.inLength.toInt32(),
header: true,
ansi: true
}))
//打印密钥
console.log("Key: ")
console.log(hexdump(ptr(this.keyBytes), {
length: this.keyLength.toInt32(),
header: true,
ansi: true
}))
//打印 IV
console.log("IV: ")
if(this.ivBuffer != 0){
console.log(hexdump(ptr(this.ivBuffer), {
length: this.keyLength.toInt32(),
header: true,
ansi: true
}))}
else{console.log("None")}
}
},
/**
* Called synchronously when about to return from CCCrypt.
*
* See onEnter for details.
*
* @this {object} - Object allowing you to access state stored in onEnter.
* @param {function} log - Call this function with a string to be presented to the user.
* @param {NativePointer} retval - Return value represented as a NativePointer object.
* @param {object} state - Object allowing you to keep state across function calls.
*/
onLeave: function (log, retval, state) {
if (this.operation == 1) {
//打印解密后的原文
console.log("out buffer:")
console.log(hexdump(ptr(this.outBuffer), {
length: this.outLength.toInt32(),
header: true,
ansi: true
}))
//打印密钥
console.log("Key: ")
console.log(hexdump(ptr(this.keyBytes), {
length: this.keyLength.toInt32(),
header: true,
ansi: true
}))
//打印 IV
console.log("IV: ")
if(this.ivBuffer != 0){
console.log(hexdump(ptr(this.ivBuffer), {
length: this.keyLength.toInt32(),
header: true,
ansi: true
}))}
else{console.log("None")}
}
}
}
将上述代码保存后, 利⽤frida对⽬标应⽤进⾏插桩处理, 并调⽤该脚本, 便可以⾃动化处理使⽤该应⽤
frida-trace -U -p 14534 -i "CCCrypt"