我正在尝试让一个内存驻留的镜像来校验和本身,这被证明是说起来容易做起来难。
代码首先在交叉开发平台上编译,生成.elf输出。使用一个实用程序来剥离二进制映像,然后将该映像连同映像大小一起烧录到目标平台上的闪存中。当目标启动时,它将二进制文件复制到ram的正确区域,并跳转到该区域。该实用程序还计算elf中发往ram的所有字的校验和,该校验和也被烧录到闪存中。因此,我的镜像理论上可以使用先验的起始地址和闪存中保存的大小来校验和它自己的ram驻留镜像,并与闪存中保存的总和进行比较。
不管怎么说,这就是理论。问题是,一旦映像开始执行,随着变量的修改,.data
部分也会发生变化。到完成求和时,已经求和的图像不再是实用程序计算求和的图像。
通过将校验和例程移到应用程序中所有其他初始化的前面,我已经消除了由应用程序定义的变量引起的更改(如果完整性检查失败,为什么还要运行它,对吧?),但杀手是C运行时本身。似乎有一些项目与malloc
和指针转换相关,甚至在进入main()
之前就已经改变了。
整个自我校验和C代码的想法是不是很差劲?如果有一种方法可以将应用程序和CRT .data分到不同的部分,我就可以避免CRT的混乱,但有人可能会争辩说,如果目标是在执行(大部分)之前对映像进行完整性检查,那么初始化的CRT数据应该是其中的一部分。有没有办法让代码校验和在RAM中像这样呢?
顺便说一句,我似乎被这个要求卡住了。就我个人而言,我认为最好的方法是在传输到ram之前,对闪存中的二进制文件进行校验和,并信任加载程序和ram。偏执狂总会在某个地方结束,对吧?
其他细节:工具链是GNU,图像包含.text
,.rodata
和.data
作为一个连续加载的块。没有操作系统,这是裸机嵌入式。主加载器本质上是将my二进制文件memcpy
到ram中,在预定的地址。不会发生重定位。不使用VM。校验和只需要在初始化时测试一次。
更新了发现通过这样做..
__attribute__((constructor)) void sumItUp(void) {
// sum it up
// leave result where it can be found
}
。。除了初始化malloc
/sbrk
变量,以及"impure.o“和"locale.o”所拥有的一些变量之外,我几乎可以在所有事情之前完成求和。现在,我从项目链接器脚本中知道了malloc
/sbrk
的值。如果inpure.o和locale.o可以减轻,可能会在业务中。
更新既然我可以控制入口点(通过flash中对主加载器的说明),现在最好的攻击角度似乎是使用一段自定义汇编程序代码来设置堆栈和sdata指针,调用校验和例程,然后分支到“普通”_start代码。
发布于 2012-12-31 23:19:12
如果校验和执行得足够早,您可以只使用堆栈变量,而不是写入任何数据段变量-也就是说,执行校验和所需的所有操作和之前的所有步骤都只使用局部变量来存储数据,当然您可以读取全局数据。
我相当确信正确的方式是信任闪存和加载程序来加载闪存中的内容。如果你想对代码进行校验和,当然,假设它没有被加载程序修改--例如,共享库的运行时加载或者可执行文件本身的重定位,比如随机虚拟地址空间等等。但是,一旦执行正确启动,从闪存加载的数据就不再可靠。
如果其他人要求你这样做,那么请向他们解释这是不可行的,并且“目前的要求”是“坏的”。
发布于 2013-01-01 01:13:15
我建议像upx这样的可执行打包程序来处理这个问题。
在其他答案和你的问题中,有几件事,由于缺乏更好的术语,让我感到紧张。
发布于 2013-01-01 01:40:02
您是否可以使用链接器脚本将impure.o和locale.o放在其他所有内容之前或之后,从而允许您对除这些和malloc/sbrk内容之外的所有内容进行校验和?我猜malloc和sbrk是在加载应用程序的引导加载程序中调用的,所以它们造成的混乱是无法消除的吗?
这不是一个简单地告诉你与这个需求作斗争的答案,但我同意这似乎是想过头了。我相信你不能透露太多细节,但我假设规范的作者关心的是恶意用户/黑客,而不是由于宇宙射线等导致的常规内存崩溃。在这种情况下,如果恶意用户/黑客可以更改加载到RAM中的内容,他们就可以更改您的校验和例程(它本身是从RAM运行的,对吗?)为了总是返回一个满意的状态,不管他们不再运行的校验和例程设计得有多好。
即使他们担心常规的内存损坏,此校验和例程也只会捕获在原始复制到内存期间发生的错误,这实际上是发生此类错误的可能性最小的时间,原因很简单,因为系统运行的时间不够长,因此发生损坏事件的概率很高。
https://stackoverflow.com/questions/14103061
复制相似问题