zoukankan      html  css  js  c++  java
  • iOS Hook UDID 验证

    本文为迁移文章,原发布时间为 2018-12-29 07:32:07

    几个月前有朋友想做一个 Fake UDID 的越狱插件,我就研究了一下,做了个插件。今天又有朋友问我类似的需求,我想还是直接写篇文章帮助有需要的人。

    这个需求是这样的,有些 App (特别是企业签名的)在安装的时候会要求你先装一个描述文件来获取你手机的 UDID,然后用这个 UDID 来验证你的设备是否在他们的数据库里有记录,我们希望能够每次安装描述文件的时候都返回一个新的 Fake UDID 来迷惑 App 的服务器以为我们是新安装的机型。

    这个功能同样可以满足另一个需求,就是你想要下载某个 ADHoc 签名的 App,但是你的设备并不在分发列表里,这时候你可以解析一下签名查看列表,然后在安装前验证 UDID 时返回一个列表内的 UDID。虽然越狱设备根本不需要搞这么麻烦。。。

    我通过调试发现做这个验证的是 ManagedConfiguration 框架,进程名 profiled,框架中 MCHTTPTransaction 类的 data 字段就是发送给 UDID 请求网站的回调服务器的数据,包含 UDID 等数据,它使用 PKCS#7 签名。这个字段使用 NSString 无法正确解码,所以用 Objective-C 字符串的方式没办法编辑它。

    我最初尝试直接返回未签名的 XML,在类似于 fir.im 这种没有做签名校验的网站是可以通过的,但是在 udid.io 这种做了签名校验的网站通不过。
    所以我尝试找它的签名函数,也确实成功拿到了,但是没有找到计算和签名这个 data 字段的函数,故而条路放弃。

    我尝试在 udid.io 被回调之前在 Charles 中修改掉 UDID 的值,该网站依然返回正确响应,这可以证明 XML 内容和签名是无关的。

    因为无法解码,那么这样一来最简单的方式就是直接修改 HEX。响应体中 XML 里 UDID 元素的样式如下:

    <key>UDID</key>
    	<string>4194e4e27qdf84df725d487431fce8e11fd991</string>
    

    那么我们可以先找到 <key>UDID</key> <string> 的位置,然后向后偏移 40(UDID 的长度)再检查是不是 </string>, 如果符合这种情况,就把中间的 UDID 替换掉。

    直接贴代码吧,完整工程在

    static short const kUDIDLength = 40;
    static short const kPrefixLength = 25;
    static short const kSuffixLength = 9;
    
    /// <key>UDID</key>
    	<string>
    static uint8_t const kPrefix[kPrefixLength] = {
        0x3C, 0x6B, 0x65, 0x79, 0x3E, 0x55, 0x44, 0x49,
        0x44, 0x3C, 0x2F, 0x6B, 0x65, 0x79, 0x3E, 0x0A,
        0x09, 0x3C, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67,
        0x3E
    };
    
    /// </string>
    static uint8_t const kSuffix[kSuffixLength] = {
        0x3C, 0x2F, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3E
    };
    
    static NSString *generateRandomString(NSInteger length) {
        /// 总长度 40, 其中 25 位数字, 其他是字母. (其实网站并不会验证这个规则...)
        NSMutableString *randomizedText = [NSMutableString stringWithString:@"df9249d4418qe1e79c87d1a58fe4247434eff1d1"];
        NSString *buffer = nil;
        for (NSInteger i = randomizedText.length - 1, j; i >= 0; i--) {
            j = arc4random() % (i + 1);
            buffer = [randomizedText substringWithRange:NSMakeRange(i, 1)];
            [randomizedText replaceCharactersInRange:NSMakeRange(i, 1) withString:[randomizedText substringWithRange:NSMakeRange(j, 1)]];
            [randomizedText replaceCharactersInRange:NSMakeRange(j, 1) withString:buffer];
        }
        return [randomizedText copy];
    }
    
    static NSData *replacedUUIDData(NSData *data) {
        NSUInteger minLength = kSuffixLength + kUDIDLength + kSuffixLength;
        if (data.length <= minLength) {
            return data;
        }
    
        uint8_t *buffer = (uint8_t *)[data bytes];
        uint32_t bufferSize = 0;
        uint8_t *bufferBegin = buffer;
        uint8_t *bufferEnd = buffer + data.length;
        while (bufferBegin != bufferEnd && bufferSize < data.length) {
            if (0 == memcmp(bufferBegin, kPrefix, kPrefixLength)) {
                if (0 == memcmp(bufferBegin + kPrefixLength + kUDIDLength, kSuffix, kSuffixLength)) {
                    NSString *fakeUDID = generateRandomString(40);
                    HBLogDebug(@"Found UDID location, trying to replace it with %@", fakeUDID);
                    strncpy((char *)bufferBegin + kPrefixLength, [fakeUDID UTF8String], kUDIDLength);
                    break;
                }
            }
            ++bufferBegin;
            ++bufferSize;
        }
        
        return [NSData dataWithBytes:buffer length:data.length];
    }
    
    %hook MCHTTPTransaction
    
    - (void)setData:(id)arg1 {
        %log;
        %orig(replacedUUIDData(arg1));
    }
    
    %end
    
  • 相关阅读:
    java路径两种写法"/"和"\"以及 ./和../以及/之间的区别?
    几张图轻松理解String.intern()和String
    面向对象编程三大特性------封装、继承、多态
    markdown操作手册
    index索引的一些简单理解
    Mac 上flink的安装与启动
    C语言实现俄罗斯方块游戏
    Maven_学习、搭建、应用
    PHP学习笔记---高级知识
    软件设计师笔记---寻址方式
  • 原文地址:https://www.cnblogs.com/VincentXue/p/14589800.html
Copyright © 2011-2022 走看看