本文首发于: https://xz.aliyun.com/t/14894
编译时对字符串常量加密,在产物中敏感字符串不以明文存储,所以在特征免杀中有较好的效果。
常见的方案
使用LLVM pass实现字符串加密
通常思路都是在pass中对字符串常量加密并添加解密函数,在初始化或者引用时解密字符串。
从稳定性上看,需要实现临界区或增加标志位,防止多线程竞争同时解密。
从免杀效果上看,由于字符串仍在原地解密,无法绕过内存查杀。
参考链接:
- https://github.com/Breathleas/notes-2/blob/master/llvm/ollvm/3_string_encrypt.md
- https://github.com/tsarpaul/llvm-string-obfuscator
- https://obfuscator.re/omvll/passes/strings-encoding/
- https://github.com/61bcdefg/Hikari-LLVM15
C++17 constexpr
https://github.com/JustasMasiulis/xorstr 是通过C++17 constexpr特性实现的字符串常量加密。
来看一个xorstr的例子
1 | int main() { |
1 | movabs rax, -4762152789334367252 |
相对于llvm的方案而言优势在于将字符串常量解密在栈上
- 完成就没有线程安全问题了
- 栈上内存可能很快就被覆盖,一定程度上也解决了内存查杀问题
另外就是支持x86,arm下通过AVX、SSE、NEON等指令进行加速解密。
最大问题也非常突出: C++狗都不学 C++从学习难度,产物体积等完全没有优势
用zig实现编译时字符串常量加密
本文使用zig 0.13.0
zig也有类似于C++17 constexpr的特性,使用 comptime
关键字
官方文档在此: https://ziglang.org/documentation/0.13.0/#comptime
通过很简单的代码即可实现编译时字符串常量加密,如下:
1 | const std = @import("std"); |
1 | movabs rax, 8245869312136934253 |
可以点击此链接体验: https://zig.godbolt.org/z/dTzbe8arY
实现动态密钥
上面的例子的key是固定的,如果在zig使用中每次编译生成不通的key?
参考: https://ziggit.dev/t/how-to-implement-conditional-compilation-in-zig/379/3
在build.zig中添加,相当于定义了一个 option
模块,并添加了 key
的定义
1 | var options = b.addOptions(); |
并修改main.zig中key的定义
1 | const key = @import("option").key; |
最终效果
完整代码
https://github.com/howmp/zigxorstr
更新历史
2025年1月25日
- 更新到zig 0.13.0
- 通过负优化不需要引用常量(没有只读数据段rdata/rodata)