Windows API 与编程调用
从这个章节起,我们将学习一些有关编程与二进制的技能,为下个章节的恶意软件以及安全工具开发奠定基础。学习恶意软件开发的原因有多种,从进攻型安全的角度来看,我们通常需要针对客户的环境执行某些恶意任务,当涉及到参与中使用的工具类型时,通常有三个主要选择。第 1 种是开源工具,这些工具通常由安全供应商签名甚至没有签名,并且会在安全性相对任何成熟的组织中被检测到,因此在执行攻击的时候,它们并不总是可靠的。第 2 种是购买工具,预算较大的团队通常会选择购买工具,以便在项目期间节省宝贵的时间。这些工具通常是闭源的,并且具备更加出色的免杀能力。以及,开发定制工具。因为这些工具是定制的,所以它们没有被安全供应商们分析或标记,这使得攻击者在免疫检测方面具有优势,这就是恶意软件开发知识对于更成功的红队行动至关重要的地方。
Windows 架构
Windows 架构
为了学习较为底层的理论,我们首先需要了解 Windows 架构。运行 Windows 操作系统的机器内的处理器可以在两种不同的模式下运行:用户模式 (又称为 Ring 3) 和内核模式 (又称为 Ring 0)。
应用程序运行在用户模式下,操作系统组件运行在内核模式下。大多数用户活动将发生在用户模式,但应用程序也会在需要时过渡到内核模式。Win32 API (例如 kernel32.dll) 旨在成为开发人员的第一个调用端口。 然后这些 API 将调用较低级别的 API,例如 ntdll.dll。Microsoft 有意不记录大多数 NTAPI,并且可以随时对其进行更改。他们可能会更改其他用户模式 DLL 与 NTDLL 交互的方式,只要原始用户模式 DLL 接口不变即可
当应用程序想要完成一项任务时,例如创建文件,它无法自行完成。 唯一可以完成任务的实体是内核,因此应用程序必须遵循特定的函数调用流程。
我们来看看流程中所涉及到的重要概念:
用户进程:由用户执行的应用程序,例如记事本、Chrome 浏览器或 Microsoft Word。
子系统 DLL:包含用户进程调用的 API 函数的 DLL,例如 kernel32.dll 导出 CreateFile Windows API 函数,其他常见的子系统 DLL 例如 ntdll.dll、advapi32.dll、user32.dll 等。
ntdll.dll:系统范围的 DLL,它是用户模式下可用的最低层。这是一个特殊的 DLL,用于创建实现从用户模式到内核模式的转换,这通常称为原生 API (Native API)或 NTAPI。
执行内核:这就是所谓的 Windows 内核,它调用内核模式中可用的其他驱动程序和模块来完成任务。Windows 内核部分存储在 C:\Windows\System32 下名为 ntoskrnl.exe 的文件中。
函数调用
以创建文件为例子,应用程序可以调用 kernel32.dll 中的 CreateFileW 从磁盘打开文件,其中 Kernel32.dll 是一个关键的 DLL,被大多数应用程序加载。然后 CreateFileW 将调用对应的 NTAPI 函数: ntdll.dll 中的 NtCreateFile,而 ntdll.dll 又使用系统调用 (syscall) 转换到内核 (ntoskrnl.exe) 以访问文件系统硬件,从调用堆栈的角度来看,这类似于 UserApp.exe -> kernel32.dll -> ntdll.dll -> ntoskrnl.exe。
打开 WinDBG (当前为演示目的,稍后小节教授具体用法),附加到 Notepad.exe 进程。给 kernel32 的 CreateFileW 设置断点,随即,我们便到达了调用该 WinAPI 的地址。不过跟我们想象的略有不一样,这个地址并没有执行 CreateFileW 的实际代码,而是跳转到了 kernelbase.dll 的 CreateFileW。这是因为在最近几年的 Windows 版本里,advapi32.dll 与 kernel32.dll 中的部分 API 被迁移到了新的 DLL 文件 kernelbase.dll 中。
0:002> bp kernel32!createfilew
0:002> g
Breakpoint 0 hit
KERNEL32!CreateFileW:
00007ffd`2ba60760 ff25da2c0600 jmp qword ptr [KERNEL32!_imp_CreateFileW (00007ffd`2bac3440)] ds:00007ffd`2bac3440={KERNELBASE!CreateFileW (00007ffd`2a0f4f80)}
0:000> p
KERNELBASE!CreateFileW:
00007ffd`2a0f4f80 488bc4 mov rax,rsp
前进一步,继而进入 Kernelbase 中的 CreateFileW API。
从 CreateFileW 到 NtCreateFile 中间还有着大量的指令,但最终我们看到了调用 Ntdll 中 NtCreateFile 的指令。
给该位置设置断点,继续执行,果然到达了 NtCreateFile API 的入口。
下图,则是 NtCreateFile 的 syscall stub (执行 syscall 的代码段),而高亮的 55h,这里就是对应的 syscall 编号了。
syscall stub 的模式如下:
mov r10, rcx
mov eax, <syscall 编号>
syscall
ret
syscall 编号,即 SSN,在不同版本直接可能有所不同,我们可以在 https://j00ru.vexillium.org/syscalls/nt/64/ 网站上查询各个操作系统版本的 SYSCALL 列表。
实际上应用程序可以直接调用 syscall,而无需通过 WinAPI,WinAPI 只是充当原生 API 的包装器。话虽如此,但原生 API 更难使用,因为它们没有被 Microsoft 正式记录。此外,微软也建议不要直接使用原生API 函数,因为它们会在没有告知的情况下随时更改。
Windows 内存管理
该部分会介绍 Windows 内存的基础知识,因为了解 Windows 如何处理内存对于编写高级恶意软件至关重要。
内存与分页
现代操作系统中的内存是不直接映射到物理内存 (RAM) 的,相反,虚拟内存地址由映射到物理内存地址的进程使用。 这有几个原因,但最终目标是尽可能多地节省物理内存。 虚拟内存可以映射到物理内存,但也可以存储在磁盘上。 通过虚拟内存寻址,多个进程可以共享同一个物理地址,同时拥有唯一的虚拟内存地址。 虚拟内存依赖于内存分页的概念,它将内存分成 4kb 的块,称为页。