ترقية IDA Pro. نقوم بإصلاح انحرافات وحدات المعالج


مرحبا بالجميع


بعد وقت طويل منذ كتابة المقال الأول ، ما زلت قررت ، وإن كان قليلاً ، كتابة مقالات حول موضوع تعديل / تحسين IDA Pro .


ستناقش هذه المقالة كيفية إصلاح عضادات العوارض بشكل صحيح في وحدات المعالج هذه ، والتي لا تملك مصدرها ، ولكن الدعامات لا تعطي البث المباشر. لسوء الحظ ، لا يمكن أن تُعزى جميع المشاكل المذكورة أدناه إلى عضادات ، لذا من غير المرجح أن ينفذها المطورون.


نحن نوطن البق


ملاحظة: فيما يلي الأخطاء في وحدة Motorola M68000 (المفضلة والأكثر استخدامًا) سيتم النظر فيها.


لذا ، فإن أول شيء غير قادر على : معالجة نسبة إلى سجل الكمبيوتر . الخطأ هو أن القائمة المفككة لمثل هذه التعليمات ليست صحيحة دائمًا. ألق نظرة على لقطة الشاشة:

يبدو أنه لا يوجد خطأ هنا. علاوة على ذلك ، لا يتداخل وجودها مع التحليل. ولكن ، لم يتم تجميع كود التشغيل بشكل صحيح. دعونا نلقي نظرة على التشنج في بعض أدوات التفكيك على الإنترنت:

نرى أن العنوان يجب أن يكون نسبيًا إلى تسجيل الكمبيوتر الشخصي لأنه يقع عنوان وجهة الرابط ضمن النطاق signed short .


الكانتان : "مرايا" لذاكرة الوصول العشوائي ، وبعض المناطق الأخرى. لأن نظرًا لأن العنوان في m68k هو 24 بت ، فيجب إعادة توجيه جميع المكالمات إلى المناطق الأعلى (أو العكس بالعكس ، الأدنى) إلى نفس النطاق مثل الإسنادات الترافقية.


هناك ثلاثة غير قادر (وليس حتى غير قادر ، ولكن الافتقار إلى الوظائف): ما يسمى بمحاكيات lineA ( 1010 ) و lineF ( 1111 ). هذه هي رموز opcodes التي لم تكن مجموعة الأوامر الأساسية كافية لها ، لذلك يجب معالجتها بطريقة خاصة عن طريق ناقلات المقاطعة. يعتمد حجم الشفرات فقط على التنفيذ على مستوى المعالج. رأيت فقط تنفيذ ثنائي البايت. سنضيف.


المربى الرابع : تعليمات المصيدة N لا تعطي crefs للخطافات نفسها.


Jamb Five : يجب أن تجعل تعليمات movea.w xref كاملًا لعنوان من رابط كلمة ، ولكن ليس لدينا سوى رقم كلمة .


نقوم بإصلاح الخلل (القالب)


من أجل فهم كيفية إصلاح وحدة معالج معينة ، تحتاج إلى فهم الإمكانيات التي لدينا في هذا الموضوع من حيث المبدأ وما يشكل "إصلاح".


في الواقع ، "المثبت" هو مكون إضافي عادي. يمكن كتابة ذلك في Python ، ولكنني فعلت كل شيء في "الإيجابيات". تعاني قابلية النقل فقط ، ولكن إذا تعهد شخص ما بإعادة كتابة المكون الإضافي في Python ، فسأكون ممتنًا للغاية.


أولاً ، قم بإنشاء مشروع DLL فارغ في Visual Studio : ملف-> جديد-> مشروع-> معالج سطح مكتب Windows-> مكتبة الارتباط الديناميكي (.dll) ، أيضًا عن طريق تحديد مربع الاختيار مشروع فارغ وإلغاء تحديد الباقي:


قم بفك حزمة IDA SDK ، واكتبها في وحدات ماكرو Visual Studio ( سأستخدم 2017 ) بحيث يمكنك الرجوع إليها بسهولة في المستقبل. في الوقت نفسه ، سنضيف ماكرو للمسار إلى IDA Pro .


انتقل إلى عرض -> نوافذ أخرى -> إدارة الممتلكات :


لأن نحن نعمل مع SDK الإصدار 7.0 ، سيكون التجميع بواسطة مترجم x64 . لذلك ، حدد Debug | x64 -> Microsoft.Cpp.x64.user -> الخصائص :


انقر فوق الزر " إضافة ماكرو" في قسم " وحدات ماكرو المستخدم" ، واكتب الماكرو IDA_SDK هناك مع المسار الذي قمت بإخراج حزمة SDK منه :


نفعل نفس الشيء مع IDA_DIR (المسار إلى IDA Pro الخاص بك):

ألاحظ أن IDA يتم تعيينه افتراضيًا على ٪ Program Files٪ ، الأمر الذي يتطلب حقوق المسؤول.


لنقم أيضًا بإزالة تكوين Win32 (في هذه المقالة لن أؤثر على الترجمة لأنظمة x86 ) ، مع ترك خيار x64 فقط.


قم بإنشاء ملف ida_plugin.cpp فارغ. نحن لا نضيف رمز حتى الان.
الآن من الممكن ضبط الترميز والإعدادات الأخرى لـ C ++ :




نكتب الادراج:


والمكتبات من SDK :



الآن أضف قالب الكود:


كود Ida_plugin.cpp
 #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 }; 

نقوم بإصلاح الخلل (نحن نفهم القالب)


print_op() الدالتين print_op() و print_insn() لفهم العلامات التي تم تعيينها بواسطة وحدة المعالج الحالية للحصول على إرشادات معينة. هذا ضروري إذا أردنا العثور على بعض العلامات لرموز التشغيل الموجودة ، ثم استخدامها عند الإصلاح.


في الواقع ، نص "الإصلاح" هو hook_idp() . لتلبية احتياجاتنا ، نحتاج إلى تنفيذ ثلاث عمليات استدعاء:


  1. processor_t::ev_ana_insn : مطلوب إذا لم يكن هناك تنفيذ لبعض رموز opcodes في وحدة المعالج
  2. processor_t::ev_emu_insn : هنا يمكنك إنشاء إسنادات ترافقية للبيانات / التعليمات البرمجية ، والتي يتم الرجوع إليها بواسطة رموز opcodes الجديدة (أو لا يتم الرجوع إلى الرموز القديمة)
  3. processor_t::ev_out_mnem : يجب أن يتم إنتاج processor_t::ev_out_mnem opcodes الجديدة بطريقة أو بأخرى. كل شيء هنا

init_plugin() الدالة init_plugin() التصحيح الخاص بنا من التحميل في وحدات المعالج الأخرى.
حسنًا ، والأهم من ذلك - نعلق المكالمة بأكملها على أحداث وحدة المعالج:


 hook_to_notification_point(HT_IDP, hook_idp, NULL); 

هناك حاجة إلى الحيلة مع المتغير العام ana_addr بحيث لا ana_insn في العودية عند محاولة الحصول على معلومات حول تعليمات لا نقوم بتحليلها يدويًا. نعم ، للأسف ، هذا "العكاز" يستمر لفترة طويلة جدًا ، حتى من الإصدارات القديمة.

تصحيح المشكلة رقم 1


من أجل حل هذه المشكلة بشكل صحيح ، كان علي أن أتعامل كثيرًا مع الاستنتاج الأول ، الذي قمت بتطبيقه للتو لهذه المهمة. كنت أعلم أنه في بعض الحالات ، تعرض المؤسسة الدولية للتنمية بنجاح روابط حول جهاز الكمبيوتر (في التعليمات حيث توجد قفزة على طاولة الإزاحة ، والتي ليست بعيدة عن التعليمات الحالية ، بالإضافة إلى فهرس الحالة) ، ولكن بالنسبة لتعليمات lea ، لم يتم تنفيذ تعيين العنوان بشكل صحيح. ونتيجة لذلك ، وجدت تعليمات القفز هذه ، واكتشفت العلامات التي يجب تعيينها بحيث يتم عرض جهاز الكمبيوتر مع الأقواس:



إصلاح المشكلة رقم 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) // lea table(pc),Ax break; short diff = op.addr - out->ea; if (diff >= SHRT_MIN && diff <= SHRT_MAX) { out->Op1.type = o_displ; out->Op1.offb = 2; out->Op1.dtype = dt_dword; out->Op1.phrase = 0x5B; out->Op1.specflag1 = 0x10; } } break; } } return out->size; } break; 

تصحيح المشكلة رقم 2


كل شيء بسيط هنا. ما عليك سوى إخفاء العناوين لنطاق معين: 0xFF0000-0xFFFFFF (لذاكرة الوصول العشوائي) و 0xC00000-0xC000FF (لذاكرة فيديو VDP ). الشيء الرئيسي هنا هو التصفية حسب نوع المعامل o_near و o_mem .


إصلاح المشكلة رقم 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; // for any mirrors if ((op.addr & 0xE00000) == 0xE00000) // RAM mirrors op.addr |= 0x1F0000; if ((op.addr >= 0xC00000 && op.addr <= 0xC0001F) || (op.addr >= 0xC00020 && op.addr <= 0xC0003F)) // VDP mirrors op.addr &= 0xC000FF; } break; } } return out->size; } break; 

تصحيح المشكلة رقم 3


في الواقع ، لإضافة كود التشغيل المطلوب ، يجب عليك:


  1. تحديد مؤشرات لرموز opcodes الجديدة. يجب أن تبدأ جميع الفهارس الجديدة بـ CUSTOM_INSN_ITYPE
     enum m68k_insn_type_t { M68K_linea = CUSTOM_INSN_ITYPE, M68K_linef, }; 
  2. يتم تشغيل opcodes lineA / lineF إذا تم العثور على وحدات البايت في التعليمات البرمجية: 0xA0 / 0xF0 . لذا نقرأ بايت واحد
  3. الحصول على ارتباط إلى ناقل معالج. في أول 64 ياردة من الرأس ، في حالتي ، هناك ناقلات المقاطعة. في الموضعان 0x0A و 0x0B ، توجد معالجات lineA / lineF :
     value = get_dword(0x0A * sizeof(uint32)); // ... value = get_dword(0x0B * sizeof(uint32)); 
  4. في ev_emu_insn أضف ev_emu_insn إلى المعالجات وإلى العبارة التالية حتى لا تتم مقاطعة تدفق التعليمات البرمجية :
      insn->add_cref(insn->Op1.addr, 0, fl_CN); // code ref insn->add_cref(insn->ea + insn->size, insn->Op1.offb, fl_F); // flow ref 
  5. في ev_out_mnem كود التشغيل المخصص لدينا:
     const char *mnem = (outbuffer->insn.itype == M68K_linef) ? "line_f" : "line_a"; outbuffer->out_custom_mnem(mnem); 


حل المشكلة 3
 enum m68k_insn_type_t { M68K_linea = CUSTOM_INSN_ITYPE, M68K_linef, }; /* after includes */ 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; 

إصلاح المشكلة 4


يتم حلها بهذه الطريقة: نجد رمز opp لتعليمات trap ، نحصل على الفهرس من التعليمات ، ونأخذ ناقل معالج من هذا الفهرس. تحصل على شيء مثل هذا:



حل المشكلة 4
 case processor_t::ev_emu_insn: { const insn_t *insn = va_arg(va, const insn_t*); if (insn->itype == 0xB6) // trap #X { qstring name; ea_t trap_addr = get_dword((0x20 + (insn->Op1.value & 0xF)) * sizeof(uint32)); get_func_name(&name, trap_addr); set_cmt(insn->ea, name.c_str(), false); insn->add_cref(trap_addr, insn->Op1.offb, fl_CN); return 1; } } break; 

تصحيح المشكلة رقم 5


هنا أيضًا ، كل شيء بسيط: أولاً ، نقوم بالتصفية حسب عملية movea.w . بعد ذلك ، إذا كان المعامل من نوع الكلمة ، ويشير إلى ذاكرة الوصول العشوائي (RAM) ، فإننا نقوم بعمل الارتباط بطريقة رائعة ، بالنسبة إلى القاعدة 0xFF0000 الأساسية. سيبدو هذا:



إصلاح المشكلة رقم 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) // movea break; if (op.value & 0xFF0000 && op.dtype == dt_word) { op.value &= 0xFFFF; } } break; } } return out->size; } break; case processor_t::ev_emu_insn: { const insn_t *insn = va_arg(va, const insn_t*); for (int i = 0; i < UA_MAXOP; ++i) { const op_t &op = insn->ops[i]; switch (op.type) { case o_imm: { if (insn->itype != 0x7F || op.n != 0 || op.dtype != dt_word) // movea break; op_offset(insn->ea, op.n, REF_OFF32, BADADDR, 0xFF0000); } break; } } } break; 

الاستنتاجات


في الواقع ، إن إصلاح الوحدات الحالية ليست مهمة بسيطة للغاية ، إذا كانت لا تتعلق فقط بتنفيذ رموز opcodes غير المعروفة ، ولكن شيئًا أكثر تعقيدًا.
يستغرق الأمر عدة ساعات لتصحيح التطبيق الحالي ، وفهم ما يحدث فيه (أحيانًا حتى عكس وحدة النسبة المئوية). لكن النتيجة تستحق العناء.


رابط للمصدر: https://github.com/lab313ru/m68k_fixer

Source: https://habr.com/ru/post/ar424263/


All Articles