Mobile wallpaper
1133 字
6 分钟

MacOS绕过SSL Pining问题

2026-01-20
浏览量 加载中...
NOTE

本文主要内容

主要是用 Frida 来绕过 MacOS 上的 SSL Pining 问题,方便调试一些应用。主要是做一个备忘录,方便以后查阅,并不涉及非常深入的用法。

最近在尝试逆向部分AI平台时,有些平台只提供了客户端版本,没有网页版本,就导致调试起来比较麻烦。然后发现有些应用启用了 SSL Pining,导致 ProxyMan 抓包时会报错 SSL Pinning Failed

Electron 应用绕过 SSL Pining#

  1. 首先需要先判断应用是否基于 Electron,可以通过查看应用包内是否有 Electron Framework 来判断。可以通过 ls /Applications/xxx.app/Contents/Frameworks来判断,出现 Electron Framework.framework 就说明是 Electron 应用。
  2. 先使用最简单的办法,直接二进制方式启动程序,然后添加 /Applications/xxx.app/Contents/MacOS/xxx --ignore-certificate-errors 参数,忽略证书错误。
    • 此方法针对的主要是 Electron 主进程绕过 SSL Pining 问题,渲染进程或者其他子进程此方法无效。
  3. 如果你发现是 Electron 的其他进程存在 SSL Pining 问题,可以使用 inspect 邪修办法尝试解决(不一定有效)。
    • 修改启动参数为:/Applications/xxx.app/Contents/MacOS/xxx --inspect --ignore-certificate-errors
    • Chrome 访问 chrome://inspect,此时应该会出现一个设备,如下图,点击 inspect 即可:
      CleanShot 2026-01-20 at 17.40.26@2x
    • 出现DevTools后,打开 Console,然后输入以下代码:
    const tls = require('tls')
    const https = require('https')
    // 保存原始函数
    const originalConnect = tls.connect
    const originalRequest = https.request
    const originalGet = https.get
    // Hook tls.connect
    tls.connect = function(...args) {
    let options = args[0]
    if (typeof options === 'object') {
    options.rejectUnauthorized = false
    options.checkServerIdentity = () => undefined
    }
    console.log('🔓 TLS connect intercepted:', options?.host || options?.servername)
    return originalConnect.apply(this, args)
    }
    // Hook https.request
    https.request = function(url, options, callback) {
    if (typeof url === 'string') {
    if (typeof options === 'object') {
    options.rejectUnauthorized = false
    } else {
    options = { rejectUnauthorized: false }
    }
    } else if (typeof url === 'object') {
    url.rejectUnauthorized = false
    }
    console.log('🔓 HTTPS request intercepted')
    return originalRequest.apply(this, arguments)
    }
    // Hook https.get
    https.get = function(url, options, callback) {
    if (typeof url === 'string') {
    if (typeof options === 'object') {
    options.rejectUnauthorized = false
    } else {
    options = { rejectUnauthorized: false }
    }
    } else if (typeof url === 'object') {
    url.rejectUnauthorized = false
    }
    console.log('🔓 HTTPS get intercepted')
    return originalGet.apply(this, arguments)
    }
    console.log('✅ All TLS/HTTPS hooks installed - SSL verification disabled')
    • 运行完成后,此时应该就可以绕过 SSL Pining 了,ProxyMan 应该会显示明文了。如果还是不行,那么只能使用 Frida 来进行动态注入了。

使用 Frida 绕过 SSL Pining#

  1. 安装 Frida:

    Terminal window
    pip install frida-tools
  2. 编写 Frida 脚本 ssl_kill.js,内容如下:

    Process.enumerateModules()
    .filter(m => m.name.toLowerCase().includes("ssl"))
    .forEach(m => console.log("ssl module:", m.name, m.base));
    function findSymbol(name) {
    // 先全局搜(如果你的 Frida 支持的话)
    if (Module.findExportByName && typeof Module.findExportByName === "function") {
    const p = Module.findExportByName(null, name);
    if (p) return p;
    }
    // 再在可能的 libssl 名字里搜(macOS 上名字经常不是 libssl.dylib)
    const candidates = ["libssl.dylib", "libssl.1.1.dylib", "libssl.3.dylib"];
    for (const m of candidates) {
    const mod = Process.findModuleByName(m);
    if (!mod) continue;
    const p = mod.findExportByName(name);
    if (p) return p;
    }
    return null;
    }
    var SSL_get_verify_result = findSymbol("SSL_get_verify_result");
    var SSL_set_verify = findSymbol("SSL_set_verify");
    var SSL_CTX_set_verify = findSymbol("SSL_CTX_set_verify");
    console.log(">>> 开始注入 SSL Bypass...");
    // 1. Hook SSL_get_verify_result
    // 这个函数通常在握手完成后被调用,返回值 0 表示验证成功 (X509_V_OK)
    if (SSL_get_verify_result) {
    Interceptor.attach(SSL_get_verify_result, {
    onLeave: function(retval) {
    // 无论原来验证结果如何,强行篡改为 0 (成功)
    retval.replace(ptr(0));
    }
    });
    console.log("[+] Hooked SSL_get_verify_result (强制返回验证成功)");
    } else {
    console.log("[-] 未找到 SSL_get_verify_result (可能被剥离或改名)");
    }
    // 2. Hook SSL_set_verify
    // 这个函数用来设置验证模式。我们将模式强制设为 0 (SSL_VERIFY_NONE)
    if (SSL_set_verify) {
    Interceptor.attach(SSL_set_verify, {
    onEnter: function(args) {
    // args[1] 是 mode 参数,强制设为 0
    args[1] = ptr(0);
    }
    });
    console.log("[+] Hooked SSL_set_verify (强制禁用验证模式)");
    } else {
    console.log("[-] 未找到 SSL_set_verify");
    }
    // 3. Hook SSL_CTX_set_verify
    // 上下文级别的设置,同样强制设为 0
    if (SSL_CTX_set_verify) {
    Interceptor.attach(SSL_CTX_set_verify, {
    onEnter: function(args) {
    // args[1] 是 mode 参数
    args[1] = ptr(0);
    }
    });
    console.log("[+] Hooked SSL_CTX_set_verify (强制禁用上下文验证)");
    } else {
    console.log("[-] 未找到 SSL_CTX_set_verify");
    }
    console.log(">>> 注入完成,请检查 Proxyman 是否出现明文数据。");
  3. 使用 Frida 注入脚本:

    Terminal window
    sudo frida -n "应用进程名" -l ssl_kill.js
  4. 在没关闭 SIP 的情况下,大概率会注入失败,报错 Failed to attach: unable to access process with pid 5131 from the current user account,这种情况可以重新签名二进制文件来绕过限制

    • 先找到应用的二进制文件路径,比如 /Applications/xxx.app/Contents/Resources/bin/xxx
    • 然后执行以下命令重新签名:
      Terminal window
      # 进入应用目录
      cd /Applications/xxx.app/Contents/Resources/xxx/xxx
      # 删除原有签名
      sudo xattr -cr xxx
      # 创建一个plist文件,内容如下
      echo '<?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
      <key>com.apple.security.get-task-allow</key>
      <true/>
      </dict>
      </plist>' > debug.plist
      # 重新签名
      codesign -f -s - --entitlements debug.plist /Applications/xxx.app/Contents/Resources/bin/xxx/xxx
      codesign -d --entitlements - /Applications/xxx.app/Contents/Resources/bin/xxx/xxx
    • 最后执行 sudo frida -l ssl_kill.js -n "应用进程名" 重新注入脚本。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

最后更新于 2026-01-20,距今已过 56 天

部分内容可能已过时

评论区

目录