我已经多次阅读这句话(如下),最近一次这里,并且我一直对如何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
?这有什么历史意义吗?
答案1
我们来试试吧。这是一个简单的 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
,而且我们已经完成了。
答案2
这取决于“修补二进制文件”的含义。
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
当然,你必须事后验证它是否真的有效。
答案3
是的,可以使用 dd 修补二进制文件。
要使用来自 stdin 的数据修补文件:
dd of=file.bin bs=1 count=2 conv=notrunc
然后输入要修补的(文本)数据。即使在提示时输入超过 2 个字符,上述内容也仅修补 2 个字节(从文件开头)。
要修补文件中间,请通过提供文件数据的位置(地址)seek
来开始修补。例如。
dd of=file.out bs=1 count=2 seek=2 conv=notrunc
或者
echo "<address: data>" | xxd -r - file.out
如果修补不可打印的字符,请使用要修补的数据创建一个二进制临时文件。例如。
dd if=file.in of=file.out bs=1 count=2 seek=2 conv=notrunc
或者
xxd -r hexoffsets.in file.out
或在标准输入上提供带有偏移量的十六进制数据
echo "<address: hex-data>" | xxd -r - file.out
定义:
patch的意思是把hexdump转换成二进制(来自xxd manpage)
在 Ubuntu 16.04.7 上测试