从ARM指令中区分数据

问题描述:

在(32位)ARM Linux内核中,如何区分嵌入代码段的数据和指令?从ARM指令中区分数据

最好有一个轻量级的方法,比如位掩码,它可以很容易地实现。将一个拆装器嵌入到内核中并不明智。

一般来说,你所要求的是不可能的。

考虑这个功能恰好使用的数据值过大,编码为直接:

@ 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之上。

  • 紧随无条件分支之后的任何东西都可能是文字数据,特别是如果您有符号并且可以告诉它非常接近以下函数的开头,或者您对所查找的数据有一定的了解,它看起来像数据。后一点当然是相关的,如果你只是眼球反汇编 - 在实践中,文字数据往往是地址的东西,通常脱颖而出像一个拇指拇指一旦你看整个代码。

  • 检查文字是否是文字的最可靠方法是查看前面的代码(最多1025条指令),检查以该地址为目标的PC相对负载。你只需要检查字面加载编码(这是你的简单掩码操作),然后解码相对偏移量,如果你找到一个。理想情况下,您想要解决ARM/Thumb的问题,以避免检测不适当的编码时出现误报,并且在最绝对的病态情况下,您仍然可以运行前面的文字池中的某些数据,看起来像是文字加载定位你的地址;永不说永不。

当然,这仍然是假设编译器/汇编程序自动发出的文字池;当涉及到完全手写汇编代码时,所有投注都关闭:

patch_nop2: 
    ldr r1, [pc, #-4] 
    mov r0, r0 
    str r1, [r0] 
    bx lr 

是代码?是。它是数据吗?是。 *顺便说一句,ARM和Thumb代码之间的区分归结为基本上与此相同的问题 - “这个位模式是什么意思?” - 在没有外部帮助的情况下也同样不重要。

†没有双关语意

+0

和[这里是一个更狡猾的一个(http://stackoverflow.com/q/38294896/3156750)。 – Notlikethat