升级IDA Pro。 我们修复处理器模块的边框


大家好


自撰写第一篇文章以来已经有相当长的时间了,尽管有一点点我还是决定写一些关于修改/改进IDA Pro的文章


本文将讨论如何正确修复那些处理器模块中的边框,这些处理器模块的来源不多,但这些边框却无法解决。 不幸的是,并非下面列出的所有问题都可以归因于外框,因此开发人员不太可能实现它们。


我们本地化错误


注意:以下将考虑Motorola M68000模块中的错误(我最喜欢并且经常使用)。


因此,第一个不能 :相对于PC寄存器的寻址。 错误是这些说明的反汇编清单并不总是正确的。 看一下屏幕截图:

这里似乎没有错误。 而且,它的存在不会干扰分析。 但是,操作码未正确汇编。 让我们看一下一些在线反汇编程序中的异常:

我们看到寻址应该相对于PC寄存器,因为 链接目标地址在带signed short范围内。


第二句 :RAM和其他一些区域的“镜像”。 因为 由于m68k中的寻址 24位的,因此所有对较高区域(反之亦然,较低区域)的调用都必须重定向到与交叉引用相同的范围。


共有三种情况 (宁可,甚至没有,但缺乏功能):所谓的lineA1010 )和lineF1111 )仿真器。 这些操作码没有基本的命令集,因此必须通过中断向量以特殊方式对其进行处理。 操作码的大小仅取决于处理器级别的实现。 我只看到了两个字节的实现。 我们将添加。


卡纸四陷阱#N指令不给钩子本身提供引用


门框五movea.w指令应从链接对地址进行完整的外部参照 ,但我们只有字号


我们修复错误(模板)


为了了解如何修复特定的处理器模块,您需要了解我们在该主题上原则上具有哪些可能性以及什么构成“修复”。


实际上,“修复程序”是一个普通的插件。 它有点像可以用Python编写,但是我做了所有的“加”操作。 只有可移植性受到影响,但是如果有人承诺用Python重写该插件,我将非常感激。


首先,在Visual Studio中创建一个空的DLL项目: 文件->新建->项目-> Windows桌面向导->动态链接库 (.dll),也通过选中空项目 复选框并取消选中其余部分:


解压缩IDA SDK并将其写入Visual Studio宏(我将使用2017 ),以便将来您可以轻松引用它。 同时,我们将为IDA Pro的路径添加一个宏。


转到查看 -> 其他Windows- > 媒体资源管理器


因为 我们正在使用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_insn()来了解当前处理器模块为某些指令设置的标志。 如果我们要为现有操作码找到一些标志,然后在修复时使用它们,则这是必需的。


实际上,我们的“修复程序”的主体是hook_idp()函数。 在其中,根据我们的需要,我们需要实现三个回调:


  1. processor_t::ev_ana_insn :如果处理器模块中未实现某些操作码,则需要
  2. processor_t::ev_emu_insn :在这里您可以在数据/代码上创建交叉引用,这些交叉引用由新操作码引用(或不引用旧操作码)
  3. processor_t::ev_out_mnem :必须以某种方式输出新的操作码。 都在这里

init_plugin()函数init_plugin()我们的补丁程序加载到其他处理器模块中。
好吧,最重要的是,我们在处理器模块的事件上挂起了整个回调:


 hook_to_notification_point(HT_IDP, hook_idp, NULL); 

需要使用全局变量ana_addr的技巧,以便在尝试获取有关我们不手动解析的指令的信息时, ana_insn不会递归。 是的,a,这个“拐杖”即使在旧版本中也能持续很长时间。

更正问题编号1


为了正确解决此问题,我不得不对首次亮相的结论进行很多修改,而我刚刚为此任务实现了这一结论。 我知道在某些情况下, IDA会成功显示有关PC的链接(在偏移量表上有跳转的指令中,该指令与当前指令相距不远,加上case-index),但是对于lea指令,地址映射未正确实现。 结果,我找到了这样一条跳转指令,并找出了应该设置哪些标志以便显示带括号的PC



解决问题编号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 (用于RAM)和0xC00000-0xC000FF (用于VDP视频存储器)。 这里最主要的是按操作数o_nearo_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. 定义新操作码的索引。 所有新索引都必须以CUSTOM_INSN_ITYPE开头
     enum m68k_insn_type_t { M68K_linea = CUSTOM_INSN_ITYPE, M68K_linef, }; 
  2. 如果在代码0xA0 / 0xF0中找到字节,则会触发lineA / lineF操作码。 所以我们读一个字节
  3. 获取指向处理程序向量的链接。 在我的情况下,在标头的前64码中有中断向量。 在位置0x0A0x0B处有 lineA / lineF处理程序
     value = get_dword(0x0A * sizeof(uint32)); // ... value = get_dword(0x0B * sizeof(uint32)); 
  4. ev_emu_insnev_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


它是通过以下方式解决的:找到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; 

结论


实际上,如果不仅涉及未知操作码的实现,而且涉及更复杂的事情,则修复现有模块不是一件容易的事。
调试现有的实现需要花费许多时间,这需要了解其中发生的事情(有时甚至是百分比模块的反面)。 但是结果值得。


链接到源代码: https : //github.com/lab313ru/m68k_fixer

Source: https://habr.com/ru/post/zh-CN424263/


All Articles