用户模式下dump进程内存

dump完整进程内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <windows.h>
#include <dbghelp.h>
#include <stdio.h>

#pragma comment(lib, "dbghelp.lib")

BOOL CreateMiniDump(DWORD dwPID, const char* szDumpFilePath)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
if (hProcess == NULL)
{
printf("无法打开进程 %d,错误码:%d\n", dwPID, GetLastError());
return FALSE;
}

HANDLE hDumpFile = CreateFileA(szDumpFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDumpFile == INVALID_HANDLE_VALUE)
{
printf("无法创建转储文件,错误码:%d\n", GetLastError());
CloseHandle(hProcess);
return FALSE;
}

MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = NULL;
mdei.ClientPointers = FALSE;

BOOL bSuccess = MiniDumpWriteDump(hProcess, dwPID, hDumpFile, MiniDumpWithFullMemory, &mdei, NULL, NULL);
if (!bSuccess)
{
printf("MiniDumpWriteDump 失败,错误码:%d\n", GetLastError());
}
else
{
printf("成功创建转储文件:%s\n", szDumpFilePath);
}

CloseHandle(hDumpFile);
CloseHandle(hProcess);

return bSuccess;
}

int main(int argc, char* argv[])
{
if (argc != 3)
{
printf("用法:%s <进程ID> <转储文件路径>\n", argv[0]);
return 1;
}

DWORD dwPID = atoi(argv[1]);
const char* szDumpFilePath = argv[2];

if (CreateMiniDump(dwPID, szDumpFilePath))
{
printf("进程 %d 的内存转储已保存到 %s\n", dwPID, szDumpFilePath);
}
else
{
printf("创建内存转储失败。\n");
}

return 0;
}

创建mini dump

要将代码修改为生成最小转储(Mini Dump),只需将 MiniDumpWriteDump 函数的 DumpType 参数从 MiniDumpWithFullMemory 更改为 MiniDumpNormal。MiniDumpNormal 是 MINIDUMP_TYPE 枚举中的一个常量,表示仅包含捕获所有线程的堆栈跟踪所需的信息。

以下是修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <windows.h>
#include <dbghelp.h>
#include <stdio.h>

#pragma comment(lib, "dbghelp.lib")

BOOL CreateMiniDump(DWORD dwPID, const char* szDumpFilePath)
{
// 打开目标进程,获取必要的访问权限
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
if (hProcess == NULL)
{
printf("无法打开进程 %d,错误码:%d\n", dwPID, GetLastError());
return FALSE;
}
// 创建转储文件
HANDLE hDumpFile = CreateFileA(szDumpFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDumpFile == INVALID_HANDLE_VALUE)
{
printf("无法创建转储文件,错误码:%d\n", GetLastError());
CloseHandle(hProcess);
return FALSE;
}

// 设置异常信息
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = NULL;
mdei.ClientPointers = FALSE;

// 写入转储文件,使用 MiniDumpNormal
BOOL bSuccess = MiniDumpWriteDump(hProcess, dwPID, hDumpFile, MiniDumpNormal, &mdei, NULL, NULL);
if (!bSuccess)
{
printf("MiniDumpWriteDump 失败,错误码:%d\n", GetLastError());
}
else
{
printf("成功创建最小转储文件:%s\n", szDumpFilePath);
}

// 关闭句柄
CloseHandle(hDumpFile);
CloseHandle(hProcess);

return bSuccess;
}

int main(int argc, char* argv[])
{
if (argc != 3)
{
printf("用法:%s <进程ID> <转储文件路径>\n", argv[0]);
return 1;
}

DWORD dwPID = atoi(argv[1]);
const char* szDumpFilePath = argv[2];

if (CreateMiniDump(dwPID, szDumpFilePath))
{
printf("进程 %d 的最小内存转储已保存到 %s\n", dwPID, szDumpFilePath);
}
else
{
printf("创建最小内存转储失败。\n");
}

return 0;
}

dump进程的某个模块

要在 C 语言中转储(dump)特定进程的某个模块(如 DLL 或 EXE 文件),可以使用 Windows API 提供的 CreateFileReadProcessMemory 函数。以下是实现此功能的步骤和示例代码:

步骤:

  1. 获取目标进程的句柄: 使用 OpenProcess 函数,传入目标进程的进程 ID(PID),以获取该进程的句柄。
  2. 枚举目标进程的模块: 使用 CreateToolhelp32SnapshotModule32FirstModule32Next 函数,遍历目标进程加载的模块列表,找到需要转储的模块。
  3. 读取模块内存并保存: 使用 ReadProcessMemory 函数读取目标模块的内存数据,并将其写入到本地文件中,以完成转储。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

BOOL DumpModule(DWORD dwPID, const char* szModuleName, const char* szDumpFilePath)
{
HANDLE hProcess = NULL;
HANDLE hModuleSnap = NULL;
MODULEENTRY32 me32 = {0};
BOOL bRet = FALSE;

// 打开目标进程
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
if (hProcess == NULL)
{
printf("无法打开进程 %d,错误码:%d\n", dwPID, GetLastError());
return FALSE;
}

// 获取目标进程的模块快照
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (hModuleSnap == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot 失败,错误码:%d\n", GetLastError());
CloseHandle(hProcess);
return FALSE;
}

me32.dwSize = sizeof(MODULEENTRY32);

// 遍历模块列表,寻找目标模块
if (Module32First(hModuleSnap, &me32))
{
do
{
if (_stricmp(me32.szModule, szModuleName) == 0)
{
// 找到目标模块,开始转储
HANDLE hDumpFile = CreateFileA(szDumpFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDumpFile == INVALID_HANDLE_VALUE)
{
printf("无法创建转储文件,错误码:%d\n", GetLastError());
break;
}

// 分配缓冲区
BYTE* pBuffer = (BYTE*)malloc(me32.modBaseSize);
if (pBuffer == NULL)
{
printf("内存分配失败。\n");
CloseHandle(hDumpFile);
break;
}

SIZE_T bytesRead;
if (ReadProcessMemory(hProcess, me32.modBaseAddr, pBuffer, me32.modBaseSize, &bytesRead))
{
DWORD bytesWritten;
if (WriteFile(hDumpFile, pBuffer, (DWORD)bytesRead, &bytesWritten, NULL))
{
printf("成功转储模块 %s 到 %s\n", szModuleName, szDumpFilePath);
bRet = TRUE;
}
else
{
printf("写入转储文件失败,错误码:%d\n", GetLastError());
}
}
else
{
printf("读取进程内存失败,错误码:%d\n", GetLastError());
}

free(pBuffer);
CloseHandle(hDumpFile);
break;
}
} while (Module32Next(hModuleSnap, &me32));
}
else
{
printf("Module32First 失败,错误码:%d\n", GetLastError());
}

CloseHandle(hModuleSnap);
CloseHandle(hProcess);

if (!bRet)
{
printf("未找到模块 %s。\n", szModuleName);
}

return bRet;
}

int main(int argc, char* argv[])
{
if (argc != 4)
{
printf("用法:%s <进程ID> <模块名称> <转储文件路径>\n", argv[0]);
return 1;
}

DWORD dwPID = atoi(argv[1]);
const char* szModuleName = argv[2];
const char* szDumpFilePath = argv[3];

if (DumpModule(dwPID, szModuleName, szDumpFilePath))
{
printf("模块 %s 的转储已保存到 %s\n", szModuleName, szDumpFilePath);
}
else
{
printf("转储模块失败。\n");
}

return 0;
}

注意事项:

  1. 权限要求: 确保以足够的权限运行此程序,以便访问目标进程的内存。
  2. 兼容性: 此代码适用于 Windows 平台,使用了 Windows 特定的 API。
  3. 错误处理: 在实际应用中,应添加更多的错误处理和异常捕获机制,以提高程序的稳定性和可靠性。

通过上述步骤和代码,可以在 C 语言中实现对特定进程模块的转储。

驱动程序dump进程内存

驱动程序实现读取进程虚拟内存地址数据的功能,用户层调用驱动程序读取进程的全部数据后创建新的文件保存到硬盘中。

驱动程序dump进程完整内存

  1. 驱动代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <ntifs.h>

#define IOCTL_READ_PROCESS_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define DEVICE_NAME L"\\Device\\MyDevice"
#define SYMBOLIC_LINK_NAME L"\\DosDevices\\MyDevice"

__declspec(dllimport) NTSTATUS NTAPI MmCopyVirtualMemory(
PEPROCESS SourceProcess,
PVOID SourceAddress,
PEPROCESS TargetProcess,
PVOID TargetAddress,
SIZE_T BufferSize,
KPROCESSOR_MODE PreviousMode,
PSIZE_T ReturnSize);

typedef struct _KERNEL_READ_REQUEST {
ULONG ProcessId;
PVOID Address;
SIZE_T Size;
PVOID Output;
} KERNEL_READ_REQUEST, * PKERNEL_READ_REQUEST;

NTSTATUS ReadProcessMemory(ULONG ProcessId, PVOID SourceAddress, PVOID TargetBuffer, SIZE_T BufferSize, SIZE_T* BytesRead) {
PEPROCESS TargetProcess;
NTSTATUS Status;

Status = PsLookupProcessByProcessId((HANDLE)ProcessId, &TargetProcess);
if (!NT_SUCCESS(Status)) {
KdPrint(("PsLookupProcessByProcessId failed: 0x%X\n", Status));
return Status;
}

Status = MmCopyVirtualMemory(TargetProcess, SourceAddress, PsGetCurrentProcess(), TargetBuffer, BufferSize, KernelMode, BytesRead);
if (!NT_SUCCESS(Status)) {
KdPrint(("MmCopyVirtualMemory failed: 0x%X\n", Status));
}

ObDereferenceObject(TargetProcess);
return Status;
}

NTSTATUS DeviceIoControlHandler(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION IoStackIrp = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG BytesIO = 0;

if (IoStackIrp->Parameters.DeviceIoControl.IoControlCode == IOCTL_READ_PROCESS_MEMORY) {
PKERNEL_READ_REQUEST ReadInput = (PKERNEL_READ_REQUEST)Irp->AssociatedIrp.SystemBuffer;
SIZE_T BytesRead;
Status = ReadProcessMemory(ReadInput->ProcessId, ReadInput->Address, ReadInput->Output, ReadInput->Size, &BytesRead);
BytesIO = sizeof(KERNEL_READ_REQUEST);
}

Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = BytesIO;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
UNICODE_STRING symbolicLinkName;
RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_LINK_NAME);
IoDeleteSymbolicLink(&symbolicLinkName);
IoDeleteDevice(DriverObject->DeviceObject);
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS status;
PDEVICE_OBJECT deviceObject = NULL;
UNICODE_STRING deviceName;
UNICODE_STRING symbolicLinkName;

// 初始化设备名称和符号链接名称
RtlInitUnicodeString(&deviceName, DEVICE_NAME);
RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_LINK_NAME);

// 创建设备对象
status = IoCreateDevice(
DriverObject,
0,
&deviceName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject
);

if (!NT_SUCCESS(status)) {
KdPrint(("Failed to create device object: 0x%X\n", status));
return status;
}

// 创建符号链接
status = IoCreateSymbolicLink(&symbolicLinkName, &deviceName);
if (!NT_SUCCESS(status)) {
KdPrint(("Failed to create symbolic link: 0x%X\n", status));
IoDeleteDevice(deviceObject);
return status;
}

// 设置驱动程序的卸载例程
DriverObject->DriverUnload = DriverUnload;

// 设置设备控制请求的处理函数
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControlHandler;

return STATUS_SUCCESS;
}

  1. 用户层代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    #include <windows.h>
    #include <stdio.h>

    #define IOCTL_READ_PROCESS_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)

    typedef struct _KERNEL_READ_REQUEST {
    ULONG ProcessId;
    PVOID Address;
    SIZE_T Size;
    PVOID Output;
    } KERNEL_READ_REQUEST, * PKERNEL_READ_REQUEST;

    BOOL DumpProcessMemory(HANDLE hDevice, ULONG processId, PVOID address, SIZE_T size, const char* outputFilePath) {
    KERNEL_READ_REQUEST request;
    request.ProcessId = processId;
    request.Address = address;
    request.Size = size;
    request.Output = malloc(size);
    if (request.Output == NULL) {
    printf("内存分配失败。\n");
    return FALSE;
    }

    DWORD bytesReturned;
    BOOL success = DeviceIoControl(
    hDevice,
    IOCTL_READ_PROCESS_MEMORY,
    &request,
    sizeof(KERNEL_READ_REQUEST),
    request.Output,
    (DWORD)size,
    &bytesReturned,
    NULL
    );

    if (success) {
    FILE* outputFile = fopen(outputFilePath, "wb");
    if (outputFile) {
    fwrite(request.Output, 1, size, outputFile);
    fclose(outputFile);
    printf("成功将进程内存转储到文件:%s\n", outputFilePath);
    }
    else {
    printf("无法创建输出文件。\n");
    success = FALSE;
    }
    }
    else {
    printf("DeviceIoControl 调用失败,错误码:%lu\n", GetLastError());
    }

    free(request.Output);
    return success;
    }

    int main() {
    const char* deviceName = "\\\\.\\MyDevice"; // 替换为实际的设备名称
    HANDLE hDevice = CreateFileA(
    deviceName,
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL
    );

    if (hDevice == INVALID_HANDLE_VALUE) {
    printf("无法打开设备,错误码:%lu\n", GetLastError());
    return 1;
    }

    ULONG targetProcessId = 1234; // 替换为目标进程的 PID
    PVOID targetAddress = (PVOID)0x00000000; // 替换为目标内存地址
    SIZE_T dumpSize = 4096; // 替换为要转储的字节数
    const char* outputFilePath = "process_dump.bin";

    if (!DumpProcessMemory(hDevice, targetProcessId, targetAddress, dumpSize, outputFilePath)) {
    printf("转储进程内存失败。\n");
    }

    CloseHandle(hDevice);
    return 0;
    }

利用驱动程序dump进程某个模块内存

驱动程序代码不变,只需修改用户层代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

#define IOCTL_READ_PROCESS_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define DEVICE_NAME "\\\\.\\MyDevice"

typedef struct _KERNEL_READ_REQUEST {
ULONG ProcessId;
PVOID Address;
SIZE_T Size;
PVOID Output;
} KERNEL_READ_REQUEST, * PKERNEL_READ_REQUEST;

// 获取指定进程的指定模块信息
BOOL GetModuleInfo(DWORD dwPID, const char* moduleName, PVOID* baseAddress, SIZE_T* moduleSize) {
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (hModuleSnap == INVALID_HANDLE_VALUE) {
printf("CreateToolhelp32Snapshot failed.\n");
return FALSE;
}

MODULEENTRY32 me32;
me32.dwSize = sizeof(MODULEENTRY32);

if (Module32First(hModuleSnap, &me32)) {
do {
if (_stricmp(me32.szModule, moduleName) == 0) {
*baseAddress = me32.modBaseAddr;
*moduleSize = me32.modBaseSize;
CloseHandle(hModuleSnap);
return TRUE;
}
} while (Module32Next(hModuleSnap, &me32));
}
else {
printf("Module32First failed.\n");
}

CloseHandle(hModuleSnap);
return FALSE;
}

// 将数据保存到文件
BOOL SaveToFile(const char* fileName, PVOID data, SIZE_T size) {
HANDLE hFile = CreateFileA(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Failed to create file %s, error code: %d\n", fileName, GetLastError());
return FALSE;
}

DWORD bytesWritten;
BOOL result = WriteFile(hFile, data, (DWORD)size, &bytesWritten, NULL);
CloseHandle(hFile);

if (!result || bytesWritten != size) {
printf("Failed to write data to file %s, error code: %d\n", fileName, GetLastError());
return FALSE;
}

return TRUE;
}

int main(int argc, char* argv[]) {
if (argc != 4) {
printf("Usage: %s <ProcessID> <ModuleName> <OutputFile>\n", argv[0]);
return 1;
}

DWORD dwPID = atoi(argv[1]);
const char* moduleName = argv[2];
const char* outputFile = argv[3];

// 获取模块信息
PVOID moduleBaseAddress = NULL;
SIZE_T moduleSize = 0;
if (!GetModuleInfo(dwPID, moduleName, &moduleBaseAddress, &moduleSize)) {
printf("Failed to get module info for %s in process %d.\n", moduleName, dwPID);
return 1;
}

// 分配缓冲区
PVOID buffer = VirtualAlloc(NULL, moduleSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (buffer == NULL) {
printf("Failed to allocate memory.\n");
return 1;
}

// 打开设备
HANDLE hDevice = CreateFileA(DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
printf("Failed to open device %s, error code: %d\n", DEVICE_NAME, GetLastError());
VirtualFree(buffer, 0, MEM_RELEASE);
return 1;
}

// 设置读取请求
KERNEL_READ_REQUEST readRequest;
readRequest.ProcessId = dwPID;
readRequest.Address = moduleBaseAddress;
readRequest.Size = moduleSize;
readRequest.Output = buffer;

DWORD bytesReturned;
BOOL success = DeviceIoControl(hDevice, IOCTL_READ_PROCESS_MEMORY, &readRequest, sizeof(readRequest), &readRequest, sizeof(readRequest), &bytesReturned, NULL);
CloseHandle(hDevice);

if (!success) {
printf("DeviceIoControl failed, error code: %d\n", GetLastError());
VirtualFree(buffer, 0, MEM_RELEASE);
return 1;
}

// 保存数据到文件
if (!SaveToFile(outputFile, buffer, moduleSize)) {
VirtualFree(buffer, 0, MEM_RELEASE);
return 1;
}

printf("Successfully dumped module %s of process %d to %s\n", moduleName, dwPID, outputFile);
VirtualFree(buffer, 0, MEM_RELEASE);
return 0;
}