
Halo semuanya
Setelah cukup lama sejak penulisan artikel pertama, saya masih memutuskan, meskipun sedikit, untuk menulis artikel tentang topik memodifikasi / meningkatkan IDA Pro .
Artikel ini akan membahas cara memperbaiki jambs dengan benar dalam modul prosesor tersebut, sumber yang tidak Anda miliki, tetapi jambs tidak memberikan siaran langsung. Sayangnya, tidak semua masalah yang tercantum di bawah ini dapat dikaitkan dengan tiang tembok, sehingga pengembang tidak mungkin menerapkannya.
Kami melokalkan bug
Catatan: selanjutnya kesalahan akan dipertimbangkan dalam modul Motorola M68000 (favorit saya dan sangat sering digunakan).
Jadi, cant pertama : pengalamatan relatif terhadap register PC . Kesalahannya adalah bahwa daftar yang dibongkar untuk instruksi semacam itu tidak selalu benar. Lihatlah tangkapan layar:

Sepertinya tidak ada kesalahan di sini. Apalagi kehadirannya tidak mengganggu analisis. Tapi, opcode tidak dirakit dengan benar. Mari kita lihat disas dalam beberapa disassembler online:

Kami melihat bahwa pengalamatan harus relatif terhadap register PC , karena Tautan alamat tujuan termasuk dalam rentang signed short
.
Tidak bisa dua : "mirror" untuk RAM, dan beberapa daerah lain. Karena Karena pengalamatan dalam m68k adalah 24-bit, maka semua panggilan ke daerah yang lebih tinggi (atau sebaliknya, lebih rendah) harus dialihkan ke kisaran yang sama dengan referensi silang.
Ada tiga cant (bukan, bahkan bukan cant, tetapi kurangnya fungsionalitas): yang disebut lineA ( 1010 ) dan lineF ( 1111 ) emulator. Ini adalah opcode semacam itu yang tidak cukup untuk set perintah dasar, oleh karena itu mereka harus diproses dengan cara khusus oleh vektor interupsi. Ukuran opcodes hanya bergantung pada implementasi di tingkat prosesor. Saya hanya melihat implementasi dua byte. Kami akan menambahkan.
Jam empat : jebakan #N instruksi jangan berikan crefs ke hooks sendiri.
Jamb lima : instruksi movea.w harus membuat xref penuh ke alamat dari tautan kata , tetapi kami hanya memiliki nomor kata .
Kami memperbaiki bug (templat)
Untuk memahami bagaimana cara memperbaiki modul prosesor tertentu, Anda perlu memahami kemungkinan apa yang kami miliki pada subjek ini pada prinsipnya dan apa yang merupakan "perbaikan".
Sebenarnya, "fixer" adalah plugin biasa. Seperti, bisa ditulis dalam Python , tapi, saya melakukan semua yang ada di "plus". Hanya portabilitas yang menderita, tetapi jika seseorang berjanji untuk menulis ulang plugin dengan Python , saya akan sangat berterima kasih.
Pertama, buat proyek DLL kosong di Visual Studio : File-> Baru-> Proyek-> Windows Desktop Wizard-> Dynamic link library (.dll), juga dengan mencentang kotak centang Proyek Kosong dan menghapus centang sisanya:

Buka paket IDA SDK , dan tulis di makro Visual Studio (saya akan menggunakan 2017 ) sehingga di masa depan Anda dapat dengan mudah referensi itu. Pada saat yang sama, kami akan menambahkan makro untuk jalur ke IDA Pro .
Pergi ke Lihat -> Windows Lainnya -> Manajer Properti :

Karena kami bekerja dengan SDK versi 7.0 , kompilasi akan dilakukan oleh x64- compiler. Karena itu, pilih Debug | x64 -> Microsoft.Cpp.x64.user -> Properti :

Klik tombol Tambahkan Makro di bagian Makro Pengguna , dan tulis makro IDA_SDK di sana dengan jalur tempat Anda membongkar SDK :

Kami melakukan hal yang sama dengan IDA_DIR (jalur ke IDA Pro Anda ):

Saya perhatikan bahwa IDA diatur secara default ke % Program Files% , yang memerlukan hak administrator.
Mari kita juga menghapus konfigurasi Win32 (dalam artikel ini saya tidak akan mempengaruhi kompilasi untuk sistem x86 ), hanya menyisakan opsi x64 .
Buat file ida_plugin.cpp kosong. Kami belum menambahkan kode.
Sekarang dimungkinkan untuk mengatur encoding, dan pengaturan lain untuk C ++ :



Kami menulis inklusi:

Dan perpustakaan dari SDK :


Sekarang tambahkan templat kode:
Ida_plugin.cpp kode #include <ida.hpp> #include <idp.hpp> #include <ua.hpp> #include <bytes.hpp> #include <loader.hpp> #include <offset.hpp> #define NAME "M68000 proc-fixer plugin" #define VERSION "1.0" static bool plugin_inited; static bool my_dbg; //-------------------------------------------------------------------------- static void print_version() { static const char format[] = NAME " v%s\n"; info(format, VERSION); msg(format, VERSION); } //-------------------------------------------------------------------------- static bool init_plugin(void) { if (ph.id != PLFM_68K) return false; return true; } #ifdef _DEBUG static const char* const optype_names[] = { "o_void", "o_reg", "o_mem", "o_phrase", "o_displ", "o_imm", "o_far", "o_near", "o_idpspec0", "o_idpspec1", "o_idpspec2", "o_idpspec3", "o_idpspec4", "o_idpspec5", }; static const char* const dtyp_names[] = { "dt_byte", "dt_word", "dt_dword", "dt_float", "dt_double", "dt_tbyte", "dt_packreal", "dt_qword", "dt_byte16", "dt_code", "dt_void", "dt_fword", "dt_bitfild", "dt_string", "dt_unicode", "dt_3byte", "dt_ldbl", "dt_byte32", "dt_byte64", }; static void print_insn(const insn_t *insn) { if (my_dbg) { msg("cs=%x, ", insn->cs); msg("ip=%x, ", insn->ip); msg("ea=%x, ", insn->ea); msg("itype=%x, ", insn->itype); msg("size=%x, ", insn->size); msg("auxpref=%x, ", insn->auxpref); msg("segpref=%x, ", insn->segpref); msg("insnpref=%x, ", insn->insnpref); msg("insnpref=%x, ", insn->insnpref); msg("flags["); if (insn->flags & INSN_MACRO) msg("INSN_MACRO|"); if (insn->flags & INSN_MODMAC) msg("OF_OUTER_DISP"); msg("]\n"); } } static void print_op(ea_t ea, const op_t *op) { if (my_dbg) { msg("type[%s], ", optype_names[op->type]); msg("flags["); if (op->flags & OF_NO_BASE_DISP) msg("OF_NO_BASE_DISP|"); if (op->flags & OF_OUTER_DISP) msg("OF_OUTER_DISP|"); if (op->flags & PACK_FORM_DEF) msg("PACK_FORM_DEF|"); if (op->flags & OF_NUMBER) msg("OF_NUMBER|"); if (op->flags & OF_SHOW) msg("OF_SHOW"); msg("], "); msg("dtyp[%s], ", dtyp_names[op->dtype]); if (op->type == o_reg) msg("reg=%x, ", op->reg); else if (op->type == o_displ || op->type == o_phrase) msg("phrase=%x, ", op->phrase); else msg("reg_phrase=%x, ", op->phrase); msg("addr=%x, ", op->addr); msg("value=%x, ", op->value); msg("specval=%x, ", op->specval); msg("specflag1=%x, ", op->specflag1); msg("specflag2=%x, ", op->specflag2); msg("specflag3=%x, ", op->specflag3); msg("specflag4=%x, ", op->specflag4); msg("refinfo["); opinfo_t buf; if (get_opinfo(&buf, ea, op->n, op->flags)) { msg("target=%x, ", buf.ri.target); msg("base=%x, ", buf.ri.base); msg("tdelta=%x, ", buf.ri.tdelta); msg("flags["); if (buf.ri.flags & REFINFO_TYPE) msg("REFINFO_TYPE|"); if (buf.ri.flags & REFINFO_RVAOFF) msg("REFINFO_RVAOFF|"); if (buf.ri.flags & REFINFO_PASTEND) msg("REFINFO_PASTEND|"); if (buf.ri.flags & REFINFO_CUSTOM) msg("REFINFO_CUSTOM|"); if (buf.ri.flags & REFINFO_NOBASE) msg("REFINFO_NOBASE|"); if (buf.ri.flags & REFINFO_SUBTRACT) msg("REFINFO_SUBTRACT|"); if (buf.ri.flags & REFINFO_SIGNEDOP) msg("REFINFO_SIGNEDOP"); msg("]"); } msg("]\n"); } } #endif static bool ana_addr = 0; static ssize_t idaapi hook_idp(void *user_data, int notification_code, va_list va) { switch (notification_code) { case processor_t::ev_ana_insn: { insn_t *out = va_arg(va, insn_t*); if (ana_addr) break; ana_addr = 1; if (ph.ana_insn(out) <= 0) { ana_addr = 0; break; } ana_addr = 0; #ifdef _DEBUG print_insn(out); #endif for (int i = 0; i < UA_MAXOP; ++i) { op_t &op = out->ops[i]; #ifdef _DEBUG print_op(out->ea, &op); #endif } return out->size; } break; case processor_t::ev_emu_insn: { const insn_t *insn = va_arg(va, const insn_t*); } break; case processor_t::ev_out_mnem: { outctx_t *outbuffer = va_arg(va, outctx_t *); //outbuffer->out_custom_mnem(mnem); //return 1; } break; default: { #ifdef _DEBUG if (my_dbg) { msg("msg = %d\n", notification_code); } #endif } break; } return 0; } //-------------------------------------------------------------------------- static int idaapi init(void) { if (init_plugin()) { plugin_inited = true; my_dbg = false; hook_to_notification_point(HT_IDP, hook_idp, NULL); print_version(); return PLUGIN_KEEP; } return PLUGIN_SKIP; } //-------------------------------------------------------------------------- static void idaapi term(void) { if (plugin_inited) { unhook_from_notification_point(HT_IDP, hook_idp); plugin_inited = false; } } //-------------------------------------------------------------------------- static bool idaapi run(size_t /*arg*/) { return false; } //-------------------------------------------------------------------------- const char comment[] = NAME; const char help[] = NAME; //-------------------------------------------------------------------------- // // PLUGIN DESCRIPTION BLOCK // //-------------------------------------------------------------------------- plugin_t PLUGIN = { IDP_INTERFACE_VERSION, PLUGIN_PROC | PLUGIN_MOD, // plugin flags init, // initialize term, // terminate. this pointer may be NULL. run, // invoke plugin comment, // long comment about the plugin // it could appear in the status line // or as a hint help, // multiline help about the plugin NAME, // the preferred short name of the plugin "" // the preferred hotkey to run the plugin };
Kami memperbaiki bug (kami memahami templat)
Fungsi print_op()
dan print_insn()
diperlukan untuk memahami flag mana yang diatur oleh modul prosesor saat ini untuk instruksi tertentu. Ini diperlukan jika kita ingin menemukan beberapa flag untuk opcodes yang ada, kemudian menggunakannya ketika memperbaikinya.
Sebenarnya, isi "perbaikan" kami adalah fungsi hook_idp()
. Di dalamnya, untuk kebutuhan kita, kita perlu menerapkan tiga panggilan balik:
processor_t::ev_ana_insn
: diperlukan jika tidak ada implementasi beberapa opcode dalam modul prosesorprocessor_t::ev_emu_insn
: di sini Anda dapat membuat referensi silang pada data / kode, yang direferensikan oleh opcodes baru (atau yang lama tidak direferensikan)processor_t::ev_out_mnem
: entah bagaimana opcodes baru harus berupa output. Semuanya ada di sini
Fungsi init_plugin()
patch kami memuat ke modul prosesor lainnya.
Nah, dan yang paling penting - kami menutup seluruh panggilan balik pada acara-acara modul prosesor:
hook_to_notification_point(HT_IDP, hook_idp, NULL);
Trik dengan ana_addr
variabel global diperlukan agar ana_insn
tidak masuk rekursi ketika mencoba untuk mendapatkan informasi tentang instruksi yang tidak kita uraikan secara manual. Ya, sayangnya, "tongkat penyangga" ini bertahan sangat lama, bahkan dari versi lama.
Koreksi untuk masalah nomor 1
Untuk menyelesaikan masalah ini dengan benar, saya harus mengotak-atik kesimpulan debut, yang baru saja saya terapkan untuk tugas ini. Saya tahu bahwa dalam beberapa kasus, IDA berhasil menampilkan tautan tentang PC (dalam instruksi di mana ada lompatan pada tabel offset, yang tidak jauh dari instruksi saat ini, ditambah dengan case-index), tetapi untuk instruksi lea
, pemetaan alamat tidak diimplementasikan dengan benar. Akibatnya, saya menemukan instruksi lompatan seperti itu, dan menemukan flag apa yang harus diatur sehingga PC dengan tanda kurung ditampilkan:

Perbaiki masalah nomor 1 case processor_t::ev_ana_insn: { insn_t *out = va_arg(va, insn_t*); if (ana_addr) break; ana_addr = 1; if (ph.ana_insn(out) <= 0) { ana_addr = 0; break; } ana_addr = 0; for (int i = 0; i < UA_MAXOP; ++i) { op_t &op = out->ops[i]; switch (op.type) { case o_near: case o_mem: { if (out->itype != 0x76 || op.n != 0 || (op.phrase != 0x09 && op.phrase != 0x0A) || (op.addr == 0 || op.addr >= (1 << 23)) || op.specflag1 != 2)
Koreksi untuk masalah nomor 2
Semuanya sederhana di sini. Cukup sembunyikan alamat ke rentang tertentu: 0xFF0000-0xFFFFFF (untuk RAM) dan 0xC00000-0xC000FF (untuk memori video VDP ). Hal utama di sini adalah memfilter menurut jenis operan o_near
dan o_mem
.
Perbaiki masalah nomor 2 case processor_t::ev_ana_insn: { insn_t *out = va_arg(va, insn_t*); if (ana_addr) break; ana_addr = 1; if (ph.ana_insn(out) <= 0) { ana_addr = 0; break; } ana_addr = 0; for (int i = 0; i < UA_MAXOP; ++i) { op_t &op = out->ops[i]; switch (op.type) { case o_near: case o_mem: { op.addr &= 0xFFFFFF;
Koreksi untuk masalah nomor 3
Sebenarnya, untuk menambahkan opcode yang diinginkan, Anda harus:
- Tentukan indeks untuk opcode baru. Semua indeks baru harus dimulai dengan
CUSTOM_INSN_ITYPE
enum m68k_insn_type_t { M68K_linea = CUSTOM_INSN_ITYPE, M68K_linef, };
- lineA / lineF opcodes dipicu jika byte ditemukan dalam kode: 0xA0 / 0xF0 . Jadi kita membaca satu byte
- Dapatkan tautan ke vektor handler. Dalam 64 yard pertama dari header, dalam kasus saya, ada vektor interupsi. Pada posisi 0x0A dan 0x0B ada penangan lineA / lineF :
value = get_dword(0x0A * sizeof(uint32));
- Di
ev_emu_insn
tambahkan ev_emu_insn
ke handler dan ke pernyataan berikut agar aliran kode tidak terputus:
insn->add_cref(insn->Op1.addr, 0, fl_CN);
- Di
ev_out_mnem
opcode khusus kami:
const char *mnem = (outbuffer->insn.itype == M68K_linef) ? "line_f" : "line_a"; outbuffer->out_custom_mnem(mnem);

Solusi untuk Masalah 3 enum m68k_insn_type_t { M68K_linea = CUSTOM_INSN_ITYPE, M68K_linef, }; case processor_t::ev_ana_insn: { insn_t *out = va_arg(va, insn_t*); if (ana_addr) break; uint16 itype = 0; ea_t value = out->ea; uchar b = get_byte(out->ea); if (b == 0xA0 || b == 0xF0) { switch (b) { case 0xA0: itype = M68K_linea; value = get_dword(0x0A * sizeof(uint32)); break; case 0xF0: itype = M68K_linef; value = get_dword(0x0B * sizeof(uint32)); break; } out->itype = itype; out->size = 2; out->Op1.type = o_near; out->Op1.offb = 1; out->Op1.dtype = dt_dword; out->Op1.addr = value; out->Op1.phrase = 0x0A; out->Op1.specflag1 = 2; out->Op2.type = o_imm; out->Op2.offb = 1; out->Op2.dtype = dt_byte; out->Op2.value = get_byte(out->ea + 1); } return out->size; } break; case processor_t::ev_emu_insn: { const insn_t *insn = va_arg(va, const insn_t*); if (insn->itype == M68K_linea || insn->itype == M68K_linef) { insn->add_cref(insn->Op1.addr, 0, fl_CN); insn->add_cref(insn->ea + insn->size, insn->Op1.offb, fl_F); return 1; } } break; case processor_t::ev_out_mnem: { outctx_t *outbuffer = va_arg(va, outctx_t *); if (outbuffer->insn.itype != M68K_linea && outbuffer->insn.itype != M68K_linef) break; const char *mnem = (outbuffer->insn.itype == M68K_linef) ? "line_f" : "line_a"; outbuffer->out_custom_mnem(mnem); return 1; } break;
Perbaiki untuk Masalah 4
Ini dipecahkan dengan cara ini: kami menemukan opcode untuk instruksi trap
, kami mendapatkan indeks dari instruksi, dan kami mengambil vektor handler dari indeks ini. Anda mendapatkan sesuatu seperti ini:

Solusi untuk Masalah 4 case processor_t::ev_emu_insn: { const insn_t *insn = va_arg(va, const insn_t*); if (insn->itype == 0xB6)
Koreksi untuk masalah nomor 5
Di sini juga, semuanya sederhana: pertama, saring dengan operasi movea.w
. Kemudian, jika operan adalah dari jenis kata, dan mengacu pada RAM, kami membuat referensi dengan cara yang curam, relatif terhadap basis 0xFF0000. Ini akan terlihat seperti ini:

Perbaiki masalah nomor 5 case processor_t::ev_ana_insn: { insn_t *out = va_arg(va, insn_t*); if (ana_addr) break; ana_addr = 1; if (ph.ana_insn(out) <= 0) { ana_addr = 0; break; } ana_addr = 0; for (int i = 0; i < UA_MAXOP; ++i) { op_t &op = out->ops[i]; switch (op.type) { case o_imm: { if (out->itype != 0x7F || op.n != 0)
Kesimpulan
Sebenarnya, memperbaiki modul yang ada bukanlah tugas yang sangat sederhana, jika menyangkut bukan hanya implementasi opcodes yang tidak diketahui, tetapi sesuatu yang lebih rumit.
Butuh waktu berjam-jam untuk debug implementasi yang ada, pemahaman tentang apa yang terjadi di dalamnya (kadang-kadang bahkan kebalikan dari modul persen). Tetapi hasilnya sepadan.
Tautan ke sumber: https://github.com/lab313ru/m68k_fixer