混淆校验和守卫
Obfuscation of checksum guards
作为我项目的一部分,我必须在名为校验和保护的 C 程序中插入小代码。这些守卫所做的是,他们使用对指令操作码进行操作的函数(add、xor 等)来计算一部分代码的校验和值。因此,如果有人篡改了该代码区域中的指令(添加、修改、删除),校验和值将发生变化,入侵将被检测到。
这是讨论这项技术的研究论文:
https://www.cerias.purdue.edu/assets/pdf/bibtex_archive/2001-49.pdf
这是守卫模板:
guard:
add ebp, -checksum
mov eax, client_addr
for:
cmp eax, client_end
jg end
mov ebx, dword[eax]
add ebp, ebx
add eax, 4
jmp for
end:
现在,我有两个问题。
将守卫放在程序集中会比放在源程序中更好吗?
假设我将它们放在程序集中(在适当的位置)我应该使用什么样的混淆来防止守卫模板很容易被看到? (因为当我有超过 1 个守卫时,攻击者不应该轻易找出所有守卫模板并将所有守卫一起禁用,因为那样会使代码没有安全性)
提前谢谢你。
从攻击者(无消息来源)的角度来看,第一个问题无关紧要;他正在篡改最终的二进制机器代码,无论它是从 .c 还是 .s 生成的,都将产生零差异。所以我主要担心如何生成具有适当校验和的正确二进制文件。我不知道如何在 C 源代码中获得正确的校验和。但我可以想象在 C 编译器创建的汇编程序文件上有一些外部工具 运行,以某种 post-process 方式 - 在将 .s 文件编译成 .o 之前。 但是...请记住,一些调用和地址只是相对偏移量,加载到内存中的二进制文件由 OS 加载程序根据链接器的 table,使它们指向真实的内存地址。因此数据字节将改变(操作码将保持不变)。
你的守卫模板没有考虑到这一点,并且也对整个操作码和数据字节进行校验和(一些高级守卫有操作码定义,checksum/encrypt/decipher只有操作码本身没有操作数字节)。
否则它很整洁,结果是损坏的 ebp 值,破坏了 (*) 使用堆栈变量的任何 C 代码。但这仍然是人工测试,您可以简单地注释掉 add ebp,-checksum 和 add ebp,ebx 让守卫无害。
(*) 请注意,您必须在一些经典 C 代码之间放置守卫,才能从无效的 ebp 值中获得一些真正的运行时问题。如果你把它放在以 pop ebp 结尾的子程序的末尾,一切都会很好。
那么第二个问题:
你肯定想要更多的恶意方法来保护正确的值,而不仅仅是 ebp 损坏。通常最难(删除)的方法是使校验和值成为某些计算的一部分,最终结果只会稍微倾斜,因此不可能认真使用软件,但用户需要时间才能注意到。
你也可以使用一些真正的代码循环来添加你的校验和,所以简单地跳过整个循环也会跳过有效代码(但我可以想象这个代码只能手动添加到从 C 生成的程序集中,所以你会每次新编译特定 C 源代码后都必须重做。
然后可以通过任何可以想象的突变(使用不同的寄存器、修改指令顺序、指令变体)来混淆特定的保护模板,尝试搜索具有突变编码的病毒以获得一些想法。
我没有读那篇论文,但是从图中我会说主要的一点是让那些警戒区域重叠,所以修补其中一个会影响另一个,我觉得是这样的额外的糖分使其有点功能(尽管这看起来仍然是对 8 位游戏破解者的正常挑战;),甚至没有 "hard" 级别)。但这也意味着您需要非常狡猾的外部工具来计算依赖关系的循环树,并以正确的顺序插入保护模板,或者完全手动再次执行。
当然手动做的时候,你必须在每次新的C编译后做,所以只有在非常珍贵和昂贵的东西上,或者坚如磐石的东西上才值得付出努力table,在那里你不会为接下来的 10 年左右制作另一个修订版...:D
作为我项目的一部分,我必须在名为校验和保护的 C 程序中插入小代码。这些守卫所做的是,他们使用对指令操作码进行操作的函数(add、xor 等)来计算一部分代码的校验和值。因此,如果有人篡改了该代码区域中的指令(添加、修改、删除),校验和值将发生变化,入侵将被检测到。
这是讨论这项技术的研究论文:
https://www.cerias.purdue.edu/assets/pdf/bibtex_archive/2001-49.pdf
这是守卫模板:
guard:
add ebp, -checksum
mov eax, client_addr
for:
cmp eax, client_end
jg end
mov ebx, dword[eax]
add ebp, ebx
add eax, 4
jmp for
end:
现在,我有两个问题。
将守卫放在程序集中会比放在源程序中更好吗?
假设我将它们放在程序集中(在适当的位置)我应该使用什么样的混淆来防止守卫模板很容易被看到? (因为当我有超过 1 个守卫时,攻击者不应该轻易找出所有守卫模板并将所有守卫一起禁用,因为那样会使代码没有安全性)
提前谢谢你。
从攻击者(无消息来源)的角度来看,第一个问题无关紧要;他正在篡改最终的二进制机器代码,无论它是从 .c 还是 .s 生成的,都将产生零差异。所以我主要担心如何生成具有适当校验和的正确二进制文件。我不知道如何在 C 源代码中获得正确的校验和。但我可以想象在 C 编译器创建的汇编程序文件上有一些外部工具 运行,以某种 post-process 方式 - 在将 .s 文件编译成 .o 之前。 但是...请记住,一些调用和地址只是相对偏移量,加载到内存中的二进制文件由 OS 加载程序根据链接器的 table,使它们指向真实的内存地址。因此数据字节将改变(操作码将保持不变)。
你的守卫模板没有考虑到这一点,并且也对整个操作码和数据字节进行校验和(一些高级守卫有操作码定义,checksum/encrypt/decipher只有操作码本身没有操作数字节)。
否则它很整洁,结果是损坏的 ebp 值,破坏了 (*) 使用堆栈变量的任何 C 代码。但这仍然是人工测试,您可以简单地注释掉 add ebp,-checksum 和 add ebp,ebx 让守卫无害。
(*) 请注意,您必须在一些经典 C 代码之间放置守卫,才能从无效的 ebp 值中获得一些真正的运行时问题。如果你把它放在以 pop ebp 结尾的子程序的末尾,一切都会很好。
那么第二个问题:
你肯定想要更多的恶意方法来保护正确的值,而不仅仅是 ebp 损坏。通常最难(删除)的方法是使校验和值成为某些计算的一部分,最终结果只会稍微倾斜,因此不可能认真使用软件,但用户需要时间才能注意到。
你也可以使用一些真正的代码循环来添加你的校验和,所以简单地跳过整个循环也会跳过有效代码(但我可以想象这个代码只能手动添加到从 C 生成的程序集中,所以你会每次新编译特定 C 源代码后都必须重做。
然后可以通过任何可以想象的突变(使用不同的寄存器、修改指令顺序、指令变体)来混淆特定的保护模板,尝试搜索具有突变编码的病毒以获得一些想法。
我没有读那篇论文,但是从图中我会说主要的一点是让那些警戒区域重叠,所以修补其中一个会影响另一个,我觉得是这样的额外的糖分使其有点功能(尽管这看起来仍然是对 8 位游戏破解者的正常挑战;),甚至没有 "hard" 级别)。但这也意味着您需要非常狡猾的外部工具来计算依赖关系的循环树,并以正确的顺序插入保护模板,或者完全手动再次执行。
当然手动做的时候,你必须在每次新的C编译后做,所以只有在非常珍贵和昂贵的东西上,或者坚如磐石的东西上才值得付出努力table,在那里你不会为接下来的 10 年左右制作另一个修订版...:D