对于逆向研究而言frida是一款非常好用的工具,同时Frida是一款跨平台工具.话不多说 今天来hook下oc的函数 以及常用的加密的破解
1.首先需要一台越狱机 Frida的安装这里就不赘述了 新建一个工程!DesDemo
2.跟踪函数实例
分析下CCCrypt这个函数的加密 一般我们加密都会用得到这个函数
CCCryptorStatus CCCrypt(
CCOperation op, //kCCEncrypt为加密,kCCDecrypt为解密
CCAlgorithm alg, //加密方式 kCCAlgorithmAES128为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)
frida-trace -U -i CCCrypt DesDemo
找到如图所示的js文件 修改里面的代码
/*
* 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: ")
console.log(hexdump(ptr(this.ivBuffer), {
length: this.keyLength.toInt32(),
header: true,
ansi: true
}))
}
},
/**
* 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) {
}
}
control + c 终止frida 重新运行frida
为了保险起见 我字符串还进行加密了
#pragma mark 字符加密
+(NSString*)getStr {
//采用这样的方式,这些字符不会进入字符常量区.编译器直接换算成异或结果
unsigned char key[]={
(STRING_ENCRYPT_KEY ^ 'r'),
(STRING_ENCRYPT_KEY ^ '2'),
(STRING_ENCRYPT_KEY ^ '4'),
(STRING_ENCRYPT_KEY ^ '3'),
(STRING_ENCRYPT_KEY ^ '4'),
(STRING_ENCRYPT_KEY ^ '3'),
(STRING_ENCRYPT_KEY ^ '4'),
(STRING_ENCRYPT_KEY ^ '3'),
(STRING_ENCRYPT_KEY ^ '4'),
(STRING_ENCRYPT_KEY ^ '3'),
(STRING_ENCRYPT_KEY ^ '\0')
};
unsigned char * p = key;
while (((*p) ^= STRING_ENCRYPT_KEY) != '\0') p++;
return [NSString stringWithUTF8String:(const char *)key];
}
-(void)btnClick{
NSString *encryptionString=[LCdes lcEncryUseDES:[ViewController getStr]];
NSLog(@"加密后的的字符串:%@",encryptionString);
// NSString *decryptionString=[LCdes lcDecryUseDES:encryptionString];
// NSLog(@"解密后的字符串:%@",decryptionString);
}
触发函数加密
能清晰的看到函数加密前的字符