如何在ARM反汇编中检测拇指模式?

逆向工程 拆卸 部件 手臂
2021-06-23 03:53:59

我尝试为 ARM 构建一个小型反汇编程序,我想知道如何objdump设法从拇指模式指令(16 位指令宽)中整理出正常模式指令(32 位指令宽),而无需查看tCPSR 中标志。

但首先,让我们构建一个小示例并对其进行一些实验。

我写了一小段 ARM 程序集(gas语法)作为基础示例:

.arm
    mov fp, #0
    moveq   r1, r0
.thumb
    mov r0, #0
    mov fp, r0

然后,我像这样交叉编译它:

$> arm-none-eabi-gcc -Wall -Wextra -mlittle-endian -c -o arm_sample arm_sample.s

而且,这里是objdumpARM 目标文件的输出

$> objdump -d ./arm32_mov

./arm32_mov:     file format elf32-littlearm

Disassembly of section .text:
00000000 <.text>:
   0:   e3a0b000    mov fp, #0
   4:   01a01000    moveq   r1, r0
   8:   2000        movs    r0, #0
   a:   4683        mov fp, r0

但是,当我运行我的工具时,我得到:

 warning: decoder says at (0x8,0):'strmi r2, [r3], r0' : Unknown mnemonic
   0:   00 b0 a0 e3                 mov fp, #0
   4:   00 10 a0 01                 moveq   r1, r0
   8:   ...

我的工具基于libopcodes(完全像objdump),所以第三条指令只是被解释为仍然处于 32 位模式,而两条拇指模式指令只是被解释为一条 32 位指令,它给出strmi r2, [r3], r0.

我的问题是我不明白如何objdump知道在正常模式和拇指模式之间有一个切换。在发现这一点之前,我认为此信息仅在执行时通过tCPSR 状态寄存器中的标志值才可用

我试图查看代码,objdump但是,我没有看到任何依赖于架构的案例来处理 ARM 拇指模式的案例。所以,这对我来说仍然是个谜......

欢迎任何建议!

编辑

事实上,我处理了一个目标文件(用-c选项编译),所以没有那么多符号。但是,这是通过objdump以下方式获得的更详细的输出

$> objdump -x ./arm32_mov

./arm32_mov:     file format elf32-littlearm
./arm32_mov
architecture: armv4t, flags 0x00000010:
HAS_SYMS
start address 0x00000000
private flags = 5000000: [Version5 EABI]

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000000c  00000000  00000000  00000034  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000000  00000000  00000000  00000040  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00000000  00000000  00000040  2**0
                  ALLOC
  3 .ARM.attributes 00000016  00000000  00000000  00000040  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .ARM.attributes    00000000 .ARM.attributes

而且,这是输出readelf

$> readelf -a ./arm32_mov
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          148 (bytes into file)
  Flags:                             0x5000000, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 5

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 00000c 00  AX  0   0  4
  [ 2] .data             PROGBITS        00000000 000040 000000 00  WA  0   0  1
  [ 3] .bss              NOBITS          00000000 000040 000000 00  WA  0   0  1
  [ 4] .ARM.attributes   ARM_ATTRIBUTES  00000000 000040 000016 00      0   0  1
  [ 5] .shstrtab         STRTAB          00000000 000056 00003c 00      0   0  1
  [ 6] .symtab           SYMTAB          00000000 0001d4 000070 10      7   7  4
  [ 7] .strtab           STRTAB          00000000 000244 000007 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.
There are no program headers in this file.
There are no relocations in this file.
There are no unwind sections in this file.

Symbol table '.symtab' contains 7 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 SECTION LOCAL  DEFAULT    2 
     3: 00000000     0 SECTION LOCAL  DEFAULT    3 
     4: 00000000     0 NOTYPE  LOCAL  DEFAULT    1 $a
     5: 00000008     0 NOTYPE  LOCAL  DEFAULT    1 $t
     6: 00000000     0 SECTION LOCAL  DEFAULT    4 

No version information found in this file.
Attribute Section: aeabi
File Attributes
  Tag_CPU_arch: v4T
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1

解决方案(伊恩库克)

$ objdump --syms --special-syms ./arm32_mov

./arm32_mov:     file format elf32-littlearm

SYMBOL TABLE:
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l       .text  00000000 $a   <-- ARM code
00000008 l       .text  00000000 $t   <-- Thumb code
00000000 l    d  .ARM.attributes    00000000 .ARM.attributes

可以肯定的是,我再次交错了手臂代码、拇指代码和手臂代码。这是转储:

$> objdump --syms --special-syms ./arm32_mov

./arm32_mov:     file format elf32-littlearm

SYMBOL TABLE:
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l       .text  00000000 $a
00000008 l       .text  00000000 $t
0000000c l       .text  00000000 $a
00000000 l    d  .ARM.attributes    00000000 .ARM.attributes

您可以看到 ARM 符号在0x0处出现了两次,并且0xc围绕着拇指符号。

3个回答

ARM 目标文件应包含标识区域的符号,这些区域是 arm 代码 ( $a)、拇指代码 ( $t) 和文字数据 ( $d)。

您可以在 read-elf 输出中将它们视为符号 #4 和 #5。即偏移量0是手臂,偏移量8是拇指

如果您使用该--special-syms选项,obj-dump 也会输出这些符号

阅读ARM ELF ABI将帮助您了解正在发生的事情。

如果您打算尝试在 Apple (Mach-O) 可执行文件或固件 blob 中反汇编 ARM 代码,那么您将不得不使用另一种技术,因为这些符号将不存在。

这个问题在代码映射符号($a$t$d)可用的情况下得到解决但是,即使符号不可用,仍然可以为 Thumb-2 获得(几乎)完美的反汇编。

一种很有前途的技术是推测性反汇编,本文已经讨论过工具 Spedi在此处开源它至少在使用的一组有限的基准测试中优于 IDA Pro。如果可用,还可以指示 Spedi 使用 ARM 代码映射符号来获取基本事实。

该工具可以在此讨论的想法来增强拆卸,即使符号不可解决您的混合模式情况。

在编译 C/C++ 代码时,您还可以通过查看符号表来检测函数的 ARM/thumb 状态。地址的最低位对于 Thumb 设置为 1,对于 ARM 指令设置为 0。不过,要查看这一点,您可以使用readelf --symWhen usingobjdump -t andreadelf`。

$ readelf --syms foo.elf |grep strlen$
  4460: 08000195    16 FUNC    GLOBAL DEFAULT    2 strlen
$ objdump -t foo.elf |grep strlen$
  08000194 g     F .text  00000010 strlen

请注意,实际地址是 8000194(32 位对齐),但符号表条目(如 readelf 所示)显示 9000195(LSB 设置为指示拇指)。

这种方法只会告诉您函数开始时的 ARM/thumb 状态,要查看函数内的任何转换,您仍然需要查看其他答案中记录$t$a符号。

这个 LSB 的含义在ARM ELF ABI 中定义

5.5.3 符号值

除了符号值的正常规则外,以下规则也适用于 STT_FUNC 类型的符号:

  • 如果符号寻址一条 Arm 指令,则其值是指令的地址(在可重定位对象中,指令距包含它的部分开头的偏移量)。
  • 如果符号寻址 Thumb 指令,则其值是设置为零位的指令的地址(在可重定位对象中,设置为零位的部分偏移量)。
  • 为了重定位的目的,使用的值应该是指令的地址(st_value & ~1)。

注意:这允许链接器在不必参考映射的情况下区分 Arm 和 Thumb 代码符号。Arm 符号将始终具有偶数值,而 Thumb 符号将始终具有奇数值。但是,链接器应该在将区分位用于重定位之前从值中去除区分位。