Actualización de IDA Pro. Arreglamos jambas de módulos de procesador


Hola a todos


Después de mucho tiempo desde el momento de escribir el primer artículo, todavía decidí, aunque sea un poco, escribir artículos sobre el tema de modificar / mejorar IDA Pro .


Este artículo discutirá cómo arreglar adecuadamente las jambas en esos módulos de procesador, cuya fuente no tiene, pero las jambas no dan vida. Desafortunadamente, no todos los problemas enumerados a continuación se pueden atribuir a las jambas, por lo que es poco probable que los desarrolladores los implementen.


Localizamos errores


Nota: en lo sucesivo, los errores se considerarán en el módulo Motorola M68000 (mi favorito y muy usado).


Entonces, el primer no puede : direccionamiento relativo al registro de la PC . El error es que la lista desmontada para tales instrucciones no siempre es correcta. Echa un vistazo a la captura de pantalla:

Parece que no hay error aquí. Además, su presencia no interfiere con el análisis. Pero, el código de operación no está ensamblado correctamente. Veamos el disasma en algunos desensambladores en línea:

Vemos que el direccionamiento debe ser relativo al registro de PC , porque La dirección de destino del enlace se encuentra dentro del rango signed short .


Cant dos : "espejos" para RAM, y algunas otras regiones. Porque Como el direccionamiento en m68k es de 24 bits, todas las llamadas a las regiones más altas (o viceversa, más bajas) deben redirigirse al mismo rango que las referencias cruzadas.


Hay tres cant (más bien, ni siquiera un cant, sino la falta de funcionalidad): los llamados emuladores lineA ( 1010 ) y lineF ( 1111 ). Estos son códigos de operación para los cuales el conjunto básico de comandos no fue suficiente, por lo tanto, deben ser procesados ​​de manera especial por vectores de interrupción. El tamaño de los códigos de operación depende solo de la implementación a nivel de procesador. Solo vi una implementación de dos bytes. Agregaremos


Atasco cuatro : las instrucciones de trampa #N no dan crefs a los ganchos en sí.


Jamb cinco : la instrucción movea.w debe hacer una referencia completa a una dirección desde un enlace de palabra , pero solo tenemos un número de palabra .


Solucionamos errores (plantilla)


Para comprender cómo reparar un módulo de procesador específico, debe comprender qué posibilidades tenemos sobre este tema en principio y qué constituye una "solución".


En realidad, el "fijador" es un complemento ordinario. Es algo así como, se puede escribir en Python , pero hice todo en las "ventajas". Solo la portabilidad sufre, pero si alguien se compromete a reescribir el complemento en Python , estaré muy agradecido.


Primero, cree un proyecto DLL vacío en Visual Studio : Archivo-> Nuevo-> Proyecto-> Asistente de escritorio de Windows-> Biblioteca de enlaces dinámicos (.dll), también marcando la casilla de verificación Proyecto vacío y desmarcando el resto:


Desempaquete el SDK de IDA y escríbalo en las macros de Visual Studio (usaré 2017 ) para que en el futuro pueda consultarlo fácilmente. Al mismo tiempo, agregaremos una macro para la ruta a IDA Pro .


Vaya a Ver -> Otras ventanas -> Administrador de propiedades :


Porque estamos trabajando con SDK versión 7.0 , la compilación será por compilador x64 . Por lo tanto, seleccione Depurar | x64 -> Microsoft.Cpp.x64.user -> Propiedades :


Haga clic en el botón Agregar macro en la sección Macros de usuario y escriba allí la macro IDA_SDK con la ruta donde desempaquetó el SDK :


Hacemos lo mismo con IDA_DIR (la ruta a su IDA Pro ):

Observo que la IDA está configurada de manera predeterminada en % Archivos de programa% , lo que requiere derechos de administrador.


Eliminemos también la configuración de Win32 (en este artículo no afectaré la compilación para sistemas x86 ), dejando solo la opción x64 .


Cree un archivo vacío ida_plugin.cpp . Todavía no estamos agregando código.
Ahora puede establecer la codificación y otras configuraciones para C ++ :




Escribimos las inclusiones:


Y bibliotecas del SDK :



Ahora agregue la plantilla de código:


Código 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 }; 

Solucionamos errores (entendemos la plantilla)


Las funciones print_op() y print_insn() son necesarias para comprender qué indicadores establece el módulo de procesador actual para ciertas instrucciones. Esto es necesario si queremos encontrar algunos indicadores para los códigos de operación existentes, y luego usarlos para corregirlos.


En realidad, el cuerpo de nuestro "arreglo" es la función hook_idp() . En él, para nuestras necesidades, necesitamos implementar tres devoluciones de llamada:


  1. processor_t::ev_ana_insn : necesario si no hay implementación de algunos processor_t::ev_ana_insn en el módulo procesador
  2. processor_t::ev_emu_insn : aquí puede crear referencias cruzadas en datos / código, a los que hacen referencia nuevos códigos de operación (o los antiguos no están referenciados)
  3. processor_t::ev_out_mnem : los nuevos processor_t::ev_out_mnem deben de alguna manera ser enviados. Todo esta aqui

La función init_plugin() nuestro parche se cargue en otros módulos de procesador.
Bueno, y lo más importante: colgamos toda la devolución de llamada en los eventos del módulo del procesador:


 hook_to_notification_point(HT_IDP, hook_idp, NULL); 

El truco con la variable global ana_addr necesario para que ana_insn no entre en recursión al intentar obtener información sobre una instrucción que no analizamos manualmente. Sí, por desgracia, esta "muleta" dura mucho tiempo, incluso desde las versiones anteriores.

Corrección para el problema número 1


Para resolver adecuadamente este problema, tuve que jugar mucho con la conclusión de debut, que acabo de implementar para esta tarea. Sabía que en algunos casos, la IDA muestra con éxito enlaces sobre la PC (en las instrucciones donde hay un salto en la tabla de desplazamiento, que no está lejos de la instrucción actual, más un índice de casos), pero para la instrucción lea , la asignación de direcciones no se implementa correctamente. Como resultado, encontré una instrucción de salto de este tipo y descubrí qué indicadores deberían establecerse para que se muestre la PC con corchetes:



Solucione el problema número 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; 

Corrección para el problema número 2


Todo es simple aquí. Simplemente enmascare las direcciones a un rango específico: 0xFF0000-0xFFFFFF (para RAM) y 0xC00000-0xC000FF (para memoria de video VDP ). Lo principal aquí es filtrar por el tipo de operando o_near y o_mem .


Solucione el problema número 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; 

Corrección para el problema número 3


En realidad, para agregar el código de operación deseado, debe:


  1. Definir índices para nuevos códigos de operación. Todos los nuevos índices deben comenzar con CUSTOM_INSN_ITYPE
     enum m68k_insn_type_t { M68K_linea = CUSTOM_INSN_ITYPE, M68K_linef, }; 
  2. Los códigos de operación lineA / lineF se activan si se encuentran bytes en el código: 0xA0 / 0xF0 . Entonces leemos un byte
  3. Obtenga un enlace a un vector de controlador. En los primeros 64 metros del encabezado, en mi caso, hay vectores de interrupción. En las posiciones 0x0A y 0x0B hay manejadores lineA / lineF :
     value = get_dword(0x0A * sizeof(uint32)); // ... value = get_dword(0x0B * sizeof(uint32)); 
  4. En ev_emu_insn agregue ev_emu_insn a los controladores y a la siguiente instrucción para que el flujo de código no se interrumpa:
      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. En ev_out_mnem nuestro código de operación personalizado:
     const char *mnem = (outbuffer->insn.itype == M68K_linef) ? "line_f" : "line_a"; outbuffer->out_custom_mnem(mnem); 


Solución al problema 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; 

Solución para el problema 4


Se resuelve de esta manera: encontramos el código de operación para la instrucción trap , obtenemos el índice de la instrucción y tomamos un vector controlador de este índice. Obtienes algo como esto:



Solución al problema 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; 

Corrección para el problema número 5


Aquí, también, todo es simple: primero, filtramos por la operación movea.w . Luego, si el operando es del tipo de palabra y se refiere a RAM, hacemos una referencia de manera pronunciada, en relación con la base 0xFF0000. Se verá así:



Solucione el problema número 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; 

Conclusiones


De hecho, arreglar los módulos existentes no es una tarea muy simple, si se trata no solo de la implementación de códigos de operación desconocidos, sino de algo más complicado.
Se necesitan muchas horas para depurar la implementación existente, una comprensión de lo que está sucediendo en ella (a veces incluso al revés del módulo de porcentaje). Pero el resultado lo vale.


Enlace a la fuente: https://github.com/lab313ru/m68k_fixer

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


All Articles