Win*_*ser 2 architecture arm instruction-set disassembly
在(32 位)ARM Linux 内核中,如何区分代码部分中嵌入的数据和指令?
最好有一个轻量级的方法,比如位掩码,它可以很容易地实现。将反汇编程序嵌入内核是不明智的。
一般来说,你所要求的是不可能的。
考虑一下这个函数,它碰巧使用了一个太大而无法编码为立即数的数据值:
@ void patch_nop(void *code_addr);
patch_nop:
ldr r1, =0xe1a00000
str r1, [r0]
bx lr
Run Code Online (Sandbox Code Playgroud)
当它通过汇编程序并返回时,它看起来像这样:
$ arm-none-eabi-objdump -d a.out
a.out: file format elf32-littlearm
Disassembly of section .text:
00000000 <patch_nop>:
0: e59f1004 ldr r1, [pc, #4] ; c <patch_nop+0xc>
4: e5801000 str r1, [r0]
8: e12fff1e bx lr
c: e1a00000 .word 0xe1a00000
Run Code Online (Sandbox Code Playgroud)
多亏了 ELF 数据,我们仍然可以确定函数在哪里结束和文字池从哪里开始,但是 objdump 正在做的工作是挖掘部分和符号,这几乎不是“轻量级”的,谁说你有这些呢?如果你只有代码怎么办?
$ arm-none-eabi-objcopy -Obinary a.out bin
$ arm-none-eabi-objdump -D -marm -bbinary bin
bin: file format binary
Disassembly of section .data:
00000000 <.data>:
0: e59f1004 ldr r1, [pc, #4] ; 0xc
4: e5801000 str r1, [r0]
8: e12fff1e bx lr
c: e1a00000 nop ; (mov r0, r0)
Run Code Online (Sandbox Code Playgroud)
那里。嵌入在您的指令流中,您拥有数据,这是一条指令。甚至不小心碰巧看起来像指令的数据。从字面上看,您无法仅从这 32 位中推断出它们不会被执行(好吧,至少不会从该位置执行)。
有一些启发式方法可能有助于做出有根据的猜测,特别是如果可以假设任何额外的先验知识来缩小范围:
任何可以编码为立即数的东西几乎肯定是一条指令,因为编译器/汇编器首先不会将它作为文字发出。但是,您最好至少知道前面的代码是 ARM 还是 Thumb,以便知道合适的直接范围是*。
任何未定义指令通常都是数据,除非碰巧它的代码想要故意引发 undef 异常。而且您基本上必须拥有大部分反汇编程序来检查某些内容是否与任何定义的编码不匹配。在 ARM/Thumb 之上。
紧随无条件分支之后的任何内容都可能是文字数据,特别是如果您有符号并且可以看出它非常接近以下函数的开头,或者如果您对要查找的数据有一些了解并且它看起来像数据。如果你只是盯着反汇编,后一点当然是相关的——实际上,文字数据往往是地址之类的东西,一旦你把代码作为一个整体来看,它通常会像一个拇指酸痛†一样突出。
检查某些内容是否为文字的最可靠方法是查看前面的代码(最多 1025 条指令),检查针对该地址的 PC 相关负载。您只需要检查文字加载编码(这是您的简单位掩码操作),然后在找到相对偏移量时对其进行解码。理想情况下,您希望解决 ARM/Thumb 问题,以避免因检查不适当的编码而产生误报,并且在最绝对病态的情况下,您仍然可能会遇到前面的文字池中的一些数据,这些数据恰好看起来像文字负载目标你的地址; 永远不要把话说绝了。
当然,这仍然是假设编译器/汇编器自动发出的文字池;当谈到完全手写的汇编代码时,所有的赌注都没有了:
patch_nop2:
ldr r1, [pc, #-4]
mov r0, r0
str r1, [r0]
bx lr
Run Code Online (Sandbox Code Playgroud)
是代码吗?是的。是数据吗?是的。
* 顺便提一下,区分 ARM 和 Thumb 代码归结为与这个问题本质上相同的问题——“这个位模式是什么意思?” - 并且在没有外部帮助的情况下同样重要。
† 无意双关语