Windows内部结构
由于 Windows 机器构成了企业基础设施的大部分,红队需要了解 Windows 的内部结构以及如何(滥用)使用它们。在制作攻击性工具或漏洞利用时,红队可以滥用这些来帮助规避和利用。
进程
进程维护并代表程序的执行;一个应用程序可以包含一个或多个进程。一个流程有许多组件,它被分解成要存储和交互的组件。根据官方文档所说:”每个进程都提供执行程序所需的资源。进程具有虚拟地址空间、可执行代码、系统对象的开放句柄、安全上下文、唯一的进程标识符、环境变量、优先级、最小和最大工作集大小以及至少一个执行线程。”
微软的参考文档:
https://learn.microsoft.com/en-us/windows/win32/procthread/about-processes-and-threads
进程是根据应用程序的执行而创建的。进程是 Windows 运行的核心,Windows 的大多数功能都可以包含为应用程序并具有相应的进程。以下是启动进程的默认应用程序的一些示例。
以下是启动进程的默认应用程序的一些示例:
- MsMpEng(微软防御者)
- wininit(键盘和鼠标)
- lsass(凭证存储)
攻击者可以瞄准进程来逃避检测并将恶意软件隐藏为合法进程。攻击者可以针对进程来实现潜在的攻击向量,如:
工艺注入(T1055)
https://attack.mitre.org/techniques/T1055/
工艺镂空(T1055.012)
https://attack.mitre.org/techniques/T1055/012/
过程伪装(T1055.013)
https://attack.mitre.org/techniques/T1055/013/
流流程组件:
进程解释:
有多种实用程序可以使观察过程变得更容易
- Process Hacker 2
- Process Explorer
- Procmon
Procmon
这里使用Procmon工具来进行演示,下载地址:
https://docs.microsoft.com/en-us/sysinternals/downloads/procmon
进程树
双击进入工具后,就会来到如图所示的地方,但是会看见有很多重复的进程名称
我们可以随意选中一个进程,然后点击进程树这个图标
现在看起来就没有那么乱了
过滤器
如图进行点击,可以看见有很多可以过滤的选项,这里我过滤的是进程名称
最后可以看见,筛选出来的都是刚刚的进程名 notepad.exe
筛选notepad.exe 加载了多少个 dll
可以看见是51个
查看事件属性
选中一个进程,然后点击这个闪电标志(事件属性)
在这里我们可以看见父进程的ID,还有流程的完整性级别登其他详细的进程信息
notepad.exe的基地址
来到这里,可以看见线程堆栈参数
线程数
线程是进程使用的可执行单元,并根据设备因素进行调度。简而言之线程的定义:“控制进程的执行”。由于线程控制执行,因此这是一个常见的目标组件。线程滥用可以单独使用来帮助代码执行,或者更广泛地用于与其他 API 调用链接,作为其他技术的一部分。
线程与其父进程共享相同的细节和资源,例如代码、全局变量等。线程也有其独特的值和数据:
虚拟内存
虚拟内存是 Windows 内部工作和交互的关键组成部分。虚拟内存允许其他内部组件与内存交互,就好像它是物理内存一样,而不存在应用程序之间发生冲突的风险。
微软官方文档说明:
https://docs.microsoft.com/en-us/windows/win32/memory/virtual-address-space
虚拟内存为每个进程提供了 私有的虚拟地址空间。内存管理器用于将虚拟地址转换为物理地址。通过拥有私有虚拟地址空间而不是直接写入物理内存,进程造成损坏的风险较小。
内存管理器还将使用 页面 或 传输 来处理内存。应用程序可能使用比分配的物理内存更多的虚拟内存;内存管理器将虚拟内存转移或分页到磁盘来解决这个问题。如图:
- 32 位 x86 系统上的理论最大虚拟地址空间为 4 GB。
- 在 64 位现代系统上,理论最大虚拟地址空间为 256 TB。
管理员可以通过设置 ( increaseUserVA ) 或 AWE(地址窗口扩展)为需要更大地址空间的应用程序更改此分配布局。
AWE:
https://learn.microsoft.com/en-us/windows/win32/memory/address-windowing-extensions
动态链接库
DLL “一个包含可由多个程序同时使用的代码和数据的库”
windows文档:
https://docs.microsoft.com/en-us/troubleshoot/windows-client/deployment/dynamic-link-library#:~:text=A%20DLL%20is%20a%20library,common%20dialog%20box%20related%20functions.
当DLL作为程序中的函数加载时,该 DLL 被指定为依赖项。由于程序依赖于 DLL,因此攻击者可以瞄准 DLL 而不是应用程序来控制执行或功能的某些方面。
- DLL劫持 ( T1574.001 )
https://attack.mitre.org/techniques/T1574/001/
- DLL旁载 ( T1574.002 )
https://attack.mitre.org/techniques/T1574/002/
- DLL注入 ( T1055.001 )
https://attack.mitre.org/techniques/T1055/001/
DLL的创建方式与任何其他项目/应用程序没有什么不同;它们只需要轻微的语法修改即可工作。Visual C++ Win32 动态链接库项目的 DLL 示例:
#include "stdafx.h" // 预编译头文件,用于包含常用的系统和标准库头文件
#define EXPORTING_DLL
// 一个宏定义,用于在代码中标记导出函数
#include "sampleDLL.h" //包含示例 DLL 的头文件
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
)
// 这是 DLL 的入口函数,当 DLL 被加载或卸载时会调用该函数。
// 参数 hModule 是 DLL 模块的句柄,ul_reason_for_call 是调用原因的标志,
// lpReserved 是保留参数。
{
return TRUE; //DllMain 函数只是简单地返回 TRUE
}
void HelloWorld() // 一个自定义的函数 HelloWorld()
{
MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}
// Windows API 函数 MessageBox,用于显示一个消息框
// 消息框显示 "Hello World",标题为 "In a DLL",并有一个确定按钮
便携式可执行格式
可执行文件和应用程序是 Windows 内部如何在更高级别上运行的很大一部分。PE(可移植可执行文件)格式定义了有关可执行文件和存储数据的信息。PE 格式还定义了数据组件如何存储的结构。
PE(可移植可执行文件)格式是可执行文件和目标文件的总体结构。PE(可移植可执行文件)和COFF(通用对象文件格式)文件组成了PE 格式。
PE 数据最常见于可执行文件的十六进制转储中。下面我们将对 calc.exe 的十六进制转储分解为 PE 数据的部分,对其进行了解。
首先使用 DIE 打开 目标calc.exe 文件,下面那个红框框是DiE 报告的入口点 0000000140001830
然后点击这里,如图所示
然后在这里,我们可以看见许多地址的值。红框框指的是 .data段虚拟地址
PE 数据的结构
主要为分为七个部分
DOS头定义文件类型
MZ标头将文件格式定义为.exe。我们可以从十六进制转储部分中看到
DOS存根
DOS存根是默认在文件开头运行的程序,用于打印兼容性消息,不会影响该文件的任何功能。
DOS 存根打印该消息 This program cannot be run in DOS mode
PE文件头
PE文件头提供二进制文件的 PE 头信息。定义文件的格式,包含签名和图像文件头,以及其他信息头。
它是人类可读输出最少的部分。以从下面的十六进制转储部分中的存根识别 PE 文件头的开头。
图像可选头有一个欺骗性的名字,它是PE文件头的重要组成部分
数据字典是图像可选标头的一部分。它们指向图像数据目录结构。
部分表将定义图像中的可用部分和信息,都可以从十六进制转储部分中的表中识别每个部分的定义。
各部分可以定义文件的内容和数据:
Winwows API
Windows API 提供与 Windows 操作系统的关键组件交互的本机功能。该 API 被许多人广泛使用,包括红队成员、威胁参与者、蓝队成员、软件开发人员和解决方案提供商。
子系统和硬件交互
程序经常需要访问或修改Windows子系统或硬件,但为了维护机器的稳定性而受到限制。为了解决这个问题,微软发布了Win32 API,一个用于用户模式应用程序和内核之间接口的库。
Windows 通过两种不同的模式来区分硬件访问:用户模式和内核模式。这些模式决定了应用程序或驱动程序允许的硬件、内核和内存访问。各模式之间的API或系统调用接口,向系统发送信息以在内核模式中进行处理。
下面是用户应用程序如何使用 API 调用来修改内核组件的直观表示。
应用程序在使用 API 之前将先经过语言运行时。
Windows API 的组件
Win32 API(通常称为 Windows API)具有多个依赖组件,用于定义 API 的结构和组织。
我们通过自上而下的方法分解 Win32 API。我们假设 API 是顶层,构成特定调用的参数是底层。如图:
操作系统库
Win32 库的每个 API 调用都驻留在内存中,并且需要一个指向内存地址的指针。由于ASLR(地址空间 布局 R随机化)实现,获取这些函数指针的过程变得模糊; 每种语言或软件包都有独特的过程来克服 ASLR。
Windows 头文件
Microsoft 发布了 Windows 头文件(也称为 Windows 加载程序),作为与 ASLR 实现相关的问题的直接解决方案。将概念保持在较高的水平,在运行时,加载程序将确定正在进行哪些调用,并创建一个 thunk 表来获取函数地址或指针。
官方文档:
https://learn.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers
一旦windows.h文件包含在非托管程序的顶部;可以调用任何 Win32 函数
P/Invoke
Microsoft 将 P/Invoke 或平台调用描述为“一种允许您从托管代码访问非托管库中的结构、回调和函数的技术”。
P/invoke 提供了处理从托管代码调用非托管函数(或者换句话说,调用 Win32 API)的整个过程的工具。P/invoke 将通过导入包含非托管函数或 Win32 API 调用的所需DLL来启动。下面是导入带有选项的 DLL 的示例。
using System;
using System.Runtime.InteropServices;
public class Program
{
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
...
}
// 使用user32.dll文件导入了DLL "DLLImport"
注意:不包含分号是因为 p/invoke 函数尚未完成。在第二步中,我们必须将托管方法定义为外部方法。该extern关键字将通知运行时先前导入的特定DLL 。下面是创建外部方法的示例。
using System;
using System.Runtime.InteropServices;
public class Program
{
...
private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
}
现在我们可以将函数作为托管方法调用,但我们正在调用非托管函数!
API调用结构
API 调用是 Win32 库的第二个主要组件。API调用功能可以通过修改命名方案和附加代表性字符来扩展。下面是 Microsoft 支持的命名方案的字符表:
字符 | 解释 |
---|---|
A | 表示采用 ANSI 编码的 8 位字符集 |
W | 代表Unicode编码 |
Ex | 为 API 调用提供扩展功能或输入/输出参数 |
详细可以参考这个:
https://docs.microsoft.com/en-us/windows/win32/learnwin32/working-with-strings
每个 API 调用还有一个预定义的结构来定义其输入/输出参数。Windows-api 文档的相应 API 调用文档页面上找到大多数这些结构 ,以及每个 I/O 参数的说明:
https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list
我们以WriteProcessMemoryAPI 调用为例:获得的调用的 I/O 结构 。
BOOL WriteProcessMemory(
[in] HANDLE hProcess,
[in] LPVOID lpBaseAddress,
[in] LPCVOID lpBuffer,
[in] SIZE_T nSize,
[out] SIZE_T *lpNumberOfBytesWritten
);
C API 实现
Microsoft 提供了 C 和 C++ 等低级编程语言以及一组预配置的库,我们可以使用它们来访问所需的 API 调用。windows.h 文件头用于定义调用结构并获取函数指针。要包含 Windows 标头,请将以下行添加到任何 C 或 C++ 程序之前。
如:
#include <windows.h>
常见滥用的 API 调用
Win32 库中的多个 API 调用很容易被用于恶意活动。最常被滥用的 API:
参考
https://tryhackme.com/jr/windowsinternals
https://tryhackme.com/jr/windowsapi