سایت توسط Cloudflare بسته شده است، Turnstile در هر درخواست دوم ظاهر میشود و طراحی هر دو هفته یک بار تغییر میکند. در عین حال، همان سرویس یک برنامه موبایل دارد که به طور مستقیم به بکاند متصل میشود و JSON آمادهای دریافت میکند — بدون چالشها، بدون نشانهگذاری، با یک طرح پایدار از فیلدها. این همان «API پنهان» است: رابطی که مستند نشده اما کاملاً کاربردی است و توسط مشتری رسمی استفاده میشود.
به صورت مرحله به مرحله بررسی میکنیم که چگونه میتوان آن را با استفاده از mitmproxy پیدا کرد، چه کاری با certificate pinning انجام دهیم و چرا در مرحله مقیاسگذاری جمعآوری بدون پروکسی همه چیز خراب میشود.
اصلاً چرا باید به ترافیک برنامه سر بزنیم
خزیدن در نسخه وب و فراخوانی API خصوصی — این دو وظایف متفاوتی از نظر هزینه هستند. مقایسه کنید:
- وب. به یک مرورگر بدون سر نیاز دارید، دور زدن ضد ربات، تجزیه HTML، تعمیر منظم انتخابگرها. یک درخواست = مگابایت ترافیک و ثانیهها زمان پردازش.
- API خصوصی. یک درخواست HTTP معمولی با چند هدر، پاسخ — JSON فشرده با فیلدهای نوعبندی شده. اغلب دادههای بیشتری نسبت به آنچه که رابط نشان میدهد، ارائه میدهد: شناسههای داخلی، پرچمها، فیلدهای خدماتی.
بکاندهای موبایل به طور تاریخی از وب کمتر محافظت شدهاند. دلیل آن ساده است: پلتفرمهای ضد ربات برای ترافیک مرورگر طراحی شدهاند (چالشهای JS، canvas، سیگنالهای رفتاری) و مشتری موبایل از آنها عبور نمیکند. به جای آن، توسعهدهندگان به کلید استاتیک برنامه و TLS-pinning تکیه میکنند — و هر دو در دستگاه محلی حذف میشوند.
چه چیزی لازم است
- mitmproxy — یک پروکسی HTTPS با کد منبع باز (بیش از ۴۴۰۰۰ ستاره در GitHub، شاخه فعلی ۱۲.۲.۲ در آوریل ۲۰۲۶ منتشر شد، به Python 3.12+ نیاز دارد). با یک فرمان نصب میشود: pip install mitmproxy. HTTP/1، HTTP/2، HTTP/3، WebSocket و TCP خام را درک میکند و با TLS 1.2 و 1.3 کار میکند.
- دستگاه Android یا شبیهساز با دسترسی روت. تجربه نشان میدهد که Android 7–11 راحتترین است: نسخههای جدید کار با گواهیها را بسیار سختتر کردهاند.
- ADB برای ارتباط با دستگاه و Frida (pip install frida-tools) — در صورتی که برنامه گواهی را pin کند، لازم است.
mitmproxy سه رابط کاربری بر روی یک موتور دارد: mitmproxy (TUI ترمینالی)، mitmweb (رابط وب، راحتتر برای مبتدیان) و mitmdump (بدون سر، برای اسکریپتها و خودکارسازی).
مرحله ۱. راهاندازی پروکسی
رابط وب را به گونهای راهاندازی میکنیم که به اتصالات خارجی گوش دهد، نه فقط localhost:
mitmweb --web-host 0.0.0.0
به طور پیشفرض، پروکسی در پورت ۸۰۸۰ راهاندازی میشود. در اولین راهاندازی، mitmproxy یک مرکز صدور گواهی اختصاصی ایجاد میکند و کلیدها را در دایرکتوری ~/.mitmproxy قرار میدهد. چهار فایل در آنجا ظاهر میشود: mitmproxy-ca.pem (گواهی به همراه کلید خصوصی)، mitmproxy-ca-cert.pem (فقط گواهی)، mitmproxy-ca-cert.p12 برای ویندوز و mitmproxy-ca-cert.cer — فرمت برای Android.
مرحله ۲. هدایت دستگاه از طریق پروکسی
در تنظیمات Wi-Fi در تلفن، پروکسی دستی را انتخاب میکنیم: IP کامپیوتر شما در شبکه محلی و پورت ۸۰۸۰. سپس در مرورگر دستگاه، دامنه خاص mitm.it را باز میکنیم — این صفحهای است که در mitmproxy گنجانده شده و خود پلتفرم را شناسایی کرده و فرمت مناسب گواهی را با دستورالعمل ارائه میدهد.
در iOS، این روند از سه بخش تشکیل شده و همه در نیمه دوم فراموش میکنند: دانلود پروفایل از طریق Safari، نصب آن در «تنظیمات → عمومی → VPN و مدیریت دستگاه»، و سپس بهطور جداگانه فعال کردن اعتماد کامل در «تنظیمات → عمومی → درباره این دستگاه → اعتماد به گواهیها». بدون مرحله آخر، گواهی نصب شده اما کار نمیکند.
اگر نمیخواهید با تنظیمات Wi-Fi سر و کار داشته باشید، mitmproxy یک حالت سرور VPN دارد: mitmweb --mode wireguard. دستگاه با استفاده از کلاینت WireGuard متصل میشود و ترافیک به طور شفاف بدون تنظیم دستی پروکسی در سیستم ضبط میشود.
مرحله ۳. دیوار اصلی — اعتماد به گواهی
در اینجا بیشتر تلاشها شکست میخورد. مشکل دقیقاً دو تا است و این دو مشکل متفاوت هستند.
CAهای کاربر از سال ۲۰۱۶ مورد توجه نیستند
از Android 7 Nougat (API 24) به طور پیشفرض برنامهها فقط به مخزن گواهی سیستم اعتماد میکنند. CA کاربر نادیده گرفته میشود، مگر اینکه توسعهدهنده به وضوح آن را در Network Security Config مجاز کرده باشد — از طریق بلوک <certificates src="user" /> در trust anchors. این یک تصمیم آگاهانه از سوی گوگل برای کاهش سطح حمله بوده و نمیتوان آن را با تنظیمات تلفن دور زد. بهطور جالب، Chrome نیز به گواهیهای کاربر اعتماد نمیکند. در Android 11 محدودیتها حتی شدیدتر شدهاند.
نتیجه عملی: در دستگاهی با روت، گواهی mitmproxy باید در مخزن سیستم قرار گیرد، نه در مخزن کاربر. به همین دلیل روت در لیست الزامات قرار دارد و نه «ترجیحی».
Certificate pinning
دیوار دوم — pinning: برنامه یک اثر انگشت از گواهی سرور مورد انتظار را در خود دارد و از صحبت با هر کس دیگری امتناع میکند. حتی CA سیستم نیز در اینجا نجاتبخش نیست. تحقیق ACM در سال ۲۰۲۲ نشان داد که pinning در عمودیهای «پرخطر» (بانکها، تاکسیها، ارزهای دیجیتال) رایج است، اما اغلب به طور ناقص پیادهسازی میشود و به همین دلیل قابل دور زدن است.
ابزارهای مختلفی برای این کار وجود دارد و هر کدام به شیوهای متفاوت آن را حل میکنند:
- Frida — ویرایش رفتار در زمان اجرا: توابع بررسی گواهی را هک کرده و آنها را مجبور میکنیم که موفقیت را برگردانند. در این حالت برنامه تغییر نمیکند — این گزینه انعطافپذیرترین است. راهاندازی معمول: frida -U -f com.target.app -l ssl_bypass.js --no-pause.
- apk-mitm — به طور خودکار pinning را به طور ایستا از فایل APK حذف میکند.
- android-unpinner — APK را دوباره بستهبندی میکند و Frida و اسکریپتهای حذف pinning را وارد میکند.
- objection — ابزارک بالای Frida که میتواند هم iOS و هم Android را مدیریت کند.
- ssl-kill-switch2 — pinning را در برنامههای iOS و macOS غیرفعال میکند.
اگر دامنه خاصی به شدت pin شده و مانع کار میشود، میتوان آن را به سادگی با گزینه ignore_hosts (که یک عبارت منظم را میپذیرد) از ضبط خارج کرد — ترافیک بدون رمزگشایی از mitmproxy عبور خواهد کرد.
مرحله ۴. پیدا کردن درخواست مورد نظر
بعد از این، روال است. برنامه را باز کرده، دقیقاً یک عمل معنادار انجام میدهیم (کارت محصول را باز کنیم، فید را پیمایش کنیم، فیلتر را اعمال کنیم) و میبینیم که چه درخواستهایی ظاهر شدهاند. در رابط ترمینالی این کار به سرعت انجام میشود: Z لیست جریانها را پاک میکند، Enter درخواست انتخاب شده را باز میکند، E آن را صادر میکند — از جمله با فرمان آماده curl.
چه چیزی را در درخواست ضبط شده جستجو کنیم:
- نقطه پایانی و پارامترها. اغلب آنها به وضوح بیشتر از آنچه که رابط برنامه استفاده میکند، هستند.
- کلید مشتری. کلاسیک ژانر — شناسه استاتیک که در برنامه جاسازی شده است. در تجزیه و تحلیل معروف API عمومی MyAnimeList، این کلید هدر x-mal-client-id با مقدار 6591a087c62b3e94d769cd8e35ffe909 بود که دسترسی به نقاط پایانی api.myanimelist.net/v3/anime/season و /v3/anime با دو دهها پارامتر را باز میکرد.
- User-Agent. برای مشتریان موبایل خاص است و بخشی از «مجوز» به شمار میرود — در همان مثال این MAL (ios, 139) است.
- توکنها و مدت زمان آنها. بلافاصله بررسی کنید که آیا کلید استاتیک است یا بهروزرسانی میشود: این موضوع بر کل معماری جمعآوری بعدی تأثیر میگذارد.
curl صادر شده را به راحتی میتوان به کد تبدیل کرد از طریق curlconverter — یک درخواست آماده برای requests دریافت خواهید کرد و سپس با یک کلاینت HTTP معمولی کار میکنید، بدون هیچ مرورگری.
مرحله ۵. مقیاسگذاری — و کجا همه چیز خراب میشود
در اینجا ناامیدیای که همه کسانی که تلاش کردهاند با آن آشنا هستند، پیش میآید: از یک IP خانگی، API خصوصی در نیم ساعت اول به خوبی پاسخ میدهد، و سپس شروع به ارسال ۴۲۹ و ۴۰۳ میکند. بکاندهای موبایل از جعل مشتری کمتر محافظت شدهاند، اما محدودیتها بر اساس IP سختتر هستند — سرور فرض میکند که پشت آدرس یک تلفن وجود دارد، نه یک پارسر با بیست جریان.
از اینجا نتیجهگیریهای عملی به دست میآید.
- پروفایل درخواستها را معتبر نگه دارید. یک برنامه واقعی ۵۰ درخواست در ثانیه نمیفرستد و به طور دقیق طبق برنامهریزی عمل نمیکند. ترتیب فراخوانیها نیز اهمیت دارد: مشتری واقعی ابتدا پیکربندی جلسه را درخواست میکند، سپس محتوا را.
- بار را بین آدرسها توزیع کنید. یک IP = یک «تلفن». درباره استراتژیهای چرخش، تأخیر با jitter و exponential backoff به تفصیل در مقاله چگونه محدودیت نرخ API را هنگام خزیدن از طریق پروکسی دور بزنیم بررسی شده است.
- جغرافیا را در نظر بگیرید. بسیاری از APIهای موبایل محتوای متفاوت و قیمتهای مختلفی را بسته به کشور آدرس ارائه میدهند — این هم محدودیت و هم فرصت است.
دیباگ کردن به راحتی در mitmproxy انجام میشود: این ابزار میتواند به پروکسی بالادستی متصل شود. فرمان mitmdump --mode upstream:http://example.com:8081 تمام ترافیک را به سمت upstream هدایت میکند و احراز هویت به آن با گزینه --upstream-auth در فرمت username:password تعیین میشود. به این ترتیب شما همان درخواستها را میبینید که قبلاً بود، اما از یک آدرس خارجی میروند — میتوانید بلافاصله بررسی کنید که API چگونه به کشور خاص یا نوع IP واکنش نشان میدهد.
چه نوع پروکسی برای API موبایل انتخاب کنیم
انتخاب در اینجا انتزاعی نیست، بلکه از اینکه شما چه کسی را جعل میکنید ناشی میشود.
- پروکسیهای موبایل — گزینه اولویتدار. شما ترافیک برنامه را شبیهسازی میکنید و آدرس اپراتور موبایل برای بکاند به طور کامل طبیعی به نظر میرسد: به دلیل CGNAT، واقعاً صدها مشترک پشت یک آدرس نشستهاند، بنابراین محدودیتها برای چنین IPهایی نرمتر است. پروکسیهای موبایل 4G/LTE مناسب هستند.
- پروکسیهای مسکونی — میانه کارآمد، اگر حجمها بزرگ باشد و وابستگی به اپراتور بحرانی نباشد: IPهای خانگی ارائهدهندگان پوشش وسیعی از نظر جغرافیایی با قیمت مناسب ارائه میدهند. اینها پروکسیهای مسکونی هستند.
- پروکسیهای دیتاسنتر — فقط برای نقاط پایانی بدون بررسی جدی اعتبار آدرس. ASN آنها به سرعت شناسایی میشود و در بکاند موبایل این موضوع عجیب به نظر میرسد: تلفنها در دیتاسنترها وجود ندارند.
دامهای زیرآبی که دیر متوجه میشوند
- HTTP/3. پشتیبانی از QUIC در mitmproxy وجود دارد و به طور پیشفرض فعال است، اما در ترافیک واقعی موبایل محدود است: اغلب اتصال مجبور به بازگشت به HTTP/2 از طریق دستکاری ALPN میشود. QUIC بهترین عملکرد را در حالتهای reverse و WireGuard دارد.
- API خصوصی بدون هشدار تغییر میکند. این API هیچ تعهدی به سازگاری معکوس ندارد — این یک رابط داخلی است. نسخه برنامه در User-Agent یک روز دیگر پشتیبانی نخواهد شد و جمعآوری به طور خاموش شروع به دریافت پاسخهای خالی میکند. نه تنها کدهای پاسخ، بلکه ساختار JSON را نیز نظارت کنید.
- «کلید پیدا کردم» و «مجوز دریافت کردم» را اشتباه نگیرید. کلید استاتیک مشتری — مجوزی برای جمعآوری نامحدود نیست.
درباره جنبه قانونی
ضبط ترافیک در دستگاه خود قانونی و یک عمل روزمره برای دیباگ است که توسط توسعهدهندگان موبایل و متخصصان امنیتی استفاده میشود. مرزها از آنجا شروع میشود: شرایط استفاده از سرویس را رعایت کنید، دادههای شخصی را بدون مبنای قانونی جمعآوری نکنید (در اتحادیه اروپا این موضوع به وضوح توسط GDPR تنظیم میشود)، به نقاط پایانی که نیاز به احراز هویت شخص دیگری دارند دست نزنید و بار را در سطحی نگه دارید که مانع کار سرویس نشود. راهنمای عملی: اگر دادهها در برنامه برای هر کاربری بدون ورود به حساب کاربری قابل مشاهده است — شما در منطقه نسبتاً آرامی هستید؛ اگر برای دسترسی به یک حساب کاربری دیگر نیاز دارید — شما دیگر در آن محدوده نیستید.
خلاصه
طرح کارآمد است و هفتهها زحمت با ضد ربات را صرفهجویی میکند: mitmproxy را راهاندازی میکنیم، گواهی را در مخزن سیستم دستگاه با روت قرار میدهیم، در صورت نیاز pinning را از طریق Frida حذف میکنیم، یک درخواست معنادار را ضبط میکنیم، آن را به curl صادر میکنیم و به Python بازنویسی میکنیم. سپس وظیفه از دسته «دور زدن حفاظت» به «به آرامی توزیع بار» تبدیل میشود — و با چرخش آدرسها، وقفههای منطقی و نوع مناسب پروکسی حل میشود. سادهترین کار این است که با پروکسیهای موبایل شروع کنید: آنها به ترافیکی که بکاند انتظار دارد نزدیکترین هستند.
```