文本字符串:嵌入式软件中的隐藏漏洞
多年来,台式计算机的安全性一直是人们关注的焦点。一旦机器连接到互联网,就内在地存在遭受某种攻击的可能性。这种渗透可能是为了窃取数据、破坏系统,或以某种方式改变其运行。防护手段已众所周知并广泛应用。嵌入式系统似乎一直免疫于此类问题,因为它们很少联网,且代码通常存储在某种ROM中。但情况已发生改变。现代系统中很大一部分已连接到互联网,将代码复制到RAM中执行已成为常见做法。这意味着安全性现在已成为嵌入式软件设计的重要考量因素。
安全与防护的区别
英语是一种极佳的沟通工具,它非常富有表现力,能实现精准且细微的交流。然而在日常言语中,我们大多比较懒惰,经常不完全准确地使用词汇。我要举的例子是 safety(安全)和 security(防护/安保)这两个词几乎被互换使用,仿佛它们是同义词。我认为听到的最佳定义大致如下:安全是保护世界免受设备伤害的过程;防护是保护设备免受世界伤害。防护是我今天的主题。
何时需要工业级加密
如果系统真的需要做到绝对防弹,则需要工业级加密。这通常需要特定的硬件支持,虽然现成可得,但对于不需要如此高安全性的应用可能被视为大材小用。在这种情况下,还有其他选择,这正是我想探讨的内容。
黑客如何分析设备内存
如果黑客能访问设备的内存内容,他们就能开始弄清设备的功能及实现方式。这是改变其运行的第一阶段。代码可能被反汇编,从而揭示逻辑。没有加密,几乎无法阻止这一点。黑客接下来可能会查看数据的十六进制/ASCII转储,寻找其中有意义的模式和可识别的结构。这是可以采取一些预防措施的地方。虽然加密可能不是选项,但混淆是一种可能性。
数据混淆的目标
数据混淆的目标是通过让数据难以被识别其本质,从而延缓或阻止黑客。扫描内存转储时,最容易发现的就是文本字符串。所以我将重点放在这里。
C/C++中字符串的存储方式
在C/C++代码中,文本字符串通常就是包含ASCII码的字节序列,以空字节结尾。这非常容易被发现,所以我要改变它。首先,不使用空终止符,而是将每个字符串的第一个字节作为长度说明符。字符串的字符将稍微打乱其数据,使其看起来不那么熟悉——我所做的只是交换每个字节的两个半字节(nibble)。我需要一个实用程序,输入明文字符串,生成带有适当初始化的数组声明。以下是该实用程序的核心函数:
void scramble(int index, unsigned char *input) { unsigned char *charpointer, character; printf("unsigned char string%d[%d] = {0x%02x,", index, strlen(input)+1, strlen(input)); charpointer = input; while(*charpointer) { character = *charpointer++; character = ((character & 0x0f) << 4) | ((character & 0xf0) >> 4); printf("0x%02x", character); if (*charpointer) printf(","); } printf("}; // \"%s\"\n", input); }
如果我给这个函数传递索引4和字符串"Hello world"(挺原创吧?),输出将是:
unsigned char string4[12] = {0x0b, 0x84, 0x56, 0xc6, 0xc6, 0xf6, 0x02, 0x77, 0xf6, 0x27, 0xc6, 0x46}; // "Hello world"
我可以将其复制粘贴到代码中,然后只需编写一个函数在需要显示时解混淆文本。除了给每个字符串一个索引号,我还可以通过用字符串替换 index 参数来给它一个任意名称。注意生成的代码在一定程度上是自文档化的,因为注释以可读形式显示了字符串,但这仅出现在源代码中。如果黑客能访问你的源代码,那你麻烦大了,我无法再提供帮助!
解混淆过程代码示例
以下是演示解混淆过程的代码:
void main() { unsigned char temp, buffer[50]; int count = string4[0], index=0; while(count--) { temp = string4[index+1]; temp = ((temp & 0x0f) << 4) | ((temp & 0xf0) >> 4); buffer[index] = temp; index++; } buffer[index] = 0; printf("-%s-\n", buffer); }
其他混淆方法
每个字节交换半字节只是混淆的众多方法之一。另一种可能是将每个字符左旋三位。以下是实现该功能的代码:
unsigned char leftrotate3(unsigned char c) { c = (c << 3) | (c >> 5); return c; }
我概述的混淆技术是基于逐字符打乱字符串。也可以对整个字符串进行操作。例如,将字符串视为一长串比特序列,向左旋转任意位数。我将这个算法的编码留给更有热情的读者。
副作用:简化多语言版本
值得注意的是,将所有文本字符串本地化的一个副作用是,为其他语言制作不同版本的软件变得非常简单。
重要提醒
我必须重申并强调,数据混淆远非防弹,充其量只能拖延严肃的黑客。如果没有别的,解混淆代码也可能被反汇编。该技术的诀窍在于让混淆成为一条难以追踪的线索。如果你需要真正更高的安全性,必须考虑全面加密。
嵌入式