模块 67 - Syscalls - 重构经典注入
恶意软件开发课程 - Syscalls - 重构经典注入
模块 67 - Syscalls - 重构经典注入#
Syscalls - 重构经典注入#
简介#
在本模块中,我们将使用直接系统调用(Direct Syscalls)重新实现之前讨论过的经典进程注入技术。我们将把所有的 WinAPI 替换为它们对应的系统调用版本:
VirtualAlloc/Ex➜ NtAllocateVirtualMemory ↗VirtualProtect/Ex➜ NtProtectVirtualMemory ↗WriteProcessMemory➜ NtWriteVirtualMemory ↗CreateThread/RemoteThread➜ NtCreateThreadEx ↗
所需系统调用详解#
1. NtAllocateVirtualMemory#
这是 VirtualAllocEx 的底层版本。
NTSTATUS NtAllocateVirtualMemory(
IN HANDLE ProcessHandle, // 目标进程句柄
IN OUT PVOID *BaseAddress, // 返回分配后的基地址(指针的指针)
IN ULONG_PTR ZeroBits, // 设为 0
IN OUT PSIZE_T RegionSize, // 申请的大小(指针,会被修改)
IN ULONG AllocationType, // MEM_COMMIT | MEM_RESERVE
IN ULONG Protect // 内存保护属性
);c💡 初学者提示:引用传递
与 WinAPI 不同,
NtAllocateVirtualMemory要求BaseAddress和RegionSize都通过地址传递(使用&)。这是因为系统调用执行完后,可能会修改这些值。例如,即使你只申请 100 字节,系统也会按一页(4096 字节)进行向上取整,并把实际分配的大小填回RegionSize。
2. NtProtectVirtualMemory#
对应 VirtualProtectEx。同样要求 BaseAddress 和 NumberOfBytesToProtect 是引用传递。
3. NtWriteVirtualMemory#
对应 WriteProcessMemory。参数逻辑与 WinAPI 几乎一致。
4. NtCreateThreadEx#
这是 CreateRemoteThread 的底层实现。它非常强大且灵活,但在基础注入中,我们只需将其大部分复杂参数设为 NULL 即可。
实战方案 A:直接获取函数地址 (GetProcAddress)#
这是最简单的方法。我们通过 GetModuleHandle 拿到 ntdll.dll,然后动态获取上述函数的地址并调用。
实战方案 B:使用 SysWhispers3 (间接系统调用)#
为了绕过 Hook,我们可以使用 SysWhispers3 生成间接系统调用代码。
操作步骤:
- 使用脚本生成代码:
python syswhispers.py -m jumper_randomized -f NtAllocateVirtualMemory,NtProtectVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o SysWhispers - 将生成的
.h,.c和.asm文件添加到 Visual Studio 项目。 - 在项目属性中启用 MASM (Microsoft Macro Assembler)。
- 将
.asm文件的项类型设置为Microsoft Macro Assembler。
这样,当你调用 NtAllocateVirtualMemory 时,它会通过你自己的汇行代码跳转到内核,从而彻底无视 EDR 的监控。
实战方案 C:使用地狱之门 (Hell’s Gate)#
我们也可以集成地狱之门技术,在运行时动态搜寻 SSN。我们需要:
- 更新
VX_TABLE结构体以包含四个新函数。 - 使用新的 Seed 生成 Djb2 哈希。
- 在
main函数中初始化表格并调用动态解析后的系统调用。
关键细节:内存对齐问题#
由于 NtAllocateVirtualMemory 会修改 RegionSize 的值(将其向上取整为 4096 的倍数),在编写代码时必须小心。
// 错误示例:
SIZE_T sSize = 272;
NtAllocateVirtualMemory(..., &sSize, ...); // sSize 现在变成了 4096
// 下一步写入时,你会尝试写入 4096 字节,这可能会导致缓冲区溢出或写入失败
NtWriteVirtualMemory(..., pPayload, sSize, ...); c正确做法:始终使用一个原始副本保存 Payload 的真实大小。
🎯 总结#
在本模块中,我们学习了:
- API 替换映射:掌握了从高级 WinAPI 到低级 NtAPI 的转换关系。
- 底层调用约定:理解了 NtAPI 中普遍存在的“引用传递”要求及内存对齐逻辑。
- 工程实践:学会了如何在 Visual Studio 中集成 ASM 汇编代码,并通过三种不同的方案(动态调用、SysWhispers、地狱之门)实现隐蔽注入。
📚 下一步学习
下一个模块将探讨 Syscalls - Reimplementing Mapping Injection。我们将学习如何使用系统调用来实现更隐蔽的内存映射注入技术!