11.苹果ios逆向-FridaHook-ios中的算法-CC_SHA1(sha1算法)
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
内容参考于:图灵Python学院
工具下载:
链接:https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwd=zy89
提取码:zy89
复制这段内容后打开百度网盘手机App,操作更方便哦
上一个内容:10.苹果ios逆向-FridaHook-ios中的算法-CCMD5
下图红框是app中的商品页面,接下来要通过代码,把这些数据全部获取出来
打开Charles,并开启代理
然后刷新页面抓一个数据包,然后回到手机
如下图红框商品名中存在片仔癀这三个字,然后回到Charles
按CTRL+shift+F,搜索片仔癀,如下图红框就找到了页面数据的数据包
双击上图红框位置,可以跳转到请求位置,如下图红框
然后开始分析请求头,请求参数中的加密数据
首先是请求头,如下图红框有一个api_sign加密参数
请求参数中,有下图红框的两个是加密参数
然后复制为curl
使用curl转成Python代码后,运行它,如下图它是可以正常请求到数据的
如下图红框
然后删除最后一个f,然后点击Execute
然后会发现它的返回值是404,请求不成功了
然后刷新页面重新抓一个数据包,对比两个数据包的参数,不变的都不是加密参数,如下图请求头中只有下图红框中的api_sign参数是变化的,如果想做数据采集它是必须要进行处理的
然后是请求参数中mars_cid和skey经过两个数据包的对比,可以看出它俩的值是固定的,直接写死就可以,不需要进行解密,所以加密参数只有api_sign这一个
api_sign数据的长度是40
40位16进制的数,它可能是SHA1算法,如下图利用大模型分析
然后就可以使用sha1算法进行开盲盒,然后使用frida-trace -Up 2840 -i CC_SHA1,拦截sha1算法,首先使用frida-ps -Ua查询pid
执行frida-trace -Up 2840 -i CC_SHA1指令,创建下图红框的js文件
然后编辑CC_SHA1.js文件,让它打印入参和返回值,然后使用 frida-trace -Up 2840 -i CC_SHA1 -o sha1.txt,这个-o可以把打印的参数写到sha1.txt文件中,如果没有sha1.txt文件它会进行创建,创建位置是运行frida-trace目录位置
{/*** onEnter: 当CC_SHA1函数被调用时触发(进入函数时)* @param {function} log - 日志输出函数,用于打印信息到控制台* @param {array} args - 函数参数数组,每个元素为NativePointer类型* @param {object} state - 全局状态对象,用于跨函数调用保存数据* @this {object} - 局部状态对象,用于在onEnter和onLeave之间传递数据*/onEnter(log, args, state) {// 保存第一个参数(待加密数据的指针)到局部状态,供onLeave使用this.args0 = args[0]; // 保存第三个参数(加密结果输出缓冲区的指针)到局部状态,供onLeave使用this.args2 = args[2]; },/*** onLeave: 当CC_SHA1函数执行完成即将返回时触发(离开函数时)* @param {function} log - 日志输出函数* @param {NativePointer} retval - 函数返回值指针* @param {object} state - 全局状态对象* @this {object} - 局部状态对象,可访问onEnter中保存的数据*/onLeave(log, retval, state) {// 1. 读取加密结果(SHA-1哈希值)// 从输出缓冲区指针(this.args2)读取20字节数据(SHA-1固定输出20字节)var ByteArray = Memory.readByteArray(this.args2, 20); // 将读取到的原始字节数据转换为Uint8Array,便于逐字节处理var uint8Array = new Uint8Array(ByteArray);// 用于存储十六进制字符串形式的哈希值var str = "";// 遍历每个字节,转换为两位十六进制字符串for(var i = 0; i < uint8Array.length; i++) {// 将单字节转换为十六进制var hextemp = (uint8Array[i].toString(16))// 若只有一位(如"a"),补前导0变为两位(如"0a")if(hextemp.length == 1){hextemp = "0" + hextemp}// 拼接所有字节的十六进制表示str += hextemp;}// 2. 处理输入参数(待加密的数据)var argStr; // 用于存储输入参数的字符串表示try{// 尝试将输入数据以UTF-8字符串形式读取// 若输入是文本数据,这里会直接显示可读字符串argStr = this.args0.readUtf8String();}catch(e){// 若读取UTF-8失败(非文本数据,如二进制),则转为十六进制表示// 读取20字节输入数据(根据实际场景可调整长度)var argBytes = Memory.readByteArray(this.args0, 20);var argHex = "";var arr = new Uint8Array(argBytes);// 遍历每个字节转换为十六进制for(var i = 0; i < arr.length; i++){var h = arr[i].toString(16);if(h.length == 1)h = "0" + h; // 补前导0argHex += h;}// 标记为非UTF8数据,并附加十六进制值argStr ="[非UTF8数据] " + argHex;}// 3. 输出日志:打印输入参数和加密结果log(`CC_SHA_入参=${argStr}`);log(`CC_SHA_返回值=${str}`);}
}
效果图,可以找到api_sign
然后开始算法还原,如下图使用sha1算法加密,结果正常
然后分析入参怎么来的,如下图,通过观察发现入参可以拆分成两部分,第一部分是写死的固定的,第二部分是通过sha1算法加密的结果
然后它也是分两段,第一段是固定值,第二段是请求参数
所以可以得到结论,api_sign的值是通过两次sha1算法得到的,第一次是使用sha1对请求参数进行加密,第二次是对请求参数加密的结果进行sha1加密,然后就可以写代码了,下图使用Python进行还原成功
如果代码看不懂,就去问ai,让ai用白话给你解释
import hashlibimport requestsdef cal_sign(params):"""计算签名的函数,采用两次SHA-1哈希计算,结合固定盐值增强安全性功能说明:1. 处理多种类型的输入参数(字典、列表、元组、字符串)2. 对参数进行标准化处理(排序、拼接)3. 结合固定盐值进行两次SHA-1哈希计算,生成最终签名:param params: 待计算签名的参数,支持类型:- dict: 键值对形式的参数(如{"a":1, "b":2})- list/tuple: 包含键值对的可迭代对象(如[("a",1), ("b",2)])- str: 已拼接好的参数字符串(如"a=1&b=2"):return: 字符串类型的签名结果(32位十六进制字符串)"""# 处理参数:将不同类型的输入标准化为排序后的参数字符串if isinstance(params, (list, tuple, dict)):# 若为字典,先转换为键值对迭代器(items())if hasattr(params, 'items'): # 判断是否为字典(具有items方法)params = params.items()# 对参数按键名排序后,拼接为 "key=value&key=value" 格式# sorted(params) 会根据键名的ASCII码排序,确保参数顺序唯一params = '&'.join(f'{k}={v}' for k, v in sorted(params))# 将参数转换为字节流(SHA-1计算需要字节类型输入)if isinstance(params, str):params = params.encode() # 默认使用utf-8编码转换为bytes# 固定盐值(与服务端约定的密钥,用于增强哈希安全性)salt = b'b93af00b1270239d67bafdf9cbb2523b'# 第一次SHA-1计算:盐值 + 处理后的参数# salt + params 表示将盐值与参数字节流拼接s1 = hashlib.sha1(salt + params).hexdigest() # 得到160位哈希值的十六进制字符串(40位)# 第二次SHA-1计算:盐值 + 第一次计算的结果(需先转换为字节流)s2 = hashlib.sha1(salt + s1.encode()).hexdigest() # 再次哈希,增强安全性return s2data = {'api_key': '34a65f18bae9439589ae5f889bc37075','app_name': 'shop_iphone','app_version': '9.56.6','bizParams': '{"menu_code":"daohangmeizhuang"}','catTabContext': '','channel_flag': '0_1','clickFrom': 'tenhole_classify_auto','client': 'iphone','couponIds': '','darkmode': '0','deeplink_cps': '','device_client_type': '0','device_model': 'iPhone9,1'
}headers = {'Host': 'mapi.xxxxx.com','content-type': 'application/x-www-form-urlencoded','accept': '*/*','authorization': 'OAuth api_sign='+cal_sign(data),'x-vip-host': 'mapi.xxxx.com','user-agent': 'Spec/9.56.6 (iPhone; iOS 15.6.1; Scale/2.00)','accept-language': 'zh-Hans-CN;q=1',
}response = requests.post('https:/xxxxxx/v1', headers=headers, data=data)
print(response.text)