组织| 郑丽媛
出品| CSDN(ID:CSDNnews)
素有“围墙花园”之称的苹果,一直以“安全”为由,恪守各种在外界看来十分霸道的规则:为了安全,严禁第三方更换产品电池; 为了安全,他们不同意开设第三方应用商店; 为了安全起见,不允许“侧面加载”应用程序......
一切为了安全,一切为了用户。 既然如此,为什么AppStore中会有这么多恶意软件呢? 为什么最近有安全研究人员声称苹果一直忽视iOS15中仍然存在的三个零日漏洞?
对此,开发者 Denis Tokarev 否认了 AppStore 安全的说法,并详细介绍了恶意软件进入 AppStore 的过程。 他坦言:“苹果不允许任何AppStore替代品的真正原因是,他们可以从所有应用内购买中抽取30%的佣金,这对他们来说是一项非常赚钱的生意。”
被敷衍的苹果羞辱
丹尼斯·托卡列夫之所以突然如此悲伤,是因为他去年3月至5月先后向苹果报告了iOS中的四个零日漏洞。 苹果公司曾向他承诺,会将这个漏洞添加到安全内容列表中,但他错过了这个约会。
在这四个漏洞中,苹果只是在iOS14.7更新中悄悄修补了其中一个漏洞,但并未将其添加到安全内容列表中。 至于其他三个零日漏洞,直到iOS15仍然存在。
苹果的做法让丹尼斯·托卡列夫非常不满。 他认为,除了敷衍地检测系统漏洞外,苹果还隐瞒了其在iOS14中修补漏洞的行为。
丹尼斯·托卡列夫试图就这个问题与苹果沟通,但对方从未回应。 他一怒之下写道,这三个漏洞分别名为Gamed、Nehelperinstalledapps和Nehelperwifiinfo,iOS14.7中修复的漏洞为Analyticsd。
在文章引发公众关注后,苹果终于对丹尼斯·托卡列夫做出了回应:
“我们已经听取了您的博客文章和有关此问题的其他报告。我们感谢您延迟回复您,但我们仍在调查并找出如何解决此问题以保护客户。再次感谢您花时间向我们报告此问题,我们感谢您的帮助。如果您有疑问,请告诉我们。”
然而,为时已晚。 丹尼斯·托卡列夫再次斥责苹果近乎“被迫”的应对心态,并为一些人指责恶意代码是否真的能进入AppStore感到难过:“他们如此怀疑我是可以理解的,因为苹果总是一遍又一遍地重复,让人们相信AppStore是安全的。”
为此,Denis Tokarev再次刷屏,详细解释了恶意软件如何通过初审进入AppStore(包括源代码)。
只需混淆函数名称的字符串即可
首先,Denis Tokarev 强调,当应用程序的二进制补码文件上传到苹果服务器时,将会对其进行静态分析。 但显然这个过程不仅仅只是根据预定义的私有API集合(这个API只允许Apple自己的App使用)来检测补码文件中的字符串列表,而且还没有做太多其他工作。
如果在应用程序中测量私有API的使用,Apple将不会上传应用程序的二进制补码文件并向应用程序作者发送短信:
“我们在您最近交付的应用 [APP_NAME_AND_VERSON] 中发现一个或多个问题,请更正以下问题并重新上传。
ITMS-90338:使用非公共 API - 应用程序包含或继承来自 [APP_NAME] 的非公共类:GKFamiliarPlayerInternal、GKFriendPlayerInternal、GKLocalPlayerInternal。 如果您的源代码中的方法名称与上面列出的私有Apple API一致,请修改您的方法名称,以防止该应用程序在以后的提交中被标记。 据悉,如果您的App自带的静态库中也存在上述一个或多个API检测网站漏洞软件,则必须将其删除。 欲了解更多信息,请访问。 ”
遇到这些情况该怎么办? DenisTokarev给出了两种方式:
比如,首先找到一个类GKLocalPlayerInternal(这是Gamed漏洞中会用到的类),比如NSClassFromString("GKLocalPlayerInternal")。 由于GKLocalPlayerInternal是私有API检测网站漏洞软件,因此在解析补码文件时会发现它。 此外,开发人员可以通过多种方式隐藏它。 比如GKLocalPlayerInternal可以简单的分成几个部分:NSClassFromString(["GKLoc","lPlayerInternal"].joined(separator: "a")),这样就不会被静态分析检查到。
Gamed漏洞通过这些方式混淆了所有私有API的使用,因此在接受Apple静态分析时并未发现。
Denis Tokarev 表示,他发现 AppStore 上一款下载量数亿的 App 使用了这些技术。 该应用程序支持iOS9,因此开发人员必须使用私有API来修复UIKit错误并改善那些难以安装最新iOS版本的用户的体验(苹果认为某些设备已经过时,因此放弃了对它们的支持)。
举个反例,从 iOS7 开始,苹果就对 App 图标使用了特殊的圆角设计,并用 App 中的所有 UI 组件(包括按钮和提醒)覆盖了这些设计。 而且,这些圆角设计在iOS13中只提供给第三方开发者。 与此同时,苹果开始为自己的App和系统组件调用iOS11中CALayer类的私有方法setContinouslyCorners。 因此,如果应用程序开发者希望旧iOS版本的用户体验到一致的应用程序界面,则必须使用私有API,这违反了AppStore规则。
不仅是Gamed漏洞,Denis Tokarev发现的其他三个漏洞(Analyticsd、Nehelperinstalledapps和Nehelperwifiinfo)都使用了Apple认为属于私有API一部分的C函数,并且他已经更新了这三个漏洞的源代码,以便它们可以动态调用这些函数并逃避Apple的静态分析。
实际流程及代码
丹尼斯·托卡列夫(Denis Tokarev)通过理论分享了如何通过凯撒密码来实现这一过程。
首先,dlopen和dlsym函数可以加载动态库并解析其中的符号,但考虑到这可能是由AppStore初审小组衡量的,开发者可以“曲线救国”:每个iOS补码文件都会导出一个名为dyld_stub_binder的符号,它与dlopen和dlsym源自同一个库——也就是说,开发者可以估计dlopen和dlsym函数与dyld_stub_binder的距离显存中的binder,然后只使用这个函数地址来调用它们。
需要注意的一点是,Denis Tokarev 表示,这个距离,即倾斜量,会根据 iOS 版本和设备型号这两个参数而变化。 他在Github上分享的三个漏洞的源代码中的偏差量是iPhone7Plus和iOS15.0对应的值,因此如果与开发者的实际设备不符,则需要重新估算。
以下是估计倾斜量的方法:
printf("%lld\n",(long long)dyld_stub_binder - (long long)dlopen);
printf("%lld\n",(long long)dyld_stub_binder - (long long)dlsym);
当倾斜量可用时,开发人员可以通过定义自己的函数来调用 dlopen 和 dlsym:
// dlopen
void * normal_function1(const char * arg1, int arg2) {
return ((void *(*)(const char *, int))((long long)dyld_stub_binder - 20780))(arg1, arg2);
}
// dlsym
void * normal_function2(void * arg1, const char * arg2) {
return ((void *(*)(void *, const char *))((long long)dyld_stub_binder - 20648))(arg1, arg2);
}
在Swift上导出后,开发者可以重新绘制检测App是否已安装的代码,而无需导出和引用任何符号(不仅是二补文件默认导出的dyld_stub_binder):
let dylib = normal_function1("/usr/lib/system/libxpc.dylib", 0)
let normalFunction3 = unsafeBitCast(normal_function2(dylib, "xpc_connection_create_mach_service"), to: (@convention(c) (UnsafePointer
, DispatchQueue?, UInt64) -> (OpaquePointer)).self) let normalFunction4 = unsafeBitCast(normal_function2(dylib, "xpc_connection_set_event_handler"), to: (@convention(c) (OpaquePointer, @escaping (OpaquePointer) -> Void) -> Void).self)
let normalFunction5 = unsafeBitCast(normal_function2(dylib, "xpc_connection_resume"), to: (@convention(c) (OpaquePointer) -> Void).self)
let normalFunction6 = unsafeBitCast(normal_function2(dylib, "xpc_dictionary_create"), to: (@convention(c) (OpaquePointer?, OpaquePointer?, Int) -> OpaquePointer).self)
let normalFunction7 = unsafeBitCast(normal_function2(dylib, "xpc_dictionary_set_uint64"), to: (@convention(c) (OpaquePointer, UnsafePointer
, UInt64) -> Void).self) let normalFunction8 = unsafeBitCast(normal_function2(dylib, "xpc_dictionary_set_string"), to: (@convention(c) (OpaquePointer, UnsafePointer
, UnsafePointer ) -> Void).self) let normalFunction9 = unsafeBitCast(normal_function2(dylib, "xpc_connection_send_message_with_reply_sync"), to: (@convention(c) (OpaquePointer, OpaquePointer) -> OpaquePointer).self)
let normalFunction10 = unsafeBitCast(normal_function2(dylib, "xpc_dictionary_get_value"), to: (@convention(c) (OpaquePointer, UnsafePointer
) -> OpaquePointer?).self)
func isAppInstalled(bundleId: String) -> Bool {
let connection = normalFunction3("com.apple.nehelper", nil, 2)
normalFunction4(connection, { _ in })
normalFunction5(connection)
let xdict = normalFunction6(nil, nil, 0)
normalFunction7(xdict, "delegate-class-id", 1)
normalFunction7(xdict, "cache-command", 3)
normalFunction8(xdict, "cache-signing-identifier", bundleId)
let reply = normalFunction9(connection, xdict)
if let resultData = normalFunction10(reply, "result-data"), normalFunction10(resultData, "cache-app-uuid") != nil {
return true
}
return false
}
总之,这一切的目的都是为了不通过静态分析来衡量,所以使用凯撒密码对包含函数名的字符串进行混淆,或者像第一种方式一样将其分割成几段。
丹尼斯·托卡列夫对这两种方式非常有信心:“如果苹果敢说在审核过程中也能发现这一点,那么我会想出不同的方式来处理并全部发布。”
高度主观的初步审查过程
“欺骗”静态分析后,应用程序将进入AppStore的初步审核流程,丹尼斯·托卡列夫觉得这个过程非常主观:基本上,随机分配的测试人员将应用程序下载到他的iPad上,然后全屏点击,然后根据自己对AppStore规则的理解和主观意见做出是否允许上架的决定。
然而,这个过程有明显的缺点:通常,恶意软件会连接到远程服务器,发送有关当前用户会话的详细信息,并询问是否执行单独的恶意操作。 Apple服务器仅检查Apple测试人员或普通用户是否正在使用该App,并据此发送响应。 这意味着最初的审查者只会看到一个看起来“不错”的恶意应用程序,而不会发现任何可疑的东西,并允许其进入AppStore。
为此,那些年AppStore中的恶意软件一直层出不穷,而苹果总是在被别人发现后将此类应用下架,甚至在个别情况下对举报者视而不见:开发者Kosta Eleftheriou曾明确指出,AppStore中存在许多带有虚假评论的盗版应用。 但最终,苹果没有听取 KostaEleftheriou 的建议,在 Denis Tokarev 看来,这是因为苹果也从这款盗版应用中受益:昂贵的订阅费减少 30%。
此外,丹尼斯·托卡列夫还引用了他的一些个人经历来证明AppStore的初步审核是多么“主观和随意”:
为此,丹尼斯·托卡列夫表示,苹果的行为完全是反竞争的,而且还包含着歧视部分开发者的意思。 即使苹果最近因日本的集体诉讼而允许第三方支付应用程序,但这还不够。
他主张人们必须向苹果施压,要求其开放平台,允许AppStore替代和侧载,让开发者得到公平对待:“面对压迫和不公正,我们必须站在一起,为自由而战!”
那么,您对丹尼斯·托卡列夫的实践和倡议有何看法?
参考链接:
10月23日-24日,前往南京参加1024程序员节,展示了一线技术专家和议程,与众多开发者同事交流合作,庆祝了程序员自己的春节。 会议嘉宾包括倪光南教授、龚克教授、王怀民教授、MySQL之父、Kubernetes联合创始人、RISC-V国际基金会CTO、PostgreSQL全球发展集团联合创始人、MongoDBCTO……
portant;overflow-wrap: break-word !important;">portant;overflow-wrap: break-word !important;">inktype="2" style="outline: 0px;color: rgb(0, 122, 170);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">☞inktype="2">阿里旗下App接入微信支付;马斯克成世界首富;PostgreSQL 14 RC 1发布|极客头条
inktype="2" style="outline: 0px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">☞inktype="2">干掉 Android 2.3!