[macOS翻译]如何在 Mach-O 应用程序中注入代码。第三部分

729 阅读11分钟

本文由 简悦 SimpRead转码, 原文地址 jon-gabilondo-angulo-7635.medium.com

动态代码注入技术。摘要。

动态代码注入技术。摘要。

这是动态注入系列的最后一部分,旨在学习如何在已编译的 Mac OS X 应用程序中注入动态链接库。

这项任务的主要动机是能够注入 Organismo iOS/Mac 仪器库。Organismo 是编程学生和用户界面爱好者学习应用程序内部结构的好工具。在学习过程中,我们还能了解到苹果公司的一些优秀安全机制,它们能让我们的电脑更加安全。

本文的测试在 Monterey 12.5 中进行。

github.com/JonGabilond…

Dynamic Code Injection. The Dynamic Loader — dyld.

在本系列中,我们只关注 "动态代码注入 "技术,即应用程序已经构建和编译,我们希望在运行时将二进制库或框架加载到应用程序中,以便对其执行某些检测。

在 OS X Mountain Lion(10.8,2012 年 7 月 25 日)之前,在启动时将库加载到应用程序非常简单。动态加载器过去和现在都被编程为读取环境 DYLD_INSERT_LIBRARIES,并加载分配给它的路径中定义的库。从 Mountain Lion 开始,"加固应用程序 "和 System Integrity Protection(SIP)安全措施使 dyld(动态加载器或加载器) 忽略 DYLD_INSERT_LIBRARIES,更具体地说是忽略所有 DYLD_ 前缀的环境变量。这在 第一部分第二部分 中有详细介绍。对于程序员来说,这是 Apple 的文档: developer.apple.com/documentati…

我们在第一部分和第二部分中看到,有些应用程序(如 Calculator.app)仍未加固,DYLD_INSERT_LIBRARIES 仍在工作。对于那些已加固的应用程序,我们通过重新签署临时或专有的合法开发者证书来取消加固,但这并不是一个万能的解决方案。苹果公司有一个强大的安全机制,即 "权限"(entitlements)的概念,它定义了一个应用程序可以做什么,而这些权限是通过 "Provision Profiles "授予开发者的,开发者必须遵守严格的规则才能通过开发者门户网站获得这些权限。除此之外,苹果公司还拥有数百种 私人权限,只有苹果公司才能设置自己的代码,除了经过苹果公司授权签名的代码外,其他代码都无法运行。

所有这些都会导致使用开发者或分发者证书重新签名的应用程序最终要么会破坏签名并被 amfid 禁止,要么会在启动后很快崩溃。

第二部分最后提出了一个更成功的建议,即禁用 SIP 和 AMFI。这确实会增加成功几率,但并不推荐使用这种方法,因为它会让你的电脑变成十年前的 Mac,而且非常容易受到攻击。

Injecting Code with SIP and AMFI Enabled

在动态代码注入系列的最后一部分,我们将在 Mac OS X 的默认最高安全级别下探索代码注入的极限。在上一部分中,我们建议禁用 SIP 和 AMFI,但这一次我们希望保留它们,并有机会进一步了解 OS X 的安全层。

AMFI 是苹果移动文件完整性。顾名思义,它起源于 iOS,负责防止运行未经适当签名或授权的代码。它由两个部分组成:

  1. amfid 是 Apple Mobile File Integrity 的守护进程,位于 /System/Library/LaunchDaemons/com.apple.MobileFileIntegrity.plist,可启动 /usr/libexec/amfi
  2. 它的客户端是 /System/Library/Extension 中的AppleMobileFileIntegrity扩展。AMFI 扩展会从 NVRAM 中加载受信任的密钥,同时还与无根控制器和 DataVault 控制器有关。amfid 通过调用 trustd 进行检查,从而检查系统上运行的文件的完整性。trustd 是一个启动代理守护进程,为评估所有进程的证书信任度提供服务,包括静态代码检查。

欲了解更多信息,请阅读一直很棒的 eclecticlight.co

Finding Hardened Apps

至于 dyld 是否会注入 DYLD_INSERT_LIBRARIES 中定义的库,这不是一个全系统配置决策,而是每个应用程序的决策。dyld 是一个链接到应用程序并在运行时加载的库。dyld 会对负责加载的应用程序的设置做出反应。

LC_LOAD_DYLINKER 加载命令包含要使用的 dyld 的路径: /usr/lib/dyld.

image.png 指定要使用的动态连接器的命令。(国际象棋应用程序)

在查找这方面的信息时,通常会发现检测应用程序是否加固的常用方法是使用 "codesign "命令并检查 flags 值。

Calculator.app 的 flags=(none) 表示该应用程序未加固。

$ codesign -dvvv /System/Applications/Calculator.app
Executable=/System/Applications/Calculator.app/Contents/MacOS/Calculator
Identifier=com.apple.calculator
Format=app bundle with Mach-O universal (x86_64 arm64e)
CodeDirectory v=20400 size=2125 flags=0x0(none) hashes=56+7 

我们还可以继续往下看,所有苹果应用程序似乎都没有签名标志......Automator、Calendar、TextEdit、FaceTime、Mail、Console、Digital Color Meter

根据 "codesign "手册,我们期待 "runtime "标志:

OPTION FLAGS

runtime On macOS versions >= 10.14.0, opts signed processes into a hardened runtime environment which includes runtime code signing enforcement, library validation, hard, kill, and debugging restrictions. These restrictions can be selectively relaxed via entitlements. Note: macOS versions older than 10.14.0 ignore the presence of this flag in the code signature.

在非苹果应用程序中搜索该标志,我们确实发现了 "运行时 "标志:

$ codesign -dvvv /Applications/WhatsApp.app
Executable=/Applications/WhatsApp.app/Contents/MacOS/WhatsApp
Identifier=desktop.WhatsApp
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=1820 flags=0x10000(runtime) hashes=46+7 location=embedded
.....
$ codesign -dvvv /Applications/SourceTree.app
Executable=/Applications/SourceTree.app/Contents/MacOS/Sourcetree
Identifier=com.torusknot.SourceTreeNotMAS
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=32138 flags=0x10000(runtime) hashes=995+5 location=embedded

代码设计标志并不总能说明应用程序是否经过加固

通过这些测试,我认为苹果应用程序没有 "运行时 "标志,而第三方(开发者)应用程序有。

这将使事先了解应用程序是否加固以及我们是否可以注入 Organismo 变得更加复杂。

根据我的实验,Calculator 没有加固,但 Digital Color Meter 加固了。那么问题来了,到底是什么让 dyld 读取 DYLD_INSERT_LIBRARIES 呢?

答案就在第二部分的文章中,权利可以加固应用程序。

我们可以从 dyld 的源代码中看到它是如何解析和删除与 DYLD_* 和 LD_LIBRARY_PATH 匹配的变量的,更有趣的是,我们可以看到限制的三个原因:

///
// For security, setuid programs ignore DYLD_* environment variables.
// Additionally, the DYLD_* enviroment variables are removed
// from the environment, so that any child processes don't see them.
//
static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
{
    // delete all DYLD_* and LD_LIBRARY_PATH environment variables
    int removedCount = 0;
    const char** d = envp;
    for(const char** s = envp; *s != NULL; s++) {
        if ( (strncmp(*s, "DYLD_", 5) != 0) && (strncmp(*s, "LD_LIBRARY_PATH=", 16) != 0) ) {
            *d++ = *s;
        }
        else {
            ++removedCount;
        }
    }
    *d++ = NULL;
    // <rdar://11894054> Disable warnings about DYLD_ env vars being ignored.  The warnings are causing too much confusion.
#if 0
    if ( removedCount != 0 ) {
        dyld::log("dyld: DYLD_ environment variables being ignored because ");
        switch (sRestrictedReason) {
            case restrictedNot:
                break;
            case restrictedBySetGUid:
                dyld::log("main executable (%s) is setuid or setgid\n", sExecPath);
                break;
            case restrictedBySegment:
                dyld::log("main executable (%s) has __RESTRICT/__restrict section\n", sExecPath);
                break;
            case restrictedByEntitlements:
                dyld::log("main executable (%s) is code signed with entitlements\n", sExecPath);
                break;
        }
    }
#endif
...

以上是限制的三个原因:

  1. setuid setgid。这些标志可以分配给应用程序,使其在运行时拥有所属用户或组的权限,即不在当前用户的上下文中运行。任何用户都可以为自己的应用程序指定 setuid 或 setgid 标志,而不必在 sudoers 文件中创建一个条目(必须由 root 用户创建)。通过 ls -l 查看文件属性时,这些位将用 "s "代替 "x "来表示。chmod "程序可以通过位屏蔽("chmod 4777 [file]")或速记命名("chmod u+s [file]")设置这些位。这是一个可被利用的漏洞。
  2. ___RESTRICT段。这是 Mach-O 文件中的一个段,可以在链接时创建。不需要具体内容。其作用类似于强化进程的标志。
  3. 权限。在应用程序的代码签名过程中,权限标志可定义应用程序的加固。可通过 Xcode 启用运行时加固来配置权限:

虽然大多数小型实用程序都接受 DYLD_INSERT_LIBRARIES,我们也可以注入 Organismo,但 Digital Color Meter 的情况并非如此。

这是注入 Organismo 的命令:

$ DYLD_INSERT_LIBRARIES=/path_to/Organismo-mac.framework/Versions/A/Organismo-mac /path_to/Digital\ Color\ Meter.app/Contents/MacOS/Digital\ Color\ Meter

The Digital Color Meter entitlements:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.private.tcc.allow</key>
    <array>
        <string>kTCCServiceScreenCapture</string>
    </array>
</dict>
</plist>

com.apple.private.tcc.allow权限仅适用于 Apple,可设置绕过 TCC 的权限,在kTCCServiceScreenCapture的情况下,二进制文件将被授权在未经用户同意的情况下执行屏幕录制。

看来,TCC 权限的存在会使应用程序变硬/受限。

解决方案。删除应用程序的签名

为了使 Digital Color Meter 可注入,我们可以使用 Dev Certificate 对应用程序进行注销,但我们不能分配 com.apple.private.tcc.allow 权限,因此我们也可以删除签名和权限,将其变成未签名的应用程序,即非受限和非加固的应用程序。

这是删除应用程序签名的命令:

$ codesign --remove-signature /aWorkingFolder/Digital\ Color\ Meter.app
# or using macho_inject
$ macho_inject --remove-signature -o ~/ModifiedApps /Applications/Digital\ Color\ Meter.app

移除签名的后果是,允许屏幕捕获的 TCC 权限将丢失 (com.apple.private.tcc.allow - kTCCServiceScreenCapture)

当我们修改后的 Digital Color Meter 启动并尝试屏幕捕捉时,OS X 会阻止该操作,因为二进制文件不具备该权限,因此它会询问用户是否允许应用程序记录计算机屏幕。

image.png OS X 提示用户是否接受 Digital Color Meter 获取屏幕截图的对话框。

现在,我们可以让修改后的 Digital Color Meter 记录屏幕了。请参阅下面的 安全与隐私 设置。

image.png 屏幕录制的安全和隐私设置。

现在 Digital Color Meter 可以注射。请参阅 Digital Color Meter 应用程序中的 Organismo Inpector

image.png 显示数字色度计内部的 Organismo 仪器。

Advanced Apple Apps Can’t be Injected

在启用 SIP AMFI 的情况下,大多数高级应用程序(如 Stocks)都无法注入。它所拥有的私人权限数量将使其无法实现。无论是移除签名还是使用我们的 Dev 证书 重签名,我们都无法启用它所需的功能。

下面是 Stocks.app 的权限示例,其中大部分只能在苹果签名的二进制文件中使用:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aps-environment</key>
    <string>production</string>
    <key>fairplay-client</key>
    <string>1417937365</string>
    <key>com.apple.itunesstored.private</key>
    <true/>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.authkit.client.private</key>
    <true/>
    <key>com.apple.private.swc.system-app</key>
    <true/>
    <key>com.apple.proactive.eventtracker</key>
    <true/>
    <key>com.apple.private.iad.news-client</key>
    <true/>
    <key>com.apple.private.iosmac.unscaled</key>
    <true/>
    <key>com.apple.security.network.client</key>
    <true/>
    <key>com.apple.private.cloudkit.spi</key>
    <array>
        <string>true</string>
    </array>
    <key>com.apple.private.canGetAppLinkInfo</key>
    <true/>
    <key>com.apple.coretelephony.Identity.get</key>
    <true/>
    <key>com.apple.locationd.effective_bundle</key>
    <true/>
    <key>com.apple.private.applemediaservices</key>
    <true/>
    <key>com.apple.private.appstorecomponents</key>
    <true/>
    <key>com.apple.private.associated-domains</key>
    <true/>
    <key>com.apple.private.iad.opt-in-control</key>
    <true/>
    <key>com.apple.CommCenter.fine-grained</key>
    <array>
        <string>spi</string>
    </array>
    <key>com.apple.companionappd.connect.allow</key>
    <true/>
    <key>com.apple.developer.associated-domains</key>
    <array>
    </array>
    <key>com.apple.private.appstored</key>
    <array>
        <string>IAPHistory</string>
    </array>
    <key>com.apple.private.accounts.allaccounts</key>
    <true/>
    <key>com.apple.private.xpc.domain-extension</key>
    <true/>
    <key>com.apple.springboard.opensensitiveurl</key>
    <true/>
    <key>com.apple.frontboard.launchapplications</key>
    <true/>
    <key>com.apple.mediaremote.set-playback-state</key>
    <true/>
    <key>com.apple.private.cloudkit.systemService</key>
    <true/>
    <key>com.apple.private.cloudkit.setEnvironment</key>
    <true/>
    <key>com.apple.private.network.socket-delegate</key>
    <true/>
    <key>com.apple.private.security.storage.Stocks</key>
    <true/>
    <key>com.apple.accounts.appleaccount.fullaccess</key>
    <true/>
    <key>com.apple.private.cfnetwork.har-capture-amp</key>
    <true/>
    <key>com.apple.developer.icloud-services</key>
    <array>
        <string>CloudKit</string>
    </array>
    <key>com.apple.private.canModifyAppLinkPermissions</key>
    <true/>
    <key>com.apple.private.security.container-required</key>
    <true/>
    <key>com.apple.private.security.system-application</key>
    <true/>
    <key>com.apple.private.coreservices.canopenactivity</key>
    <true/>
    <key>com.apple.coretelephony.CTCarrierSettings.allow</key>
    <true/>
    <key>com.apple.private.coreservices.canmaplsdatabase</key>
    <true/>
    <key>application-identifier</key>
    <string>ZL6BUSYGB3.com.apple.stocks</string>
    <key>com.apple.private.networkextension.configuration</key>
    <true/>
    <key>com.apple.springboard.allowIconVisibilityChanges</key>
    <true/>
    <key>com.apple.security.personal-information.calendars</key>
    <true/>
    <key>com.apple.private.tcc.allow</key>
    <array>
        <string>kTCCServiceAddressBook</string>
    </array>
    <key>com.apple.security.personal-information.addressbook</key>
    <true/>
    <key>com.apple.private.hid.client.event-dispatch.internal</key>
    <true/>
    <key>com.apple.private.mobileinstall.xpc-services-enabled</key>
    <true/>
    <key>com.apple.notificationcenter.widgetcontrollerhascontent</key>
    <true/>
    <key>com.apple.developer.icloud-container-environment</key>
    <string>Production</string>
    <key>com.apple.application-identifier</key>
    <string>ZL6BUSYGB3.com.apple.stocks</string>
    <key>com.apple.developer.ubiquity-kvstore-identifier</key>
    <string>com.apple.stocks</string>
    <key>com.apple.private.appleaccount.app-hidden-from-icloud-settings</key>
    <true/>
    <key>com.apple.springboard.hardware-button-service.event-consumption</key>
    <true/>
    <key>com.apple.telephonyutilities.callservicesd</key>
    <array>
        <string>access-call-providers</string>
    </array>
    <key>com.apple.private.MobileGestalt.AllowedProtectedKeys</key>
    <array>
        <string>UniqueDeviceID</string>
    </array>
    <key>com.apple.private.DistributedEvaluation.RecordAccess-com.apple.stocks.des</key>
    <true/>
    <key>com.apple.security.exception.shared-preference.read-only</key>
    <array>
        <string>com.apple.AdPlatforms</string>
    </array>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.apple.stocks</string>
        <string>group.com.apple.stocks-news</string>
    </array>
    <key>com.apple.security.exception.files.home-relative-path.read-write</key>
    <array>
        <string>/Library/Caches/com.apple.AppleMediaServices/</string>
        <string>/Library/News/</string>
    </array>
    <key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
    <array>
        <string>/Library/Caches/com.apple.AppleMediaServices/</string>
        <string>/Library/News/</string>
    </array>
    <key>com.apple.security.exception.shared-preference.read-write</key>
    <array>
        <string>com.apple.AdPlatforms</string>
        <string>com.apple.newscore</string>
        <string>com.apple.newscore2</string>
        <string>com.apple.stocks.account</string>
        <string>com.apple.stocks.iAd</string>
    </array>
    <key>com.apple.security.temporary-exception.shared-preference.read-write</key>
    <array>
        <string>com.apple.newscore</string>
        <string>com.apple.newscore2</string>
        <string>com.apple.stocks.account</string>
        <string>com.apple.stocks.iAd</string>
        <string>com.apple.AppStoreComponents</string>
        <string>com.apple.AdPlatforms</string>
    </array>
    <key>com.apple.private.cloudkit.serviceNameForContainerMap</key>
    <dict>
        <key>com.apple.stocks.private</key>
        <string>com.apple.stocks.private</string>
        <key>com.apple.news.private.secure</key>
        <string>com.apple.news.private.secure2</string>
        <key>com.apple.news.private.secure.qa</key>
        <string>com.apple.news.private.secure2</string>
        <key>com.apple.news.private.secure.staging</key>
        <string>com.apple.news.private.secure2=</string>
    </dict>
    <key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
    <array>
        <string>com.apple.commerce</string>
        <string>com.apple.siri-distributed-evaluation</string>
        <string>com.apple.appstored.xpc</string>
        <string>com.apple.appstored.xpc.request</string>
        <string>com.apple.ap.adprivacyd.opt-out</string>
        <string>com.apple.appstoreagent.xpc</string>
        <string>com.apple.companionappd</string>
        <string>com.apple.ap.adservicesd.server</string>
        <string>com.apple.ap.adprivacyd.attribution</string>
        <string>com.apple.ap.promotedcontent.pcinterface=</string>
        <string>com.apple.ap.promotedcontent.mescalinterface</string>
        <string>com.apple.ap.promotedcontent.metrics</string>
        <string>com.apple.ak.anisette.xpc</string>
        <string>com.apple.newsd.analytics</string>
    </array>
    <key>com.apple.security.exception.mach-lookup.global-name</key>
    <array>
        <string>com.apple.commerce</string>
        <string>com.apple.siri-distributed-evaluation</string>
        <string>com.apple.appstored.xpc</string>
        <string>com.apple.appstored.xpc.request</string>
        <string>com.apple.appstoreagent.xpc</string>
        <string>com.apple.companionappd</string>
        <string>com.apple.ap.adservicesd.server</string>
        <string>com.apple.ap.adprivacyd.attribution</string>
        <string>com.apple.ap.adprivacyd.opt-out</string>
        <string>com.apple.ak.anisette.xpc</string>
        <string>com.apple.newsd.analytics</string>
        <string>com.apple.ap.promotedcontent.pcinterface</string>
        <string>com.apple.ap.promotedcontent.metrics</string>
        <string>com.apple.ap.promotedcontent.mescalinterface</string>
        <string>com.apple.ap.promotedcontent.supportinterface</string>
        <string>com.apple.appstorecomponentsd.xpc</string>
    </array>
    <key>com.apple.developer.icloud-container-identifiers</key>
    <array>
        <string>com.apple.news.public</string>
        <string>com.apple.news.private</string>
        <string>com.apple.news.private.secure</string>
        <string>com.apple.news.public.staging</string>
        <string>com.apple.news.private.staging</string>
        <string>com.apple.news.private.secure.staging</string>
        <string>com.apple.news.public.qa</string>
        <string>com.apple.news.private.qa</string>
        <string>com.apple.news.private.secure.qa</string>
        <string>com.apple.news.public.sandbox</string>
        <string>com.apple.news.private.sandbox</string>
        <string>com.apple.news.private.secure.sandbox</string>
        <string>com.apple.news.public.demo1</string>
        <string>com.apple.news.private.demo1</string>
        <string>com.apple.news.private.secure.demo1</string>
        <string>com.apple.news.public.demo2</string>
        <string>com.apple.news.private.demo2</string>
        <string>com.apple.news.private.secure.demo2</string>
        <string>com.apple.stocks.private</string>
    </array>
</dict>
</plist>

当开发人员希望发布应用程序时,应将其标记为加固,这样可以大大降低应用程序的易受攻击程度。这可以通过在 Xcode 中添加 "加固运行时 "功能来实现(签名和功能部分)。

"运行时异常 "会放宽加固后的一些安全功能。正如我们前面所看到的,使用 Hardened Runtime 签名的应用程序将具有 "runtime "签名标志。

image.png Xcode 中的运行时异常。

这些是 Runtime Exceptions 的权限。它们将被插入我们二进制文件的签名中:

com.apple.security.cs.allow-dyld-environment-variables
com.apple.security.cs.disable-library-validation
com.apple.security.cs.disable-executable-page-protection
com.apple.security.cs.disable-executable-page-protection

这是必读的 Apple 文档,载于 Hardened Runtime。管理 macOS 应用程序的安全保护和资源访问。

1. 允许 DYLD 环境变量权限

一个布尔值,表示应用程序是否会受到动态链接器环境变量的影响,您可以使用动态链接器环境变量向应用程序进程注入代码。

关键字: com.apple.security.cs.allow-dyld-environment-variables 1.

2. 禁用库验证权限

布尔值,表示应用程序是否加载任意插件或框架,而无需代码签名。

关键字: com.apple.security.cs.disable-library-validation

3. 禁用可执行内存保护权限

一个布尔值,表示是否在启动应用程序和执行应用程序时禁用所有代码签名保护。

关键字: com.apple.security.cs.disable-executable-page-protection

4. 允许无符号可执行内存权限

布尔值,用于指示应用程序是否可以创建可写和可执行内存,而不受使用 MAP_JIT标志所施加的限制。

关键字: com.apple.security.cs.allow-unsigned-executable-memory

让我们选择一个非苹果应用程序进行注入实验: WhatsApp。 首先被正确签署为加固(runtime 标志):

$ codesign -dvvv /Applications/WhatsApp.app
Executable=/Applications/WhatsApp.app/Contents/MacOS/WhatsApp
Identifier=desktop.WhatsApp
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=1820 flags=0x10000(runtime) hashes=46+7 location=embedded

WhatsApp entitlements:

$ codesign -d -- entitlements :- /Applications/WhatsApp.app
Executable=/Applications/WhatsApp.app/Contents/MacOS/WhatsApp
<?xml version=”1.0" encoding=”UTF-8"?><!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “https://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version=”1.0"><dict><key>com.apple.security.device.usb</key><true/><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.device.camera</key><true/><key>com.apple.security.network.client</key><true/><key>com.apple.security.network.server</key><true/><key>com.apple.security.device.bluetooth</key><true/><key>com.apple.security.device.microphone</key><true/><key>com.apple.security.device.audio-input</key><true/><key>com.apple.developer.team-identifier</key><string>XXXXXXXXX</string><key>com.apple.security.files.downloads.read-write</key><true/><key>com.apple.security.files.user-selected.read-write</key><true/><key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/><key>com.apple.application-identifier</key><string>XXXXXXXX.desktop.WhatsApp</string><key>com.apple.security.application-groups</key><string>XXXXXXXX.desktop.WhatsApp</string></dict></plist>

这些权限对于任何开发人员来说都很熟悉,可以在 Xcode 中进行设置。我们可以看到,作为网络服务器和客户端等,它在沙盒中获得了一些访问摄像头的权限。

此外,它还有一个加固的放松机制,允许执行未签名代码: com.apple.security.cs.allow-unsigned-executable-memory

回顾我们的常规选择

这些是我们的常规选项:

  1. 删除所有签名。
  2. 重签名时不进行加固。使用我们的 Dev 证书签署,不带 "运行时 "选项。
  3. 使用加固功能辞职,并添加更多权限。使用我们的开发人员证书辞职,带有 "运行时 "选项,并添加允许注入的额外权限,即添加com.apple.security.cs.allow-dyld-environment-variables权限。

根据经验,我知道使用 Dev 证书 会使 amfi 根据验证安全权限的 provision profile 验证代码。这就是我们在退出应用程序后得到的信息:

Failure validating against provisioning profiles: No eligible provisioning profiles found during verification

实际上,最简单的方法就能得到最好的结果。

解决方案。删除签名。

WhatsApp 这个特殊的案例中,最简单的方法是删除签名,这样得到的修改后的 WhatsApp 没有任何限制,允许动态代码注入。

移除签名可以使用以下命令完成:

$ codesign --remove-signature pathtoApp

但是,该命令无法覆盖所有二进制文件。复杂的文件夹结构和二进制文件的分布使得删除 WhatsApp 中所有二进制文件中的所有签名是一项相当漫长的任务。使用 macho-inject这样的工具可以将复杂的代码设计简化为一条命令:

$ macho_inject --remove-signature -o /YourWorkingFolder/Out -i /pathToACopyOf/WhatsApp.app

生成的 WhatsApp 没有签名、权限和加固。以下命令将注入 _Organismo:

$ DYLD_INSERT_LIBRARIES=/pathTo/Organismo-mac.framework/Versions/A/Organismo-mac /pathToModified/WhatsApp.app/Contents/MacOS/WhatsApp

在这里,我们可以看到修改后的 WhatsAppOrganismo 检查器:

image.png WhatsApp 已注入 Organismo 检查器。

启用 SIP 和 AMFI 后,就不可能在已签名加固的应用程序中动态注入代码,即使尝试使用代码签名技术修改应用程序也非常困难。虽然一些简单的苹果应用程序仍未受到保护,但大多数重要的苹果应用程序都受到了保护。此外,只有苹果公司才能使用的特殊权限使苹果应用程序几乎不可能被篡改。

开发人员应用程序的情况则不同,主要有两个原因:

  1. 所有开发者都可以使用这些权限,通过编码和 provision profile 创建,可以用新的权限修改应用程序。
  2. 沙箱和加固很容易去除。如果在签名、沙箱和加固功能缺失时没有对应用程序进行编程,那么修改后的应用程序就可以在没有任何限制的情况下运行,就像我们在 WhatsApp 中看到的那样。

开发人员应始终对其应用程序进行加固,并考虑使用 运行时完整性检查 来增加保护措施,防止应用程序被冒充(修改 bundleid、供应配置文件、权限......)或签名被移除而遭到篡改。

Update ! All Apps Must be Signed on Apple Silicon

篡改 WhatsApp 的简单性令人担忧。本文的测试是在Monterey 12.5 英特尔版本中进行的。苹果公司已经意识到这一点,并正在努力使我们的计算机更加安全,因此,在苹果硅Mac上将无法进行上述篡改 WhatsApp 的操作,所有可执行文件都必须经过签名:

(developer.apple.com/documentati…)

"在苹果硅 Mac 电脑上的 macOS 11 中新增了一项新功能,从下一个 macOS Big Sur 11 测试版开始,操作系统将强制要求任何可执行文件在允许运行之前必须签署有效的签名。这种签名没有特定的身份要求:本地签发的简单临时签名就足够了,其中包括现在由链接器自动生成的签名。这一新行为并不改变我们的用户和开发人员可以在 Mac 上运行任意代码的长期既定政策,其目的是简化 Apple silicon Mac 电脑的执行政策,使系统能够更好地检测代码修改。 这项新政策不适用于在 Rosetta 下运行的已翻译 x86 二进制文件,也不适用于在英特尔平台上运行的 macOS 11。

谢谢!

jon-gabilondo-angulo-7635.medium.com/how-to-inje…

jon-gabilondo-angulo-7635.medium.com/how-to-inje…

github.com/JonGabilond…