我正在研究用于ARM Thumb2(剥离)二进制文件的反汇编程序。我已经可以恢复使用直接跳转的基本块(BB)的CFG。我的下一个目标是处理间接跳转。我目前正在基于以下示例来确定switch语句的目标,我将根据以下示例进行讨论:

arm-linux-gnueabihf-gcc-4.8 -o3 -mthumb main.c -o main.out


使用main拆卸objdump显示开关表数据字以0x83d8开头,如下所示:

int main(int argc, char** argv){
  int i = 0;
  switch (argc) {
    case 0: i++; break;
    case 1: i+=2; break;
    case 3: i+=3; break;
    case 6: i+=6; break;
    case 5: i+=8; break;
    case 7: i+=10; break;
    default: i+=87; break;
  }
  return i;
}


观察结果:开关表不偏移。
使用ldr.w pc, [base, index, lsl #2]实现间接分支,其中基数(在这里r2)存储数据字的开头地址,而索引(在这里r3)用于计算偏移量。 br />

上述观点可以概括吗?换句话说,我是否可以假定这是(大多数)ARM编译器实现开关语句的(事实上)标准方法?我在这里看不到ARM / Thumb之间的任何模式切换。例如,可以在0x8428处找到默认情况,但相应的地址存储为0x00008429


#1 楼

不,您不能假设大多数ARM编译器将采用这种方式来实现switch语句。例如,以下是gcc 5.2.1上的同一代码:

cosimo:~ moyix$ arm-none-eabi-gcc-5.2.1 -o3 -mthumb -c x.c -o x.o
cosimo:~ moyix$ arm-none-eabi-objdump -d x.o

x.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <main>:
   0:   b580        push    {r7, lr}
   2:   b084        sub sp, #16
   4:   af00        add r7, sp, #0
   6:   6078        str r0, [r7, #4]
   8:   6039        str r1, [r7, #0]
   a:   2300        movs    r3, #0
   c:   60fb        str r3, [r7, #12]
   e:   687b        ldr r3, [r7, #4]
  10:   2b07        cmp r3, #7
  12:   d81d        bhi.n   50 <main+0x50>
  14:   687b        ldr r3, [r7, #4]
  16:   009a        lsls    r2, r3, #2
  18:   4b13        ldr r3, [pc, #76]   ; (68 <main+0x68>)
  1a:   18d3        adds    r3, r2, r3
  1c:   681b        ldr r3, [r3, #0]
  1e:   469f        mov pc, r3
  20:   68fb        ldr r3, [r7, #12]
  22:   3301        adds    r3, #1
  24:   60fb        str r3, [r7, #12]
  26:   e017        b.n 58 <main+0x58>
  28:   68fb        ldr r3, [r7, #12]
  2a:   3302        adds    r3, #2
  2c:   60fb        str r3, [r7, #12]
  2e:   e013        b.n 58 <main+0x58>
  30:   68fb        ldr r3, [r7, #12]
  32:   3303        adds    r3, #3
  34:   60fb        str r3, [r7, #12]
  36:   e00f        b.n 58 <main+0x58>
  38:   68fb        ldr r3, [r7, #12]
  3a:   3306        adds    r3, #6
  3c:   60fb        str r3, [r7, #12]
  3e:   e00b        b.n 58 <main+0x58>
  40:   68fb        ldr r3, [r7, #12]
  42:   3308        adds    r3, #8
  44:   60fb        str r3, [r7, #12]
  46:   e007        b.n 58 <main+0x58>
  48:   68fb        ldr r3, [r7, #12]
  4a:   330a        adds    r3, #10
  4c:   60fb        str r3, [r7, #12]
  4e:   e003        b.n 58 <main+0x58>
  50:   68fb        ldr r3, [r7, #12]
  52:   3357        adds    r3, #87 ; 0x57
  54:   60fb        str r3, [r7, #12]
  56:   46c0        nop         ; (mov r8, r8)
  58:   68fb        ldr r3, [r7, #12]
  5a:   0018        movs    r0, r3
  5c:   46bd        mov sp, r7
  5e:   b004        add sp, #16
  60:   bc80        pop {r7}
  62:   bc02        pop {r1}
  64:   4708        bx  r1
  66:   46c0        nop         ; (mov r8, r8)
  68:   00000000    .word   0x00000000


还有更复杂的方案,包括对开关值进行二进制搜索以查找正确的情况。通常,您需要进行更复杂的分析以恢复switch语句。有关此主题的一些学术著作已经存在,例如:
关于第二个问题,使用ldr pc进行的跳转必须明确指定模式。由于使用-mthumb编译了代码,因此您处于拇指模式,并且要保持拇指模式,这些地址需要将位0设置为1(请参见本页上的注释[a])。