|
马上注册,结交更多侠客,享用更多功能,让你轻松玩转侠外论坛。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
随着 iOS 逆向门槛越来越低,国内灰产也越来越多。自动抢红包则成为了 iOS 逆向入门级项目,GitHub 上也出现了技术水平参差不齐的微信插件的 Repo。而这些插件,往往都是基于一个多开微信。
写这篇文章最主要的目的还是提醒部分厂商,加强 App 安全防护意识,减少灰产的可乘之机。
在 iOS 设备上安装多个同一个 App 的方式只有一种,修改 Info.plist 中的 CFBundleIdentifier 。
在整个 iOS 生态中,Bundle Identifier 是作为一个 App 的唯一标识符存在。
对于拥有相同的 Bundle Identifier 的 App,无论 Binary 和资源文件都多大的差异ios多开,iOS 都会将它们视为同一个 App。
对于拥有不同的 Bundle Identifier 的 App,也无论 Binary 与和资源文件是否一致,iOS 会将它们视为不同 App。
而 Info.plist 文件,是整个 App 的信息、配置、权限的信息整合文件,其在 App 中起到至关重要的作用。
为了防止 Info.plist 被恶意篡改,iOS 提供一种数字签名技术。通过该技术,计算出 Info.plist 文件的 Hash 值,加密后存入到签名文件中。在安装时与安装后,可通过该签名文件存的 Hash 值进行文件签名校验。也因此,App 签名后无法修改 Info.plist 文件;而即使是已安装 App 的 Info.plist 文件,修改后也会导致 App 闪退。
所以可以得出结论,对于已安装的 App 的 Info.plist 文件的 CFBundleIdentifier 值不会被修改
通过上述结论,由于 Info.plist 文件的不可修改性质,我们可以在 App 运行时来读取 Info.plist 文件中的值来判断该值是否与出产时候相同,从而判断当前进程是否是一个多开 App。
Foundation.framework 提供了几种获取 App 的 Bundle Identifier 方法,基本如下:
NSBundle.mainBundle.bundleIdentifier;
[NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleIdentifier"];
NSBundle.mainBundle.infoDictionary[@"CFBundleIdentifier"];
[NSDictioanry dictionaryWithContentsOfFile:@"Info.plist"][@"CFBundleIdentifier"]
使用上面几种的任意一种,就可以获取到当前 App 的 Bundle Identifier 值,之后通过 -[NSString isEqualToString:] 方法来判断是否分身。
在已经知道如何检测多开的时候,就可以知道如何防止 App 检测多开了。
简单的来说,就是干掉上面的几个方法,强制返回一个原来的值即可。
具体思路是判断返回值是不是真实的 Bundle Identifier,如果是则返回原来的 Bundle Identifier。这样做的目的防止影响到别的对象以及别的 key 对应的值。
由于 NSDictionary 的特殊性质,通过 key 获取 value 的方式有多种,这几种方法也需要被干掉:
info[@"CFBundleIdentifier"]; // -[NSDictionary objectForKeyedSubscript:]
[info objectForKey:@"CFBundleIdentifier"];
[info valueForKey:@"CFBundleIdentifier"];
在此安利一个我自己写的比较轻量级的非越狱平台 hook 工具——MUHook。仿照 Captain Hook 写的宏集合,同时比 Captain Hook 更加方便快捷,也无需 CydiaSubstrate.dylib。(功能还在完善中)
以微信为例,代码如下:
/*
* 假设当前分身的 Bundle Identifier 值为 com.unique.xin
*/
#import
#define CFBundleIdentifier @"CFBundleIdentifier"
#define ORIG_VALUE @"com.tencent.xin"
#define REAL_VALUE @"com.unique.xin"
/*
* 这里干掉了 -[NSBundle bundleIdentifier] 方法
*/
MUHInstanceImplementation(NSBundle, bundleIdentifier, NSString *) {
NSString *orig = MUHOrig(NSBundle, bundleIdentifier);
if ([orig isEqualToString:REAL_VALUE]) {
orig = ORIG_VALUE;
}
return orig;
}
/*
* 这里干掉了 -[NSBundle objectForInfoDictionaryKey:] 方法
*
* MUHInstanceImplementation 注释:定义一个 Hook 的实现体
* 第一个参数是要 Hook 的类
* 第二个参数是自己取的方法名,会在下面的 MUHMain 和 MUHOrig 用到,与实际方法名可以不一致,但是要保证唯一性
* 第三个参数是返回值类型
* 第四个参数以及之后的参数是 Hook 方法的实际参数表。
*
* 如果你要 Hook 一个 Class Method 比如 +[UIImage imageNamed]
* 请使用 MUHClassImplementation,具体用法同上
*/
MUHInstanceImplementation(NSBundle, objForInfoKey, id, NSString *key) {
/*
* MUHOrig 注释:调用原方法
* 第一个参数是原方法的类
* 第二个参数是自己取的方法名
* 第三个参数以及之后的参数是传入的实际参数
*/
NSString *orig = MUHOrig(NSBundle, objForInfoKey, key);
if ([key isKindOfClass:[NSString class]]) {
if ([key isEqualToString:CFBundleIdentifier]) {
if ([orig isEqualToString:REAL_VALUE]) {
orig = ORIG_VALUE;
}
}
}
return orig;
}
MUHInstanceImplementation(NSBundle, infoDictionary, NSDictionary *) {
NSMutableDictionary *info = [MUHOrig(NSBundle, infoDictionary) mutableCopy];
if (self == NSBundle.mainBundle) {
info[CFBundleIdentifier] = REAL_VALUE;
}
return [info copy];
}
MUHInstanceImplementation(NSDictionary, objectForKey, id, NSString *key) {
id orig = MUHOrig(NSDictionary, objectForKey, key);
if ([key isKindOfClass:[NSString class]]) {
if ([key isEqualToString:CFBundleIdentifier]) {
if ([orig isEqualToString:REAL_VALUE]) {
id = ORIG_VALUE;
}
}
}
return orig
}
MUHInstanceImplementation(NSDictionary, valueForKey, id, NSString *key) {
id orig = MUHOrig(NSDictionary, valueForKey, key);
if ([key isKindOfClass:[NSString class]]) {
if ([key isEqualToString:CFBundleIdentifier]) {
if ([orig isEqualToString:REAL_VALUE]) {
id = ORIG_VALUE;
}
}
}
return orig
}
MUHInstanceImplementation(NSDictionary, objectForKeyedSubscript, id, NSString *key) {
id orig = MUHOrig(NSDictionary, objectForKeyedSubscript, key);
if ([key isKindOfClass:[NSString class]]) {
if ([key isEqualToString:CFBundleIdentifier]) {
if ([orig isEqualToString:REAL_VALUE]) {
id = ORIG_VALUE;
}
}
}
return orig
}
/*
* MUHMain 注释:定义一个拥有 constructor 属性的函数。
* 当 dyld 加载此二进制文件到内存中的时候会自动调用此函数,完成运行前的 hook 工作
*/
void MUHMain() {
/*
* MUHHookInstanceMessage 注释:让上面定义的某个 Instance Hook 实现体生效
* 第一个参数是上面实现体的类
* 第二个参数是上面实现体自己取的方法名
* 第三个参数是对应的 SEL
*
* 如果要让一个 Class Hook 生效,可以使用
* MUHHookClassMessage()
* 使用方式同上
*/
MUHHookInstanceMessage(NSBundle, bundleIdentifier, bundleIdentifier);
MUHHookInstanceMessage(NSBundle, infoDictionary, infoDictionary);
MUHHookInstanceMessage(NSBundle, objectForInfoDictionaryKey, objectForInfoDictionaryKey:);
MUHHookInstanceMessage(NSDictionary, objectForKey, objectForKey:);
MUHHookInstanceMessage(NSDictionary, valueForKey, valueForKey:);
MUHHookInstanceMessage(NSDictionary, objectForKeyedSubscript, objectForKeyedSubscript:);
}
以上内容就是[ios多开]iOS 多开检测,反多开检测,反反多开检测的相关内容介绍,喜欢侠外游戏论坛的朋友可以关注我们。
12下一页 |
|