本文介绍如何根据进程 ID 获取进程命令行参数,使用 Windows NTDLL 提供的未公开 API 实现。

由于需要使用到Windows应用层未公开的 API,而且这些 API 在 Windows 头文件中未提供函数定义,因此我们需要使用GetProcessAddress动态地从 ntdll.dll 中获取函数地址。

动态获取函数地址本不是一件困难的事情,但是当涉及调用函数比较多时,这个操作就显得比较繁琐了,我们可以直接使用下面的库来简化上述操作。
https://github.com/winsoft666/ndk

下面介绍如何使用ndk库来根据进程 ID 获取进程命令行参数。

首先需要包含头文件:

1
2
3
4
extern "C" {
#pragma warning(disable : 4005)
#include "ntndk.h"
}

并引入对应 lib:x86\ntdll.libx64\ntdll.lib

拿去用吧

具体获取命令行参数的方式已封装到下面GetProcessCommandLine函数,方便读者食用。

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
// 返回值为0表示成功
DWORD GetProcessCommandLine(unsigned int pid, std::wstring& strResult) {
BOOL result = false;

SetLastError(0);
HANDLE hProcess = NULL;
PPEB ppebCopy = NULL;
PRTL_USER_PROCESS_PARAMETERS pRtlProcParamCopy = NULL;
PWSTR wBufferCopy = NULL;
do
{
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!hProcess)
break;

PROCESS_BASIC_INFORMATION basicInfo;
ZeroMemory(&basicInfo, sizeof(PROCESS_BASIC_INFORMATION));

NTSTATUS status = NtQueryInformationProcess(hProcess,
ProcessBasicInformation,
&basicInfo,
sizeof(PROCESS_BASIC_INFORMATION),
NULL);

if (!NT_SUCCESS(status))
break;

PPEB ppeb = basicInfo.PebBaseAddress;
ppebCopy = (PPEB)malloc(sizeof(PEB));
if (!ppebCopy)
break;

result = ReadProcessMemory(hProcess,
ppeb,
ppebCopy,
sizeof(PEB),
NULL);

if (!result)
break;

PRTL_USER_PROCESS_PARAMETERS pRtlProcParam = ppebCopy->ProcessParameters;
pRtlProcParamCopy = (PRTL_USER_PROCESS_PARAMETERS)malloc(sizeof(RTL_USER_PROCESS_PARAMETERS));
if (!pRtlProcParamCopy)
break;

result = ReadProcessMemory(hProcess,
pRtlProcParam,
pRtlProcParamCopy,
sizeof(RTL_USER_PROCESS_PARAMETERS),
NULL);
if (!result)
break;

PWSTR wBuffer = pRtlProcParamCopy->CommandLine.Buffer;
USHORT len = pRtlProcParamCopy->CommandLine.Length;
wBufferCopy = (PWSTR)malloc(len + 2);
if (!wBufferCopy)
break;
memset(wBufferCopy, 0, len + 2);

result = ReadProcessMemory(hProcess,
wBuffer,
wBufferCopy, // command line goes here
len,
NULL);

if (result)
strResult = wBufferCopy;
} while (false);

DWORD dwGLE = 0;
if (!result) {
dwGLE = GetLastError();
}

if (ppebCopy)
free(ppebCopy);

if (pRtlProcParamCopy)
free(pRtlProcParamCopy);

if (wBufferCopy)
free(wBufferCopy);

if (hProcess)
CloseHandle(hProcess);

return dwGLE;
}