这个 x86 二进制混淆的基本前提准确吗? (只有系统调用及其参数对程序的结果很重要)
Posted
技术标签:
【中文标题】这个 x86 二进制混淆的基本前提准确吗? (只有系统调用及其参数对程序的结果很重要)【英文标题】:Is this fundamental premise for x86 binary obfuscation accurate? (That only system calls and their arguments matter to the outcome of the program) 【发布时间】:2022-01-23 00:36:58 【问题描述】:这与在用户模式下运行的代码有关。为简单起见,假设我们已经获取了程序调用的任何共享库/操作系统 API,并将它们静态链接到内存中,因此我们没有调用任何抽象层,只是直接使用系统调用。
我正在做一个实验,通过识别某些非可变状态(我认为系统调用和跳转)来实现二进制混淆,而无需构建相对先进的混淆方法。基本上,我模拟程序存储执行的每条指令的状态更改。当我到达系统调用或跳转时,我将其标记为边界,并且在两个边界之间执行的每条指令都标记为“函数”。我的理论是,在用户模式程序中,系统调用是唯一在该程序。换句话说,无论您希望您的程序在用户模式下对系统进行什么更改,系统调用都是唯一的方法。
如果我错了,请停在这里。
因此,基于这种理解,我假设我可以以几乎无限种方式改变这些函数中的每一个,只要结果指令在函数末尾导致相同的状态,以便系统调用参数保持不变完全相同的。当然,控制流也需要保留,所以我也将跳转视为保留状态。我通过使用蒙特卡洛树搜索来解决变异状态中的所需状态来实现这一点。换句话说,如果我遵循这些规则并将变异的程序重新组合在一起并更新所有跳转以达到他们之前指向的相同函数,我的程序应该在外部执行它最初的相同目标,但通过不同的指令。
这是一个可视化图表。如果放大,它是清晰的,但 SO 的压缩使它有点模糊。
这个概念只是为了混淆内存、寄存器和指令序列分析通道(如果没有进一步的说明,不会“完全”混淆它们)
我的前提有缺陷吗?
【问题讨论】:
顺便说一句,这与问题无关,请原谅我,但是在学习链接器时,我遇到了 Ian Taylor 最精彩的20 part series on linkers(博客),我突然想到那些关注 x86 标签的人会喜欢这个资源。没有“下一个”导航,因此您可以通过将 URL 从/38
修改为 /39
等来移动到下一个。
在mmap(MAP_SHARED, PROT_WRITE)
之后,写入内存最终会影响磁盘上文件的内容,或者对读取共享内存区域的其他进程可见。许多程序不会对任何文件执行此操作,但通常使用共享内存进行进程间通信(尤其是与 X 服务器的通信)。尽管如此,它可能并没有完全使您关于系统调用之前/之后的 reg/mem 状态的论点在大多数情况下都是唯一相关的事情。
【参考方案1】:
在mmap(MAP_SHARED, PROT_READ|PROT_WRITE)
之后,写入内存最终会影响磁盘上文件的内容,并且/或者对读取共享内存区域的其他进程可见。 (大多数进程间通信使用共享内存不是通过磁盘文件,而是匿名 SHM,如 shmat(2)
或 shm_open(3)
)
许多程序不会对任何文件执行此操作,但将共享内存用于 IPC(尤其是 X 服务器)有点常见。
不过,它可能不会完全使您关于 reg/mem 状态的论点在系统调用之前/之后成为大多数时间唯一相关的事情。
多线程程序通过内存与自身通信;线程共享它们的整个地址空间,加载/存储指令的顺序对于内存排序可能很重要。对于原子性,无论是使用一个 dword mov
完成 4 字节加载还是稍后合并的 4 字节加载。 atomic pure-load 和 pure-store 只是简单的指令,只有 atomic RMW 需要lock
前缀(假设你正在编译运行在多核机器上,否则lock
前缀可以省略。)
【讨论】:
有趣,我得先在单线程上证明它。以上是关于这个 x86 二进制混淆的基本前提准确吗? (只有系统调用及其参数对程序的结果很重要)的主要内容,如果未能解决你的问题,请参考以下文章