【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

0x00 本文缘起:
此App用来给ios越狱机器清理系统垃圾,用了的人都感觉良好。之前不懂iOS应用破解时就是找别人修改好的版本用,导致不能及时用上新版,后来通过学习了解iOS破解后有了自己破解的想法,当时信心满满咨询了C版,等我还没回过神来,C版已经分析写好注册机形式的插件直接注册了,但存在一个联网就注册失效的问题,之前的做法就是断网使用,这是去年之前的小经历,出于工作忙的原因我也没再分析,直到今天又想起此事,又重新捡起这个问题用软件,发现注册也失效了,然后跟C版要了之前注册插件的源进行参考分析,在此感谢C版。

0x01 所需工具:

MacBook Pro(或虚拟机)
iPhone7(iOS13.5)
IDA Pro
Theos
0x02 探测敌情:
分析一款应用之前,先是进行探测,看看应用有什么限制之类。下载最新版v7.9.0进入App,需要注册、广告开启等等…,如下图所示。
【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

0x03 寻找入口:
使用爱思助手导出相关文件(越狱软件一般都没有加壳)。如下图所示。
【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

然后把主文件拖到IDA进行分析,我的习惯就是对函数窗口进行关键字搜索。这里用License关键字试试,果不然奇然,有结果,如下图所示。
【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

第一个方法存在checkLicense这种敏感字眼,进去看看伪代码(F5),幸福来得有点突然,貌似这里就是检查注册与广告处理。如下图所示。
【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

至此,切入点就找到了,此处的sub_1000A8124应该是注册控制流程的关键了。接下来进行详细分析。

0x04 网络验证:
进入sub_1000A8124(),流程由qword_100648CB0全局变量控制,继续对其查找交叉引用,如下图所示。
【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

找到两处赋值方法调用!666啊,进展非常顺利,如下图片所示。

【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

先看sub_100066740函数并对其查找交叉引用,如下图所示。

【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

结果如下图所示。

【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

sub_100065D50``函数伪代码如下:

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 本地算法:
继之前所说的两处赋值方法的第二处,继续查找引用,如下图所示。

【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

逐个查看后,直接定位算法核心sub_1000663D4,算法分析请看代码注释部分:

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 插件代码:
算法分析完毕了哦! 是时候把码了:

#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);
}

编译安装:

【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

0x07 光明之巅:

【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

运行日志如下图所示。

【ios】大神论坛之iCleaner Pro 网络验证和注册算法分析

完美注册!不再联网验,感谢C版!

0x08 注意事项:

尽量用新版分析,生成的插件老版可能不会加载!
之前说的失效原因是新版更改了bundleId,添加进去即可:
{

    Filter = {
            Bundles = (
                    "com.exile90.icleanerpro",
                    "com.ivanobilenchi.icleaner",
            );
    };

}
不提供bin文件,感谢观看。
版权声明:本文由small-q原创,版权归small-q所有,欢迎分享文章,转载请保留出处!

上一篇:数据竞赛实战(4)——交通事故理赔审核


下一篇:golang 数据类型之间的转换