如何使 Unix 二进制文件自包含?

发布于 2024-11-23 15:31:50 字数 1871 浏览 4 评论 0原文

我有一个 Linux 二进制文件,没有源代码,可以在一台机器上运行,我想制作一个独立的包,可以在同一架构的不同机器上运行。实现这一目标的方法是什么?

就我而言,两台机器具有相同的体系结构,相同的 Ubuntu 内核,但目标机器没有 make 并且在 /lib下的文件版本错误/usr

我的一个想法是使用 chroot 并重新创建二进制文件使用的文件系统的子集,可能使用 strace 来找出它需要什么。是否已经有一个工具可以做到这一点?

对于后代,以下是我如何确定进程打开哪些文件

#!/usr/bin/python
# source of trace_fileopen.py
# Runs command and prints all files that have been successfully opened with mode O_RDONLY
# example: trace_fileopen.py ls -l
import re, sys, subprocess, os

if __name__=='__main__':
  strace_fn = '/tmp/strace.out'
  strace_re = re.compile(r'([^(]+?)\((.*)\)\s*=\s*(\S+?)\s+(.*)$')

  cmd = sys.argv[1]
  nowhere = open('/dev/null','w')#
  p = subprocess.Popen(['strace','-o', strace_fn]+sys.argv[1:], stdout=nowhere, stderr=nowhere)
  sts = os.waitpid(p.pid, 0)[1]

  output = []
  for line in open(strace_fn):
    # ignore lines like --- SIGCHLD (Child exited) @ 0 (0) ---
    if not strace_re.match(line):
      continue
    (function,args,returnval,msg) = strace_re.findall(line)[0]
    if function=='open' and returnval!='-1':
      (fname,mode)=args.split(',',1)
      if mode.strip()=='O_RDONLY':
        if fname.startswith('"') and fname.endswith('"') and len(fname)>=2:
          fname = fname[1:-1]
        output.append(fname)
  prev_line = ""
  for line in sorted(output):
    if line==prev_line:
      continue
    print line
    prev_line = line

更新 LD_LIBRARY_PATH 解决方案的问题在于 /lib 被硬编码到解释器中并优先于 LD_LIBRARY_PATH,因此本机版本将首先加载。解释器被硬编码到二进制文件中。一种方法可能是修补解释器并将二进制文件运行为 patched_interpreter mycommandline 问题是,当 mycommandlinejava 开头时,这不会之所以能工作,是因为 Java 设置了 LD_LIBRARY_PATH 并自行重新启动,这会求助于旧的解释器。对我有用的解决方案是在文本编辑器中打开二进制文件,找到解释器(/lib/ld-linux-x86-64.so.2),并将其替换为相同长度修补解释器的路径

I have a Linux binary, without sources, that works on one machine, and I'd like to make a self-contained package that would run on a different machine of the same architecture. What is a way of achieving this?

In my case, both machines have the same architecture, same Ubuntu kernel, but target machine doesn't have make and has wrong version of files under /lib and /usr

One idea I had was to use chroot and recreate a subset of the filesystem that the binary uses, possibly using strace to figure out what it needs. Is there a tool that does this already?

For posterity, here's how I figure out which files a process opens

#!/usr/bin/python
# source of trace_fileopen.py
# Runs command and prints all files that have been successfully opened with mode O_RDONLY
# example: trace_fileopen.py ls -l
import re, sys, subprocess, os

if __name__=='__main__':
  strace_fn = '/tmp/strace.out'
  strace_re = re.compile(r'([^(]+?)\((.*)\)\s*=\s*(\S+?)\s+(.*)

Update
The problem with LD_LIBRARY_PATH solutions is that /lib is hardcoded into interpreter and takes precedence over LD_LIBRARY_PATH, so native versions will get loaded first. The interpreter is hardcoded into the binary. One approach might be to patch the interpreter and run the binary as patched_interpreter mycommandline Problem is that when mycommandline is starts with java, this doesn't work because Java sets-up LD_LIBRARY_PATH and restarts itself, which resorts to the old interpreter. A solution that worked for me was to open the binary in the text editor, find the interpreter (/lib/ld-linux-x86-64.so.2), and replace it with same-length path to the patched interpreter

) cmd = sys.argv[1] nowhere = open('/dev/null','w')# p = subprocess.Popen(['strace','-o', strace_fn]+sys.argv[1:], stdout=nowhere, stderr=nowhere) sts = os.waitpid(p.pid, 0)[1] output = [] for line in open(strace_fn): # ignore lines like --- SIGCHLD (Child exited) @ 0 (0) --- if not strace_re.match(line): continue (function,args,returnval,msg) = strace_re.findall(line)[0] if function=='open' and returnval!='-1': (fname,mode)=args.split(',',1) if mode.strip()=='O_RDONLY': if fname.startswith('"') and fname.endswith('"') and len(fname)>=2: fname = fname[1:-1] output.append(fname) prev_line = "" for line in sorted(output): if line==prev_line: continue print line prev_line = line

Update
The problem with LD_LIBRARY_PATH solutions is that /lib is hardcoded into interpreter and takes precedence over LD_LIBRARY_PATH, so native versions will get loaded first. The interpreter is hardcoded into the binary. One approach might be to patch the interpreter and run the binary as patched_interpreter mycommandline Problem is that when mycommandline is starts with java, this doesn't work because Java sets-up LD_LIBRARY_PATH and restarts itself, which resorts to the old interpreter. A solution that worked for me was to open the binary in the text editor, find the interpreter (/lib/ld-linux-x86-64.so.2), and replace it with same-length path to the patched interpreter

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

残花月 2024-11-30 15:31:50

正如其他人提到的,静态链接是一种选择。除了与 glibc 的静态链接在每个版本中都会出现一些问题(抱歉,没有参考资料;只是我的经验)。

您的 chroot 想法可能有点矫枉过正。

据我所知,大多数商业产品使用的解决方案是使它们的“应用程序”成为一个 shell 脚本,设置 LD_LIBRARY_PATH ,然后运行实际的可执行文件。大致如下:

#!/bin/sh
here=`dirname "$0"`
export LD_LIBRARY_PATH="$here"/lib
exec "$here"/bin/my_app "$@"

然后,您只需将所有相关 .so 文件的副本转储到 lib/ 下,将可执行文件放在 bin/ 下,将脚本放入 .,并运送整个树。

(为了具有生产价值,如果 "$here"/lib 不为空,则将其正确添加到 LD_LIBRARY_PATH 等)

[编辑,以配合您的更新]

我认为您可能对什么是硬编码、什么不是感到困惑。 ld-linux-x86-64.so.2 是动态链接器本身;你是正确的,它的路径被硬编码到 ELF 标头中。但其他库不是硬编码的;它们由动态链接器搜索,该链接器将遵循LD_LIBRARY_PATH

如果您确实需要不同的 ld-linux.so,则无需修补 ELF 标头,只需运行动态链接器本身即可:

/path/to/my-ld-linux.so my_program <args>

这将使用您的链接器而不是 ELF 标头中列出的链接器。

修补可执行文件本身是邪恶的。请考虑一下在您继续前进后必须维护您的东西的可怜人...没有人会期望您手动破解 ELF 标头。 任何人都可以读取 shell 脚本正在执行的操作。

只是我的 0.02 美元。

As others have mentioned, static linking is one option. Except static linking with glibc gets a little more broken with every release (sorry, no reference; just my experience).

Your chroot idea is probably overkill.

The solution most commercial products use, as far as I can tell, is to make their "application" a shell script that sets LD_LIBRARY_PATH and then runs the actual executable. Something along these lines:

#!/bin/sh
here=`dirname "$0"`
export LD_LIBRARY_PATH="$here"/lib
exec "$here"/bin/my_app "$@"

Then you just dump a copy of all the relevant .so files under lib/, put your executable under bin/, put the script in ., and ship the whole tree.

(To be production-worthy, properly prepend "$here"/lib to LD_LIBRARY_PATH if it is non-empty, etc.)

[edit, to go with your update]

I think you may be confused about what is hard-coded and what is not. ld-linux-x86-64.so.2 is the dynamic linker itself; and you are correct that its path is hard-coded into the ELF header. But the other libraries are not hard-coded; they are searched for by the dynamic linker, which will honor LD_LIBRARY_PATH.

If you really need a different ld-linux.so, instead of patching the ELF header, simply run the dynamic linker itself:

/path/to/my-ld-linux.so my_program <args>

This will use your linker instead of the one listed in the ELF header.

Patching the executable itself is evil. Please consider the poor person who has to maintain your stuff after you move on... Nobody is going to expect you to have hacked the ELF header by hand. Anybody can read what a shell script is doing.

Just my $0.02.

無處可尋 2024-11-30 15:31:50

CDE 有一些软件旨在完全满足您的需求。这是谷歌技术讨论
http://www.youtube.com/watch?v=6XdwHo1BWwY

There's CDE a bit of software designed to do exactly what you want. Here's a google tech talk about it
http://www.youtube.com/watch?v=6XdwHo1BWwY

烧了回忆取暖 2024-11-30 15:31:50

几乎肯定有更好的答案,但您可以使用 ldd 命令(例如 ls 二进制文件)找出二进制文件需要哪些库:

$ ldd /bin/ls
linux-vdso.so.1 =>  (0x00007ffffff18000)
librt.so.1 => /lib/librt.so.1 (0x00007f5ae565c000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00007f5ae543e000)
libacl.so.1 => /lib/libacl.so.1 (0x00007f5ae5235000)
libc.so.6 => /lib/libc.so.6 (0x00007f5ae4eb2000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00007f5ae4c95000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5ae588b000)
libdl.so.2 => /lib/libdl.so.2 (0x00007f5ae4a90000)
libattr.so.1 => /lib/libattr.so.1 (0x00007f5ae488b000)

一旦有了这个,您就可以制作副本并将其放在目标计算机上的正确位置。

There are almost certainly better answers, but you can find out what libraries the binary needs with the ldd command (example for the ls binary):

$ ldd /bin/ls
linux-vdso.so.1 =>  (0x00007ffffff18000)
librt.so.1 => /lib/librt.so.1 (0x00007f5ae565c000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00007f5ae543e000)
libacl.so.1 => /lib/libacl.so.1 (0x00007f5ae5235000)
libc.so.6 => /lib/libc.so.6 (0x00007f5ae4eb2000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00007f5ae4c95000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5ae588b000)
libdl.so.2 => /lib/libdl.so.2 (0x00007f5ae4a90000)
libattr.so.1 => /lib/libattr.so.1 (0x00007f5ae488b000)

Once you have this, you could make copies and put them in the proper locations on the target machine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文