Windows的内部结构&API

Windows的内部结构&API_第1张图片

Windows内部结构

由于 Windows 机器构成了企业基础设施的大部分,红队需要了解 Windows 的内部结构以及如何(滥用)使用它们。在制作攻击性工具或漏洞利用时,红队可以滥用这些来帮助规避和利用。

进程

进程维护并代表程序的执行;一个应用程序可以包含一个或多个进程。一个流程有许多组件,它被分解成要存储和交互的组件。根据官方文档所说:”每个进程都提供执行程序所需的资源。进程具有虚拟地址空间、可执行代码、系统对象的开放句柄、安全上下文、唯一的进程标识符、环境变量、优先级、最小和最大工作集大小以及至少一个执行线程。”
微软的参考文档:

https://learn.microsoft.com/en-us/windows/win32/procthread/about-processes-and-threads

进程是根据应用程序的执行而创建的。进程是 Windows 运行的核心,Windows 的大多数功能都可以包含为应用程序并具有相应的进程。以下是启动进程的默认应用程序的一些示例。
以下是启动进程的默认应用程序的一些示例:

  • MsMpEng(微软防御者)
  • wininit(键盘和鼠标)
  • lsass(凭证存储)

Windows的内部结构&API_第2张图片
攻击者可以瞄准进程来逃避检测并将恶意软件隐藏为合法进程。攻击者可以针对进程来实现潜在的攻击向量,如:

工艺注入(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/

流流程组件:
Windows的内部结构&API_第3张图片
进程解释:
Windows的内部结构&API_第4张图片
有多种实用程序可以使观察过程变得更容易

  • Process Hacker 2
  • Process Explorer
  • Procmon

Procmon

这里使用Procmon工具来进行演示,下载地址:

https://docs.microsoft.com/en-us/sysinternals/downloads/procmon

进程树

双击进入工具后,就会来到如图所示的地方,但是会看见有很多重复的进程名称
Windows的内部结构&API_第5张图片
我们可以随意选中一个进程,然后点击进程树这个图标
Windows的内部结构&API_第6张图片
现在看起来就没有那么乱了
Windows的内部结构&API_第7张图片

过滤器

如图进行点击,可以看见有很多可以过滤的选项,这里我过滤的是进程名称
Windows的内部结构&API_第8张图片
Windows的内部结构&API_第9张图片
最后可以看见,筛选出来的都是刚刚的进程名 notepad.exe
Windows的内部结构&API_第10张图片筛选notepad.exe 加载了多少个 dll
Windows的内部结构&API_第11张图片
可以看见是51个

查看事件属性

选中一个进程,然后点击这个闪电标志(事件属性)
在这里我们可以看见父进程的ID,还有流程的完整性级别登其他详细的进程信息
Windows的内部结构&API_第12张图片
notepad.exe的基地址
Windows的内部结构&API_第13张图片
来到这里,可以看见线程堆栈参数
Windows的内部结构&API_第14张图片

线程数

线程是进程使用的可执行单元,并根据设备因素进行调度。简而言之线程的定义:“控制进程的执行”。由于线程控制执行,因此这是一个常见的目标组件。线程滥用可以单独使用来帮助代码执行,或者更广泛地用于与其他 API 调用链接,作为其他技术的一部分。
线程与其父进程共享相同的细节和资源,例如代码、全局变量等。线程也有其独特的值和数据:
Windows的内部结构&API_第15张图片

虚拟内存

虚拟内存是 Windows 内部工作和交互的关键组成部分。虚拟内存允许其他内部组件与内存交互,就好像它是物理内存一样,而不存在应用程序之间发生冲突的风险。
微软官方文档说明:

https://docs.microsoft.com/en-us/windows/win32/memory/virtual-address-space

虚拟内存为每个进程提供了 私有的虚拟地址空间。内存管理器用于将虚拟地址转换为物理地址。通过拥有私有虚拟地址空间而不是直接写入物理内存,进程造成损坏的风险较小。
内存管理器还将使用 页面 或 传输 来处理内存。应用程序可能使用比分配的物理内存更多的虚拟内存;内存管理器将虚拟内存转移或分页到磁盘来解决这个问题。如图:
Windows的内部结构&API_第16张图片

  • 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.

Windows的内部结构&API_第17张图片
当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 格式。
Windows的内部结构&API_第18张图片
PE 数据最常见于可执行文件的十六进制转储中。下面我们将对 calc.exe 的十六进制转储分解为 PE 数据的部分,对其进行了解。

首先使用 DIE 打开 目标calc.exe 文件,下面那个红框框是DiE 报告的入口点 0000000140001830
Windows的内部结构&API_第19张图片
然后点击这里,如图所示
Windows的内部结构&API_第20张图片
然后在这里,我们可以看见许多地址的值。红框框指的是 .data段虚拟地址
Windows的内部结构&API_第21张图片

PE 数据的结构

主要为分为七个部分

DOS头定义文件类型

MZ标头将文件格式定义为.exe。我们可以从十六进制转储部分中看到
Windows的内部结构&API_第22张图片

DOS存根

DOS存根是默认在文件开头运行的程序,用于打印兼容性消息,不会影响该文件的任何功能。
DOS 存根打印该消息 This program cannot be run in DOS mode
image.png

PE文件头

PE文件头提供二进制文件的 PE 头信息。定义文件的格式,包含签名和图像文件头,以及其他信息头。
它是人类可读输出最少的部分。以从下面的十六进制转储部分中的存根识别 PE 文件头的开头。
Windows的内部结构&API_第23张图片
图像可选头有一个欺骗性的名字,它是PE文件头的重要组成部分
数据字典是图像可选标头的一部分。它们指向图像数据目录结构。
部分表将定义图像中的可用部分和信息,都可以从十六进制转储部分中的表中识别每个部分的定义。
Windows的内部结构&API_第24张图片
各部分可以定义文件的内容和数据:
Windows的内部结构&API_第25张图片

Winwows API

Windows API 提供与 Windows 操作系统的关键组件交互的本机功能。该 API 被许多人广泛使用,包括红队成员、威胁参与者、蓝队成员、软件开发人员和解决方案提供商。

子系统和硬件交互

程序经常需要访问或修改Windows子系统或硬件,但为了维护机器的稳定性而受到限制。为了解决这个问题,微软发布了Win32 API,一个用于用户模式应用程序和内核之间接口的库。

Windows 通过两种不同的模式来区分硬件访问:用户模式和内核模式。这些模式决定了应用程序或驱动程序允许的硬件、内核和内存访问。各模式之间的API或系统调用接口,向系统发送信息以在内核模式中进行处理。
image.png
下面是用户应用程序如何使用 API 调用来修改内核组件的直观表示。
Windows的内部结构&API_第26张图片
应用程序在使用 API 之前将先经过语言运行时。

Windows API 的组件

Win32 API(通常称为 Windows API)具有多个依赖组件,用于定义 API 的结构和组织。
我们通过自上而下的方法分解 Win32 API。我们假设 API 是顶层,构成特定调用的参数是底层。如图:
Windows的内部结构&API_第27张图片

操作系统库

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 

常见滥用的 API 调用

Win32 库中的多个 API 调用很容易被用于恶意活动。最常被滥用的 API:
Windows的内部结构&API_第28张图片

参考

https://tryhackme.com/jr/windowsinternals
https://tryhackme.com/jr/windowsapi

你可能感兴趣的:(APT,windows,安全)