# 进程与线程

在恶意软件开发领域，诸多**代码注入**、**防御规避**技术是围绕着进程与线程展开的，因此我们首先需要理解进程与线程的相关概念。

### **进程与线程**

Windows 进程是指当前运行在 Windows 主机上的**程序或者应用的实例**，每个进程都与其他进程隔离，并拥有由操作系统分配的私有资源。进程可以是用户或者操作系统开启的，消耗着如内存、磁盘空间等资源。

线程是进程内的最小执行单元，每个 Windows 进程由 1 个或多个线程并发运行。进程中运行的每个线程共享进程的内存和资源。 与进程不同，线程之间不是相互隔离的，可以直接与进程中的其他线程交互。

总之，进程是一个正在运行的程序，拥有操作系统分配的独自的资源，而线程是进程内的执行路径。进程中的多个线程共享进程的资源，但独立地并发地执行。因此，虽然一个进程可以在其中运行多个线程，但每个线程独立操作，执行自己的指令，这个概念是并发编程的关键，其中同时执行多个任务以提高程序的效率和性能。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-06/scaled-1680-/bjPyuwuA85H6tVBD-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-06/bjPyuwuA85H6tVBD-image.png)

### **进程内存**  


Windows 进程也使用内存来存储数据和指令。当进程创建时，会被分配内存，分配的内存量可以由进程本身设置。操作系统使用虚拟内存和物理内存来管理内存。通过创建可由应用程序访问的虚拟地址空间，虚拟内存允许操作系统使用比物理可用内存更多的内存。这些虚拟地址空间被划分为页，然后分配给进程。

进程可以有不同类型的内存：

**私有内存**：专用于单个进程，不能被其他进程所共享，这种类型的内存用于存储特定于进程的数据。  
**映射内存**：可以在 2 个或多个进程之间共享，它用于在进程之间共享数据，例如共享库、共享内存段和共享文件。映射内存对其他进程可见，但不会被其他进程修改。  
**映像内存**：包含可执行文件的代码和数据，它用于存储进程使用的代码和数据，例如程序的代码、数据和资源。 映像内存通常与加载到进程地址空间中的 DLL 文件相关。

### **PEB**

进程环境块 (PEB) 是 Windows 中的一种数据结构，其中包含有关进程的信息，例如**进程的参数**、**启动信息**、**分配的堆信息**和**加载的 DLL** 等。操作系统使用 PEB 来存储正在运行的进程的信息，Windows 加载程序使用它来启动应用程序。它还存储有关进程的信息，例如**进程 ID** (PID) 和**可执行文件的路径**。

创建的每个进程都有自己的 PEB 数据结构，C 语言下的 PEB 结构体如下所示：

```c++
typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged;
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
  PVOID                         Reserved4[3];
  PVOID                         AtlThunkSListPtr;
  PVOID                         Reserved5;
  ULONG                         Reserved6;
  PVOID                         Reserved7;
  ULONG                         Reserved8;
  ULONG                         AtlThunkSListPtr32;
  PVOID                         Reserved9[45];
  BYTE                          Reserved10[96];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
  BYTE                          Reserved11[128];
  PVOID                         Reserved12[1];
  ULONG                         SessionId;
} PEB, *PPEB;
```

微软并没有在文档中记录所有的元素，并且 PEB 可能在以后进行修改。该结构体中的一些元素对于进程的操作较为重要

##### **BegingDebugged**

该元素表示当前进程是否正在被 Debug，当进程正在被调试时，该元素被设置为 1 (TRUE)，反之则为 0 (FALSE)。

##### **LDR**

Ldr 是指向 PEB 中的 **PEB\_LDR\_DATA 结构体**的指针，该结构包含有关进程加载的 DLL 模块的信息。它包含了进程中加载的 **DLL 的列表**、每个 DLL 的**基址**以及**大小**，Windows 加载程序使用它来跟踪进程中加载的 DLL。我们则可以通过 LDR 枚举进程载入的 DLL 以及查找进程内存中的特定 DLL。

 **PEB\_LDR\_DATA** 结构体如下所示：

```c++
typedef struct _PEB_LDR_DATA {
  BYTE       Reserved1[8];
  PVOID      Reserved2[3];
  LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
```

我们可以通过 LDR 枚举进程载入的 DLL 以及查找进程内存中的特定 DLL，以及寻找特定 DLL (例如 kernel32.dll) 的基址。并且根据这些信息来实现自定义的 **GetModuleHandle**。

##### **ProcessParameters**

ProcessParameters 是 PEB 中的结构体，包含了**映像路径**、传递给进程的**命令行参数**等信息。Windows 加载程序将这些参数添加到进程的 PEB 结构中。ProcessParameters 是指向 **RTL\_USER\_PROCESS\_PARAMETERS** 结构体的指针，代码如下所示：

```c++
typedef struct _RTL_USER_PROCESS_PARAMETERS {
  BYTE           Reserved1[16];
  PVOID          Reserved2[10];
  UNICODE_STRING ImagePathName;
  UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
```

ProcessParameter 可被利用于实现**命令行伪造攻击**，我们在下一章节会介绍到。

##### **PostProcessInitRoutine**

PEB 结构中的 PostProcessInitRoutine 元素用于存储指向一个函数的指针，该函数在进程中的所有线程完成 TLS (**线程本地存储**) 初始化后由操作系统调用。该函数可用于执行该进程所需的任何其他初始化任务。

##### **SessionId**

PEB 中的 SessionID 元素是分配给单个会话的唯一标识符，用于追踪会话期间的用户活动。

### **TEB**

TEB (线程环境块) 是 Windows 中存储有关**线程信息**的结构体，是为进程中的每个线程分配的数据块，操作系统使用它来管理线程。它包含线程的环境、安全上下文和其他相关信息。它存储在线程的**栈**中，供 Windows 内核用来管理线程。

在 C 语言中，TEB 的结构体如下所示：

```c++
typedef struct _TEB {
  PVOID Reserved1[12];
  PPEB  ProcessEnvironmentBlock;
  PVOID Reserved2[399];
  BYTE  Reserved3[1952];
  PVOID TlsSlots[64];
  BYTE  Reserved4[8];
  PVOID Reserved5[26];
  PVOID ReservedForOle;
  PVOID Reserved6[4];
  PVOID TlsExpansionSlots;
} TEB, *PTEB;
```

TEB 中的一些元素较为重要，我们分别来查看：

##### **ProcessEnvironmentBlock**

该元素是 PEB 结构体的指针，PEB 在上文刚讨论过。

##### **<span style="box-sizing: border-box;">TlsSlots</span>**

<span style="box-sizing: border-box; -webkit-print-color-adjust: exact;">**线程本地存储** (TLS) 是一种机制，使得给定多线程进程中的每个线程都可以分配位置来存储**线程特定的数据**。TlsSlots 是一个包含 64 个指针的数组，可用于存储特定于线程的数据。如果应用需要存储线程特定的数据，则它可以使用这些槽位之一来存储指向该数据的指针。</span>

##### **TlsExpansionSlots**

<span style="box-sizing: border-box; -webkit-print-color-adjust: exact;">如果需要超过 64 个 TLS 槽位，系统会分配一组额外槽位，并且指向该数组的指针存储在 TlsExpansionSlots 中。</span>

在 Windows 操作系统上，每个进程都有一个唯一的进程标识符 PID，这是操作系统在创建进程时分配的。同样的概念也适用于正在运行的线程，正在运行的线程也有着唯一的 ID 与其他的线程进行区分。

有了唯一的标识符后，分别可以用 OpenProcess 与 OpenThread 来获得进程或线程的句柄：

```c++
HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwProcessId
);
```

```c++
HANDLE OpenThread(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwThreadId
);
```