
Hallo an alle,
Nach einer ziemlich langen Zeit seit dem Schreiben des ersten Artikels habe ich mich immer noch entschlossen, wenn auch ein wenig, Artikel zum Thema Modifizieren / Verbessern von IDA Pro zu schreiben.
In diesem Artikel wird erläutert, wie Sie die Pfosten in den Prozessormodulen ordnungsgemäß beheben, deren Quelle Sie nicht haben, die Pfosten jedoch nicht live übertragen. Leider können nicht alle der unten aufgeführten Probleme auf die Pfosten zurückgeführt werden, sodass Entwickler sie wahrscheinlich nicht implementieren werden.
Wir lokalisieren Fehler
Hinweis: Im Folgenden werden Fehler im Motorola M68000- Modul (mein Favorit und sehr häufig verwendet) berücksichtigt.
Also, die erste Neigung : Adressierung relativ zum PC- Register. Der Fehler ist, dass die zerlegte Auflistung für solche Anweisungen nicht immer korrekt ist. Schauen Sie sich den Screenshot an:

Hier scheint es keinen Fehler zu geben. Darüber hinaus stört sein Vorhandensein die Analyse nicht. Der Opcode ist jedoch nicht korrekt zusammengestellt. Schauen wir uns den Dysasmus in einem Online-Disassembler an:

Wir sehen, dass die Adressierung relativ zum PC- Register sein sollte, weil Die Link-Zieladresse fällt in den signed short
Bereich.
Cant zwei : "Spiegel" für RAM und einige andere Regionen. Weil Da die Adressierung in m68k 24-Bit ist, müssen alle Aufrufe an die höheren (oder umgekehrt, niedrigeren) Regionen in den gleichen Bereich wie Querverweise umgeleitet werden.
Es gibt drei Überhöhungen (eher nicht einmal eine Überhöhung, sondern mangelnde Funktionalität): die sogenannten Emulatoren lineA ( 1010 ) und lineF ( 1111 ). Dies sind solche Opcodes, für die der grundlegende Befehlssatz nicht ausreichte, weshalb sie in besonderer Weise von Interruptvektoren verarbeitet werden müssen. Die Größe der Opcodes hängt nur von der Implementierung auf Prozessorebene ab. Ich habe nur eine Zwei-Byte-Implementierung gesehen. Wir werden hinzufügen.
Jam 4 : Trap # N- Anweisungen geben den Hooks selbst keine Crefs .
Jamb fünf : Die Anweisung movea.w sollte eine vollständige XRef zu einer Adresse aus einem Wortlink erstellen , aber wir haben nur eine Wortnummer .
Wir beheben Fehler (Vorlage)
Um zu verstehen, wie ein bestimmtes Prozessormodul repariert wird, müssen Sie verstehen, welche Möglichkeiten wir zu diesem Thema im Prinzip haben und was eine „Korrektur“ darstellt.
Eigentlich ist der "Fixer" ein gewöhnliches Plugin. Es kann in Python geschrieben werden , aber ich habe alles in den "Pluspunkten" getan. Nur die Portabilität leidet, aber wenn sich jemand verpflichtet, das Plugin in Python neu zu schreiben, bin ich sehr dankbar.
Erstellen Sie zunächst ein leeres DLL- Projekt in Visual Studio : Datei-> Neu-> Projekt-> Windows-Desktop-Assistent-> Dynamische Linkbibliothek (.dll). Aktivieren Sie außerdem das Kontrollkästchen Leeres Projekt und deaktivieren Sie den Rest:

Entpacken Sie das IDA SDK und schreiben Sie es in die Visual Studio- Makros (ich werde 2017 verwenden ), damit Sie in Zukunft problemlos darauf verweisen können. Gleichzeitig fügen wir ein Makro für den Pfad zu IDA Pro hinzu .
Gehen Sie zu Ansicht -> Andere Fenster -> Eigenschaften-Manager :

Weil Wir arbeiten mit SDK Version 7.0 , die Kompilierung erfolgt durch den x64- Compiler. Wählen Sie daher Debug | x64 -> Microsoft.Cpp.x64.user -> Eigenschaften :

Klicken Sie im Abschnitt Benutzermakros auf die Schaltfläche Makro hinzufügen und schreiben Sie dort das Makro IDA_SDK mit dem Pfad, in den Sie das SDK entpackt haben:

Wir machen dasselbe mit IDA_DIR (dem Pfad zu Ihrem IDA Pro ):

Ich stelle fest, dass der IDA standardmäßig auf % Programme% eingestellt ist , was Administratorrechte erfordert.
Entfernen wir auch die Win32- Konfiguration (in diesem Artikel wird die Kompilierung für x86- Systeme nicht beeinflusst), wobei nur die Option x64 übrig bleibt.
Erstellen Sie eine leere Datei ida_plugin.cpp . Wir fügen noch keinen Code hinzu.
Jetzt ist es möglich, die Codierung und andere Einstellungen für C ++ festzulegen :



Wir schreiben die Einschlüsse:

Und Bibliotheken aus dem SDK :


Fügen Sie nun die Codevorlage hinzu:
Ida_plugin.cpp Code #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 };
Wir beheben Fehler (wir verstehen die Vorlage)
Die Funktionen print_op()
und print_insn()
werden benötigt, um zu verstehen, welche Flags vom aktuellen Prozessormodul für bestimmte Anweisungen gesetzt werden. Dies ist notwendig, wenn wir einige Flags für die vorhandenen Opcodes finden möchten, um sie dann beim Reparieren zu verwenden.
Tatsächlich ist der Hauptteil unseres "Fix" die Funktion hook_idp()
. Darin müssen wir für unsere Bedürfnisse drei Rückrufe implementieren:
processor_t::ev_ana_insn
: Wird benötigt, wenn einige Opcodes im Prozessormodul nicht implementiert sindprocessor_t::ev_emu_insn
: hier können sie Querverweise auf Daten / Code erstellen, auf die durch neue Opcodes verwiesen wird (oder auf alte nicht verwiesen wird)processor_t::ev_out_mnem
: neue processor_t::ev_out_mnem
müssen irgendwie ausgegeben werden. Es ist alles hier
Die Funktion init_plugin()
unser Patch in andere Prozessormodule init_plugin()
.
Nun, und vor allem - wir legen den gesamten Rückruf auf die Ereignisse des Prozessormoduls:
hook_to_notification_point(HT_IDP, hook_idp, NULL);
Der Trick mit der globalen Variablen ana_addr
benötigt, damit ana_insn
nicht ana_addr
, wenn versucht wird, Informationen zu einer Anweisung ana_insn
, die wir nicht manuell analysieren. Ja, leider hält diese "Krücke" sehr lange, auch von den alten Versionen.
Korrektur für Problem Nummer 1
Um dieses Problem richtig zu lösen, musste ich viel an der Debüt-Schlussfolgerung basteln, die ich gerade für diese Aufgabe implementiert habe. Ich wusste, dass in einigen Fällen die IDA erfolgreich Links über den PC anzeigt (in Anweisungen, in denen ein Sprung auf die Offset-Tabelle erfolgt, die nicht weit von der aktuellen Anweisung entfernt ist, plus einem Fallindex), aber für die lea
Anweisung ist die Adresszuordnung nicht korrekt implementiert. Als Ergebnis fand ich eine solche Sprunganweisung und fand heraus, welche Flags gesetzt werden sollten, damit der PC mit Klammern angezeigt wird:

Beheben Sie das Problem Nummer 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)
Korrektur für Problem Nummer 2
Hier ist alles einfach. Maskieren Sie einfach die Adressen in einem bestimmten Bereich: 0xFF0000-0xFFFFFF (für RAM) und 0xC00000-0xC000FF (für VDP- Videospeicher). Die Hauptsache hier ist, nach dem Typ des Operanden o_near
und o_mem
.
Problem Nummer 2 beheben 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;
Korrektur für Problem Nummer 3
Um den gewünschten Opcode hinzuzufügen, müssen Sie Folgendes tun:
- Definieren Sie Indizes für neue Opcodes. Alle neuen Indizes müssen mit
CUSTOM_INSN_ITYPE
beginnen
enum m68k_insn_type_t { M68K_linea = CUSTOM_INSN_ITYPE, M68K_linef, };
- lineA / lineF- Opcodes werden ausgelöst, wenn Bytes im Code 0xA0 / 0xF0 gefunden werden . Also lesen wir ein Byte
- Holen Sie sich einen Link zu einem Handler-Vektor. In den ersten 64 Metern des Headers gibt es in meinem Fall Interruptvektoren. An den Positionen 0x0A und 0x0B gibt es LineA / LineF-Handler :
value = get_dword(0x0A * sizeof(uint32));
ev_emu_insn
Sie in ev_emu_insn
den ev_emu_insn
und der folgenden Anweisung ev_emu_insn
hinzu, damit der Code-Fluss nicht unterbrochen wird:
insn->add_cref(insn->Op1.addr, 0, fl_CN);
ev_out_mnem
in ev_out_mnem
unseren benutzerdefinierten Opcode aus:
const char *mnem = (outbuffer->insn.itype == M68K_linef) ? "line_f" : "line_a"; outbuffer->out_custom_mnem(mnem);

Lösung zu Problem 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;
Behebung von Problem 4
Es wird auf diese Weise gelöst: Wir finden den Opcode für den trap
Befehl, wir erhalten den Index aus dem Befehl und wir nehmen einen Handler-Vektor aus diesem Index. Sie bekommen so etwas:

Lösung zu Problem 4 case processor_t::ev_emu_insn: { const insn_t *insn = va_arg(va, const insn_t*); if (insn->itype == 0xB6)
Korrektur für Problem Nummer 5
Auch hier ist alles einfach: Zuerst filtern wir nach der Operation movea.w
. Wenn der Operand vom Worttyp ist und sich auf RAM bezieht, machen wir eine Referenz in steiler Weise relativ zur Basis 0xFF0000. Es wird so aussehen:

Beheben Sie das Problem Nummer 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)
Schlussfolgerungen
In der Tat ist das Reparieren vorhandener Module keine sehr einfache Aufgabe, wenn es nicht nur um die Implementierung unbekannter Opcodes geht, sondern auch um etwas Komplizierteres.
Das Debuggen der vorhandenen Implementierung dauert viele Stunden, um zu verstehen, was darin geschieht (manchmal sogar die Umkehrung des Prozentmoduls). Aber das Ergebnis ist es wert.
Link zur Quelle: https://github.com/lab313ru/m68k_fixer