Kami hadir untuk perhatian Anda bagian kedua dari artikel tentang rekayasa balik firmware perangkat Badak Berkedip berdasarkan kelas master di konferensi
SMARTRHINO-2018 .
Pada bagian pertama artikel, firmware perangkat dimuat ke disassembler IDA dan analisis awal dari perintah protokol perangkat dilakukan. Perintah individual diuji pada perangkat yang berfungsi.
Pada bagian kedua, analisis tugas firmware yang tersisa akan dilakukan.
Biarkan saya mengingatkan Anda, setelah menganalisis tugas Bluetooth dalam hal mengendalikan LED, diputuskan untuk beralih ke tugas LED, karena tugas awalnya adalah membuat aplikasi untuk mengendalikan LED, dan untuk ini diperlukan pemahaman terperinci tentang operasi firmware.
File firmware tersedia untuk studi independen.
Semua informasi disediakan hanya untuk tujuan pendidikan.Di bawah kucing ada banyak badak yang berkedip.
Tugas LED
Secara singkat: analisis lengkap tugas yang bertanggung jawab untuk mengganti LED. Analisis tipe data dan variabel global.Tugas LED diwakili oleh fungsi
x_leds_task , yang terletak di
0x08005A08
.
Selain garis aneh "Aku punya kekuatan super ..." di fungsi utama tugas LED, Anda dapat memperhatikan baris
"hue> max: ubah bersinar \ r \ n" .

Pada saat yang sama, kita melihat situasi yang akrab - (WORD *) (v26 + 4). Di menu konteks variabel v26, pilih item "Konversikan ke struct *", lalu tunjukkan struktur yang dibuat sebelumnya:

Mengingat
v5 = v26
, kami mengulangi operasi
"Konversikan ke struct *" untuk variabel v5.
Kami terus menyusun kode dan data. Atur representasi hex di mana-mana. Ganti nama:
- v5 - dipimpin ;
- v6 - idx ;
- v8 - hue_1 ;
- v9 - hue_2 ;
- v26 - _led ;
Kode membaik. Tetapi beberapa variabel masih melukai mata, misalnya, variabel v23:


Rupanya, v23 adalah array 4 byte.idx adalah indeks LED; indeks ini ditambahkan ke alamat basis; dengan cara ini, akses dilakukan ke elemen-elemen pada perpindahan yang sama - ini adalah bagaimana array berperilaku.
Kami menetapkan tipe
char v23[4]
dan
menamainya menjadi
leds_smth , kode menjadi lebih cantik:

Anda juga dapat mencatat bahwa hasil dari fungsi x_queue_recv dikembalikan ke variabel v25:
x_queue_recv(&v25, leds_queue, 1000);
Tetapi mungkin tidak jelas bagaimana data yang Anda butuhkan dalam struktur
_led . Faktanya adalah bahwa variabel v25 dan _led
terletak di dekatnya pada stack - ini dapat dipahami oleh fakta bahwa dalam dekompilasi mereka ditulis pada baris yang berdekatan. Lokasi variabel pada tumpukan dapat dilihat di jendela terpisah jika Anda mengklik dua kali pada variabel:

Mereka mungkin struktur, atau kompiler telah melakukan optimasi. Dengan demikian, dapat dikatakan bahwa data dari tugas Bluetooth ditransmisikan ke tugas LED. Untuk mengetahui lebih tepat, saya akan memeriksa perangkat - untuk nol LED melalui Bluetooth saya akan mengirimkan nilai
0x208 ,
0x2D0 ,
0x398 ,
0x3E9 , yang dapat dilihat pada kode:

Hasil pemeriksaan nilai rona pada perangkat:
- 0x208 - LED berhenti beralih dengan lancar dan diatur dalam warna: merah, hijau, biru, ungu;
- 0x2D0 - LED mulai beralih lagi;
- 0x398 - tidak ada yang berubah;
- 0x3E9 - tidak ada yang berubah.
Jika Anda melihat kode lagi, Anda dapat melihat bahwa nilai 0x398 dapat dikaitkan secara logis dengan nilai kurang dari 0x167 (nilai yang berbeda ditetapkan untuk elemen array
leds_smth ). Oleh karena itu, saya akan melakukan pemeriksaan ini: pertama, saya akan mengatur LED pertama menjadi hijau (rona = 0x78,
LED 010078FF20
), sementara tiga LED lainnya terus mengubah warnanya.
Sekarang saya akan
LED 010398FFFF
protokol Bluetooth
LED 010398FFFF
- setelah ini LED pertama telah beralih ke mode switching warna umum.
Dengan demikian, nilai rona 0x398 me-reset nilai warna statis, yang berarti bahwa array leds_smth berisi flag (0 atau 1) untuk LED yang ditempati:
- 0 - LED tidak sibuk, berpartisipasi dalam perpindahan warna yang halus ( hue = 0x398 );
- 1 - LED sibuk, pengguna mengatur warna statis ( rona <= 0x167 ).
Ganti nama leds_smth menjadi
leds_busy .
Sekarang tujuan dari blok kode berikut harus menjadi jelas:

Siklus dalam baris 83-101 melakukan mosaik warna yang halus dengan langkah peralihan warna 5:
v12 += 5
. Jika LED memiliki warna statis menyala, maka LED ini tidak berpartisipasi dalam mosaik. Setelah siklus ada garis pencantuman jangka pendek semua LED.
Ganti nama:
- sub_800678A - x_led_set_hsv ;
- v12 - hue_step ;
- v13, v17, v18, v19 - led0_busy , led1_busy , led2_busy , led3_busy ;
- v11, v20, v21, v22 - hue0 , hue1 , hue2 , hue3 ;
- dword_200004C4 - led_control .
Fungsi sub_80039FE mungkin melakukan timeout (jika tidak LED tidak beralih dengan lancar, tetapi langsung), sebut saja
x_sleep , dan variabel v16 adalah
led_timeout .
Tujuan dari fungsi sub_8006934 belum jelas, tetapi digunakan di mana-mana setelah mengatur warna pada LED - Anda dapat menyebutnya
x_led_fix_color .
Setelah penggantian nama ini, mudah untuk memahami fungsi
sub_8006944 (dipanggil dalam rona <= 0x167 cabang):

Ini hanya melakukan pemeriksaan tambahan untuk menentukan warna LED. Ganti nama fungsi sub_8006944 menjadi
x_led_set_hsv_wrap (suffix
_wrap - penjelasan bahwa ini adalah "pembungkus" di atas fungsi lain) dan tetapkan prototipe berikut untuknya:
signed int __fastcall x_led_set_hsv_wrap(int led_control, signed int idx, int hue, char sat, char val)
Mari kita kembali satu tingkat ke fungsi x_leds_task. Sekali lagi melihat kode, Anda dapat menemukan bahwa cabang "hue> 0x3E8" mulai terlihat seperti ini:

Artinya, nilai rona lebih besar dari 0x3E8 harus mengubah batas waktu mosaik berwarna. Saya akan memeriksa dengan mengirimkan beberapa nilai ke perangkat:
- hue = 0x3E9 - LED mulai beralih dengan cepat:

- hue = 0xFFFF - LED mulai beralih dengan sangat lambat:

Saat keluar dari siklus utama tugas LED, fungsi
sub_8003C44 digunakan , yang juga digunakan dalam fungsi sub_8005070:

Ganti nama:
- sub_8005070 - x_freeMsg ;
- sub_8003C44 - x_free_queue .
Lebih jauh dalam tugas LED, cabang berikut tidak bisa tidak menarik perhatian:

Anda dapat mencoba menjalankan perintah
LED B816D8D90000FFFF
. Tetapi jika Anda ingat bahwa hanya 2 karakter yang diambil sebagai indeks LED, upaya untuk mencapai kode ini jelas tidak berhasil. Biarkan utas ini untuk nanti. Ganti nama fungsi sub_8004AE8 menjadi
x_mad_blinking , dan sekarang saatnya untuk memperbaiki tanda tangan dari fungsi
x_printf (terakhir kali saya menulis tanda tangan yang salah):
void x_printf(const char *format, ...)
Siklus utama tugas LED dibongkar, tetapi masih ada kode di awal tugas.
Mari kita lihat kodenya:

Pada baris 49, kemungkinan besar LED diperiksa untuk ketersediaan dan, jika terjadi kesalahan, panggilan dilakukan ke fungsi sub_8004BBC, yang mematikan interupsi dan memulai loop tak terbatas di mana baris "../Drivers/STM32F0xx_HAL_Driver/Src/stm32f0xx_hal_gpio.cog digunakan". Kemungkinan besar, itu adalah fungsi
tegas atau serupa.
Ganti nama:
- sub_8004BBC - x_gpio_assert ;
- sub_800698C - x_check_gpio .
Tujuan fungsi
sub_8006968 akan menjadi jelas jika Anda melihat perangkat dengan hati-hati saat dihidupkan:
Keempat LED bersamaan menyala merah, pertama, hijau, lalu biru. Setelah itu, mereka diatur oleh warna: 0-merah, 1-hijau, 2-biru, 3-ungu. Dan baru kemudian mereka mulai beralih mosaik.
Karena mosaik dimulai pada siklus tugas utama, adalah logis bahwa garis 58-61 sebelum siklus utama bertanggung jawab atas penyertaan jangka pendek dari berbagai warna pada LED, dan garis 52-56 bertanggung jawab untuk mengatur merah-hijau-biru pada semua LED sekaligus. Ganti nama fungsi sub_8006968 menjadi
x_led_all_set_rgb (RGB - murni berdasarkan dugaan, sesuai dengan argumen yang diteruskan).
Keanehan dalam tugas LED
Secara singkat: mendefinisikan fungsi kode yang berisi garis-garis aneh. Menghasilkan kata sandi untuk perangkat.Sekarang mari kita beralih ke bagian paling awal dari fungsi x_leds_task:
"Eraze" ,
"gen" ,
"flash" ,
"reset" - mengapa ini semua ???
Mari kita coba mencari tahu.
Biarkan sub_80066BC menjadi
x_leds_task_init .
Mari kita lihat sub_8006B38:

Memset air murni, setuju?

Kembali ke x_leds_task. Ada yang salah dengan tipe variabel v24:

IDA membuat sedikit kesalahan dengan jenisnya, tetapi komentar dengan tanda tumpukan membantu kami. Antara variabel v24 dan v25 sebanyak 12 byte (0x44 - 0x38). Oleh karena itu, kami mengganti nama v24 menjadi
buf dan menetapkan tipe
unsigned __int8 buf[12]
(Ida akan memperingatkan bahwa tipe data baru lebih besar daripada yang lama - kami setuju).
Selanjutnya Fungsi sub_8004CE4:

Ganti nama
a1 menjadi
buf ,
v1 ke
_buf .
Fungsi sub_8006B26:

Apakah Anda mengenalinya?
Dan jika tanpa makeup?
Tentu saja
memcpy . Ganti nama.
Maka tujuan dari fungsi sub_8004CE4 adalah untuk mendapatkan beberapa data di alamat
0x08007C00 . Omong-omong, alamat ini berada dalam kisaran alamat memori flash mikrokontroler (dan firmware, khususnya). Ganti nama sub_8004CE4 menjadi
x_read_data_0x08007C00 .
Garis Fungsi X_leds_task 36:
if ( (unsigned int)buf[0] - 65 > 0x19 )
Ubah tampilan data (tombol R pada angka 65, tombol H pada angka 0x19):
if ( (unsigned int)buf[0] - 'A' > 25 )
Setelah sedikit refleksi, Anda dapat memahami bahwa ini adalah semacam tes kisaran alfabet Latin AZ.
Selanjutnya, menggunakan prompt dalam bentuk format string, ganti nama:
- sub_8004C10 - x_erase ;
- sub_80059C8 - x_gen ;
- sub_8004C84 - x_flash .
Fungsi sub_8003C66 tidak melakukan apa pun yang luar biasa - hanya meningkatkan beberapa variabel global - mengubah nama sub_8003C66 menjadi
x_smth_inc .
Fungsi
x_erase sebenarnya tidak menerima argumen apa pun - ini dapat diverifikasi di disassembler:

Di dalam x_erase, alamat akrab 0x08007C00 digunakan dan tiga fungsi yang tidak diketahui diakses:
Sekilas pada ketiga fungsi ini, kita melihat bahwa mereka mengakses alamat dalam kisaran
0x40022000 - 0x400223FF . Dokumentasi untuk mikrokontroler dengan jelas menyatakan bahwa ini adalah kisaran
"FLASH Interface" . Artinya, fungsi x_erase menghapus sepotong memori flash - hebat!
Rupanya, fungsi x_flash menulis ke memori flash, setelah memeriksa panjang baris untuk menulis (omong-omong, argumen a2 dan a3 berlebihan di sini - kami akan membantu Ide):

Dan semua ini terjadi di "perangkat penerangan" ???
Nah, bagaimana dengan fungsi
x_gen ? Setelah melihat cepat dan mengganti nama variabel, itu akan terlihat seperti ini:

Fungsi
sub_8006CB4 terlihat seperti ini:

Dan
sub_8006D10 - seperti ini:

Jangan menahan keinginan untuk mencari di internet untuk konstanta tidak senonoh yang indah ini:
0xABCD ,
0x1234 ,
0xE66D ,
0xDEEC ,
0x4C957F2D dan
0x5851F42D . Jika Internet belum sepenuhnya dilarang, Anda mungkin akan menemukan konstanta ini di sumber untuk
fungsi acak . Tidak heran fungsi induknya disebut x_gen.
Ini juga situasi yang sangat khas: panggil srand () sebelum loop, dan panggil acak () di loop, jadi beri nama:
- sub_8006D10 - x_rand;
- sub_8006CB4 - x_srand.
Pembaca yang ingin tahu, dengan melihat ke fungsi
sub_8005098 , dapat mengetahui dari
mana asal benih untuk fungsi srand.
Dengan demikian, fungsi x_gen menghasilkan
string acak dengan ukuran yang ditentukan .
Setelah baris yang dihasilkan ditulis ke memori flash, perangkat reboot:

Sepertinya semacam reboot aneh. Tetapi jika kita melihat daftar tugas perangkat ini, kita akan menemukan "watchdogTask" di antara mereka. Jelas, jika ada "tugas macet", pengawas reboot.
Tugas LED kecuali mode MadBlinking dapat dianggap dianalisis.
Mari kita lihat garis-garis apa tugas-tugas lain dalam sistem:

Setelah memulihkan tautan ke string dalam kode, Anda dapat melihat gambar ini:

Pertama, ada tautan ke string dengan nama tugas, lalu tautan ke fungsi tugas utama. Dan mereka digunakan dalam fungsi
utama , di mana tugas-tugas ini diluncurkan:

Mari kita jalankan nama yang hilang:
- sub_80050FC - x_sensor_task ;
- sub_8004AAC - x_watchdogTask ;
- sub_8005440 - x_uartRxTask .
Tugas pengawas
Pengawas tugas tidak melakukan sesuatu yang sangat menarik:

Ganti nama:
- dword_200003F8 - wd_variable ;
- sub_8001050 - x_update_wd_var .
Tugas UART
Secara singkat: cari data dan fungsi yang memiliki tautan dari berbagai fungsi. Penentuan tujuan mereka.Melihat tugas UART dengan cepat memungkinkan Anda mendeteksi pengiriman data ke antrian tidak dikenal yang ditentukan oleh variabel
unk_200003EC :

Setelah memulihkan tautan ke variabel ini melalui pencarian biner, kita akan melihat bahwa selain x_uartRxTask, ia digunakan di utama (di sana, antrian dibuat, tampaknya) dan dalam fungsi yang tidak diketahui sejauh ini
sub_80051EC :

Ganti nama:
- sub_80051EC - x_recvMsg_uart_queue ;
- unk_200003EC - uart_queue .
Lihat referensi silang ke x_recvMsg_uart_queue:
- sub_8005250;
- x_bluetooth_task.
Pertama, lihat fungsi
sub_8005250 :

Setelah berpikir, ganti nama:
- unk_2000034C - cmd_count ;
- a1 - cmd ;
- v4 - _cmd ;
- v6 adalah rsp ;
- sub_8005250 - x_bluetooth_cmd .
Mari kita lihat sekarang di mana x_bluetooth_cmd masih digunakan. Semua tautan tambahan hanya dari tugas Bluetooth, saatnya kembali ke sana.
Kembali ke tugas bluetooth
Secara singkat: analisis akhir tugas Bluetooth. Cari otorisasi tanpa kata sandi.
Jika Anda melihat tempat-tempat di mana fungsi
sub_8006A84 digunakan , dan Anda tidak terlalu malas dan memeriksa isi perutnya, tidak akan ada keraguan - ini adalah
calloc . Ini logis - untuk menerima data ke dalam buffer, Anda harus terlebih dahulu membuat buffer ini.
Sekarang
sub_8006DBC . Mari kita lihat (variabel sudah diubah namanya):

Mengingat fungsi pustaka C standar untuk bekerja dengan string, kita akan melihat
strstr (mencari substring) di sini dan dengan berani mengubah nama itu.
Mari kita melihat kode fungsi x_bluetooth_task -
mungkin ada sesuatu yang berubah di sini sejak kunjungan terakhir . Dalam prosesnya, kami beri nama variabel:
- v2 - _state ;
- v3 - data_len .
Ada fungsi
sub_80052E2 tepat di sebelahnya. Dengan analogi dengan fungsi-fungsi yang menarik angka dari perintah Bluetooth, itu menarik string dengan panjang yang ditentukan - sebut saja
x_get_str .
Kami melanjutkan:
- v26 - isEcho ;
- v6 - meow_str ;
- v10 - uart_cmd_byte ;
- v11 - uart_cmd_str ;
- v12 - str_0 ;
- v13 - str_1 ;
- v14 - format_str ;
- sub_8000F5C - x_blink_small_led .
Selesai dengan ganti nama cepat:
- v19 - kata sandi ; (karena ada baris tentang otorisasi dan kata sandi di sebelahnya)
- sub_8004CC0 - x_check_password ;
- sub_8006AF4 - x_free (karena kata sandi, cmd dan bt_args adalah pointer ke objek dinamis (centang ini!), memori harus dibebaskan setelah menggunakannya);
- sub_8006DAC - x_strcpy (periksa!)
Sekarang jelajahi cabang-cabang
BACA ,
MENULIS ,
AUTP ,
SETP .
Seperti yang ditunjukkan oleh pengujian pada perangkat yang sedang berjalan, diperlukan otorisasi untuk perintah BACA, WRIT, SETP. Upaya otorisasi dengan perintah AUTP membawa kita ke fungsi
x_check_password untuk memverifikasi kata sandi:

Ternyata panjang kata sandi harus 8 karakter dan kata sandi dibandingkan (dalam fungsi sub_8006B08) dengan byte pada alamat
0x08007C00 - tempat string yang dihasilkan dari karakter acak AZ disimpan.
Ternyata, tanpa mengetahui kata sandi, kita tidak bisa masuk ke perangkat. Yah, atau hampir tidak bisa ...
Perhatikan di mana variabel
auth_flag digunakan :

Ternyata itu digunakan tidak hanya dalam tugas Bluetooth. Dan di sini kita belum melihat tugas Sensor. Kami pergi ke sana.
Tugas sensor
Secara singkat: apa yang dilakukan tombol sentuh?Sesuai dengan praktik pemrograman terbaik, seluruh tugas Sensor cocok dalam satu layar IDA. Dan ini tidak bisa lain kecuali bersukacita:

Baris ke baris ...
- "TSC% d \ r \ n" - baris ini akan membuat Anda berpikir tentang pengontrol sentuh Sensing untuk mikrokontroler STM32;
- "AUTH BTN \ r \ n" - tombol otorisasi ???
- βSET AUTH% d \ r \ nβ - setel tanda otorisasi?
Mari kita lihat bagaimana perangkat akan berperilaku jika Anda menekan tombol sentuh (apakah Anda menyadari bahwa badak di kaki memiliki tombol sentuh?):

Saat ditekan sebentar, LED merah kecil menyala. Dengan lama tekan, LED ini menyala untuk waktu yang lama.
Jika kita menghubungkan ini dengan kode, kita dapat mengasumsikan bahwa fungsi
sub_8000708 adalah fungsi untuk mendapatkan waktu saat ini. Kemudian, jika perbedaan antara waktu saat ini dan mulai menyentuh sensor lebih dari 1000 (1 detik), maka LED menyala selama
0xEA60 milidetik (1 menit). Tetapi variabel auth_flag sangat menarik, yang diatur ke 1 dengan menekan agak lama pada tombol sentuh, memberikan
penyerang akses ke administrator akses "fixture pencahayaan" ke fungsi istimewa.
Dengan demikian, setelah otorisasi "dengan tombol", Anda dapat membaca kata sandi yang tersimpan di perangkat (perintah BACA), menulis ke RAM (fungsi WRIT) atau mengatur kata sandi baru (SETP).
Gila berkedip
Secara singkat: dapatkah cabang kode Mad Blinking yang aneh dijalankan?Mari kita kembali ke tugas Bluetooth dan melakukan penggantian nama lagi.
- v21 - vip_smth (belum jelas apa yang ada di sana);
- v22 - vip_str (string dengan ukuran yang tidak diketahui, diekstraksi dari argumen);
- v23 - mad_led - menetapkan "Konversi ke struct *" dan tentukan struct_LED .
Dan di sini kita melihat angka
0xB816D8D9 (ditemukan di bagian pertama artikel dalam tugas Bluetooth) sebagai indeks LED. Kode ini akan dieksekusi jika verifikasi dilakukan:
if ( sub_8005520(vip_str, vip_smth) == 0x46F70674 )
Ganti nama sub_8005520 menjadi
x_vip_check dan lihatlah:

Karena argumen pertama adalah string (setidaknya string dilewatkan ke fungsi ini), kode ini menunjukkan bahwa argumen kedua adalah panjang string ini (atau panjang yang harus diproses). Ganti nama:
Mari kita lihat fungsi
sub_8000254 :

Sekarang lihat
sub_8000148 . Inilah awalnya:

Ini hanya sepertiga dari fungsi ... Mmmm ... Enak! Penggali yang berpengalaman akan dengan mudah melihat di sini ...
Apa?operasi divisi integer.
Bagaimana bisa digali?Jika Anda melakukan upaya, maka dari fungsi sub_8000254 Anda dapat membuka x_printf (melalui beberapa fungsi lainnya). Poin penting untuk dibuat pada titik ini adalah bahwa biasanya semua fungsi standar cukup standar . Ini berarti Anda dapat mencoba mencari di domain publik setidaknya beberapa kode sumber fungsi yang sedang diselidiki, sehingga penelitian ini lebih produktif.
Jadi, kita mengambil sumber printf, lalu kita melihat vfprintf , membandingkannya dengan kode dari firmware yang dipelajari. Menggunakan kode sumber, kita keluar ke fungsi itoa dan menyimpulkan bahwa fungsi sub_8000254 adalah % operator operator (mengambil sisa divisi), dan fungsi panjang yang mengerikan ini tidak lebih dari mengambil bagian integer divisi (operasi div).
Sebuah pertanyaan yang sah mungkin muncul - mengapa demikian? Faktanya adalah bahwa tidak ada DIV, operasi MOD dalam mikrokontroler tertentu, oleh karena itu, kompiler menggantikan panggilan fungsi individu, bukan operator ini. Ngomong-ngomong, berikut adalah beberapa
fungsi matematika lainnya.
Jangan lupa ganti nama saat menggali.
Dengan demikian, fungsi
x_vip_check , menghitung ... Dan ini akan menjadi
pekerjaan rumah Anda.
Omong-omong, jika Anda menjalankan perintah
VIP yang benar, kami mendapatkan "badak di disko":

Laporan singkat tentang firmware
Firmware perangkat ini didasarkan pada sistem operasi real-time FreeRTOS. Sistem memiliki tugas-tugas berikut:
- Tugas Bluetooth . Memproses perintah yang datang dalam bentuk teks melalui Bluetooth.
- Tugas LED . Mengontrol LED warna sesuai dengan perintah Bluetooth.
- Tugas sensor . Menyalakan LED merah, memungkinkan otorisasi jangka pendek tanpa kata sandi pada perangkat.
- Tugas UART . Memungkinkan Anda berinteraksi dengan modul Bluetooth melalui port UART internal (digunakan untuk menginisialisasi Bluetooth).
- Tugas Watchdog . Melacak pembekuan.
Studi ini tidak memperhitungkan kemampuan untuk membaca data dari port UART (kontak Tx / GND).
Ringkasan
Selama kelas master di konferensi, hanya fungsi kontrol LED utama yang dibongkar. Peserta paling aktif disajikan dengan "badak" eksperimental mereka.
Menurut pendapat saya, βbadakβ menghasilkan tata letak yang layak untuk kursus pelatihan tentang rekayasa balik dan pencarian kerentanan. Fitur tata letak mungkin kemampuan untuk mengubah firmware sebanyak yang Anda suka, setiap kursus memiliki firmware sendiri. Tidak seperti mem-parsing file yang dapat dieksekusi, membalikkan firmware memungkinkan Anda untuk lebih memahami:
- cara bekerja dengan IDA;
- prinsip interaksi antara firmware dan perangkat;
- Prinsip operasi RTOS.
Terima kasih banyak untuk semua yang membaca sampai akhir!