Shell 混淆加密:解释型语言的安全防护

25 年 9 月 6 日 星期六
2035 字
11 分钟

先回答摘要的答案:Shell 的安全防护最终是不写 Shell,而是转为写 C++Go 等语言并且使用带有混淆加密功能的编译器

前言

Shell 作为有举足轻重地位的解释型语言,安全防护必然是少不了的一个内容,可以怎么防护是一个需要深入的问题

常见混淆加密方案

过于简单或过于复杂的不在此提起

明文混淆加密

gzexe

gzexe 的原理较为简单,本质就是通过 gzip 将源码压缩,放置于脚本文件的尾部,然后解压执行

如果要变种也很简单,比如说多层压缩多层编码嵌套,但是并没有什么实质作用

gzexe 的优点
  • 由于是把处理后的二进制数据直接放在脚本文件内,通过文本编辑器无法直接编辑
gzexe 的缺点
  • 由于是把处理后的二进制数据直接放在脚本文件内,执行时需要获取 $0 (也就是自身),从而导致兼容性较差,无法通过 ./source 执行
  • 只是单纯把源码处理,可读性极强解密非常简单
gzexe 的解密

看执行逻辑,然后直接执行对应的解压/解码就可以了

变量混淆

这种混淆的出处暂未找到,本质是将源码中出现的每一个字符都替换成可读性差的变量

变量混淆的优点
  • 源码被替换成变量,可读性较差
  • 不需要获取 $0兼容性优秀
变量混淆的缺点
  • 出发点是较好的,但是将源码替换变量后直接使用 eval 执行,极易解密
  • 对于较为复杂的源码,可能无法加密
变量混淆的解密

直接把 eval 改成 echo 然后执行,源码就会直接输出出来了

可执行混淆加密

shc

shc 的实现也不复杂,将字符串加密后调用 sh -c 执行

特征也很明显,反编译后看伪代码可以很明显地看出调用 sh -c 执行的这一过程

shc 的优点
  • 有字符串加密,如果要直接在二进制层面解密,并非易事
  • 通过 sh -c 执行,兼容性尚可
shc 的缺点
  • 通过 sh -c 执行,极易解密
  • 获取到的 $0 不是其真实名称
shc 的解密

执行后读取 cmdline 即可

shellc/xbash

shellc 是一个强度较高的项目,xbash 的强度更是更上一层楼

shellc/xbash 的优点
  • shellc兼容性较高,支持 shellperlpythonnodeRscriptphp 等语言
  • shellc功能多,可用性强
  • xbash 实现了内置解释器,交给其他解释器和直接内置解释器是完全不一样的强度
shellc/xbash 的缺点
  • 反调试不够多,但是由于 xbash 闭源,不知道其具体手段
shellc/xbash 的解密

作者已写,只不过 xbash 的话,没有一些技术手段是弄不出来的

玉龙加密

有这么个东西,但是我没有看到有谁在用,但也说说吧,它还叫什么 XXC 加密?

本质也较为简单,实现的代码极为低劣,处理过程极其抽象

流程是通过 upx 压缩传入,成功则视为二进制,失败则视为脚本

然后处理 upx 特征,并将其加密存放于事先编译好的可执行的尾部

执行的时候是将其解密至 /data/data 内的随机目录,然后如果是可执行就直接执行,如果是脚本则调用 sh 执行

执行后会立即删除解密后的文件

玉龙加密的优点

没有任何优点,可能就是看起来唬人吧?

玉龙加密的缺点
  • 本身的实现就很抽象
  • 获取到的工作目录不是其真实目录
玉龙加密的解密

要直接在二进制层面解密较为简单,找到尾部标志的位置读取出其尾部数据,然后进行异或解密即可

如果是通过执行来解密,只需要给 /data/data 设置 chattr +a,然后把 chattr 指令直接干掉即可(因为执行的时候会调用 chattr -a 来防止破解)

进阶混淆加密方案

只提大概方向,不提具体实现

进阶混淆加密大纲

  • 不留任何中间文件
  • 做好反调试
  • 做好真实性校验
  • 开头固然重要,执行时更重要

明文进阶混淆加密

明文实际上非常不安全,因为执行完全受到执行者的掌控,所以只能做到尽可能地降低可读性,这里以 AW 加密举例

外层壳

shell
`:|echo 4L0B|tr 0-9A-Z a-z` <<- zako\ desu\ ne \
{ \
    for __zako_... in false true \;\
      do \$__zako_... \&\& `:|echo 4L0B|tr 0-9A-Z a-z` \
        \"\`                 \
            tr -d @%^\\\\\\n \
            \| `:|echo 10I4|tr 0-9A-Z a-z`64 -d \
            \| bzcat         \
            \| zcat          \
            \| cat           \
        \`\"               \;\
    done\
\;}
...
zako desu ne

中层壳

shell
__urusai_...=0 <<-sukui\ you\ no\ nai\ hentai `:|echo|sed -E s\ .\*\ MDSvqy==\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|tr a-zA-Z A-Za-z|base64 -d|sed -E s\ \(.\)\(.\)\ \\\\\2\\\\\1\ g` while\ :\;do\ __urusai_...=\$\(\(__urusai_...+1\)\)\;case\ \$__urusai_...\ in\ 3\)echo\ "\"\``:|echo|sed -E s\ .\*\ wy0n\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|tr a-zA-Z A-Za-z|base64 -d|sed -E s\ \(.\)\(.\)\ \\\\\2\\\\\1\ g`|tr a-zA-Z A-Za-z|sed -E s\ \(.\)\(.\)\(.\)\(.\)\ \\\\\4\\\\\2\\\\\1\\\\\3\ g|`:|echo|sed -E s\ .\*\ wyLjZC2qslKb\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|tr a-zA-Z A-Za-z|base64 -d|sed -E s\ \(.\)\(.\)\ \\\\\2\\\\\1\ g`|`:|echo|sed -E s\ .\*\ MEHj3y=q\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|tr a-zA-Z A-Za-z|base64 -d|sed -E s\ \(.\)\(.\)\ \\\\\2\\\\\1\ g`\`\""\;__urusai_...=\$?\;break\;\;\$__urusai_...\)`:|echo|sed -E s\ .\*\ MDSvqy==\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|tr a-zA-Z A-Za-z|base64 -d|sed -E s\ \(.\)\(.\)\ \\\\\2\\\\\1\ g`\ $(:|echo|sed -E s\ .\*\ S4ihaaaaaacawZoaRqdmrbdmi6HRD4aw0fmkMywp8/FhbNSA1jGQRVAC2DKlrYKHuFgH2S9TH77mdGtAKuHaNHsfkQGEHNAgrmB7hOIFv9obvBYzs/jF0RWrPa/+hxmv8Ut+lTDFDrfDVSpaZ1MGL+8PNIghFBKK9nhApqb7gQs3aqaM=a=a\ \;s\ \(.\)\(.\)\(.\)\(.\)\ \\\4\\\2\\\1\\\3\ g\;q|tr a-zA-Z A-Za-z|`:|echo|sed -E s\ .\*\ wyLjZC2qslKb\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|tr a-zA-Z A-Za-z|base64 -d|sed -E s\ \(.\)\(.\)\ \\\\\2\\\\\1\ g`|`:|echo|sed -E s\ .\*\ 3y0Pqy==\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|tr a-zA-Z A-Za-z|base64 -d|sed -E s\ \(.\)\(.\)\ \\\\\2\\\\\1\ g`|sed -E s\ \(.\)\(.\)\ \\\2\\\1\ g)\;\;esac\;done\;\(exit\ \$__urusai_...\)
...
sukui you no nai hentai

内层壳(由于换行非常影响可读性,故已去除)

shell
<<-zako-no-kuse-ni `:|echo|sed -E s\ .\*\ 566716c6\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|xxd -r -p` `:|echo|sed -E s\ .\*\ 566716c6\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|xxd -r -p` "\"\``:|echo|sed -E s\ .\*\ 361647\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|xxd -r -p`|sed -E s\ \(.\)\(.\)\ \\\\\2\\\\\1\ g\;s\ \\\\\^\ =\ g|tr a-zA-Z A-Za-z|tr -d @%^|`:|echo|sed -E s\ .\*\ 26163756634302d246\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|xxd -r -p`|`:|echo|sed -E s\ .\*\ a7361647\ \;s\ \(.\)\(.\)\ \\\2\\\1\ g\;q|xxd -r -p`\`\""
...
zako-no-kuse-ni

变量名称中的 ... 是随机名称,其他的则是具体的处理后内容

AW 加密是一个较为简单的多层壳混淆加密,其主要目的是提供艺术性较强的混淆加密,主要防的是低技术人群

AW 加密主要实现了多层壳低可读性反调试,由于项目性质,其中并没有添加例如更强的混淆、源码分割、真实性校验、更强的反调试等

并且基于纯 POSIX Shell 实现,在 set -e 情况下也可正常运行,兼容性极佳

AW 加密实现原理

简单来说,就是把 eval 等关键代码去特征化,并且尽可能写不规范但是可正常运行的语句,引号几乎不用,绝大部分都是基于转义连接字符串

这种方案并不保险,不过要增加强度,增强反调试策略、验证解释器/指令真实性、混淆封装指令、分隔处理后的源码、添加伪源码即可,就目前的强度而言,拿来做案例绰绰有余

更强的内容自然是机密

可执行进阶混淆加密

由于强度偏高,不提供实际案例

外置解释器方案

为了不重蹈覆辙 shc 直接 sh -c 带来的后果,可以采用管道执行,还可以通过多线程来确保解释器真实性(如果执行者做了管道转发那就很难说了),但是切忌一次性执行逐行执行更稳妥

缺点是必须手动执行 exit,不过这只是个小问题,需要注意的是 $0 需要通过 exec -a 加上一点小手段才能解决,命令行传入则是使用 set -- 解决

内置解释器方案

都做到这个地步了,要做的就是反调试防内存了,要去掉解释器内一切可能暴露源码的内容

当然,有混淆加密的编译器静态编译符号剥离同样也相当重要

总结

能别写解释型语言就别写解释型语言啊!

文章标题: Shell 混淆加密:解释型语言的安全防护

文章作者: 白彩恋

文章链接: https://oom-wg.dev/posts/shell-obf-enc [复制]

写作时间:


商业转载请联系作者获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。
本文采用 File to Download Public Resources License 进行许可。