scraping// reverse-engineering-mobile-apis

مهندسی معکوس API اپلیکیشنهای موبایل و بایپس SSL Pinning با Frida

تکنیکهای عملی شنود ترافیک اندروید، دور زدن Certificate Pinning در لایه Java و Native، و استفاده از Frida RPC به عنوان اوراکل امضای رمزنگاری در پایتون.

احسان شهبازی·۱۴۰۴/۰۷/۲۷·۱۶ دقیقه مطالعه
// SBU Data Science Lab

چرا APIهای موبایل طلاییترین منبع استخراج داده هستند؟

زمانی که وبسایت یک پلتفرم بزرگ پشت لایههای پیچیده کپچا، چالشهای جاوااسکریپت و تغییر مداوم کلاسهای DOM قرار دارد، اپلیکیشن موبایل همان پلتفرم معمولاً به یک API بسیار پایدار، سریع و مبتنی بر JSON یا Protobuf متصل است. به دلیل اینکه کاربران موبایل همیشه اپلیکیشن خود را بهروزرسانی نمیکنند، تیمهای بکاند مجبور به حفظ Backward Compatibility برای ماهها یا سالها هستند.

آناتومی دفاعی در اندروید: از گواهی سیستم تا SSL Pinning

برای مشاهده ترافیک HTTPS اپلیکیشن در ابزارهایی مثل mitmproxy یا Burp Suite، با دو مانع اصلی روبهرو میشوید:

  • محدودیت گواهیهای User در اندروید ۷ به بالا: از اندروید Nougat، اپلیکیشنها به صورت پیشفرض به گواهیهای نصبشده توسط کاربر اعتماد نمیکنند و گواهی CA پراکسی باید در مسیر سیستمی /system/etc/security/cacerts/ روی دستگاه روتشده قرار گیرد یا فایل APK با ابزارهایی مثل apk-mitm پچ شود.
  • مکانیسم SSL / Certificate Pinning: اپلیکیشن هش کلید عمومی (SPKI SHA-256) یا خود گواهی سرور را درون کد خود هاردکد میکند و حتی اگر گواهی پراکسی شما در سطح سیستمعامل معتبر باشد، اتصال TLS را قطع میکند.

پیادهسازیهای رایج Pinning در فریمورکهای مختلف

در اپلیکیشنهای Native اندروید (Kotlin/Java)، معمولاً از کلاس okhttp3.CertificatePinner یا TrustManagerImpl استفاده میشود. اما در اپلیکیشنهای Flutter، فریمورک از پشته شبکه سیستمعامل استفاده نکرده و مستقیماً با کتابخانه کامپایلشده libflutter.so (مبتنی بر BoringSSL) ارتباط برقرار میکند که نیازمند هوک کردن توابع C/C++ در سطح آفستهای باینری است.

تزریق داینامیک در زمان اجرا (Dynamic Instrumentation) با Frida

ابزار Frida به ما اجازه میدهد بدون نیاز به دیکامپایل، تغییر کدهای Smali و ریبیلد کردن APK، مستقیماً در زمان اجرا به پروسه اپلیکیشن در حافظه متصل شویم و رفتار متدها را تغییر دهیم. اما قدرت واقعی Frida فراتر از بایپس SSL Pinning است: استفاده از Frida RPC.

بسیاری از اپلیکیشنهای مدرن به هر درخواست هدرهایی مانند X-Request-Signature یا X-Device-Token اضافه میکنند که داخل یک کتابخانه مبهمسازیشده Native (.so) با الگوریتمهای پیچیده تولید میشود. به جای صرف هفتهها وقت برای مهندسی معکوس کدهای Assembly در Ghidra یا IDA Pro، میتوانیم تابع امضاکننده داخل خود اپلیکیشن را از طریق پایتون فراخوانی کنیم!

اسکریپت جامع بایپس SSL Pinning و پل ارتباطی Frida RPC با پایتون

در کد زیر، ابتدا اسکریپت جاوااسکریپت Frida همزمان Pinning را در OkHttp3 و TrustManagerImpl خنثی میکند و سپس متد داخلی تولید امضای اپلیکیشن را به عنوان یک API از طریق rpc.exports در اختیار اسکرپر پایتونی قرار میدهد:

frida_rpc_signer.py
python
import frida
import requests
from typing import Dict, Any

FRIDA_HOOK_SCRIPT = """
Java.perform(function () {
    // 1. Bypass OkHttp3 CertificatePinner
    try {
        var CertificatePinner = Java.use('okhttp3.CertificatePinner');
        CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, peerCertificates) {
            console.log('[+] Bypassed OkHttp3 pinning for: ' + hostname);
            return;
        };
    } catch (err) {
        console.log('[-] OkHttp3 CertificatePinner not found');
    }

    // 2. Bypass Android Conscrypt TrustManagerImpl
    try {
        var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
        TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
            console.log('[+] Bypassed TrustManagerImpl for: ' + host);
            return untrustedChain;
        };
    } catch (err) {
        console.log('[-] TrustManagerImpl hook skipped');
    }
});

// 3. Expose Internal App Signing Function as an RPC Endpoint
rpc.exports = {
    generateSignature: function (payload, timestamp) {
        var result = '';
        Java.perform(function () {
            var CryptoHelper = Java.use('com.targetapp.security.CryptoManager');
            result = CryptoHelper.signRequestNative(payload, timestamp);
        });
        return result;
    }
};
"""

class MobileApiScraper:
    def __init__(self, package_name: str):
        self.device = frida.get_usb_device(timeout=5)
        pid = self.device.spawn([package_name])
        self.session = self.device.attach(pid)
        self.script = self.session.create_script(FRIDA_HOOK_SCRIPT)
        self.script.load()
        self.device.resume(pid)
        self.rpc = self.script.exports_sync

    def fetch_protected_endpoint(self, endpoint: str, body_json: str, timestamp: str) -> Dict[str, Any]:
        # Ask the live Android app to compute the exact cryptographic HMAC/JNI signature
        signature = self.rpc.generate_signature(body_json, timestamp)
        headers = {
            'User-Agent': 'TargetApp/4.18.0 (Android 14; Pixel 7)',
            'X-Timestamp': timestamp,
            'X-Signature': signature,
            'Content-Type': 'application/json'
        }
        resp = requests.post(endpoint, data=body_json, headers=headers, timeout=10)
        resp.raise_for_status()
        return resp.json()

نکته کلیدی مهندسی: هنگام مواجهه با کتابخانههای Native (.so) که با OLLVM مبهمسازی شدهاند، هرگز زمان خود را صرف بازنویسی الگوریتم رمزنگاری در پایتون نکنید. یک شبیهساز سبک Android (یا برد ARM واقعی) بالا بیاورید، با Frida RPC به پروسه متصل شوید و از خود اپلیکیشن به عنوان یک میکروسرویس امضاکننده (Signing Oracle) با توان هزاران امضا در ثانیه استفاده کنید.