我一直在研究一个小技巧来减小可执行文件的文件大小。我知道存在可以正确压缩可执行文件的工具,但这更多的是为了我自己的享受,而不是任何严肃的事情。
我的想法是用 gzip 压缩可执行文件,然后将其作为数组嵌入到另一个称为启动器的 C 程序中。当启动器运行时,它会设置一个管道系统,如下所示:
parent launcher -> fork 1 of launcher -> fork 2 of launcher
fork 1 将自身转换为 gzip,因此它会解压缩父级提供给它的任何内容,并将解压后的版本吐出到 fork 2。
这就是黑客开始发挥作用的地方。Fork 2 尝试执行文件“/dev/fd/n”,其中 n 是从 fork 1 到 fork 2 的管道的文件号。本质上,这意味着 fork 2将尝试执行 gzip 输出的任何二进制文件。
但是,这不起作用(令人惊讶。)我尝试跟踪我的示例实现,并且在“/dev/fd/n”上执行 execv 的行返回 -1 EACCES(权限被拒绝)。但是,如果我打开终端并运行 ls -l/dev/fd/,我会得到类似以下内容的信息:
lrwx------ 1 blackle users 64 Nov 10 05:14 0 -> /dev/pts/0
lrwx------ 1 blackle users 64 Nov 10 05:14 1 -> /dev/pts/0
lrwx------ 1 blackle users 64 Nov 10 05:14 2 -> /dev/pts/0
lr-x------ 1 blackle users 64 Nov 10 05:14 3 -> /proc/17138/fd
所有这些都具有用户(我)的权限+x。这意味着它应该是可执行的,不是吗?或者这只是一个非常奇怪的内核边缘情况,说它没有获得权限,但实际上它无法执行,因为它不是一个真正的文件。
近 7 年后更新
随着 Linux“memfd”的出现,现在可以在 Linux 上创建不接触文件系统的自解压可执行文件。请参阅:https://gitlab.com/PoroCYon/vondehi
最佳答案
只有支持mmap
的文件才可以执行。不幸的是,由于管道的顺序性质和有限的缓冲区大小,管道不能以这种方式映射(它可能需要再次重新读取早期的代码,这些代码在读取一次后就会消失)。
您会更幸运,而不是使用管道在 ramfs 中创建文件,将其映射到父级内存空间的某个区域,将未压缩的代码复制到 mmap 中,最后让子级执行ramfs 中的文件,最后取消父级中 ramfs 中文件的链接,以便在子级退出时自动释放该文件。
希望对你有帮助,如有不清楚的地方请评论。
关于c - 当无法执行管道时如何在 Linux 上创建自解压可执行文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26841487/