Minggu ini, NSA (
Badan Keamanan Nasional ) tiba-tiba memberikan hadiah kepada umat manusia, membuka sumber kerangka kerja rekayasa balik perangkat lunak mereka. Komunitas para insinyur terbalik dan pakar keamanan dengan antusiasme yang besar mulai mengeksplorasi mainan baru. Menurut umpan balik, itu alat yang sangat luar biasa, mampu bersaing dengan solusi yang ada, seperti IDA Pro, R2 dan JEB. Alat itu disebut Ghidra dan sumber daya profesional penuh dengan kesan dari para peneliti. Sebenarnya, mereka punya alasan bagus: tidak setiap hari organisasi pemerintah menyediakan akses ke alat internal mereka. Saya sendiri sebagai insinyur balik profesional dan analis malware tidak bisa lewat juga. Saya memutuskan untuk menghabiskan satu atau dua akhir pekan dan mendapatkan kesan pertama dari alat ini. Saya telah bermain sedikit dengan pembongkaran dan memutuskan untuk memeriksa ekstensibilitas alat. Dalam seri artikel ini, saya akan menjelaskan pengembangan tambahan Ghidra, yang memuat format khusus, yang digunakan untuk menyelesaikan tugas KKP. Karena ini adalah kerangka kerja yang besar dan saya telah memilih tugas yang cukup rumit, saya akan memecah artikel menjadi beberapa bagian.
Pada akhir bagian ini saya berharap untuk mengatur lingkungan pengembangan dan membangun modul minimal, yang akan dapat mengenali format file WebAssembly dan akan menyarankan disassembler yang tepat untuk memprosesnya.
Mari kita mulai dengan deskripsi tugas. Tahun lalu perusahaan keamanan FireEye menyelenggarakan kontes CTF, bernama flare-on. Selama kontes, para peneliti harus menyelesaikan dua belas tugas, yang berkaitan dengan rekayasa balik. Salah satu tugas adalah untuk meneliti aplikasi web, yang dibangun dengan WebAssembly. Ini format yang dapat dieksekusi yang relatif baru, dan sejauh yang saya tahu, tidak ada alat yang sempurna untuk menghadapinya. Selama tantangan, saya mencoba beberapa alat yang mencoba untuk mengalahkannya. Itu adalah skrip sederhana dari github dan dekompiler yang dikenal, seperti IDA pro dan JEB. Anehnya, saya sudah berhenti di chrome, yang menyediakan disassembler dan debugger yang cukup bagus untuk WebAssembly. Tujuan saya adalah untuk menyelesaikan tantangan dengan ghidra. Saya akan menjelaskan studi ini selengkap mungkin dan memberikan semua informasi yang mungkin untuk mereproduksi langkah saya. Mungkin, sebagai orang, yang tidak memiliki banyak pengalaman dengan instrumen, saya mungkin masuk ke beberapa detail yang tidak perlu, tetapi begitulah adanya.
Tugas yang akan saya gunakan untuk belajar dapat diunduh dari
situs tantangan flareon5. Ada file 05_web2point0.7z: arsip dienkripsi dengan kata menakutkan yang
terinfeksi . Ada tiga file dalam arsip: index.html, main.js dan test.wasm. Mari kita buka file index.html di browser dan periksa hasilnya:

Nah, itulah yang akan saya kerjakan. Mari kita mulai dengan belajar html, terutama karena ini adalah bagian termudah dari tantangan. Kode html tidak mengandung apa pun kecuali memuat skrip main.js.
<!DOCTYPE html> <html> <body> <span id="container"></span> <script src="./main.js"></script> </body> </html>
Script tidak melakukan hal yang rumit juga, meskipun terlihat sedikit lebih bertele-tele. Itu hanya memuat file test.wasm dan menggunakannya untuk membuat instance WebAssembly. Kemudian ia membaca parameter "q" dari url dan meneruskannya ke metode match, diekspor oleh instance. Jika string dalam parameter tidak benar, skrip menunjukkan gambar yang telah kita lihat di atas, dalam hal pengembang FireEye disebut "Tumpukan kotoran".
let b = new Uint8Array(new TextEncoder().encode(getParameterByName("q"))); let pa = wasm_alloc(instance, 0x200); wasm_write(instance, pa, a); let pb = wasm_alloc(instance, 0x200); wasm_write(instance, pb, b); if (instance.exports.Match(pa, a.byteLength, pb, b.byteLength) == 1) {
Solusi dari tugas ini adalah menemukan nilai dari parameter q yang membuat fungsi "cocok" mengembalikan "Benar". Untuk melakukan ini, saya akan membongkar file test.wasm dan menganalisis algoritma fungsi Match.
Tidak ada kejutan, dan saya akan mencoba melakukannya di Ghidra. Tetapi pertama-tama saya harus menginstalnya. Instalasi dapat (dan harus) diunduh dari
https://ghidra-sre.org/ . Karena ini ditulis dalam Java, hampir tidak ada persyaratan khusus untuk instalasi, itu tidak memerlukan upaya khusus untuk menginstal. Semua yang Anda butuhkan adalah membongkar arsip dan menjalankan aplikasi. Satu-satunya hal yang diperlukan adalah memperbarui JDK dan JRE ke versi 11.
Mari kita buat proyek ghidra baru (
File-> Proyek Baru ), dan menyebutnya "wasm" /

Kemudian tambahkan untuk memproyeksikan file test.wasm (
File β Impor file ) dan lihat bagaimana ghidra dapat menghadapinya

Yah, itu tidak bisa apa-apa. Ia tidak mengenali format dan tidak dapat membongkar apa pun, karena itu sama sekali tidak berdaya untuk menangani tugas ini. Akhirnya kita sampai pada pokok bahasan artikel. Tidak ada yang tersisa untuk dilakukan, tetapi menulis modul, yang dapat memuat file wasm, menganalisisnya dan membongkar kode-kodenya.
Pertama-tama saya telah mempelajari semua dokumentasi yang tersedia. Sebenarnya, hanya ada satu dokumen yang cocok, menunjukkan proses pengembangan add-ons: slide GhidraAdvancedDevelopment. Saya akan mengikuti dokumen, memberikan deskripsi pukulan demi pukulan.
Sayangnya, pengembangan add-on membutuhkan penggunaan gerhana. Semua pengalaman saya dengan gerhana adalah pengembangan dua game gdx untuk Android pada tahun 2012. Sudah dua minggu penuh rasa sakit dan penderitaan, setelah itu saya menghapusnya dari pikiran saya. Harapan setelah 7 tahun pembangunan itu lebih baik daripada dulu.
Mari mengunduh dan menginstal gerhana dari
situs resmi.
Kemudian, instal ekstensi untuk pengembangan ghidra:
Bantuan Goto eclipse
β Instal menu
Perangkat Lunak Baru , klik tombol
Tambah dan pilih GhidraDev.zip dari / Extensions / Eclipse / GhidraDev /. Instal dan mulai ulang ekstensi. Ekstensi, menambahkan templat ke menu proyek baru, memungkinkan untuk men-debug modul dari gerhana dan mengkompilasi modul ke paket distribusi.
Seperti berikut dari dokumen pengembang langkah-langkah berikut harus dilakukan untuk menambahkan modul untuk memproses format biner baru:
- Buat kelas, jelaskan struktur data
- Kembangkan loader. Loader harus diwarisi dari kelas AbstractLibrarySupportLoader . Itu membaca semua data yang diperlukan dari file, memeriksa integritas data dan mengkonversi data biner ke representasi internal, mempersiapkannya untuk analisis
- Kembangkan analisa. Analyzer diwarisi dari kelas AbstractAnalyzer . Dibutuhkan struktur data yang disiapkan oleh loader dan menjelaskannya (saya tidak benar-benar yakin apa artinya, tapi saya berharap untuk mengerti selama pengembangan)
- Tambahkan prosesor. Ghidra memiliki abstraksi: Prosesor. Ini ditulis dalam bahasa deklaratif internal dan menjelaskan set instruksi, tata letak memori dan fitur arsitektur lainnya. Saya akan membahas topik ini, menulis disassembler.
Sekarang, ketika kita memiliki semua teori yang diperlukan, saatnya untuk membuat proyek modul. Berkat GhidraDev, ekstensi gerhana yang dipasang sebelumnya, kami memiliki templat modul di menu
File-> New project .

Wizard menanyakan komponen apa yang diperlukan. Seperti yang sudah dijelaskan sebelumnya, kita membutuhkan dua di antaranya: loader dan analyzer.

Wizard membuat kerangka proyek dengan semua bagian yang diperlukan: penganalisis kosong dalam file WasmAnalyzer.java, pemuat kosong dalam file WasmLoader.java dan kerangka bahasa dalam direktori / data / bahasa.

Mari kita mulai dengan loader. Seperti yang disebutkan, itu harus diwarisi dari kelas AbstractLibrarySupportLoader dan memiliki tiga metode yang akan kelebihan beban:
- getName - metode ini harus nama internal loader. Ghidra menggunakannya di berbagai tempat, misalnya, untuk mengikat loader ke prosesor
- findSupportedLoadSpecs - callback, dieksekusi, ketika pengguna memilih file untuk diimpor. Dalam pemanggil panggil balik ini harus memutuskan apakah ia dapat memproses file dan mengembalikan instance dari kelas LoadSpec, memberi tahu pengguna bagaimana file dapat diproses
- load - callback, dieksekusi, setelah pengguna memuat file. Dalam metode ini loader mem-parsing struktur file dan memuat ke ghidra. Akan menggambarkannya lebih detail di artikel selanjutnya
Metode pertama dan paling sederhana adalah getName, itu hanya mengembalikan nama loader
public String getName() { return "WebAssembly"; }
Metode kedua untuk mengimplementasikan adalah findSupportedLoadSpecs. Itu disebut oleh alat selama impor file dan harus memverifikasi apakah loader dapat memproses file. Jika metode ini dapat mengembalikan objek dari kelas
LoadSpec , mengatakan objek apa yang digunakan untuk memuat file dan prosesor apa yang akan membongkar kodenya.
Metode dimulai dari verifikasi format. Seperti berikut dari
spec , delapan byte pertama dari file wasm harus berupa tanda tangan β\ 0asmβ dan versi.
Untuk mengurai header, saya membuat kelas WasmHeader, mengimplementasikan antarmuka
StructConverter , yang merupakan antarmuka dasar untuk menggambarkan data terstruktur. Konstruktor WasmHeader menerima objek
BinaryReader - abstraksi, yang digunakan untuk membaca data dari sumber biner yang sedang dianalisis. Konstruktor menggunakannya untuk membaca header dari file input
private byte[] magic; private byte [] version; public WasmHeader(BinaryReader reader) throws IOException { magic = reader.readNextByteArray(WASM_MAGIC_BASE.length()); version = reader.readNextByteArray(WASM_VERSION_LENGTH); }
Loader menggunakan objek ini untuk memverifikasi tanda tangan file. Kemudian, jika berhasil, cari prosesor yang sesuai. Itu panggilan metode permintaan kelas
QueryOpinionService , dan memberikannya nama loader ("Webassembly"). OpinionService mencari prosesor yang terkait dengan loader ini dan mengembalikannya.
List<QueryResult> queries = QueryOpinionService.query(getName(), MACHINE, null);
Tentu hal itu tidak menghasilkan apa-apa, karena ghidra tidak tahu prosesor, disebut WebAssembly dan itu perlu mendefinisikannya. Seperti yang saya katakan sebelumnya, wizard membuat kerangka bahasa dalam data direktori / bahasa.

Pada tahap saat ini ada dua file yang mungkin menarik: Webassembly.opinion dan Wbassembly.ldefs. File .opinon mengatur korespondensi antara loader dan prosesor.
<opinions> <constraint loader="WebAssembly" compilerSpecID="default"> <constraint primary="1" processor="Webassembly" size="16" /> </constraint> </opinions>
Ini berisi xml sederhana dengan beberapa atribut. Itu perlu mengatur nama loader ke atribut "loader" dan nama prosesor menjadi atribut "prosesor", keduanya adalah "Webassembly". Pada langkah ini saya akan mengisi parameter lain dengan nilai acak. Segera setelah saya tahu lebih banyak tentang prosesor prosesor Webassembly, saya akan mengubahnya menjadi nilai yang benar.
File .ldefs menjelaskan fitur prosesor, yang harus menjalankan kode dari file.
<language_definitions> <language processor="Webassembly" endian="little" size="16" variant="default" version="1.0" slafile="Webassembly.sla" processorspec="Webassembly.pspec" id="wasm:LE:16:default"> <description>Webassembly Language Module</description> <compiler name="default" spec="Webassembly.cspec" id="default"/> </language> </language_definitions>
Atribut βprosesorβ harus sama dengan prosesor atribut dari file .opinion. Mari kita biarkan bidang lain tidak tersentuh. Tetapi ingat lain kali bahwa mungkin untuk mengatur bittness registri (atribut "ukuran"), file yang menggambarkan arsitektur prosesor "processorspec" dan file, yang berisi deskripsi kode dalam bahasa deklaratif khusus "slafile". Ini akan berguna untuk bekerja pada pembongkaran.
Sekarang, saatnya untuk kembali ke loader dan mengembalikan spesifikasi loader.
Semuanya siap untuk uji coba. Plugin untuk GhidraDev telah menambahkan opsi jalankan β
Jalankan β Jalankan Sebagai β Ghidra β ke gerhana:

Ini menjalankan ghidra dalam mode debug dan menyebarkan modul di sana, memberikan peluang besar untuk bekerja dengan alat ini dan pada saat yang sama menggunakan debugger untuk memperbaiki kesalahan dalam modul yang sedang dikembangkan. Tetapi pada tahap sederhana ini tidak ada alasan untuk menggunakan debugger. Seperti sebelumnya, saya akan membuat proyek baru, mengimpor file dan melihat apakah upaya saya terbayar. Tidak seperti yang terakhir kali, file dikenali sebagai WebAssembly, dan loader mengusulkan prosesor yang sesuai untuk itu. Itu berarti semuanya berfungsi, dan modul saya dapat mengenali format.

Pada artikel selanjutnya saya akan memperluas loader, dan membuatnya tidak hanya mengenali, tetapi juga menggambarkan struktur file wasm. Saya pikir pada tahap ini, setelah lingkungan diatur, itu akan mudah dilakukan.
Kode modul tersedia di repositori
github .