某APP的API签名破解记录
本帖最后由 王小劣 于 2018-11-6 19:09 编辑最近因为要去除一些常用 APP 丧心病狂的切屏广告所以接触了一下 iOS 逆向,正巧群里有人问怎么破解某APP的API签名,就拿来练练手.
### 准备工作
- 某APP砸壳后的.ipa文件
- MonkeyDev
- IDA Pro
- class-dump 头文件
### 查找分析定位相关函数
- 通过搜索常用的加密关键词 "md5" "sign" 以及 API 相关函数名很容易定位到了一个叫`XYAPIEncodeSign`的类
- 查看该类的头文件为
```
+(id)md5:(id)arg1 useLower:(_Bool)arg2;
+(id)URLencodeSpacetoPlus:(id)arg1;
+(id)signWithQueryItems:(id)arg1;
```
- 编写代码 hook `+ (id)signWithQueryItems:(id)arg1;` 方法
```
CHDeclareClass(XYAPIEncodeSign)
CHClassMethod1(id, XYAPIEncodeSign, signWithQueryItems,id,arg1){
id result = CHSuper1(XYAPIEncodeSign, signWithQueryItems, arg1);
NSLog(@"XYAPIEncodeSign signWithQueryItems ---result:%@",result);
return result;
}
```
运行后其中一次的打印结果(非真实打印)为
```
XYAPIEncodeSign signWithQueryItems ---result:c347e5dfd0af7ace125b959f621ab268
```
可以看到会直接返回一个 md5 加密后的32位sign`c347e5dfd0af7ace125b959f621ab268`,通过和网络抓包对比结果一致,说明这个类方法确实是我们想要的签名生成方法.
### 查看类方法的实现
- 把.app 拖入 IDA 解析完毕后,搜索`signWithQueryItems`,按 F5 可以得到伪代码(部分)如下
```
id __cdecl +(XYAPIEncodeSign_meta *self, SEL a2, id a3)
{
v24 = objc_msgSend(v23, "URLencodeSpacetoPlus:", v14);
v25 = (void *)objc_retainAutoreleasedReturnValue(v24);
v26 = objc_msgSend(v25, "mutableCopy");
objc_release(v14);
objc_release(v25);
v28 = (void *)objc_retain(v49, v27);
v29 = objc_msgSend(&OBJC_CLASS___NSMutableString, "new");
v30 = v26;
if ( objc_msgSend(v26, "length") )
{
v52 = v29;
v31 = 0LL;
do
{
v32 = (unsigned __int64)objc_msgSend(v26, "characterAtIndex:", v31);
v33 = (unsigned __int64)objc_msgSend(v28, "length");
v34 = (unsigned __int64)objc_msgSend(v28, "characterAtIndex:", v31 - v31 / v33 * v33);
v35 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("%i"), v34 ^ v32);
v36 = objc_retainAutoreleasedReturnValue(v35);
objc_msgSend(v52, "appendString:", v36);
objc_release(v36);
++v31;
}
while ( (unsigned __int64)objc_msgSend(v26, "length") > v31 );
v22 = v51;
v29 = v52;
}
v37 = objc_msgSend(v22, "class");
v38 = objc_msgSend(v37, "md5:useLower:", v29, 1LL);
v39 = (void *)objc_retainAutoreleasedReturnValue(v38);
v40 = objc_msgSend(v22, "class");
v41 = objc_msgSend(v39, "stringByAppendingString:", v28);
v42 = objc_retainAutoreleasedReturnValue(v41);
v43 = v42;
v44 = objc_msgSend(v40, "md5:useLower:", v42, 1LL);
v45 = (void *)objc_retainAutoreleasedReturnValue(v44);
}
```
- 通过查看伪代码我们发现该方法内调用了类方法`URLencodeSpacetoPlus`,那么就先 hook 一下这个方法看看它做了什么
```
CHClassMethod1(id, XYAPIEncodeSign, URLencodeSpacetoPlus,id,arg1){
id result = CHSuper1(XYAPIEncodeSign, URLencodeSpacetoPlus, arg1);
NSLog(@"XYAPIEncodeSign URLencodeSpacetoPlus ----arg1:%@ ---result:%@ " ,arg1,result);
return result;
}
```
打印结果为(非真实数据)`"deviceId%3DF88CEADF-0D03-XXXX-XXXX-846D2E0A021Cdevice_fingerprint%3D201711130033453bdc3fad93e398c2cdfecd28355c732f01b633f31603a881device_fingerprint1%3D201711130033453bdc3fad93e398c2cdfecd28355c732f01b633f31603a881lang%3Dzhpath%3D/api/sns/v1/recommend/user/statusplatform%3DiOSsid%3Dsession.1540012876934309754t%3D1540028140"`,因为可以一眼看出来这个是传入参数根据 key 排列后的字符串进行了UrlEncode编码得到的,就不需要再去查看其具体实现了.
- 接下来我们可以直接从`URLencodeSpacetoPlus `后进行分析可以看出 sign 的计算代码为
```
if ( objc_msgSend(v26, "length") )
{
v52 = v29;
v31 = 0LL;
do
{
v32 = (unsigned __int64)objc_msgSend(v26, "characterAtIndex:", v31);
v33 = (unsigned __int64)objc_msgSend(v28, "length");
v34 = (unsigned __int64)objc_msgSend(v28, "characterAtIndex:", v31 - v31 / v33 * v33);
v35 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("%i"), v34 ^ v32);
v36 = objc_retainAutoreleasedReturnValue(v35);
objc_msgSend(v52, "appendString:", v36);
objc_release(v36);
++v31;
}
while ( (unsigned __int64)objc_msgSend(v26, "length") > v31 );
v22 = v51;
v29 = v52;
}
v37 = objc_msgSend(v22, "class");
v38 = objc_msgSend(v37, "md5:useLower:", v29, 1LL);
v39 = (void *)objc_retainAutoreleasedReturnValue(v38);
v40 = objc_msgSend(v22, "class");
v41 = objc_msgSend(v39, "stringByAppendingString:", v28);
v42 = objc_retainAutoreleasedReturnValue(v41);
v43 = v42;
v44 = objc_msgSend(v40, "md5:useLower:", v42, 1LL);
v45 = (void *)objc_retainAutoreleasedReturnValue(v44);
objc_release(v43);
```
### 还原代码
- 这部分很简单就不说具体的还原方法了,直接上代码.如下
```
NSString *v26 = @"deviceId%3DF88CEADF-0D03-XXXX-XXXX-846D2E0A021Cdevice_fingerprint%3D201711130033453bdc3fad93e398c2cdfecd28355c732f01b633f31603a881device_fingerprint1%3D201711130033453bdc3fad93e398c2cdfecd28355c732f01b633f31603a881lang%3Dzhpath%3D/api/sns/v1/recommend/user/statusplatform%3DiOSsid%3Dsession.1540012876934309754t%3D1540028140";
NSString *v28 = @"F88CEADF-0D03-XXXX-XXXX-846D2E0A021C";
NSMutableString *v52 = ;
for (int i = 0; i < v26.length;i++) {
unichar v32=;
NSInteger v33 = v28.length;
unichar v34 = ;
NSString *v35 = ;
;
}
NSString *v38 = ;
NSString *v41 = ;
NSString *v44 = ;
NSLog(@"sign--------------%@",v44);
``` heavytiger 发表于 2019-3-7 17:39
想自己用来抢单,感觉抢不过别人啊。估计他们有抢单软件。
这个可能帮不了你...你可以自己尝试一下. 王小劣 发表于 2019-3-7 12:36
你要拿来做什么???可以自己试试先.
想自己用来抢单,感觉抢不过别人啊。估计他们有抢单软件。 看不懂,不过还是支持下。。 感谢楼主,谢谢 有哪里不会? 跟看天书是的谢谢分享 虽然不懂,支持一下{:1_932:} 哈哈,很认真的看了一遍,貌似还是不懂! 感谢分享啊 ios
的逆向不错啊,文章质量高 感谢楼主分享