现在,我们对系统调用有了更好的了解,并了解了从应用程序 ==> Windows Api ==>本机 Windows Api 的链。现在是时候通过使用直接系统调用来真正实现旁路了。
但首先,让我们更深入地了解一下我们在第 1 部分中已经创建的代码。我们将使用 WinDBG 来研究本机 Windows API 的代码。我们将特别关注 NtAllocateVirtualMemory 本机 Windows 函数以及这些指令在汇编中的样子!
我很感兴趣,在我们使用 Syswhispers 后,这种情况是否会发生变化!
相关视频教程
恶意软件开发(更新到了155节)
了解 WinDBG 中 NtAllocateVirtualMemory 的程序集代码
汇编代码一开始可能令人生畏,但在一些指导下,我们可以逐步分解它以了解正在发生的事情。首先,我们要使用 Windbg。
因此,我们将使用该工具来调试和查看本机 Windows API 执行的代码步骤,特别是专注于一条指令。ntAllocateVirtual 内存函数。这可以通过执行以下命令来执行:
u ntdll!NtAllocateVirtualMemory
.让我们逐行分析代码。
0:000> u ntdll!NtAllocateVirtualMemory
ntdll!NtAllocateVirtualMemory:
00007ffb`6ff8d140 4c8bd1 mov r10,rcx
00007ffb`6ff8d143 b818000000 mov eax,18h
00007ffb`6ff8d148 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
00007ffb`6ff8d150 7503 jne ntdll!NtAllocateVirtualMemory+0x15 (00007ffb`6ff8d155)
00007ffb`6ff8d152 0f05 syscall
00007ffb`6ff8d154 c3 ret
00007ffb`6ff8d155 cd2e int 2Eh
00007ffb`6ff8d157 c3 ret
第 1 行:功能序幕
00007ffb`0972d140 4c8bd1 mov r10,rcx
在第一行中,(move)指令用于将寄存器的值复制到寄存器中。这是一个重要的步骤,因为寄存器现在存储了第一个指令的地址,当系统调用完成并返回到用户空间时,该指令应该执行。mov
rcx
r10
r10
第 2 行:设置功能代码
00007ffb`0972d143 b818000000 mov eax,18h
在这里,该指令用于将寄存器设置为(十进制 24)。有趣的是,此值是指示系统调用号的代码。系统调用索引中哪些是系统调用函数必须由系统调用的!在本例中,函数 18h 代表 NtAllocateVirtualMemory。但是,对于 NtWriteVirtualMemory 或 NTFreeVirtualMemory(或在这种情况下,任何其他 Syscall 指令代码)确实具有不同的代码,这些代码被移动到 eax 并在内核模式下执行!此号码称为 Syscall ID/Syscall 号码或系统服务号码。mov
eax
0x18
第 3 行:检查全局标志
00007ffb`0972d148 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
此行执行按位运算。它根据 1 检查内存位置的最低位。test
[SharedUserData+0x308]
SharedUserData 显然与KUser_Shared_data有关。这是 Windows 操作系统中的已知结构,其中包含用户模式和内核模式代码使用的各种全局数据和指针。SharedUserData+0x308 引用这些特定偏移量之一。
#define SharedUserData ((KUSER_SHARED_DATA * const) KI_USER_SHARED_DATA)
如果我们在KUSER_Shared_data中查找0x308我们会发现它代表 ULONG SystemCall。仅用于 Windows 版本 1511 及更高版本!
第 4 行:条件跳转
00007ffb`0972d150 7503 jne ntdll!NtAllocateVirtualMemory+0x15 (00007ffb`0972d155)
此行包含一个(如果不相等则跳转)指令。它检查上一个操作设置的零标志。如果该标志不为零(表示该标志的最低位设置为 1),则跳转到地址 。这是基于标志值的条件分支。jne
test
ntdll!NtAllocateVirtualMemory+0x15
第 5 行:系统调用
00007ffb`0972d152 0f05 syscall
用于从用户模式转换为内核模式并调用负责处理所请求操作的内核函数的指令。在本例中,它根据 eax 中存储的 Syscall ID 调用系统调用!syscall
NtAllocateVirtualMemory
第 6 行:功能结语
00007ffb`0972d154 c3 ret
该指令用于从函数返回。在这种情况下,它表示函数的结束。ret
NtAllocateVirtualMemory
第 7 行:中断
00007ffb`0972d155 cd2e int 2Eh
此行包含一条带有中断向量的指令。这是在 x86-64 体系结构中触发系统调用的另一种方法。我不知道为什么 2 个系统调用列在彼此下面。除了冗余之外,我找不到一个好的理由。我注意到 int 2Eh 的使用较少,因此恶意软件开发人员开始使用它而不是 syscall 函数。int
2Eh
第 8 行:功能结语
00007ffb`0972d157 c3 ret
与第 6 行类似,这是另一条指令,指示函数的结束。它在这里是为了冗余,因为该函数可以通过 和 指令返回。ret
syscall
int
现在,我们对执行系统调用的部分有了更好的了解。还有一件重要的事情需要注意。我们不能依赖这些系统调用 ID 是静态的。我们可以使用预定义的 Syscall ID 对直接 syscall 进行编码。这将是 NTAllocateVirtualMemory 的0x0018。或 18 小时,如组件所示。但是,这仅适用于 Windows 10 及更高版本的设备(从撰写本文的那一刻开始)。在每个版本/构建中,Microsoft 都可以更改这些系统呼叫号。因此,如果我们想确保我们的漏洞利用在多个设备上有效,我们不能仅仅依赖该 ID。
我们可以使用多种技术和方法。
Syswhispers 迭代(1 到 3)
地狱之门
Freshycalls(我们不会在本博客中对此进行研究)
光环之门
我们不打算探索这些技术中的每一个的完整部门,但让我们访问各个工具并比较它们的差异。每个 SSD 的共同点是,它们不仅静态存储每个 SSD 的代码,而且直接从 Ntdll.dll 解析它。让我们看看他们是如何找到这些信息的。
地狱之门
地狱之门 我们不会真正涵盖它,因为 Halo's Gate 是基于地狱之门的更好、更新的替代品。但是,它已经通过搜索 mov 操作码从 NTDLL 动态提取系统调用值,0xb8。找到后,它会提取旁边的字节。在下面的资源中,您可以阅读有关地狱之门的完整论文!
Syswhispers(系统耳语)
Syswhispers 目前有 3 个版本。我们将重点介绍 SysWhispers 2 和 3。因为 Syswhispers 使用了我上面已经提到的静态 Syscall 编号,所以将来使用是不可行的。如下图所示,Syswhispers 包含一个文件,指示哪个 Syscall ID 号对应于哪个版本上的哪个函数:
Syswhispers2 是从 1 开始的彻底改造,从使用动态地址开始。
Syswhispers 2 (英语)
使用地址上的排序技术,根据 @modexpblog 推广的技术,从函数地址中推断出系统调用 ID。它通过对以 Zw 而不是 Nt 开头的 NTDLL 函数进行排序来做到这一点。 然后 Hashses 和 order 保存;根据顺序确定 SSN,递增 1。
在下面的 2 张图片中显示 zwWriteVirtualMemory 和 NtWriteVirtualMemory 具有相同的地址!因此,可以从未挂钩的 zw 本机函数中检索地址。
来自 Microsoft:
系统耳语3
Syswhispers3 是 Syswhispers2 的一个分支,最大的区别在于它支持多种技术。
在这篇博客中,我们将使用 Syswhispers3。但我们不会关注EGG或random_jumping。也许我们会在单独的博客中关注这一点。
光环之门
Halo's Gate 是 Hell's Gate 的改进版。这是必需的,因为如果本机函数被 EDR 挂钩,则由于所使用的技术,您永远无法通过使用地狱之门来提取 Syscall ID。这是因为 EDR 覆盖了 NTDLL 函数存根的部分内容,导致地狱之门无法正常工作。因为 EDR 不会挂钩每个 ntdll 函数。(因为这会导致用户端的延迟。Halo's Gate 解决了这个问题。它按以下方式执行此操作:如果它检测到特定的 ntdll 函数已挂钩。它将检查相邻的 Syscall 号码。正如我们在上图中看到的。当我们列出按地址排序的所有系统调用时,所有系统调用存根都会以增量方式相互跟随。
这意味着,如果你的函数被钩住了,但下面或上面的下一个函数没有被钩住,你只需要检索它的系统调用,然后简单地减去或加 1 即可获得当前函数的系统调用。
这可以通过使用 Windbg 进行检查。监视加载ntdll.dll并执行以下命令的任何应用程序
00007ffb`0972d140 4c8bd1 mov r10,rcx0
该命令执行以下操作:
它将在以“nt”开头的模块中搜索符号。对于每个匹配的符号,它将以 ASCII 格式按排序顺序显示与该符号关联的地址的内存内容。ntdll
最后,让我们重写代码,直到它只包含本机 Windows API 函数。然后让我们重写它,让它执行直接系统调用!
使用 Syswhispers3 实现
我们将使用 Syswhispers3 来实现此。我们将通过仅使用标准实现而不是使用寻蛋或random_jumps来做到这一点。
SysWhispers3 是一个开源工具,其代码是一个 Python 脚本。它将帮助我们为任何指定的系统调用生成特定的 C 和汇编代码(如果我们懒惰,则生成所有代码)。生成后,我们可以将指定的代码集成到我们原来的代码库中。完成此操作后,我们不必再从ntdll.dll检索进程地址来下载特定函数,而是可以直接通过系统调用调用内核!因此,回避任何 ntdll 函数上的任何用户土地钩子。
有关生成 3 个文件的命令,请参见下文:
一个 c 文件,对于每种情况都是相同的。
一个头文件,其中包含一些定义和基于指定函数的更改。
以及 ASM 文件,它包含 assembly 中的特定功能!
使用的命令:
00007ffb`0972d140 4c8bd1 mov r10,rcx1
现在,我们需要在 Visual Studio 中包含此代码,并确保 ASM 文件类型设置为 Microsoft 宏汇编程序。这可以通过在源文件(ASM 文件)上按鼠标右键并转到常规并进行更改来完成
右键单击项目→转到“生成依赖项”→“生成自定义项”并启用 MASM 也很重要。
在上一个博客中,我们做了一些更改。为此,我们确保仅使用本机函数:
— NtAllocateVirtualMemory
— NtWriteVirtualMemory
— NtFreeVirtualMemory
— NtCreateThreadEx
— NtWaitForSingleObject
— NT关闭
我仍然在努力通过使用 NtQuerySystemInformation 和 NtQueryInformationProcess 来转换 GetSystemInfo 和 GobalMemoryStatusEx。但是,这些并不是那么有趣,以至于它们可能会触发 EDR。当我找到解决方案时,我会对这部分写一个小的更新。
00007ffb`0972d140 4c8bd1 mov r10,rcx2
代码与第 2 部分中的代码相同。但是,正如你所看到的,它不再需要定义现在由 Syswhispers 执行的单个 Native 函数的结构。(节省大量时间)
此外,我们还通过使用真正的本机 Windows API 更改了执行代码和代码副本。
和以前一样,注释在很大程度上解释了代码。但是,让我们展示现在程序集文件中包含的内容。
如果我们查看其中一个函数的生成代码,我们会看到 NTAllocateVirtualMemory 由几个部分组成:
保存寄存器:有趣的是,Syswhispers 保存了更多的寄存器。然后,您可以将其与常规系统调用过程进行比较。我还没有发现为什么需要保存多个寄存器。为了正确执行
Syscall ID 的 has 被移动到寄存器中,并在名为 SW3_GetSyscallNumber 的 c 功能块中解析。
之后,将恢复所有已保存的寄存器。
执行 Syscall。
C 函数将调用以填充 syscall 列表。并根据存储在 ecx 寄存器中的提供的函数哈希返回相应的 Syscall ID。
00007ffb`0972d140 4c8bd1 mov r10,rcx3
在SW3_PoplateSyscallList函数中,它们执行搜索ntdll.dll基址的魔力。从 Zw* 地址创建列表。它们与 Nt 本机函数具有相同的基址。然后从那里对它进行排序。这样可以确保如果挂钩了本机函数,您仍然可以确定它应该具有的地址。
00007ffb`0972d140 4c8bd1 mov r10,rcx4
现在,如果我们开始构建代码,我们应该能够绕过在其中一个使用的本机函数上实现钩子的 EDR。好。
但是,我们还没有真正能够验证这种技术。因此,我需要编写自己的 EDR 存根,它将挂钩ntdll.dll中的一些特定的本机函数。从那里,我们将简单地计算每个应用程序接收到对这些特定函数的调用次数。有了它,我们将能够测试和验证此旁路是否正常工作。还有什么比验证我们的结果并用它来验证我们是如何被发现更好的呢?但这值得一个完整的独立博客。
在尝试在 API 监视器中查看 exe 时,我收到了一些奇怪的问题。我无法解释,因此使用存根执行验证格外重要。
我们还将研究如何检测此类攻击。例如,使用我们使用的代码,静态分析器可以看到系统上使用了系统调用。这是一个危险信号,因为普通程序永远不会从其代码中执行系统调用。
上面的代码来自我们通过使用 syswhispers3 包含的汇编代码。
结论:
我终于开始了解所使用的不同机制和技术之间的差异。令我惊讶的是,人们如何“轻松”地创建直接系统调用。但因此,了解您在做什么,尤其是您正在使用的程序在做什么更为重要。
希望它还能帮助您更好地了解直接系统调用。接下来,对于旁路和检测方法的验证过程,我们可能会检查一种称为间接系统调用挂钩或 ntdll 卸载的东西。为什么这可能有用,我们将在以后的博客中介绍。但我确信这将是一项有趣的努力。
祝您测试愉快!
二进制漏洞课程(更新中)
windows网络安全防火墙与虚拟网卡(更新完成)
windows文件过滤(更新完成)
USB过滤(更新完成)
游戏安全(更新中)
ios逆向
windbg
还有很多免费教程(限学员)
更多详细内容添加作者微信
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...