飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 48151|回复: 87

[iOS] iCleaner Pro 网验与注册算法分析

    [复制链接]
  • TA的每日心情
    奋斗
    2016-1-13 12:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2020-11-24 12:26:50 | 显示全部楼层 |阅读模式
    本帖最后由 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,需要注册、广告开启等等...,如下图所示。
    16061840993991.jpg

    0x03 寻找入口:
         使用爱思助手导出相关文件(越狱软件一般都没有加壳)。如下图所示。
    16061842644335.jpg

    然后把主文件拖到IDA进行分析,我的习惯就是对函数窗口进行关键字搜索。这里用``License``关键字试试,果不然奇然,有结果,如下图所示。
    16061844325086.jpg

    第一个方法存在``checkLicense``这种敏感字眼,进去看看伪代码(F5),幸福来得有点突然,貌似这里就是检查注册与广告处理。如下图所示。
    16061848080779.jpg

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


    0x04 网络验证:
         进入``sub_1000A8124()``,流程由``qword_100648CB0``全局变量控制,继续对其查找交叉引用,如下图所示。
    16061849887657.jpg

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

    先看``sub_100066740``函数并对其查找交叉引用,如下图所示。
    16061855295287.jpg
    结果如下图所示。
    16061855724880.jpg

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

    逐个查看后,直接定位算法核心``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);
    }
    


    编译安装:
    16061894372529.jpg

    0x07 光明之巅:
    16061889424954.jpg

    运行日志如下图所示。
    16061889117206.jpg


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


    0x08 注意事项:
         * 尽量用新版分析,生成的插件老版可能不会加载!
         * 之前说的失效原因是新版更改了bundleId,添加进去即可:
    [Objective-C] 纯文本查看 复制代码
    {
            Filter = {
                    Bundles = (
                            "com.exile90.icleanerpro",
                            "com.ivanobilenchi.icleaner",
                    );
            };
    }

    * 不提供bin文件,感谢观看。

    希望你学有所用,简单而优雅的分析希望得到你的认可,有错误欢迎指正,谢谢!

    WechatIMG9.png
    机械工业出版社正版购买通道:

    https://item.jd.com/12800426.html

    1.凡是通过以上链接购买,均可换取飘云阁安全论坛邀请码1枚(或飘云币200枚)。具体换取方式请在本公众号回复:换取

    2.根据实际情况可安排少量签名版。具体购买方式请在本公众号回复:签名版


    评分

    参与人数 14威望 +27 飘云币 +31 收起 理由
    bluesky10 + 1 + 1 感谢发布原创作品,PYG有你更精彩!
    Niwde + 1 + 1 PYG有你更精彩!
    LuckyClover + 1 + 1 原创精品 感谢分享!
    光晕 + 1 + 1 吃水不忘打井人,给个评分懂感恩!
    xiandou + 1 + 1 吃水不忘打井人,给个评分懂感恩!
    东方优源 + 1
    wangxd + 1 + 1 吃水不忘打井人,给个评分懂感恩!
    ahxuansheng00 + 1 + 1 PYG有你更精彩!
    tree_fly + 5 + 5 感谢发布原创作品,PYG有你更精彩!
    zhczf + 1 PYG有你更精彩!

    查看全部评分

    PYG19周年生日快乐!
  • TA的每日心情
    擦汗
    2019-3-20 20:06
  • 签到天数: 258 天

    [LV.8]以坛为家I

    发表于 2020-11-24 13:23:01 | 显示全部楼层
    iOS的软件 能做到怎么优雅 望尘莫及   膜拜!~~~
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2024-1-3 09:24
  • 签到天数: 207 天

    [LV.7]常住居民III

    发表于 2020-11-24 13:30:48 | 显示全部楼层
    看到IDA就膜拜了,还是个IOS的牛B可拉斯

    点评

    注意大小写:iOS  发表于 2020-11-24 14:41
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    昨天 21:04
  • 签到天数: 1481 天

    [LV.10]以坛为家III

    发表于 2020-11-24 13:38:54 | 显示全部楼层
    弱弱的问一句:这码是go?

    点评

    OC  发表于 2020-11-24 14:39
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-3-25 15:21
  • 签到天数: 487 天

    [LV.9]以坛为家II

    发表于 2020-11-24 17:13:33 | 显示全部楼层
    我说怎么C++把码破解ISO了
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-2-26 11:14
  • 签到天数: 459 天

    [LV.9]以坛为家II

    发表于 2020-11-24 20:05:32 | 显示全部楼层
    我13.3测试失败什么时候解决一下
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2024-5-12 21:54
  • 签到天数: 39 天

    [LV.5]常住居民I

    发表于 2020-11-24 20:30:07 | 显示全部楼层
    很6b的样子。
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    7 小时前
  • 签到天数: 1398 天

    [LV.10]以坛为家III

    发表于 2020-11-25 10:14:13 | 显示全部楼层
    优秀,支持大神。
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2022-11-17 20:05
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2020-11-25 16:14:27 | 显示全部楼层
    a12以上设备udid只有25位,已经用其他方式实现了
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2024-10-28 00:00
  • 签到天数: 1583 天

    [LV.Master]伴坛终老

    发表于 2020-11-25 19:39:09 | 显示全部楼层
    可惜了,手中没苹果手机~
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表