Switch cases in jump table

I have code that obfuscated with jump table calls.
IDA don’t recognize the pattern so I “specify switch idiom”.
Jumps are marked as cases and if the next jump is a const, IDA will analyze it, if not, its just

__asm { BR              X8 }

it instead of creating switches.

  1. How to make IDA show the switch cases?
  2. If I have a jump map(obtained from debugger), how can I force IDA to take the “right path” in the psuedo code? I tried ida_xref.add_cref but it only marks jumps in graph view.
    Thannks

In binary ninja it works out of the box(although it needs help NOPing useless instructions):

We will need the sample binary file for investigation.
Or, rather, the idb file, since you specifed the switch idiom’s parameters.

sure. where to send it?
Thanks

it seems you can upload it right here (or open a support ticket on our customer portal support.hex-rays.com)

Thanks. Opened a ticket SUPPORT-4728

Thank you for the idb. I was able to produce a switch using these parameters:
For 11D3A4:

  • address of jump table: 0x1896B0
  • number of elements = 4
  • start of switch idiom: 0x11D394 (CMP X8, #3)
  • input register: X8

Bu default, you get somewhat ugly switch ( (unsigned __int64)jpt_11D334[v0] ), caused by the reuse of X8 for the indirect jump. To fix it, mark the table load (LDR X8, [X20,X8,LSL#3]) as skippable (switch) instruction.

Alas, xrefs are not enough; you’d need to patch the code with unconditional jumps. Perhaps a plugin/script with ev_ana_insn hook could work, where you return a B loc_XXXX instead of the original BR X8 for cases where you know the destination.

1 Like

It doesn’t actually solve anything. I still need to manually mark each jump(somehow the api marks all correct tables and refs but IDA fails to create the actual switches) but it also creates switches for the number of functions in the jump table per jump.


Doesn’t look like a progress vs binja. Also, this is very simple example. doing this for 0x11D5A0(395 switches) is impossible

Well, you can check how the switch info is set in the SDK’s plugins/uiswitch (uses set_switch_info()) and automate it the way you like.

For 11D3A4:

  • address of jump table: 0x1896B0
  • number of elements = 4
  • start of switch idiom: 0x11D394 (CMP X8, #3)
  • input register: X8

Bu default, you get somewhat ugly switch ( (unsigned __int64)jpt_11D334[v0] ), caused by the reuse of X8 for the indirect jump. To fix it, mark the table load (LDR X8, [X20,X8,LSL#3]) as skippable (switch) instruction.

Can you please post the pseudo code of this that you get?

Original pseudocode:

  v4 = *(_QWORD *)(_ReadStatusReg(TPIDR_EL0) + 40);
  while ( 1 )
  {
    v3 = linux_eabi_syscall(__NR_gettimeofday, &v2, 0);
    v1 = v3;
    v0 = 3;
    if ( v3 <= 0xFFFFF000 )
      v0 = 1;
    switch ( (unsigned __int64)jpt_11D334[v0] )
    {
      case 0uLL:
        while ( 1 )
          ;
      case 1uLL:
        return;
      case 2uLL:
        continue;
      case 3uLL:
        *(_DWORD *)((__int64 (*)(void))`typeinfo for'`anonymous namespace'::itanium_demangle::NameWithTemplateArgs)() = -v1;
        __asm { BR              X8 }
        return;
    }
  }

After marking the LDR at 000000000011D3A0:

  v4 = *(_QWORD *)(_ReadStatusReg(TPIDR_EL0) + 40);
  while ( 1 )
  {
    v3 = linux_eabi_syscall(__NR_gettimeofday, &v2, 0);
    v1 = v3;
    v0 = 3;
    if ( v3 <= 0xFFFFF000 )
      v0 = 1;
    switch ( v0 )
    {
      case 0LL:
        while ( 1 )
          ;
      case 1LL:
        return;
      case 2LL:
        continue;
      case 3LL:
        *(_DWORD *)((__int64 (*)(void))`typeinfo for'`anonymous namespace'::itanium_demangle::NameWithTemplateArgs)() = -v1;
        __asm { BR              X8 }
        return;
    }
  }

thanks. if I keep marking the jumps, I get

void sub_11D2E8()
{
  signed __int64 v0; // x0
  __int64 v1; // x8
  struct timeval v2; // [xsp+30h] [xbp-20h] BYREF
  int v3; // [xsp+44h] [xbp-Ch]
  __int64 v4; // [xsp+48h] [xbp-8h]

  v4 = *(_QWORD *)(_ReadStatusReg(TPIDR_EL0) + 40);
  while ( 2 )
  {
    v0 = linux_eabi_syscall(__NR_gettimeofday, &v2, 0);
    v3 = v0;
    v1 = 3;
    if ( (unsigned int)v0 <= 0xFFFFF000 )
      v1 = 1;
    switch ( v1 )
    {
      case 0LL:
        while ( 1 )
          ;
      case 1LL:
        return;
      case 2LL:
        continue;
      case 3LL:
        *(_DWORD *)((__int64 (*)(void))`typeinfo for'`anonymous namespace'::itanium_demangle::NameWithTemplateArgs)() = -(int)v0;
        return;
    }
  }
}

if I compare it with binja, I can clearly see and rename the function:


Binja did it all with no intervention other then NOPing case 0