用dd修补二进制文件


32

我读过几次(下面)这个引用,最近一次是在这里阅读的,并且不断地困惑于如何dd使用它来修补任何东西,更不用说编译器了:

我30年前在学校使用的Unix系统在RAM和磁盘空间方面非常有限。特别是/usr/tmp文件系统非常小,当有人尝试编译大型程序时会导致问题。当然,无论如何,学生都不应该编写“大型程序”。大型程序通常是从“某处”复制的源代码。我们中的许多人都复制/usr/bin/cc/home/<myname>/cc,并用来dd修补二进制文件,/tmp而不是来使用/usr/tmp更大的二进制文件。当然,这只会使问题变得更糟-这些副本所占用的磁盘空间在当时确实很重要,并且现在会/tmp定期装满,从而阻止其他用户甚至编辑其文件。他们发现发生了什么事后,系统管理员执行了chmod go-r /bin/* /usr/bin/* 它“解决”了问题,并删除了我们所有的C编译器副本。

(强调我的)

dd手册页只字未提修补和不认为它可能是重新定意要做到这一点呢。

二进制文件真的可以修补dd吗?这有任何历史意义吗?


3
当然-只是od一个文件,用于字节的十六进制代码,找到所需的偏移量,决定进行编辑,然后直接bs=$patchsize count=1 seek=$((offset/bs)) conv=notrunc在其中添加补丁
。– mikeserv

3
从来没有有人覆盖过启动扇区。;)
Parthian Shot 2015年

@ParthianShot实际上,我一次用Debian LiveCD的一部分覆盖了我的引导驱动器(+根)的前260MB。O_o但是我不认为这确实是修补程序,
嘿嘿

1
或者更确切地说,这是“磁盘
销毁

Answers:


73

让我们尝试一下。这是一个简单的C程序:

#include <stdio.h>
int main(int argc, char **argv) {
    puts("/usr/tmp");
}

我们将其构建为test

$ cc -o test test.c

如果我们运行它,它将显示“ / usr / tmp”。

让我们找出“ /usr/tmp”在二进制文件中的位置:

$ strings -t d test | grep /usr/tmp
1460 /usr/tmp

-t d 将十进制偏移量打印到找到的每个字符串的文件中。

现在,我们制作一个临时文件,其中仅包含“ /tmp\0”:

$ printf "/tmp\x00" > tmp

现在我们有了二进制文件,我们知道要更改的字符串在哪里,并且我们有了一个包含替换字符串的文件。

现在我们可以使用dd

$ dd if=tmp of=test obs=1 seek=1460 conv=notrunc

这会从tmp(我们的“ /tmp\0”文件)中读取数据,使用1字节的输出块大小将其写入二进制文件,并跳至我们在写入任何内容之前发现的偏移量,并且在完成后明确不截断文件。

我们可以运行修补的可执行文件:

$ ./test
/tmp

程序输出的字符串文字已经更改,因此现在包含“ /tmp\0tmp\0”,但是字符串函数在看到第一个空字节后立即停止。这种修补仅允许使字符串更短或相同长度,而不是更长,但这足以满足这些目的。

因此dd,我们不仅可以使用来修补事物,而且还可以做到。


1
这太好了……我强烈希望我在生产环境中从未遇到过!过去,我曾使用过类似的方法将序列号转换为十六进制图像以用于微控制器,但是将自己打入脚掌实在太容易了。
迈克尔·肖

如果我想向某人提供如何修补特定二进制文件的书面说明,则宁愿给他们一个命令行进行复制/粘贴,而不是告诉他们“在十六进制编辑器中打开文件,找到/usr/tmp字符串,将其替换为/tmp,不要不要忘记尾随的\0字节,保存文件,然后用手指交叉”。或者,甚至更好的是,先执行健全性检查的Shell脚本,然后再调用dd。不幸的是,需要这样的东西经常出现在由现已解散的供应商提供的旧的软件只是要迁移到新系统。
Guntram Blohm

是的,sed在这种情况下比较好。但是您对整个过程并不完全正确,“此修补程序仅允许使字符串更短或相同长度,而不能更长”。您假设您关心紧随要修改的字符串之后的数据,或者不能让下一个字符串仅仅是原始字符串的子字符串。换句话说,如果您位于内存的.strings部分,并且具有“ / usr \ 0 / bin / bash \ 0”,则可以通过简单地将其更改为/ usr / bin / bash空字节并将其设置为“ / usr // bin / bash”(例如)。
Parthian Shot 2015年

2
@ParthianShot - sed不是更好地为这种事情-你不能这么expliciltly和精确限制sed的读/你可能会的方式写缓存dd-这是它的第一个地方是以往任何时候都用于此的全部理由。用dd可以任意放置任意字节的任意计数。这还不能说sed。如果dd在这里像手术刀一样使用,您将sed像破坏球一样使用。
mikeserv

这是一个公平的(尽管相当罕见!)点-在某些情况下,您可以通过不在乎结果或其他任意但特定的数据段来延长字符串长度。不过,我会坚持一般的说法。
迈克尔·荷马2015年

9

这取决于“修补二进制文件”的含义。

dd有时使用更改二进制文件。当然,中没有这种功能dd,但是它可以打开文件,并以特定的偏移量读取和写入内容,因此,如果您知道在哪里写入内容,瞧,有您的补丁程序。

例如,我有一个包含一些PNG数据的二进制文件。使用binwalk查找偏移量,dd将其提取(通常binwalk也提取东西,但我的副本有问题),使用进行编辑gimp,确保编辑后的文件的大小等于或小于原始文件(更改偏移量不是一件容易的事),然后用于dd将更改后的图像放回原处。

$ binwalk thebinary
[…]
4194643    0x400153     PNG image, 800 x 160, 8-bit/color RGB, non-interlaced
[…]
$ dd if=nickel bs=1 skip=4194641 count=2 conv=swab | od -i
21869 # file size in this case - depends on the binary format
$ dd if=thebinary bs=1 skip=4194643 count=21869 of=theimage.png
$ gimp theimage.png
$ pngcrush myimage.png myimage.crush.png
# make sure myimage.crush.png is smaller than the original
$ dd if=myimage.crush.png of=thebinary bs=1 seek=4194643 conv=notrunc

有时我也希望替换二进制文件中的字符串(例如路径或变量名)。尽管也可以使用来完成此操作dd,但使用起来更简单sed。您只需要确保替换的字符串与原始字符串具有相同的长度,就可以避免更改偏移量。

sed -e s@/the/old/save/path@/the/new/save/path@ -i thebinary

或选择@MichaelHomer的示例,其中添加了0字节:

sed -e 's@/usr/tmp@/tmp\x00tmp@' -i test

当然,您之后必须验证它是否真正起作用。


...假设您有一个sed可以很好地处理二进制文件的,gnu似乎就是这种情况sed,但是对于许多sed仅用于ascii文件的较早版本的s来说,却与其他任何东西(尤其是\0输入中的s)相混淆,限制了最大行长。
Guntram Blohm

1
busybox sed似乎可以很好地更改二进制文件,但是它不能\x00像GNU sed那样理解替换字符串。它需要测试,但即使如此,我认为还是值得一提,因为它比dd某些情况下简单得多。无论哪种方式,修补二进制文件都是一件容易的事。
弗罗斯特斯2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.