C语言开发中如何避免二进制文件字符数组被strings命令明文检出
首先明确strings的默认扫描规则:它会遍历二进制文件的所有字节,将**连续4~6个及以上(不同发行版默认阈值存在差异)的可打印ASCII字符(含空格、换行、制表符)**识别为字符串输出,遇到不可打印字符就会截断当前识别序列。
你之前用char array1[] = { 'd', 'd', 'd', 'd', '\0' };没被扫描到,和char/unsigned char的类型声明没有任何关系,只是因为4个连续'd'刚好卡在你本地strings版本的识别阈值下。只要你把连续可打印字符的长度拉到阈值以上,哪怕逐字符写char数组一样会被识别出来。你写的长十六进制数组里连续可打印字符长度接近20,自然会被直接抓出明文。
你的需求只是绕过默认规则的扫描,不需要高强度加密,以下两个方案实现成本极低,完全满足要求:
方案1:单字节异或混淆(推荐,无体积膨胀)
这是最轻量的实现方式:存储时把每个字节和一个自选的1字节密钥(比如0x55、0xAA这类值,随便选即可,只要别让混淆后的内容出现长段连续可打印字符)做异或运算,运行时需要用到内容时,再遍历数组把每个字节和同一个密钥异或,就能还原出原始文本。
混淆后的数组字节不存在长段连续可打印字符,strings根本不会触发识别。
示例代码:
// 混淆后的数组,每个元素 = 原字节 ^ 0x55,无长段可打印字符 unsigned char array1[] = { 0x31, 0x31, 0x31, 0x31, 0x75, 0x34, 0x34, 0x34, 0x34, 0x5f, 0x37, 0x37, 0x37, 0x37, 0x75, 0x36, 0x36, 0x36, 0x36, 0x31, 0x5f }; // 运行时还原函数,需要使用数组内容前调用一次即可 void deobfuscate(unsigned char* buf, size_t len, unsigned char key) { for (size_t i = 0; i < len; i++) { buf[i] ^= key; } } // 调用示例:deobfuscate(array1, sizeof(array1), 0x55);
这个方案数组体积和存储明文完全一致,运行时只有一次遍历异或的开销,性能几乎无影响。
方案2:间隔插入不可打印填充字节(实现最简单)
原理更直白:每存1个有效内容字节,就跟着插入1个0x00、0x01这类不可打印的填充字节,把连续可打印字符的长度强行拆成单个字节,远低于strings的识别阈值,自然不会被扫出。使用时遍历数组,每隔1个字节取1个有效字节即可还原内容。
示例代码:
// 偶数下标为有效内容,奇数下标为0x00填充 unsigned char array1[] = { 0x64,0x00, 0x64,0x00, 0x64,0x00, 0x64,0x00, 0x20,0x00, 0x61,0x00, 0x61,0x00, 0x61,0x00, 0x61,0x00, 0x0a,0x00, 0x62,0x00, 0x62,0x00, 0x62,0x00, 0x62,0x00, 0x20,0x00, 0x63,0x00, 0x63,0x00, 0x63,0x00, 0x63,0x00, 0x64,0x00, 0x0a,0x00 }; // 运行时提取有效内容 void extract_content(unsigned char* dst, const unsigned char* src, size_t src_len) { size_t dst_idx = 0; for (size_t i = 0; i < src_len; i += 2) { dst[dst_idx++] = src[i]; } dst[dst_idx] = '\0'; }
这个方案不需要做运算,逻辑最不容易写错,缺点是数组体积会比原内容大一倍,对存储体积不敏感的场景可以直接使用。
不要依赖修改段属性、调整类型声明这类偏门方法,不同版本的strings、不同编译选项都可能导致这类方法失效。上面两个方案是从strings的识别原理出发做的规避,不管是哪个版本的默认扫描规则都能稳定绕过。
内容的提问来源于stack exchange,提问作者HotDogBear

