在(32位)ARM Linux内核中,如何区分代码部分中嵌入的数据和指令?
最好采用轻量级方法,如位掩码,可以轻松实现。将解析器嵌入内核是不明智的。
答案 0 :(得分:2)
一般来说,你所要求的是不可能的。
考虑这个函数碰巧使用太大的数据值来编码为立即数:
@ void patch_nop(void *code_addr);
patch_nop:
ldr r1, =0xe1a00000
str r1, [r0]
bx lr
当它通过汇编程序并返回时,它看起来像这样:
$ 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
由于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)
有。嵌入在指令流中的是数据,这是一条指令。甚至不是偶然发生的数据看起来像一条指令。没有什么可以从那些32位中单独推断出它们不会被执行(好吧,至少不是来自那个位置)。
有一些启发式方法可能有助于做出有根据的猜测,特别是如果可以假设任何其他先验知识缩小范围:
任何可以被编码为立即数的东西几乎肯定是一条指令,因为编译器/汇编器首先不会将它作为文字发出。但是,理想情况下,您至少想知道前面的代码是ARM还是Thumb,以便知道适当的直接范围 * 。
任何未定义的指令通常都是数据,除非碰巧它想要故意引发undef异常的代码。而且你基本上必须要有大部分的反汇编程序来检查某些东西是否与任何已定义的编码相匹配。在ARM / Thumb之上。
紧跟无条件分支后的任何内容都可能是文字数据,特别是如果您有符号并且可以告诉它非常接近以下函数的开头,或者您对数据有一些了解#39;重新寻找它看起来像数据。如果您只是眼睛反汇编,后一点肯定是相关的 - 在实践中,字面数据往往是地址之类的东西,一旦您将代码看作是一般的拇指†一个整体。
检查某些内容是否为文字的最可靠方法是查看前面的代码(最多1025条指令),检查针对该地址的PC相对负载。您只需要检查文字加载编码(这是您的简单位掩码操作),然后解码相对偏移量(如果找到)。理想情况下,您希望解决ARM / Thumb问题,以避免因检查不适当的编码而出现误报,并且在最绝对的病态情况下,您仍然可能遇到前面文字池中的某些数据,这些数据看起来像文字加载定位您的地址;永远不要说永远。
当然,这仍然是假设编译器/汇编器自动发出的文字池;当谈到完全手写的汇编代码时,所有的赌注都是关闭的:
patch_nop2:
ldr r1, [pc, #-4]
mov r0, r0
str r1, [r0]
bx lr
是代码吗?是。是数据吗?是。
*顺便提一下,ARM和Thumb代码之间的辨别可归结为与此问题基本相同的问题 - &#34;这种位模式意味着什么?&#34; - 如果没有外部帮助,也同样重要。
†没有双关语