为什么这个 ARM 二进制文件会抛出“非法指令”错误并退出?

逆向工程 手臂
2021-07-02 06:08:05

问题

我希望有人可以帮助我确定为什么这个二进制文件不会执行。

它是一个闭源的、剥离的 ARM 二进制文件。也就是说,它可以在互联网上免费下载,所以在这篇文章的底部有一个链接。

目标是从固件映像中提取的 ARM 二进制文件。我已经设置了一个 ARM VM,但也尝试在 Pi 上运行二进制文件,结果相同。

这是我所看到的:

root@debian-armel:/tmp/squashfs-root/usr/bin# ./my_arm_bin 
Illegal instruction

“非法指令”错误并不是很有帮助……所以我深入挖掘了一点。

我的虚拟机

我不认为问题出在我的 VM 上。这是一个非常标准的 ARM VM 设置。来自:https : //people.debian.org/~aurel32/qemu/armel/

使用debian_squeeze_armel_standard.qcow2initrd.img-2.6.32-5-versatilevmlinuz-2.6.32-5-versatile它与 QEMU 一起启动,并转发了一些端口(ssh、http、31337 用于 gdb 内容)。我可以毫无问题地在系统上执行其他 ARM 二进制文件,包括从同一固件映像中提取的其他二进制文件。

此外,正如之前提到的,我尝试将二进制文件放到 Pi 上,但没有运气。我在 pi 上以 root 身份以及在 chroot 环境中使用提取的固件映像的 rootfs 进行了尝试,结果相同:Illegal Instruction.

r2 信息

rabin2 -I my_arm_bin

Warning: Cannot initialize dynamic strings
arch     arm
binsz    44831825
bintype  elf
bits     32
canary   false
class    ELF32
crypto   false
endian   little
havecode true
lang     c
linenum  false
lsyms    false
machine  ARM
maxopsz  16
minopsz  1
nx       false
os       linux
pcalign  0
pic      false
relocs   false
rpath    NONE
static   true
stripped true
subsys   linux
va       true

额外调试(gdb w/ gef on Pi)

因此,是时候附加一个调试器并准确查看实际抛出该错误的指令了。这是在 Pi 上完成的。

在启动和进入中断后,使用ni单步,我看到:

gef> x/20i $pc
=> 0x796a0: mov r11, #0
   0x796a4: mov lr, #0
   0x796a8: pop {r1}        ; (ldr r1, [sp], #4)
   0x796ac: mov r2, sp
   0x796b0: push    {r2}        ; (str r2, [sp, #-4]!)
   0x796b4: push    {r0}        ; (str r0, [sp, #-4]!)
   0x796b8: ldr r12, [pc, #16]  ; 0x796d0
   0x796bc: push    {r12}       ; (str r12, [sp, #-4]!)
   0x796c0: ldr r0, [pc, #12]   ; 0x796d4
   0x796c4: ldr r3, [pc, #12]   ; 0x796d8
   0x796c8: bl  0x4021a0
   0x796cc: bl  0x401fa0
   0x796d0: andeq   r2, r12, #200, 2    ; 0x32
   0x796d4: andeq   r10, r1, r12, lsl #11
   0x796d8: andeq   r2, r12, #40, 2
   0x796dc: ldr r3, [pc, #20]   ; 0x796f8
   0x796e0: ldr r2, [pc, #20]   ; 0x796fc
   0x796e4: add r3, pc, r3
   0x796e8: ldr r2, [r3, r2]
   0x796ec: cmp r2, #0
gef> 

这一切看起来都是有效的 ARM 指令。

在情况下,它相关的-所有LDR指令(0x796b8,0x796c0,0x796c4)在gdb执行时是给这样的信息:Cannot access memory at address 0x0一些 mov 指令也会抛出这个。

0x796c8: bl 0x4021a0

->   0x796c8                  bl     0x4021a0
   \->    0x4021a0                  ldr    pc,  [pc,  #-4]  ; 0x4021a4

最终我们到达这里:

gef> x/20i $pc
=> 0x20c1b30:   push    {r4, r5, r6, r7, lr}
   0x20c1b34:   sub sp, sp, #300    ; 0x12c
   0x20c1b38:   movw    r12, #0                 
   0x20c1b3c:   mov r5, r3                          --> Here is our illegal instruction
   0x20c1b40:   movt    r12, #0
   0x20c1b44:   str r1, [sp, #4]
   0x20c1b48:   movw    r1, #65336  ; 0xff38
   0x20c1b4c:   cmp r12, #0
   0x20c1b50:   ldr r3, [sp, #4]
   0x20c1b54:   str r2, [sp, #8]
   0x20c1b58:   ldrne   r12, [r12]
   0x20c1b5c:   add r2, r3, #1
   0x20c1b60:   str r0, [sp, #12]
   0x20c1b64:   movw    r3, #56376  ; 0xdc38
   0x20c1b68:   ldr r7, [sp, #8]
   0x20c1b6c:   movw    r0, #3092   ; 0xc14
   0x20c1b70:   ldr lr, [sp, #328]  ; 0x148
   0x20c1b74:   clzne   r12, r12
   0x20c1b78:   movt    r0, #685    ; 0x2ad
   0x20c1b7c:   movt    r1, #827    ; 0x33b

当然,可以在此处找到二进制文件的副本:https : //mega.nz/#!CKxBQKaI!T__d9pjpOn_rPtfvPNkkPsFWHTjg7u-vDt5AK6610ug

那么寄存器只是没有初始化为正确的值吗?这怎么可能?

套用 Archer 的话:我没有得到什么?...我认为核心概念。

我可能在这里遗漏了一个重要的想法。我希望有人可以帮助填补空白。

更新 1:

在@0xC0000022L 的建议下,我研究了确保我的 VM/Pi 的 ARM 版本与二进制版本匹配。据我所知,他们是这样做的。只是将来自 VM 的二进制文件与我的 ARM 二进制文件进行比较,我正在尝试运行它们的 ABI 匹配(32 位 ARMv5):

$ file my_arm_bin 
my_arm_bin: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.16, stripped
 $ file /usr/bin/id
/usr/bin/id: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.32, stripped

更新 2:

在@perror 建议中,我尝试强制二进制文件以拇指模式执行。这里的上下文是在“手臂”(非拇指)模式下运行时的反汇编:

gef> x/10i $pc
=> 0x20c1b38:   movw    r12, #0
   0x20c1b3c:   mov r5, r3
   0x20c1b40:   movt    r12, #0
   0x20c1b44:   str r1, [sp, #4]
   0x20c1b48:   movw    r1, #65336  ; 0xff38
   0x20c1b4c:   cmp r12, #0
   0x20c1b50:   ldr r3, [sp, #4]
   0x20c1b54:   str r2, [sp, #8]
   0x20c1b58:   ldrne   r12, [r12]
   0x20c1b5c:   add r2, r3, #1

强制拇指模式,我现在看到:

gef> set arm force-mode thumb
gef> x/10i $pc
=> 0x20c1b38:   stmia   r0!, {}
   0x20c1b3a:   b.n 0x20c213e
   0x20c1b3c:   str r3, [r0, r0]
   0x20c1b3e:   b.n 0x20c1e82
   0x20c1b40:   stmia   r0!, {}
   0x20c1b42:   b.n 0x20c21c6
   0x20c1b44:   asrs    r4, r0, #32
   0x20c1b46:   b.n 0x20c1664
   0x20c1b48:   subs    r0, r7, #4
   0x20c1b4a:   b.n 0x20c216c

在执行之前强制使用拇指模式,程序立即退出并出现以下错误:

[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x6ac

启动可执行文件、中断和切换模式会以非法指令错误退出。

1个回答

从初步分析来看,二进制文件看起来至少是 ARMv7。它在 qemu-user 下运行没有问题。

$ qemu-arm --version
qemu-arm version 2.11.0
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers

$ qemu-arm ./my_arm_bin 
+++++++++++++++++++++++++++++++++
Name:storage
Version:1.05.3
Build date:Apr 24 2018 13:16:31
Desc:DriverManager release
+++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++
Name:manager
Version:1.03.1
Build date:Apr 24 2018 13:16:31
Desc:
+++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++
Name:manager
Version:1.03.1
Build date:Apr 24 2018 13:16:31
Desc:
+++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++
Name:guictrls
Version:1.05.5
Build date:Apr 24 2018 13:16:31
Desc:GUI ctrls Relese
+++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++
Name:OS
Version:1.00.1
Build date:Apr 24 2018 13:16:31
Desc:OS Relese
------snip-------------------

但是,在 ARMv6 QEMU VM 下运行时,它会因非法指令而崩溃,类似于您的指令。深入挖掘确实有一些指令在 ARMv5 下无效。

─────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:arm ]────
    0x20c1b2c                  bl     0x20d434c
    0x20c1b30                  push   {r4,  r5,  r6,  r7,  lr}
    0x20c1b34                  sub    sp,  sp,  #300    ; 0x12c
 →  0x20c1b38                  movw   r12,  #0
    0x20c1b3c                  mov    r5,  r3
    0x20c1b40                  movt   r12,  #0
    0x20c1b44                  str    r1,  [sp,  #4]
    0x20c1b48                  movw   r1,  #65336   ; 0xff38
    0x20c1b4c                  cmp    r12,  #0
──────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "my_arm.bin", stopped, reason: SIGILL
────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
[#0] 0x20c1b38 → movw r12,  #0
[#1] 0x796cc → bl 0x401fa0

在它上面坠毁在movw r12, #0at 上0x20c1b38现在movw在 ARMv7 中引入,在 ARMv5 中不可用。同样该movt指令在 v5 下也是无效的。

在此处输入图片说明

图片来源

这解释了 ARM v5/v6 qemu 崩溃的原因。因此,要运行二进制文件,您至少需要一个 ARMv7 QEMU vm 或运行 ARMv7 处理器的 Raspberry Pi 2。