将附加代码合并到可执行文件中 (arm-linux)
我正在尝试将一些额外的日志记录代码合并到静态链接(android arm linux)可执行文件中。
(普通的跟踪方法似乎不起作用,因为它是一个守护进程,在做任何有趣的事情之前都会克隆() - 告诉 strace 遵循这个只会使其崩溃)。
十六进制编辑现有代码以将跳转指令插入到新代码中已经过测试和工作,问题是将新代码合并到可执行文件中,使其不干扰现有段,并且确实加载到可执行页面。
我已经能够将所有附加代码压缩到一个目标文件部分,但无法弄清楚如何使用 objcopy (甚至 ld)以正确加载的方式合并它 - 看来我需要调整大小和移动现有的负载段,或添加一个额外的负载段。
如果有一种方法可以将必要的存根添加到已链接且当前静态的可执行文件中,则在共享库中添加代码可能是另一种选择(然后我将在跳转指令中对存根的已知位置进行十六进制编辑,运行时然后链接器将指向添加的代码)
I'm trying to merge some extra logging code into a statically linked (android arm linux) executable.
(Normal tracing methods don't seem to work, as it's a daemon process that clone()s just before doing anything interesting - telling strace to follow this just crashes it).
Hex editing the existing code to insert jump instructions into the new code is tested and working, the problem is getting the new code merged into the executable file in such a way that it doesn't interfere with the existing segments, and does get loaded into an executable page.
I have been able to condense all of the additional code into a single object file section, but can't figure out how to use objcopy (or even ld) to merge that in such a way that it will be properly loaded - it seems I need to either resize & move the existing load segments, or add an additional one which will be honored.
Adding the code in a shared library might be another option, if there's a way to add the necessary stubs to an already linked and presently static executable (I would then hex-edit in jump instructions to the known locations of the stubs, which the runtime linker would then point at the added code)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是不优雅的,所以我对更好的想法感兴趣,但这里是我已经能够实现的基本工作的总结。
这是一种双管齐下的方法,将一个小的引导负载插入到原始 elf 的填充中,然后 mmap() 是一个任意更大的二进制 blob 来完成实际工作。
第一部分:引导有效负载
基本上,我在 .ARM.exidx 部分(在代码段顶部加载)和 .preinit_array 部分之间的一些填充中插入少量代码。这段代码只是打开另一个二进制 blob 并 mmap() 将其设置为只读且可以在硬编码虚拟地址处执行(我希望是安全的)。
为了让我插入的代码作为主可执行文件的一部分加载,我必须修改 elf 文件中加载段的大小,在本例中,它是从 0x54 开始的第二个 phdr 结构。 0x64 (0x54+0x20) 处的 p_filesz 和 0x68 (0x54+0x24) 处的 p_memsz 均已更改。
我还更改了 elf 标头中偏移量 0x18 处的 e_entry 起始地址,以指向我插入的代码。我插入的代码在完成设置后会跳转到旧的起始地址(实际上它首先跳转到较大有效负载中的第二阶段设置,然后跳转到原始地址)。
最后,我更改了我想要捕获的函数的静态链接系统调用存根,使其指向我正在 mmap() 中的较大有效负载的加载地址处的替换。
第二部分:大有效负载
这实现了正在进行的任何修改- 就我而言,用满足某些条件时记录的函数替换系统调用。由于主可执行文件是静态链接的,因此它也必须是静态链接的 - 或者更简单地说,它不能使用 C 库。相反,它使用汇编语言发出基本 I/O 的系统调用。我意识到,如果不作为可执行文件加载,我就没有持久的局部变量存储,因此在启动时,我 mmap() 一个匿名页面来保存局部变量 - 主要是我要登录的文件的 fd 以及设备驱动程序 fd 的操作应该记录哪些内容。
编译这部分有点不优雅。我正在使用 -S 开关将 gcc 编译为程序集,然后删除所有部分关键字。然后我通过 gcc 将其传回以组装并生成一个对象。我通过链接器运行此命令,指定第一个函数的名称作为入口点 (-e),并使用常用链接器脚本的自定义来删除 0x8000 起始偏移量。但由于标头的原因,仍然存在一些偏移,在本例中为 128 字节。为了保留修复,我将链接的 elf 的内容复制到二进制 blob 中,从 /dev/zero 中 dd 自己 128 个字节,然后将其放在开头......
正如我所说......这是不优雅的,所以我愿意接受更好的想法
This is inelegant, so I'd be interested in better ideas, but here's a summary of what I've been able to make basically work.
This is a two prong approach of a small bootstrap payload inserted into padding of the original elf, which then mmap()'s an arbitrarily larger binary blob in to do the actual work.
Part I: the bootstrap payload
Basically I insert a small amount of code in some padding between the .ARM.exidx section (which loads at the top of the code segment) and the .preinit_array section. This code just opens another binary blob and mmap()s it as read only and executable at a hard coded virtual address I hope is safe.
In order to get my inserted code to load as part of the main executable, I had to modify the size of the load segment in the elf file, in this case it's second phdr struct which starts at 0x54. Both the p_filesz at 0x64 (0x54+0x20) and the p_memsz at 0x68 (0x54+0x24) were changed.
I also changed the e_entry start address in the elf header at offset 0x18 to point to my inserted code. My inserted code does a jump to the old start address when it's done setting things up (actually it first jumps to stage two setup in the larger payload, which then jumps to the original).
Finally I changed the statically linked syscall stubs for the functions I wanted to trap to point to my replacements at the load address of the larger payload I'm mmap()ing in.
Part II: the big payload
This implements whatever modifications are being made - in my case, replacing syscalls with functions that log when certain conditions are met. Since the main executable is statically linked, this would have to be as well - or more simply, it can't use the C library. Instead it uses assembly language to issues syscalls for basic I/O. I realized that without being loaded as an executable I have no persistent local variable storage, so on startup I mmap() an anonymous page to hold local variables - mostly the fd of the file I'm logging to and the device driver fd's operations on which should be logged.
Compiling this part is a bit inelegant. I'm compiling to assembly with the -S switch to gcc, then removing all section keywords. I then pass that back through gcc to assemble and generate an object. I run this through the linker specifying the name of my first function as the entry point (-e) and using a customization of the usual linker script which removes the 0x8000 start offset. But there's still some offset due to headers, in this case 128 bytes. to preserve the fixups I objcopy the contents of the linked elf out into a binary blob, dd myself 128 bytes from /dev/zero, and cat that onto the beginning....
As I was saying... this is inelegant, so I'm open to better ideas