我同意建议确定什么是和不是一个好的系统调用的整体设计的答案可能很困难。事实上,单独来看,单个系统调用可能没有足够的信息。
就 Windows 的工作方式而言,实际上有两个级别的操作:
- NT 级调用。这些是您期望通过在内核模式例程
sysenter中编码的典型系统调用。ntdll.dll
- Win32 子系统级别。这些是在 、 和其他中实现
advapi32.dll的kernel32.dll功能user32.dll。其中一些直接起作用,其他可能会根据需要自行调用上述函数。
除此之外,作为一项广泛的调查,在 Win32 子系统之上还有两个抽象层:
- COM和朋友,也称为COM、COM+、DCOM、ActiveX、SuperDuper(好吧,开玩笑的)等。
- .Net 和朋友。.net 可执行文件是经过修改的 PE 二进制文件,可以加载
mscoree.dll其他内容,并且可以使用其他 .net 二进制文件或 Win32 中的 API,或通过 COM 接口。
现在,对于挂钩技术,您有:
- 核心。如今,ISR Hooking 无异于稳定性自杀,除非您想对 PatchGuard 进行逆向工程并禁用它,或者在调试器下永久运行您的操作系统。但是,过滤器驱动程序提供了在 Windows 中实现的过滤选项。这通常是防病毒产品扫描网络活动并实施防火墙的方式。
- 用户。你有几个选择:
- 在启动时修补二进制文件。@Poly 谈到了代码洞穴和重定向——给定一个目标二进制文件,一种选择是覆盖其函数中的代码,或者重写有趣 API 的函数以重定向到您的代码。通常,您没有足够的空间来修补跳转到地址空间中的任何旧位置,因此您需要在函数附近有一个“代码洞穴”来放置您的调用/全尺寸跳转。
- 对此的替代方案包括 IAT Hooking,这是一种修改二进制文件的导入表以拉入您的函数而不是原始目标的方法。
- 另一种选择是使用 DLL 重定向。在 Linux 下,您会知道这是
LD_PRELOAD, - 在简单的英语中,您会在所需的 API dll 之前加载具有相同名称和匹配符号的 DLL,并以这种方式窃取调用。它甚至是 MSVC 的一个功能,您可以在 DLL 中生成重定向链接函数,而不必为您不关心的事情实现自己的存根!
- 您可以通过覆盖结果接口类的 vtable 中的相关条目来修补 COM 调用。
- 还有其他技术,例如 DLL 注入等。
因此,如果您希望实现符合您的标准的沙箱,则需要考虑以下几点:
- 首先,你在哪里和什么挂钩。如果您将所有这些都挂钩,
kernel32.dll那么如果 API 调用不调用这些函数,它们仍然是可行的。我不知道任何这样的 COM 或 .net 代码,但我敢打赌有一些。
- 由于 PatchGuard,您需要小心内核修补。过滤器管理器提供了您想要做的大部分防病毒(因此可能是沙箱)明智的事情,但是,如果您离开过滤器管理器,您很快就会进入不稳定的领域。您可以做的事情可能会受到限制。
- 然后,假设您不使用二进制翻译,沙箱几乎可以肯定地检测到您在用户空间中放置的任何钩子。例如,让我们选择 patching
CreateFile。不受信任的代码可以简单地读取此函数的内存并检查序言是否符合预期。同样,有一些方法可以检测注入的 dll、IAT 挂钩等。
- 这同样适用于沙盒内核模式代码。如果您让不受信任的代码安装驱动程序,那么您将再次处于与它的公平竞争环境中。
这种工作已经完成——Sandboxie 是一种尝试在这种级别实现沙箱的产品,尽管它可能具有比您想要的更多的内核级别的东西。当我对这类事情知之甚少时,我只是简要回顾了 Sandboxie,但我怀疑核心组件是内核级别的,因为如果您知道预期它几乎可以撤消任何数量的用户模式挂钩。唯一的防御是拥有更高级别的权限。
您已经提到没有二进制翻译——出于这个原因,我没有介绍 Windows 中的各种调试 API。使用这些,您基本上可以像单步执行程序一样运行程序,并在进行任何调用或跳转之前对其进行分析。但是,这基本上是二进制翻译,调试无论如何都会影响性能。不仅如此,如果我正在编写一个程序来抵抗你的沙箱,还有许多已知的检测调试器的技巧(一个是你不能调试调试器,所以如果你重新启动自己并尝试调试那个新进程和不能,您正在调试。这绝对适用于ptrace),这会使程序在沙箱中无法使用。
积极的一面是,大多数非恶意软件产品很可能与您使用的任何一种技术配合得很好,因为它们可能不会试图抵制在这些环境下运行。这可能是一个非常有用的事实。
最后但并非最不重要的一点是,我为过度使用要点表示歉意!而且,我列出的挂接电话的技术清单并不详尽——肯定还有其他技术。