前些日子突然冒出一个很有意思的想法,如何将windows和linux的可执行文件拼成一个,

并能做到在不同的平台上执行不同的部分,使得软件具有兼容性的同时,又能展现出不同的功能。

于是便开始着手调研,再查询了许多资料后,自己也慢慢有了思路。

到最后发现类似的工作,已有先人实现过了,在仔细研读后发现二者思路不谋而合。

但先人做的是构建前的工作,同时不能做到功能的区分,而构建后的领域还尚属空白。

于是在完成该项目后,又在这里写一篇文章来记录下思路。

构建前实现:https://github.com/jart/cosmopolitan

构建后实现:https://github.com/LingkongSky/CrossPlatformMixed

跨平台实现

开端

众所周知,windows的可执行文件一般用PE,linux一般用ELF。

因此我们一开始的目标就是想办法在同一个文件头里实现能同时兼容两种读取规则的格式。

不过很快的,我们发现这两种格式存在着固定位置特征码的冲突,无法解决,所以转换思路。

Windows的PE头文件部分特征值位置固定,很难去改动他,所以得想办法在Linux 上动文章。

而经常使用Linux写脚本的读者都清楚,Linux下的可执行文件,除了二进制文件外,还有shell脚本文件。

因此,我们可以尝试在脚本文件嵌入二进制内容,再通过shell代码去解压执行,这便是我们的第二代思路。

而要想让Linux把一个二进制文件识别为shell,就得去研究Shell的解释机制。

Shell

首先,如果我们要写一个脚本,一般会先显式的指定解释器,也就是用我们常说的#!(shebang),如果头部两个字符为#!的话。

则使用后续的路径作为该脚本的解释器。如:#!/bin/bash。那么在这种情况下,该脚本的解释器就会被指定为/bin/bash。

但是我们的EXE要求开头两个字节固定为0x4D5A,也就是MZ,为了确保exe能正常识别,所以我们得舍弃shebang,在开头不指定解释器,

而这个脚本则会由linux自带的解释器所解释执行,也就是默认作为bash去解析。

接着,我们得想办法让shell跳过exe的部分,直接转到我们尾部的shell主体部分。

注释

在这里,我们可以使用bash中的重定向语法,也就是MZ='' << 'xxxxxxxx'
这样MZ的双引号之间的内容和’xxxxxxx’到下一个xxxxxxx之间的内容都将不被bash所解释。

这样,我们就可以通过MZ=”的双引号之间的内容把DOS头的一部分包住,剩下的内容再全部交给两个xxxxxxxx去重定向。

然后在对程序影响不大的可选值部分写入这些字符,我们就做到了第一次的双平台正常识别。此时。我们的文件头是这样的:

DOS1

DOS2

ps:此处仅为思路讲解,实操有额外细节需要注意,如注释符的选择,避开特征值和会对dos头解析产生影响的地址等

Linux二进制执行

承接上文,在两个注释符包围了整个exe后,我们就能在尾部尽情编写shell脚本了。

思路:mkdir创建文件夹->sed导出二进制数据到文件->chmod赋权->exec执行

以下为实际项目中所使用的shell模板(字母大写变量为宏):

extract_dir="EXTRACTDIR"
target_file_prefix="TARGETPREFIX"
target_file_name="TARGETNAME"
target_file_md5="TARGETMD5"

if [ ! -d $extract_dir ];then
  mkdir $extract_dir
fi

for file in ${extract_dir}"/"${target_file_prefix}*; do
  if [ -f "$file" ]; then
    file_md5=$(echo "$file" | awk -F"${target_file_prefix}" '{print $2}')
    if [ "$file_md5" != "$target_file_md5" ]; then
      rm "$file"
    fi
  fi
done

if [ ! -f ${extract_dir}"/"${target_file_name} ];then
    sed "1,/^### END OF THE SCRIPT--TAG--###/d" "$0" > ${extract_dir}"/"${target_file_name}
    chmod u+x ${extract_dir}"/"${target_file_name}
fi

# exec
${extract_dir}"/"${target_file_name}

exit;
### END OF THE SCRIPT--TAG--###

DOS3

DOS4接着再将准备好的linux可执行文件直接粘贴进尾部,结果文件在windows和linux都正常运行。

在ubuntu,debian,centos等linux系统下测试正常,对windows8+的版本也正常运行。对于小文件和大文件的测试也同样通过。

至此,想法成功实现。

Q&A

本文实现方法的局限性:

  • 虽然不影响程序运行,但仍在一定程序上破坏了文件结构,无法适用于有安全自检程序的软件。
  • 在文件传输时总是会多占一部分体积
  • 暂时未实现对不同指令集(如arm)的跨平台构建

本文实现方法的优点:

  • 实质上是解释器欺骗,因此保有了原始的运行性能
  • 可以做到功能的区分,和在没有源码的情况下进行软件的构建

感谢您的阅读。

本文项目地址:https://github.com/LingkongSky/CrossPlatformMixed