用zig实现编译时字符串常量加密
半块西瓜皮 发表于 陕西 安全工具 1052浏览 · 2024-06-21 06:01

编译时对字符串常量加密,在产物中敏感字符串不以明文存储,所以在特征免杀中有较好的效果。

常见的方案

使用LLVM pass实现字符串加密

通常思路都是在pass中对字符串常量加密并添加解密函数,在初始化或者引用时解密字符串。

从稳定性上看,需要实现临界区或增加标志位,防止多线程竞争同时解密。

从免杀效果上看,由于字符串仍在原地解密,无法绕过内存查杀。

参考链接:

C++17 constexpr

https://github.com/JustasMasiulis/xorstr 是通过C++17 constexpr特性实现的字符串常量加密。

来看一个xorstr的例子

int main() {
    std::puts(xorstr_("an extra long hello_world"));
}
movabs rax, -4762152789334367252
push rbp
mov rbp, rsp
and rsp, -32
sub rsp, 64
mov QWORD PTR [rsp], rax
mov rdi, rsp
movabs rax, -6534519754492314190
mov QWORD PTR [rsp+8], rax
......
vpxor ymm0, ymm1, YMMWORD PTR [rsp+32]
vmovdqa YMMWORD PTR [rsp], ymm0
vzeroupper
call puts
xor eax, eax
leave
ret

相对于llvm的方案而言优势在于将字符串常量解密在栈上

  1. 完成就没有线程安全问题了
  2. 栈上内存可能很快就被覆盖,一定程度上也解决了内存查杀问题

另外就是支持x86,arm下通过AVX、SSE、NEON等指令进行加速解密。

最大问题也非常突出: C++狗都不学 C++从学习难度,产物体积等完全没有优势

用zig实现编译时字符串常量加密

本文使用zig 1.11

zig也有类似于C++17 constexpr的特性,使用 comptime 关键字

官方文档在此: https://ziglang.org/documentation/0.11.0/#comptime

通过很简单的代码即可实现编译时字符串常量加密,如下:

const std = @import("std");
const key = [16]u8{1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};
const string = []const u8;

fn encrypt(comptime str: string) *[str.len]u8 {
    comptime var enstr: [str.len]u8 = undefined;
    for (0..str.len) |i| {
        enstr[i] = str[i] ^ key[i % key.len];
    }
    return enstr[0..str.len];
}
inline fn x(comptime str: string) []u8 {
    comptime var e = encrypt(str);
    var buf = e.*;
    for (0..buf.len) |i| {
        buf[i] ^= key[i % key.len];
    }
    return buf[0..str.len];
}
export fn main() void {
    var s1 = x("aaaabbbbccccddddaaaabbbbccccdddd123123123123123");
    _ = std.io.getStdOut().write(s1) catch unreachable;
}
main:
  vmovups ymm0, ymmword ptr [rip + .L__unnamed_1+15]
  vmovups ymm1, ymmword ptr [rip + .L__unnamed_1]
  xor eax, eax
  vmovups ymmword ptr [rsp - 41], ymm0
  vmovups ymmword ptr [rsp - 56], ymm1
.LBB0_1:
  cmp rax, 47
  je .LBB0_3
  mov ecx, eax
  and ecx, 15
  mov cl, byte ptr [rcx + .L__unnamed_2]
  xor byte ptr [rsp + rax - 56], cl
  inc rax
  jmp .LBB0_1
.LBB0_3:
  movabs rdi, 1
  lea rsi, [rsp - 56]
  movabs rdx, 47
  xor r8d, r8d
.LBB0_4:
  mov rax, rdi
  syscall
  mov ecx, eax
  neg ecx
  cmp rax, -4095
  cmovb ecx, r8d
  cmp cx, 4
  je .LBB0_4
  vzeroupper
  ret

.L__unnamed_1:
  .ascii "`cbegdejjcbag`ab`cbegdejjcbag`ab0005756::131266"

.L__unnamed_2:
  .ascii "\001\002\003\004\005\006\007\b\t\000\001\002\003\004\005\006"

可以点击此链接体验: https://zig.godbolt.org/z/fc3hcsseo

实现动态密钥

上面的例子的key是固定的,如果在zig使用中每次编译生成不通的key?

参考: https://ziggit.dev/t/how-to-implement-conditional-compilation-in-zig/379/3

在build.zig中添加,相当于定义了一个 option 模块,并添加了 key 的定义

var options = b.addOptions();
 var key: [16]u8 = undefined;
 std.os.getrandom(&key) catch unreachable;
 options.addOption([16]u8, "key", key);
 exe.addOptions("option", options);

并修改main.zig中key的定义

const key = @import("option").key;

最终效果

完整代码

https://github.com/howmp/zigxorstr

0 条评论
某人
表情
可输入 255