gpt4 book ai didi

c++ - 通过名称查找进程ID的快速方法

转载 作者:行者123 更新时间:2023-11-30 04:44:34 37 4
gpt4 key购买 nike

任务是:按可执行文件名称查找进程ID。
调用应用程序是32位,查找过程可以是32位或64位

解:

#include <string>
#include <iostream>

#include <windows.h>
#include <Tlhelp32.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")

size_t r_wcsstr(const wchar_t* str, const wchar_t* search)
{
for (size_t i = wcslen(str) - wcslen(search); i > 0; --i)
{
if (wcsstr(str + i, search) != NULL)
return i + 1;
}

return -1;
}

bool find_process_1(const std::wstring& name, DWORD& pid)
{
DWORD aProcesses[1024] { 0 };
DWORD cbNeeded { 0 };
DWORD cProcesses { 0 };

unsigned int i;

if (EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded) == 0)
return false;

cProcesses = cbNeeded / sizeof(DWORD);

for (i = 0; i < cProcesses; i++)
{
WCHAR module_name[MAX_PATH] { 0 };
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i]);

if (process == NULL ||
GetProcessImageFileNameW(process, module_name, sizeof(module_name) / sizeof(WCHAR)) == 0)
continue;

size_t pos = r_wcsstr(module_name, name.c_str());

if (pos != -1)
{
pid = aProcesses[i];
return true;
}
}

return false;
}

bool find_process_2(const std::wstring& name, DWORD& pid)
{
HANDLE snapshot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 process_entry = { 0 };
process_entry.dwSize = sizeof(process_entry);
bool found = false;

snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

if (snapshot == INVALID_HANDLE_VALUE)
return false;

BOOL success = Process32First(snapshot, &process_entry);
while (success == TRUE)
{
if (_wcsicmp(process_entry.szExeFile, name.c_str()) == 0)
{
pid = process_entry.th32ProcessID;
CloseHandle(snapshot);
return true;
}

success = Process32Next(snapshot, &process_entry);
}

CloseHandle(snapshot);

return false;
}

int main(int argc, WCHAR **argv)
{
unsigned long pid { 0 };

unsigned long long total { 0 };

for (int i = 0; i < 1000; ++i)
{
unsigned long long start = GetTickCount64();

find_process_1(L"Calculator.exe", pid);

total += (GetTickCount64() - start);
}

std::wcout << L"Total: " << total << L"\tper call: " << total / 1000. << std::endl;

total = 0;

for (int i = 0; i < 1000; ++i)
{
unsigned long long start = GetTickCount64();

find_process_2(L"Calculator.exe", pid);

total += (GetTickCount64() - start);
}

std::wcout << L"Total: " << total << L"\tper call: " << total / 1000. << std::endl;

return 0;

}

Total: 4094 per call: 4.094
Total: 4688 per call: 4.688

有没有比 OpenProcesses + GetProcessImageFileName更快的方法?

我也发现 QueryFullProcessImageName函数可以稍微减少find_process_1时间

UPD1:使用NtQuerySystemInformation的解决方案
代码错误,请参阅解决方案
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")
struct _SYSTEM_PROCESS_INFO
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
ULONG BasePriority;
HANDLE ProcessId;
HANDLE InheritedFromProcessId;
};

bool find_process_3(const std::wstring& name, DWORD& pid)
{
_SYSTEM_PROCESS_INFO* spi;
size_t size = 1024*1024;
PVOID buffer = VirtualAlloc(NULL, 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
ULONG real_size {0};

NTSTATUS ret = NtQuerySystemInformation(SystemProcessInformation, buffer, size, &real_size);

bool found {false};

if (NT_SUCCESS(ret) == true)
{
spi = (_SYSTEM_PROCESS_INFO*)buffer;

while(spi->NextEntryOffset)
{
if (spi->ImageName.Buffer != nullptr && _wcsicmp(spi->ImageName.Buffer, name.c_str()) == 0)
{
pid = (long)spi->ProcessId;
found = true;
break;
}

spi = (_SYSTEM_PROCESS_INFO*)((LPBYTE)spi + spi->NextEntryOffset);
}
}

VirtualFree(buffer, 0, MEM_RELEASE);
return found;
}

结果:
Total: 4562     per call: 4.562 // OpenProcess + GetProcessImageFileName
Total: 4453 per call: 4.453 // OpenProcess + QueryFullProcessImageName
Total: 5188 per call: 5.188 // CreateToolhelp32Snapshot
Total: 2797 per call: 2.797 // NtQuerySystemInformation

看起来真的更快,谢谢@RbMm

最佳答案

对于按名称获取进程ID,需要枚举进程并将其名称与给定名称进行比较。我如何至少不听准备好的系统api做到这一点(并且没有内部枚举)。当然需要了解这样做对于系统级调试工具而言是主要的-进程名称不可靠。可以是多个具有相同名称的进程,等等。

最快的底层方法-将NtQuerySystemInformation函数与SystemProcessInformation信息类一起使用。所有其他方式-基于此api。但开销巨大且信息丢失。
CreateToolhelp32Snapshot-内部使用NtQuerySystemInformation调用SystemProcessInformation函数,但使用section(关于Win32语言的文件映射)作为信息存储。在此部分复制数据,然后取消映射Process32First Process32Next-始终将再次映射部分到内存,将数据复制到您的缓冲区(并在此过程中将删除一些数据),然后取消映射部分。所有这些严重的开销。当然,如果只执行一次-您不会看到不同的图像,但是如果执行很多次-将会看到不同的速度。
EnumProcesses当然也可以将NtQuerySystemInformation函数与SystemProcessInformation一起使用,但是从所有返回的信息中-仅传递每个进程的进程标识符,而传递其他所有信息。结果,您需要调用OpenProcess并查询它的图像路径-再次是严重的开销,您无法打开 protected 进程。

当然,这里我仅描述了当前的实现。可能会改变。也许不会。但这解释了为什么NtQuerySystemInformation最快。

因此记录下来,是否正确,是否“受支持”或否-如果正确使用NtQuerySystemInformation,则是最快的方法。

may be altered or unavailable in future versions of Windows



这已经写了20年了。但仍然是错误的。我个人确定此api绝不会更改或不可用(如何至少不早于 CreateToolhelp32SnapshotEnumProcesses也会更改或不可用)-这是基本的系统api之一。没有任何理由这样做。

This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll.



这也是 谎言。存在甚至来自wdk的2个库-ntdll.lib和ntdllp.lib(此处有更多api,但如果使用crt,则该lib在某些情况下可能与crt冲突-多个定义的符号)-因此我们可以,但不需要使用 LoadLibraryGetProcAddress(对我来说非常有趣-我们如何在不调用 LoadLibraryGetProcAddress来获取 LoadLibraryGetProcAddress的地址的情况下调用 LoadLibraryGetProcAddress)。

真正的 NtQuerySystemInformation常用api函数,并称为任何常用api函数。所有我们需要的-编译器的声明和链接器的lib文件。 lib存在于wdk中(并且一直在这里),尽管msdn表示另一个

使用示例
NTSTATUS GetProcessIdByName(PCUNICODE_STRING ImageName, HANDLE& UniqueProcessId)
{
NTSTATUS status;

ULONG cb = 0x10000;

UniqueProcessId = 0;

do
{
status = STATUS_INSUFFICIENT_RESOURCES;

if (PVOID buf = new UCHAR[cb])
{
if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
{
status = STATUS_NOT_FOUND;

union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};

pv = buf;
ULONG NextEntryOffset = 0;

do
{
pb += NextEntryOffset;

if (RtlEqualUnicodeString(ImageName, &pspi->ImageName, TRUE))
{
UniqueProcessId = pspi->UniqueProcessId;
status = STATUS_SUCCESS;
break;
}

} while (NextEntryOffset = pspi->NextEntryOffset);

}

delete [] buf;
}

} while (status == STATUS_INFO_LENGTH_MISMATCH);

return status;
}

请注意,由于所需的缓冲区大小非常不稳定(所有时间都会创建/退出新线程)-需要在循环中校准此api,直到我们没有 STATUS_INFO_LENGTH_MISMATCH状态为止

注意 do while (NextEntryOffset = pspi->NextEntryOffset)循环-如果执行while循环-我们丢失了最后一个条目(系统中最新产生的进程)。和ImageName-这是 UNICODE_STRING-因此不是强制性的零终止。结果是使用假定为0终止字符串的字符串api-在此处不正确(工作是因为在此结构中实际使用了0终止字符串)正确在此处或类似位置使用 RtlEqualUnicodeString
此代码还会按名称搜索过程,直到找到第一个匹配名称。当然需要了解可以是多个具有相同名称的进程-例如svchost.exe。在实际搜索中,我们可以使用其他条件,例如sessionid,进程 token 属性,命令行等。这已经是独立的问题,取决于要求

关于c++ - 通过名称查找进程ID的快速方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57706175/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com