在iPhone运行时检测UDID篡改

21

越狱的iPhone让我很恼火,因为它使用MobileSubstrate污染了iOS上的一些基本API。

http://www.iphonedevwiki.net/index.php/MobileSubstrate

我相信许多应用程序使用UDID作为验证设备和/或用户的手段,因为它是半自动和方便的,但您应该意识到这个问题:UIDevice不像应该那样防篡改。有一个叫做UDID Faker的应用程序,在运行时轻松地使您可以欺骗别人的UDID。

http://www.iphone-network.net/how-to-fake-udid-on-ios-4/

这是它的源代码:

//
//  UDIDFaker.m
//  UDIDFaker
//

#include "substrate.h"

#define ALog(...) NSLog(@"*** udidfaker: %@", [NSString stringWithFormat:__VA_ARGS__]);
#define kConfigPath @"/var/mobile/Library/Preferences/com.Reilly.UDIDFaker.plist"

@protocol Hook
- (NSString *)orig_uniqueIdentifier;
@end

NSString *fakeUDID = nil;

static NSString *$UIDevice$uniqueIdentifier(UIDevice<Hook> *self, SEL sel) {  

    if(fakeUDID != nil) {
                 ALog(@"fakeUDID %@", fakeUDID);
        /* if it's a set value, make sure it's sane, and return it; else return the default one */
                return ([fakeUDID length] == 40) ? fakeUDID : [self orig_uniqueIdentifier];

    }
    /* ... if it doesn't then return the original UDID */
    else {
        return [self orig_uniqueIdentifier];
    }
}

__attribute__((constructor)) static void udidfakerInitialize() {  

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        NSString *appsBundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
        ALog(@"Loading UDID Faker into %@", appsBundleIdentifier);


        NSDictionary *config = [NSDictionary dictionaryWithContentsOfFile: kConfigPath];
        fakeUDID = [config objectForKey: appsBundleIdentifier];
        [fakeUDID retain];

        if(fakeUDID != nil) {

                ALog(@"Hooking UDID Faker into %@", appsBundleIdentifier);
                MSHookMessage(objc_getClass("UIDevice"), @selector(uniqueIdentifier), (IMP)&$UIDevice$uniqueIdentifier, "orig_");
        }

    [pool release];
}

如您所见,在UIDevice类中的uniqueIdentifier方法现在会在任何应用程序中返回fakeUDID。

似乎Skype和其他一些应用程序可以检测到这种污染,但我不知道该如何做到。

我的想法是:当检测到被污染的UIDevice时,启动警报并退出(0)。

有什么好的建议吗?


2
兄弟,你把这些想法都给了其他人...我的意思是,你本可以只发布问题而不附带示例代码和欺骗所需的确切API /链接。现在所有的SO用户都知道如何欺骗UDID了。 - Swapnil Luktuke
2
具有此类意图的人要么没有理解上述代码的能力,要么具备这种能力并已经了解它。 - Daniel Amitay
每个SO用户都应该知道如何使用Google。仅因为链接不在这里,就不意味着对它感兴趣的人找不到它。 - JustSid
3
我认为掩盖事实并不能帮助解决问题,反而会削弱对问题的正确理解。我们选择使用 UDID 作为身份验证方案,现在面临这个问题,正是因为我们无法找到与此问题相关的任何信息。我通过谷歌搜索“UDID Faker”就能在一分钟内找到来源。让我们一起解决这个问题,而不是自我限制。 - kenn
17
揭示一个安全漏洞时,最好引起足够的关注。这样程序员们就会了解到这个漏洞,并相应地编写他们的程序。试图对已被熟练黑客知晓的事情保密是不负责任的。 - Mihai Damian
2个回答

35

没有一种真正安全的方法来检查UDID是否真实存在。UDID是通过liblockdown获取的,它通过一个安全通道与lockdownd通信以接收UDID:

+-----------+
| your code |
+-----------+
      |
+----------+       +-------------+       +-----------+
| UIDevice |<----->| liblockdown |<=====>| lockdownd |   (trusted data)
+----------+       +-------------+       +-----------+
         untrusted user                   trusted user

设备越狱后,所有四个组件都可以被替换。


检测UDID Faker是否存在的一种方法是检查某些与其唯一性有关的标识(文件、函数等)是否存在。这是一种非常具体而又脆弱的反制手段,因为当探测方法被暴露时,欺骗者只需改变标识以隐藏其存在。

例如,UDID Faker依赖于一个plist文件 /var/mobile/Library/Preferences/com.Reilly.UDIDFaker.plist。因此,您可以检查该文件是否存在:

NSString* fakerPrefPath = @"/var/mobile/Library/Preferences/com.Reilly.UDIDFaker.plist";
if ([[NSFileManager defaultManager] fileExistsAtPath:fakerPrefPath])) {
   // UDID faker exists, tell user the uninstall etc.
}

它还定义了方法-[UIDevice orig_uniqueIdentifier],该方法可用于绕过伪造器:

UIDevice* device = [UIDevice currentDevice];
if ([device respondsToSelector:@selector(orig_uniqueIdentifier)])
   return [device orig_uniqueIdentifier];
else
   return device.uniqueIdentifier;

当然,欺骗者可以简单地重命名这些东西。


更可靠的方法在于Mobile Substrate的工作原理。注入的代码必须位于dylib/bundle中,并加载到与UIKit不同的内存区域中。因此,您只需要检查-uniqueIdentifier方法的函数指针是否在可接受的范围内。

// get range of code defined in UIKit 
uint32_t count = _dyld_image_count();
void* uikit_loc = 0;
for (uint32_t i = 0; i < count; ++ i) {
   if (!strcmp(_dyld_get_image_name(i), "/System/Library/Frameworks/UIKit.framework/UIKit")) {
     uikit_loc = _dyld_get_image_header(i);
     break;
   }
}

....

IMP funcptr = [UIDevice instanceMethodForSelector:@selector(uniqueIdentifier)];
if (funcptr < uikit_loc) {
   // tainted function
}
无论如何,UDID Faker是一种非常高级的黑客技术(即它很容易被规避)。它通过提供一个虚假的ID来劫持UIDevice和liblockdown之间的链接。
+-----------+
| your code |
+-----------+
      |
+----------+       +-------------+       +-----------+
| UIDevice |<--.   | liblockdown |<=====>| lockdownd |   (trusted data)
+----------+   |   +-------------+       +-----------+
               |   +------------+-->| UDID Faker |
                   +------------+

因此,你可以将请求设备唯一标识符的代码下移到liblockdown级别。这可以用于越狱平台的应用程序,但对于AppStore应用程序来说是不可能的,因为liblockdown是一个私有API。此外,欺骗者可以劫持liblockdown(这很容易,我希望没有人这么做),甚至替换lockdownd本身。

                   +-----------+
                   | your code |
                   +-----------+
                         |
+----------+       +-------------+       +-----------+
| UIDevice |<--.   | liblockdown |<=====>| lockdownd |   (trusted data)
+----------+   |   +-------------+       +-----------+
               |   +------------+-->| UDID Faker |
                   +------------+

(我不会在这里展示如何使用liblockdown。您应该能够从您提供的链接网站找到足够的信息。)


非常感谢您详细的答案!我部分使用了您的代码,但是出现了一些警告,也许您可以帮助我吗?我已经包含了 mach-o/dyld.h。该行 uikit_loc = _dyld_get_image_header(i) 引发了警告“语义问题:从 'const struct mach_header *' 分配给 'void ' 会放弃限定符”,以及if (funcptr < uikit_loc) : “语义问题:比较不同指针类型('IMP'(又名'id()(id,SEL,...)')和'void *')”。我该如何消除这些警告?谢谢 :) - Stefan

-3

关键是检测到JB设备并不在其上运行。


2
这有点过分,你这样就惩罚了所有越狱用户。并不是所有越狱用户都是盗版者,只需看看Cydia商店上付费应用和调整的成功就知道了。 - newenglander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接