سلام!
چند روزی بود که ذهنم درگیر این قضیه بود که سرویسهایی مثل فیلیمو، فیدیبو یا طاقچه چه جوری روی دستگاههای ما یه محتوا رو به ما نشون میدن ولی جلوی این که دست ما خارج از ساختار خود برنامه به اون محتوا برسه رو میگیرن.
به طور دقیقتر برام سوال بود که من اگر یه مهندس معکوس خیلی کاربلد باشم میتونم از محتوای این برنامهها خروجی بگیرم یا نه. برای شروع رفتم سراغ فیلیمو و تصمیم گرفتم ببینم آیا میتونم یه قسمت از یه سریالی رو دانلود کنم؟
فقط قبل از هر چیز بگم که بدیهتا از محتوای این پست نباید سواستفاده بشه و تنها هدف من اشتراک تجربهم و یه خرده آموزش هست. قطعا استخراج ویدیوهای فیلیمو و انتشارشون تجاوز به حقوق ناشر محسوب میشه و نباید این کار رو انجام داد.
البته من هم به همین دلیل سعی میکنم توضیحات رو جزیی بیان نکنم؛ به این امید که دوباره انجام دادن این کار اون قدرها هم بدیهی نباشه. گرچه کسی که بخواد دانلود بکنه خیلی ابزارهای حاضر و آمادهتری براش وجود داره و هیچ وقت محتاج روش این بلاگ نمیشه. :دی
در قدم اوّل توی تب Network از قسمت Developer Tools نگاه کردم موقعی که یه فیلم شروع به پخش میکنه چه درخواستهایی روی شبکه ارسال میشن. اوّلین چیزی که به چشم میاومد تعداد زیادی درخواست به فرمت `s-<part_id>-v1-a1.ts` بود. گفتم خب ایول این فایلهای ts که پارتهای مختلف فیلم هستن. کافیه فور بزنم دانلودشون کنم و کانورتشون کنم و کانکت کنم تا یه فایل mkv به دست بیارم.
فایل اوّل رو دانلود کردم و اومدم با ffmpeg بررسیش کنم:
ffmpeg -i s-1-v1-a1.ts
که دیدم ای دل غافل! ffmpeg تشخیص نمیده این فایل ts هست.
نتیجه مشخّصه: این فایلها رمزگذاری شدن.
یه خرده دقیقتر که درخواستهای شبکه رو نگاه کردم دیدم عه چه خوب. قبل از این که مرورگر شروع کنه به ارسال درخواستهای ts، یه درخواستی میفرسته به آدرس enc.key
دانلودش کردم و دیدم محتواش یه رشتهی ۱۶ کاراکتریه. به نظر کلیدیه که برای رمزگشایی فایلهای ts نیازش داریم!
نکتهی دیگهای که جالب توجّه بود initiator این درخواستها بود که توی همون تب Network قابل مشاهدهست. این درخواستها از فایل vendors_hls شروع میشدن. پس اون فایل رو باز کردم و با ابزارهای خود مرورگر Pretty-print کردم و شروع به بررسیش کردم. قاعدتا این کار اون قدرها هم ساده نیست چون کدهای JS روی سرور پروداکشن مینیفای(؟) میشن و اسمگذاری خیلی از متغیّرها و توابع شاید واقعا نامفهوم باشه.
با بررسی کد اوّلین چیزی که فهمیدم این بود که روش رمزنگاری AES-128 هست. امّا نمیدونستم کدوم mode از AES. سرچ کردم معمولا از کدوم mode استفاده میکنن و دیدم که CBC و GCM پرکاربردترینهان. گفتم حالا فرض کنیم CBCـه تا بعد ببینیم چی پیش میآد.
برای CBC باید دنبال یه بردار آغازین iv هم باشیم! دوباره با جستوجو در کد یه تابع به نام createInitializationVector پیدا کردم که کارش تولید همین iv بود. با breakpoint گذاشتن و این جور کارها فهمیدم که برای رمزگشایی kامین پارت فیلم، ورودی k+1 به این تابع داده میشه و این تابع هم این عدد رو به یه آرایهی بایت ۱۶تایی تبدیل میکنه(به مبنای ۲۵۶ میبردش) تا بتونه به AES-128 ورودی بده.
البته همهی اینها حدس و گمان بود تا این که این کد پایتون رو زدم:
import sys from Crypto.Cipher import AES def ivof(x): res = bytearray(16) for i in range(1, 5): res[-i] = x & 255 x >>= 8 return bytes(res) c = open(sys.argv[1], 'rb').read() iv = ivof(int(sys.argv[2])) key = open('enc.key', 'rb').read() cipher = AES.new(key, AES.MODE_CBC, iv=iv) p = cipher.decrypt(c) with open(sys.argv[3], 'wb') as f: f.write(p)
این کد موقع اجرا ۳ تا آرگومان ورودی میگیره: اوّلی آدرس فایل رمز شده، دومی عدد مربوط به iv و سومی هم آدرس فایل خروجی.
بعد از اجرای این کد پایتون و اجرای مجدّد ffmpeg روی فایل خروجی دیدم که بهبه! این دفعه ffmpeg این فایل رو میشناسه. حتّی با vlc هم میشه این فایل رو پخش کرد.
توی قسمت درخواستهای شبکه، یه فایل به اسم chunk.m3u8 وجود داره که از توش میشه لیست آدرس پارتهای مختلف فیلم و مدّت زمان هر کدوم رو دید. از توی اون فایل تعداد پارتها رو درآوردم و چون آدرسشون نسبتا آدرس تمیز و منظّمی بود، با یه فور ساده توی bash و با wget همهشون رو دانلود کردم.
این جا دیگه کار مهندسی معکوس تموم میشه. از این جا به بعدش دیگه فقط تمیزکاریه.
در نهایت برای این که یه خروجی تر و تمیز داشته باشیم، بعد از دانلود پارتها یه فور دیگه توی بش میزنیم و با اون اسکریپت پایتونمون پارتها رو دیکریپت میکنیم.
بعدش هم با روش concat demuxer که توی این لینک بهش اشاره شده فایلهای ts رو در یک فایل ts بزرگ ادغام میکنیم.
در نهایت هم با دستور زیر خروجی mkv میگیریم:
ffmpeg -i merged.ts merged.mkv
ممنون که تا آخر مطلب همراهی کردید. :-)
اگر نظری داشتید میتونید نظر بدید و اگر دوست داشتید میتونید لایک کنید. جفتشون خوشحالم میکنن. D: