You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SHC转换的shell可执行文件系统重启后运行失效输出乱码问题

故障根因

这是SHC的固有缺陷,和AWS公共AMI的默认系统配置直接相关:

  • SHC不会把Shell脚本真正编译为独立的本地机器码,它输出的.x文件本质是个简易C加载器:真正的脚本逻辑被加密后存在二进制段中,运行时必须先在系统临时目录(默认路径/tmp)解密写出一份临时可执行脚本片段,再调用系统shell执行这份片段才能正常运行。
  • AWS官方提供的Amazon Linux 2/2023、Ubuntu等常用公共AMI,默认配置了两项规则:一是重启后自动清空/tmp目录下的所有用户文件,二是默认将/tmp目录挂载为*noexec模式*,禁止该目录下的任何文件获取可执行权限。
  • 你在镜像制作阶段第一次执行./test.sh.x时,要么当时实例还没加载/tmp的noexec挂载参数,要么SHC写出的临时文件还没被清理,所以运行正常;实例重启后,旧临时文件被清空,noexec权限阻止SHC写入新的可执行临时片段,加载器执行流异常,会直接把内存中存储的加密二进制内容打印到标准输出,就是你看到的乱码。
SHC方案修复方法

如果坚持用SHC,改下编译和运行配置就能解决重启失效的问题:

  1. 提前在镜像内创建一个不会被自动清理、不会被挂载为noexec的固定临时目录,比如/opt/shc_runtime,设置全局可写权限:
    mkdir -p /opt/shc_runtime
    chmod 1777 /opt/shc_runtime
    
  2. 编译SHC二进制时,不要直接用默认的shc -f test.sh命令,手动修改生成的C源文件里的硬编码临时路径,再自行编译:
    # 先生成.x和.x.c文件
    shc -f test.sh
    # 替换硬编码的/tmp路径为你自建的目录
    sed -i 's|"/tmp"|"/opt/shc_runtime"|g' test.sh.x.c
    # 手动编译为独立二进制,去掉无用的调试符号减小体积
    gcc -s -o test_bin test.sh.x.c
    
  3. (可选)如果不想改源码,也可以在镜像的全局环境变量配置(比如/etc/profile、/etc/environment)里加一行export TMPDIR=/opt/shc_runtime,SHC运行时会优先读这个环境变量选择临时目录,避开/tmp的权限限制。
更稳定的轻量隐藏替代方案

SHC本身已经多年没有核心功能更新,坑点较多,如果只需要满足普通用户无法直接cat查看原脚本的轻量隐藏需求,下面几个方案无额外依赖、不会出现重启失效问题,稳定性远高于SHC:

  • 方案1:Base64自解压包装
    纯Shell实现,无二进制依赖,所有Linux环境通用,普通用户打开文件只能看到Base64字符串,无法直接读原脚本逻辑:
    # 先把原脚本编码为单行Base64字符串
    B64_PAYLOAD=$(base64 -w0 test.sh)
    # 生成可执行包装脚本
    cat > test_bin <<'EOF'
    #!/bin/bash
    PAYLOAD="这里替换成上一步得到的B64_PAYLOAD字符串"
    echo "$PAYLOAD" | base64 -d | bash
    EOF
    chmod +x test_bin
    
    如果要提升混淆度,可以把Base64字符串拆成多段、插入无意义的干扰变量,足够应付普通的随意查看。
  • 方案2:gzexe压缩包装
    所有Linux发行版默认自带gzexe工具,原本用于压缩可执行脚本减小体积,压缩后的文件是自解压格式,直接运行即可,普通用户打开看到的是压缩后的二进制乱码,满足轻量隐藏需求:
    # 压缩原脚本
    gzexe test.sh
    # 删除自动生成的原文件备份
    rm -f test.sh~
    # 压缩后的test.sh就是可直接执行的文件,重命名即可
    mv test.sh test_bin
    
    这个方案生成的文件本质还是Shell脚本,不依赖临时目录可执行权限,跨环境兼容性极强,不会因为系统配置变化失效。
  • 方案3:独立二进制打包
    写一个极简C加载器把脚本内容嵌入二进制,运行时直接通过管道传给Shell执行,不落地写临时文件,使用体验和SHC生成的二进制一致但没有临时目录依赖:
    1. 新建loader.c文件,内容如下:
      #include <stdlib.h>
      #define PAYLOAD "这里替换成base64 -w0 test.sh生成的字符串"
      int main() {
          char cmd[2048000]; // 缓冲区大小根据脚本长度调整,足够存下解码命令即可
          snprintf(cmd, sizeof(cmd), "echo \"%s\" | base64 -d | bash", PAYLOAD);
          return system(cmd);
      }
      
    2. 编译为静态独立二进制:
      gcc -static -s -o test_bin loader.c
      
    编译出来的二进制不依赖任何系统库和临时目录权限,不管实例怎么重启、/tmp怎么配置都能正常运行,逆向难度和SHC相当,完全满足轻量隐藏需求。

内容的提问来源于stack exchange,提问作者Vinny Pem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:09:13