
Olá pessoal
Depois de muito tempo desde a redação do primeiro artigo, ainda decidi, embora um pouco, escrever artigos sobre o tópico de modificar / melhorar o IDA Pro .
Este artigo discutirá como corrigir adequadamente os batentes nesses módulos do processador, cuja fonte você não possui, mas os batentes não fornecem ao vivo. Infelizmente, nem todos os problemas listados abaixo podem ser atribuídos aos batentes, portanto é improvável que os desenvolvedores os implementem.
Localizamos bugs
Nota: os erros a seguir no módulo Motorola M68000 (o meu favorito e usado com muita frequência) serão considerados.
Portanto, o primeiro não pode : endereçamento relativo ao registro do PC . O erro é que nem sempre a listagem desmontada de tais instruções está correta. Dê uma olhada na captura de tela:

Parece não haver erro aqui. Além disso, sua presença não interfere na análise. Mas, o opcode não está montado corretamente. Vejamos o desastre em algum desmontador on-line:

Vemos que o endereçamento deve ser relativo ao registro PC , porque O endereço de destino do link está dentro do signed short
alcance signed short
.
Cant dois : "espelhos" para RAM, e algumas outras regiões. Porque Como o endereçamento no m68k é de 24 bits, todas as chamadas para as regiões superior (ou vice-versa, inferior) devem ser redirecionadas para o mesmo intervalo das referências cruzadas.
Existem três cant (em vez disso, nem um cant, mas a falta de funcionalidade): os chamados emuladores lineA ( 1010 ) e lineF ( 1111 ). Esses são códigos de operação para os quais o conjunto básico de comandos não foi suficiente; portanto, eles devem ser processados de maneira especial por vetores de interrupção. O tamanho dos opcodes depende apenas da implementação no nível do processador. Vi apenas uma implementação de dois bytes. Nós vamos adicionar.
Atolamento quatro : as instruções #N da armadilha não dão crefs aos próprios ganchos.
A instrução cinco : movea.w deve fazer uma refex completa para um endereço a partir de um link de palavra , mas só temos um número de palavra .
Corrigimos bugs (modelo)
Para entender como consertar um módulo processador específico, você precisa entender as possibilidades que temos sobre esse assunto em princípio e o que constitui uma “correção”.
Na verdade, o "fixador" é um plugin comum. É, tipo, pode ser escrito em Python , mas fiz tudo nos "prós". Apenas a portabilidade sofre, mas se alguém se comprometer a reescrever o plugin no Python , ficarei muito agradecido.
Primeiro, crie um projeto DLL vazio no Visual Studio : Arquivo-> Novo-> Projeto-> Assistente para Área de Trabalho do Windows-> Biblioteca de vínculo dinâmico (.dll), também marcando a caixa de seleção Projeto Vazio e desmarcando o restante:

Descompacte o IDA SDK e escreva-o nas macros do Visual Studio (usarei 2017 ) para que no futuro você possa consultá-lo facilmente. Ao mesmo tempo, adicionaremos uma macro para o caminho para o IDA Pro .
Vá para Exibir -> Outras janelas -> Gerenciador de propriedades :

Porque estamos trabalhando com o SDK versão 7.0 , a compilação será feita pelo x64- compiler. Portanto, selecione Depurar | x64 -> Microsoft.Cpp.x64.user -> Propriedades :

Clique no botão Adicionar macro na seção Macros do usuário e escreva a macro IDA_SDK lá com o caminho em que você descompactou o SDK :

Faça o mesmo com o IDA_DIR (o caminho para o seu IDA Pro ):

Observo que o IDA é definido por padrão como % Program Files% , o que requer direitos de administrador.
Vamos também remover a configuração do Win32 (neste artigo, não afetarei a compilação para sistemas x86 ), deixando apenas a opção x64 .
Crie um arquivo ida_plugin.cpp vazio. Ainda não estamos adicionando código.
Agora é possível definir a codificação e outras configurações para C ++ :



Escrevemos as inclusões:

E bibliotecas do SDK :


Agora adicione o modelo 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 };
Corrigimos bugs (entendemos o modelo)
As funções print_op()
e print_insn()
são necessárias para entender quais sinalizadores são configurados pelo módulo atual do processador para determinadas instruções. Isso é necessário se quisermos encontrar alguns sinalizadores para os códigos de operação existentes e usá-los na correção.
Na verdade, o corpo da nossa "correção" é a função hook_idp()
. Nele, para nossas necessidades, precisamos implementar três retornos de chamada:
processor_t::ev_ana_insn
: necessário se não houver implementação de alguns opcodes no módulo processadorprocessor_t::ev_emu_insn
: aqui você pode criar referências cruzadas em dados / código, que são referenciados por novos opcodes (ou antigos não são referenciados)processor_t::ev_out_mnem
: novos opcodes devem ser emitidos de alguma forma. Está tudo aqui
A função init_plugin()
nosso patch seja carregado em outros módulos do processador.
Bem, e mais importante - desligamos todo o retorno de chamada sobre os eventos do módulo do processador:
hook_to_notification_point(HT_IDP, hook_idp, NULL);
O truque com a variável global ana_addr
necessário para que ana_insn
não entre em recursão ao tentar obter informações sobre uma instrução que não analisamos manualmente. Sim, infelizmente, essa "muleta" dura muito tempo, mesmo a partir das versões antigas.
Correção para o problema número 1
Para resolver adequadamente esse problema, tive que mexer muito na conclusão de estréia, que acabei de implementar para esta tarefa. Eu sabia que, em alguns casos, o IDA exibe com êxito links sobre o PC (em instruções em que há um salto na tabela de deslocamento, que não está longe da instrução atual, mais um índice de caso), mas para a instrução lea
, o mapeamento de endereços não é implementado corretamente. Como resultado, encontrei essa instrução de salto e descobri quais sinalizadores devem ser configurados para que o PC com colchetes seja exibido:

Corrigir o 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)
Correção para o problema número 2
Tudo é simples aqui. Apenas oculte os endereços em um intervalo específico: 0xFF0000-0xFFFFFF (para RAM) e 0xC00000-0xC000FF (para memória de vídeo VDP ). O principal aqui é filtrar pelo tipo de operando o_near
e o_mem
.
Corrigir o 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;
Correção para o problema número 3
Na verdade, para adicionar o código de operação desejado, você deve:
- Defina índices para novos códigos de operação. Todos os novos índices devem começar com
CUSTOM_INSN_ITYPE
enum m68k_insn_type_t { M68K_linea = CUSTOM_INSN_ITYPE, M68K_linef, };
- os códigos de linha lineA / lineF são acionados se forem encontrados bytes no código: 0xA0 / 0xF0 . Então lemos um byte
- Obtenha um link para um vetor manipulador. Nos primeiros 64 metros do cabeçalho, no meu caso, existem vetores de interrupção. Nas posições 0x0A e 0x0B, existem manipuladores lineA / lineF :
value = get_dword(0x0A * sizeof(uint32));
- Em
ev_emu_insn
adicione ev_emu_insn
aos manipuladores e à seguinte instrução para que o fluxo de código não seja interrompido:
insn->add_cref(insn->Op1.addr, 0, fl_CN);
- Em
ev_out_mnem
nosso código de operação personalizado:
const char *mnem = (outbuffer->insn.itype == M68K_linef) ? "line_f" : "line_a"; outbuffer->out_custom_mnem(mnem);

Solução para o Problema 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;
Correção para o problema 4
Isso é resolvido da seguinte maneira: encontramos o código de operação para a instrução trap
, obtemos o índice da instrução e pegamos um vetor manipulador desse índice. Você obtém algo assim:

Solução para o Problema 4 case processor_t::ev_emu_insn: { const insn_t *insn = va_arg(va, const insn_t*); if (insn->itype == 0xB6)
Correção para o problema número 5
Aqui também tudo é simples: primeiro, filtramos pela operação movea.w
. Então, se o operando for do tipo de palavra e se referir à RAM, fazemos uma referência íngreme, em relação à base 0xFF0000. Ficará assim:

Corrigir o 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)
Conclusões
De fato, consertar os módulos existentes não é uma tarefa muito simples, se se refere não apenas à implementação de códigos opc desconhecidos, mas a algo mais complicado.
Demora muitas horas para depurar a implementação existente, uma compreensão do que está acontecendo nela (às vezes até o inverso do módulo de porcentagem). Mas o resultado vale a pena.
Link para a fonte: https://github.com/lab313ru/m68k_fixer