TA的每日心情 | 奋斗 2016-1-13 12:25 |
---|
签到天数: 3 天 [LV.2]偶尔看看I
|
本帖最后由 small-q 于 2020-12-4 18:22 编辑
0x00 本文缘起:
此App用来给iOS越狱机器清理系统垃圾,用了的人都感觉良好。之前不懂iOS应用破解时就是找别人修改好的版本用,导致不能及时用上新版,后来通过学习《iOS应用逆向与安全之道》一书了解iOS破解后有了自己破解的想法,当时信心满满咨询了C版,等我还没回过神来,C版已经分析写好注册机形式的插件直接注册了,但存在一个联网就注册失效的问题,之前的做法就是断网使用,这是去年之前的小经历,出于工作忙的原因我也没再分析,直到今天又想起此事,又重新捡起这个问题用软件,发现注册也失效了,然后跟C版要了之前注册插件的源进行参考分析,在此感谢C版。
0x01 所需工具:
* MacBook Pro(或虚拟机)
* iPhone7(iOS13.5)
* IDA Pro
* Theos
* 《iOS应用逆向与安全之道》一书
0x02 探测敌情:
分析一款应用之前,先是进行探测,看看应用有什么限制之类。下载最新版``v7.9.0``进入App,需要注册、广告开启等等...,如下图所示。
0x03 寻找入口:
使用爱思助手导出相关文件(越狱软件一般都没有加壳)。如下图所示。
然后把主文件拖到IDA进行分析,我的习惯就是对函数窗口进行关键字搜索。这里用``License``关键字试试,果不然奇然,有结果,如下图所示。
第一个方法存在``checkLicense``这种敏感字眼,进去看看伪代码(F5),幸福来得有点突然,貌似这里就是检查注册与广告处理。如下图所示。
至此,切入点就找到了,此处的``sub_1000A8124``应该是注册控制流程的关键了。接下来进行详细分析。
0x04 网络验证:
进入``sub_1000A8124()``,流程由``qword_100648CB0``全局变量控制,继续对其查找交叉引用,如下图所示。
找到两处赋值方法调用!666啊,进展非常顺利,如下图片所示。
先看``sub_100066740``函数并对其查找交叉引用,如下图所示。
结果如下图所示。
``sub_100065D50``函数伪代码如下:
[Objective-C] 纯文本查看 复制代码 void __fastcall sub_100065D50(double a1)
{
double v1;
//为了文章好看,略掉
void *v27;
__int64 v28;
dispatch_time_t v29;
if ( !(byte_100647680 & 1) )
{
v1 = a1;
byte_100647681 = 0;
byte_100647680 = 1;
objc_msgSend(&OBJC_CLASS___NSNotificationCenter, "defaultCenter");
v2 = (void *)objc_retainAutoreleasedReturnValue();
objc_msgSend(v2, "postNotificationName:object:", CFSTR("FILETYPEMSG"), 0LL);
objc_release(v2);
if ( v1 <= 0.0 )
v1 = 60.0;
v4 = (void *)objc_alloc(&OBJC_CLASS___NSString, v3);
sub_100065A00((__int64)v4, v5);
v6 = objc_retainAutoreleasedReturnValue();
v7 = objc_msgSend(v4, "initWithFormat:", CFSTR("%c%@%s%@"), 0x75LL, CFSTR("di"), "d=", v6);// did机器码
objc_release(v6);
objc_msgSend(
&OBJC_CLASS___NSString,
"stringWithFormat:",
CFSTR("%s%c%s%c%c%@%c%s%@%c%s%c%c%@%s%c%s%@%s%c%@%s%c%s%c"),
"ht",
0x74LL,
"ps:",
0x2FLL,
0x2FLL,
CFSTR("ib"), // 呵呵,作者有心了,防字符串搜索快速定位
0x2DLL,
"so",
CFSTR("ft"),
0x2ELL,
"ne",
0x74LL,
0x2FLL,
CFSTR("icle"),
"aner",
0x2FLL,
"che",
CFSTR("ckRe"),
"gist",
0x72LL,
CFSTR("ati"),
"on",
0x2ELL,
"ph",
0x70LL); //https://ib-soft.net/icleaner/checkRegistration.php
v8 = objc_retainAutoreleasedReturnValue();
objc_msgSend(CFSTR("PO"), "stringByAppendingString:", CFSTR("ST"));// POST 请求
v9 = objc_retainAutoreleasedReturnValue();
objc_msgSend(
&OBJC_CLASS___NSURLRequest,
"requestWithURLString:withHTTPMethod:withHTTPBodyString:withTimeout:",
v8,
v9,
v7,
v1); // 联网构造
v10 = objc_retainAutoreleasedReturnValue();
objc_release(v9);
objc_release(v8);
objc_release(v7);
if ( !v10 )
exit(1);
v12 = (void *)objc_alloc(&OBJC_CLASS___NSURLConnection, v11);
if ( !qword_100647690 )
{
v13 = objc_msgSend(&OBJC_CLASS___NSObject, "class");
v14 = objc_allocateClassPair(v13, "ICRC", 0LL);
v15 = objc_retain();
class_addProtocol();
protocol_getMethodDescription(v15, "connection:didReceiveResponse:", 0LL, 1LL);
class_addMethod(v14, "connection:didReceiveResponse:", sub_1000666F0, v16);
protocol_getMethodDescription(v15, "connection:didReceiveData:", 0LL, 1LL);
class_addMethod(v14, "connection:didReceiveData:", sub_100066728, v17);
protocol_getMethodDescription(v15, "connectionDidFinishLoading:", 0LL, 1LL);
class_addMethod(v14, "connectionDidFinishLoading:", sub_100066740, v18);// 引用到此处
v19 = objc_retain();
objc_release(v15);
class_addProtocol();
protocol_getMethodDescription(v19, "connection:didFailWithError:", 0LL, 1LL);
class_addMethod(v14, "connection:didFailWithError:", sub_100066838, v20);
objc_registerClassPair(v14);
v22 = (void *)objc_alloc(v14, v21);
v23 = objc_msgSend(v22, "init");
v24 = qword_100647690;
qword_100647690 = (__int64)v23;
objc_release(v24);
objc_release(v19);
}
v25 = objc_retain();
v26 = v25;
v27 = objc_msgSend(v12, "initWithRequest:delegate:", v10, v25);
v28 = qword_100647678;
qword_100647678 = (__int64)v27;
objc_release(v28);
objc_release(v26);
objc_msgSend((void *)qword_100647678, "start");
v29 = dispatch_time(0LL, (signed __int64)(v1 * 1000000000.0));
dispatch_after(v29, &_dispatch_main_q, &off_1004C6C38);
objc_release(v10);
}
}
从上面的伪代码可以看出``sub_100065D50``内部方法就是一个网络检测函数,在此请各位看官想想应对方案(可不能像我之前一样断网了哦)
ok,网验已找到,接着分析注册算法部分。
0x05 本地算法:
继之前所说的两处赋值方法的第二处,继续查找引用,如下图所示。
逐个查看后,直接定位算法核心``sub_1000663D4``,算法分析请看代码注释部分:
[Objective-C] 纯文本查看 复制代码 void __fastcall sub_1000663D4(__int64 a1, __int64 a2)
{
void *v2; // x19
__int64 v3; // x20
__int64 v4; // x20
__int64 v5; // x21
__int64 v6; // x22
__int64 v7; // x23
__int64 v8; // x0
void *v9; // x24
const char *v10; // x25
char *v11; // x24
__int64 v12; // x27
char *v13; // x26
void *v14; // x20
__int64 v15; // x19
__int64 v16; // x0
__int64 v17; // x1
__int64 v18; // x21
__int64 v19; // x20
void *v20; // x0
void *v21; // x19
char *v22; // x0
char v23[16]; // [xsp+28h] [xbp-68h]
if ( !qword_100648CB8 )
{
sub_100065A00(a1, a2);
v2 = (void *)objc_retainAutoreleasedReturnValue();
if ( (unsigned __int64)objc_msgSend(v2, "length") <= 0x27 )
{
objc_msgSend(v2, "stringByPaddingToLength:withString:startingAtIndex:", 0x28LL, CFSTR("0"), 0LL);
v3 = objc_retainAutoreleasedReturnValue();
objc_release(v2);
v2 = (void *)v3;
}
objc_msgSend(v2, "substringWithRange:", 0x12LL, 5LL);// uid 取 0x12 开始 5位 即: 10b15
v4 = objc_retainAutoreleasedReturnValue();
objc_msgSend(v2, "substringToIndex:", 0x1BLL);// uid 从首位开始切片取值(大小0x1B) 即: 9c017b111b006b555a10b15baad
v5 = objc_retainAutoreleasedReturnValue();
objc_msgSend(v2, "substringWithRange:", 0xDLL, 0xALL);// uid 取 0xD 开始 0xA位 即: b555a10b15
v6 = objc_retainAutoreleasedReturnValue();
objc_msgSend(v2, "substringFromIndex:", 0x1FLL);// uid 从0x1F开始切片取值 即: 6efa7875b
v7 = objc_retainAutoreleasedReturnValue();
objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("%@%@%@%@"), v4, v5, v6, v7);// 组合成一串:10b159c017b111b006b555a10b15baadb555a10b156efa7875b
v8 = objc_retainAutoreleasedReturnValue();
v9 = (void *)objc_retainAutorelease(v8);
v10 = (const char *)objc_msgSend(v9, "UTF8String");
objc_release(v9);
strlen(v10);
CC_MD5(); // md5计算结果就是注册码
v11 = (char *)malloc(0x21uLL);
v12 = 0LL;
v13 = v11;
do
v13 += snprintf(v13, 0x21uLL, "%02x", (unsigned __int8)v23[v12++]);
while ( v12 != 0x10 );
qword_100648CB8 = (__int64)v11;
objc_release(v7);
objc_release(v6);
objc_release(v5);
objc_release(v4);
objc_release(v2);
}
if ( !qword_100648CB0 ) // 比较
{
objc_msgSend(&OBJC_CLASS___NSUserDefaults, "standardUserDefaults");
v14 = (void *)objc_retainAutoreleasedReturnValue();
objc_msgSend(v14, "stringForKey:", CFSTR("lastRequestValue"));
v15 = objc_retainAutoreleasedReturnValue();
v16 = objc_release(v14);
if ( !v15 )
{
sub_100066B74(v16, v17); // 注册文件检查位置
v18 = objc_retainAutoreleasedReturnValue();
objc_msgSend(&OBJC_CLASS___NSString, "stringWithContentsOfFile:encoding:error:", v18, 1LL, 0LL);
v19 = objc_retainAutoreleasedReturnValue();
objc_release(0LL);
objc_release(v18);
sub_100066ADC();
v15 = v19;
}
v20 = (void *)objc_retainAutorelease(v15);
v21 = v20;
v22 = (char *)objc_msgSend(v20, "UTF8String");
sub_10006683C(v22); // 引用到此处
objc_release(v21);
}
}
// 注册文件检查位置
__int64 __fastcall sub_100066B74(__int64 a1, __int64 a2)
{
void *v2; // x0
v2 = (void *)objc_alloc(&OBJC_CLASS___NSString, a2);
objc_msgSend(
v2,
"initWithFormat:",
CFSTR("%c%s%c%c%@%s%c%c%@%s%c%@%s%c%s%c%@%s%c%@%c"),
0x2FLL,
"va",
0x72LL,
0x2FLL,
CFSTR("mob"),
"ile",
0x2FLL,
0x4CLL,
CFSTR("ibra"),
"ry",
0x2FLL,
CFSTR("iCle"),
"aner",
0x2FLL,
"li",
0x63LL,
CFSTR("en"),
"se",
0x2ELL,
CFSTR("cache"),
0x64LL);
return objc_autoreleaseReturnValue();
}
0x06 插件代码:
算法分析完毕了哦! 是时候把码了:
[Objective-C] 纯文本查看 复制代码 #import <substrate.h>
#include <mach-o/dyld.h>
#import <dlfcn.h>
#import <CommonCrypto/CommonDigest.h>
#include <unistd.h>
#include <sys/types.h>
#include <mach/mach.h>
%hook NSURL
- (id)initWithString:(NSString*)url {
if ([url containsString:@"ib-soft.net"])
{
NSLog(@"[+] 网络访问->:%@",url);
%log;
/* code */
return %orig(@"http://127.0.0.1");
}
return %orig(url);
}
%end
static NSString *uniqueDeviceID(void) {
static CFStringRef (*$MGCopyAnswer)(CFStringRef);
void *gestalt = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_GLOBAL | RTLD_LAZY);
$MGCopyAnswer = (dlsym(gestalt, "MGCopyAnswer"));
return (__bridge NSString *)$MGCopyAnswer(CFSTR("UniqueDeviceID"));
}
static NSString *md5(NSString *str) {
const char *cStr = [str UTF8String];
unsigned char result[16];
CC_MD5( cStr, strlen(cStr),result );
NSMutableString *hash =[NSMutableString string];
for (int i = 0; i < 16; i++) {
[hash appendFormat:@"%02x", result[i]];
}
return hash;
}
static void genLicenseFile(NSString* hash) {
// NSLog(@"[+]Hash:%@",hash);
NSLog(@"[+] 写入注册信息");
[hash writeToFile:@"/var/mobile/Library/iCleaner/license.cached" atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
%ctor
{
NSString* udid = uniqueDeviceID();
NSLog(@"[+] 获取 did->:%@",udid);
NSString* tmpStr = [NSString stringWithFormat:@"%@%@%@%@",[udid substringWithRange:NSMakeRange(18,5)],[udid substringToIndex:27],[udid substringWithRange:NSMakeRange(13,10)],[udid substringFromIndex:31]];
NSLog(@"[+] 取值 tmpStr->:%@",tmpStr);
NSString* hash = md5(tmpStr);
NSLog(@"[+] 计算 MD5_hash->:%@",hash);
[[NSUserDefaults standardUserDefaults] setObject:hash forKey:@"lastRequestValue"];
genLicenseFile(hash);
}
编译安装:
0x07 光明之巅:
运行日志如下图所示。
完美注册!不再联网验,感谢C版!
0x08 注意事项:
* 尽量用新版分析,生成的插件老版可能不会加载!
* 之前说的失效原因是新版更改了bundleId,添加进去即可:
[Objective-C] 纯文本查看 复制代码 {
Filter = {
Bundles = (
"com.exile90.icleanerpro",
"com.ivanobilenchi.icleaner",
);
};
}
* 不提供bin文件,感谢观看。
希望你学有所用,简单而优雅的分析希望得到你的认可,有错误欢迎指正,谢谢!
机械工业出版社正版购买通道:
https://item.jd.com/12800426.html
1.凡是通过以上链接购买,均可换取飘云阁安全论坛邀请码1枚(或飘云币200枚)。具体换取方式请在本公众号回复:换取
2.根据实际情况可安排少量签名版。具体购买方式请在本公众号回复:签名版
|
评分
-
查看全部评分
|