【问题标题】:Bash script containing binary executable包含二进制可执行文件的 Bash 脚本
【发布时间】:2013-08-26 23:23:31
【问题描述】:

是否可以编写一个 bash 脚本,其中包含一个二进制可执行程序?

我的意思是一个脚本,它包含一个文本形式的可执行文件的转储,当它被执行时,它会被这个脚本转储回一个可执行文件?

我很想知道一个解决方案,它可以开箱即用,无需安装额外的软件包。有可能吗?

谢谢!

【问题讨论】:

  • 我知道这是可能的,因为我已经在 Intel 的 MKL 安装程序脚本中看到了它。我不确定如何完成,但有可能。
  • java 6 jdk 曾经有一个 80+ MB .sh 文件的分发形式,其中脚本以 exit 0 行“结束”,二进制部分紧随其后。该脚本使用tail ${tail_args} +189 "$0" > $outname 之类的行从自身中提取二进制部分;用rm 做了一个trap,为校验和做了一个sum,一个chmod 并像./$outname 一样执行它

标签: bash unix binary dump


【解决方案1】:

没错!

例如,您可以将任何二进制文件转储到文件中:

echo $binary > file

有很多安装脚本可以做这样的事情。

【讨论】:

  • printf '%s' "$binary" > file 更安全,因为echo 将附加一个额外的换行符。如何禁用它,以及处理$binary 中的反斜杠转义字符,echo 的实现可能会有所不同。
  • 如果$binary 包含 NUL (ASCII 0)、HT (ASCII 9) 或 LF (ASCII 10) 或多个连续 SP (ASCII 20) 的序列,或者如果它包含?*nullglob 选项已设置。其中很多问题可以通过编写"$binary" 而不是$binary 来解决,但即便如此,如果$binary 包含NUL,它也会失败。对于许多(大多数?)二进制文件来说,这是一个大问题。
  • @chepner:问题指定了 Bash。
  • @ruakh: bash 至少需要echo -n,但还需要检查是否设置了xpg_echo,这会影响转义字符的处理方式。使用printf 是最简单的。但是,这些都对空字符没有帮助。
【解决方案2】:

您可以将二进制文件转换为文本,然后使用 uuencode/uudecode 转换回二进制文件。

http://linux.die.net/man/1/uuencode

因此您可以将二进制数据作为文本存储在脚本中并将其输出到二进制文件中。

uuencode binaryFile > output.txt

然后将该数据放入您的脚本中,并在创建二进制文件时使用 uudecode。

【讨论】:

    【解决方案3】:

    我以前从来没有做过这样的事情;) 这将编译一些 c 源代码,创建一个包含二进制文件的 b.bash 脚本(以及用于简单开发的原始脚本)

    (a.bash)

    #!/bin/bash
    
    if [ "$0" == "b.bash" ];then
      tail -n +$[ `grep -n '^BINARY' $0|cut -d ':' -f 1` + 1 ] $0 | base64 -d > a2.out
      chmod +x a2.out
      ./a2.out
      echo $?
      exit
    fi
    
    cat "$0" > b.bash
    echo "BINARY" >> b.bash
    cat > a.c << EOF
    int     main(){
            return 12;
    }
    EOF
    gcc a.c 
    base64 a.out >> b.bash
    

    调用(a.bash 生成 b.bash):

    bash a.bash ;bash b.bash
    

    我不知道如何避免在执行前将二进制文件写入临时文件...

    【讨论】:

    • 这是一个非常好的答案 - base64 是我们可以使用的最佳解决方案,因为它默认安装在所有平台上,并且比 xxd hex 更紧凑。
    • 我需要将“-w 0”添加到 base64。否则我有换行,这会在解码时产生错误。如果通过管道传输到 tr,也可能会有所帮助。 base64 -w 0 a.out &gt;&gt; b.bash(或base64 a.out | tr &gt;&gt; b.bash我没试过stackoverflow.com/questions/18410785/…
    【解决方案4】:

    我试过了,它有效。使用 xxd 生成十六进制。

    #!/bin/bash
    
    xxd -r >foo <<'EndHere'
    0000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
    0000010: 0200 3e00 0100 0000 e003 4000 0000 0000  ..>.......@.....
    0000020: 4000 0000 0000 0000 000a 0000 0000 0000  @...............
    0000030: 0000 0000 4000 3800 0800 4000 1e00 1b00  ....@.8...@.....
    0000040: 0600 0000 0500 0000 4000 0000 0000 0000  ........@.......
    0000050: 4000 4000 0000 0000 4000 4000 0000 0000  @.@.....@.@.....
    ...
    0001960: 6400 5f65 6461 7461 006d 6169 6e00 5f69  d._edata.main._i
    0001970: 6e69 7400                                nit.
    EndHere
    chmod +x foo
    ./foo
    

    【讨论】:

    • +1。 base64 虽然比 xxd 更适合。它更紧凑。
    • 是的,但如果你有一个十六进制转储 xxd 是要走的路。
    【解决方案5】:

    那么,如果我猜对了,您想在脚本中包含一个二进制文件并在脚本退出时执行它吗?

    这是一个binarymaker 脚本(这不仅会创建一个提取二进制文件的脚本,还会将您的任何脚本与任何二进制文件合并):

    #!/bin/bash
    
    lineCount=$(wc -l "$1" | cut -f 1 -d ' ') # just get the line count
    ((lineCount+=2)) # because we are going to append a line
    
    head -n 1 "$1" > "$3" # this is done to ensure that shebang is preserved
    echo "trap 'tail -n +$lineCount \$0 > binary; chmod +x binary; ./binary' EXIT" >> "$3"
    tail -n +2 "$1" >> "$3"
    cat "$2" >> "$3"
    
    exit 0
    

    你应该这样运行它

    ./binarymaker myscript mybinary resultscript
    

    如果您运行resultscript,那么myscriptmybinary 都将被执行。之后,您可以选择将命令添加到rm 二进制文件。

    另外,不要忘记在脚本结束时exit,否则它将继续并尝试解析二进制数据。

    如果您正在使用另一个脚本而不是二进制文件,则可以像这样从管道执行它:

    tail -n +$lineCount \$0 | source /dev/stdin
    

    但它不适用于真正的二进制文件。此外,如果您的 bash 版本低于 4,则它不起作用

    【讨论】:

      【解决方案6】:

      不要像其他几个答案所暗示的那样重新发明轮子,只需使用古老的 shar 命令,这正是设计使然。

      假设您要嵌入脚本的文件是binaryfile,只需运行

      $ shar binaryfile > binaryfile.shar
      

      你已经准备好了。你有一个名为binaryfile.shar 的shell 脚本,执行时会提取binaryfile

      【讨论】:

      • 但是你需要安装sharutils 包,这不是默认的。 base64 始终可用,因为它是 GNU coreutils 的一部分。
      • @erik 您假设 OP 使用的是 Gnu/Linux,但这没有说明。 base64 不比 shar 更 POSIX,因此不能保证在 Unix 平台上可用。
      【解决方案7】:

      我不得不制作一个安装程序脚本来将它部署到一台甚至没有 tar 的机器上,并且我结束了嵌入busybox(用于 tar 和 gunzip)和 tarball 以部署在一个 shell 脚本上。我做到了dd 方式

      #!/usr/bin/env bash
      
      get_length()
      {
          ls -l "$1" | cut -f5 -d' '
      }
      
      # First parameter is the number of semicolons to add to second parameter
      # NOTE: we do not use an "add_blanks" function directly because it seems bash
      # removes duplicated blanks. The workaround is adding other character and
      # replacing later using e.g. sed.
      add_semicolons()
      {
          scratch="$2"
          for n in $(seq 1 $1)
          do
              scratch+=";"
          done
          echo $scratch
      }
      
      # Default values
      output="installer"
      input="deployable.tar.gz"
      shell="busybox"
      
      shell_len=$(get_length "$shell")
      payload_len=$(get_length "$input")
      
      # START OF INSTALLATION SCRIPT GENERATION.
      #
      # Note: generated scripts reserves 9 digits for the skip (ibs) offsets.
      # When the script is first written, these digits are written as semicolons.
      # Later when the lengths are computed, these semicolons are replaced by the
      # correct numbers, but the 9-digit length must be preserved by adding blanks
      # until 9 digits are filled. Failure to do this will cause the script length
      # to vary and offsets would need to be iteratively computed.
      
      # Add shebang
      echo "#!/usr/bin/env ash" > "$output"
      
      echo "echo Extracting installer..." >> "$output"
      # Add lines to extract binary data and extract payload
      echo "mkdir -p /tmp/installer" >> "$output"
      echo "dd if=\"$(basename $output)\" ibs=;;;;;;;;; skip=1 | dd ibs=$shell_len count=1 of=/tmp/installer/shell 2>/dev/null" >> "$output"
      echo "chmod +x /tmp/installer/shell" >> "$output";
      echo "dd if=\"$(basename $output)\" ibs=;;;;;;;;; skip=1 2>/dev/null of=/tmp/installer/payload.tar.gz 2>/dev/null" >> "$output"
      # From here on, the binary extraction is completed, do something with extracted files...
      # [...]
      # Use exit command to avoid the shell script parsing to reach the binary part
      echo "echo Done!" >> "$output"
      echo "exit 0" >> "$output"
      
      # We reserved 9 blanks for the ibs offsets. Thus fill offsets with blanks up to 9 chars total
      script_len=$(get_length "$output")
      skip_len=$((script_len + shell_len))
      
      to_add=$((9 - ${#script_len}))
      script_len_str=$(add_semicolons $to_add $script_len)
      to_add=$((9 - ${#skip_len}))
      skip_len_str=$(add_semicolons $to_add $skip_len)
      
      # Add skip values
      sed -i "4s/ibs=;;;;;;;;;/ibs=$script_len_str/" "$output"
      sed -i "4s/;/ /g" "$output"
      sed -i "6s/ibs=;;;;;;;;;/ibs=$skip_len_str/" "$output"
      sed -i "6s/;/ /g" "$output"
      
      cat "$shell" >> "$output"
      cat "$input" >> "$output"
      chmod +x "$output"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-07-20
        • 2018-06-29
        • 2014-12-23
        • 2010-12-30
        • 2012-05-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多