freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

关于Windows中获取指定进程pid的几种方法
热烈的马 2023-08-04 18:06:25 163715

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。(本文仅用于交流学习)

NtQuerySystemInformation

NtQuerySystemInformation函数在MSDN中有响应的解释,其主要作用是检索指定的系统信息,我们可以利用此函数便利系统中所有的进程,然后与我们要找的进程进行匹配,这个实现还是比较的容易。

实现代码

#include <Windows.h>
#include <winternl.h>
#include <iostream>
#include <string.h>

using namespace std;

typedef NTSTATUS(WINAPI* _NtQuerySystemInformation)(
	_In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
	_Inout_   PVOID                    SystemInformation,
	_In_      ULONG                    SystemInformationLength,
	_Out_opt_ PULONG                   ReturnLength
	);

_NtQuerySystemInformation lNtQuerySystemInformation  = (_NtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");

DWORD GetPid(LPCWSTR ProcessName)
{
	char szInfo[0x70000];
	ULONG uReturnedLEngth = 0;
	NTSTATUS status = lNtQuerySystemInformation(SystemProcessInformation, szInfo, sizeof(szInfo), &uReturnedLEngth);
	PSYSTEM_PROCESS_INFORMATION pinfo = (PSYSTEM_PROCESS_INFORMATION)szInfo;
	HANDLE hHandle = NULL;
	//printf("ProcessID: %d\tprocessName: %ws \n", dwID, pImageName);
	while (true)
	{
		if (pinfo->NextEntryOffset == 0) {
			break;
		}
		pinfo = (PSYSTEM_PROCESS_INFORMATION)((PCHAR)pinfo + pinfo->NextEntryOffset);
		if(lstrcmpiW(pinfo->ImageName.Buffer, ProcessName) == 0) {
			cout << "lsass pid is:" << (DWORD)pinfo->UniqueProcessId << endl;

			break;
		}
	}

	return (DWORD)pinfo->UniqueProcessId;
}

int main() {
	DWORD pid = GetPid(L"notepad.exe");

}

1691141427_64ccc53320f9e9305d902.png!small?1691141427586

WTSEnumerateProcessesW

WTSEnumerateProcessesW函数使用RPC服务来获取进程列表,我们主要关注WTS_PROCESS_INFOA结构

https://learn.microsoft.com/zh-cn/windows/win32/api/wtsapi32/ns-wtsapi32-wts_process_infoa

typedef struct _WTS_PROCESS_INFOA {
  DWORD SessionId;
  DWORD ProcessId;
  LPSTR pProcessName;
  PSID  pUserSid;
} WTS_PROCESS_INFOA, *PWTS_PROCESS_INFOA;
  • SessionId:进程关联的会话的远程桌面服务会话标识符
  • ProcessId:会话主机服务器上的进程标识符
  • pProcessName:包含与进程关联的可执行文件名称
  • pUserSid:进程的主要访问令牌中用户安全标识符的指针

很明显ProcessId参数即可帮助我们获得进程的pid

代码实现

#include <windows.h>
#include <wtsapi32.h>
#include <iostream>

#pragma comment(lib, "Wtsapi32.lib")
using namespace std;

DWORD GetPid(LPCWSTR ProcessName) {
	PWTS_PROCESS_INFOW wts;
	DWORD Count;
	DWORD i = 0;

	BOOL result =WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wts, &Count);
	if(result == 0){
		cout << "WTSEnumerateProcessesW Error" << endl;

		return 1;
	}
	cout << "WTSEnumerateProcessesW Success" << endl;
	for (i;i < Count; i++) {
		if (lstrcmpiW(wts[i].pProcessName, ProcessName) == 0) {
			DWORD pid = wts[i].ProcessId;
			cout << "lsass pid is:" << pid << endl;

			return pid;
			break;
		}
	}

	WTSFreeMemory(wts);

}

int main() {
	GetPid(L"notepad.exe");
}

1691141939_64ccc7330fbc4de9b4c8d.png!small?1691141939516

RegQueryValueExA

在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa中,包含了本地安全机构服务器服务(LSASS)进程。LSASS进程在Windows系统中担任验证本地用户和远程用户登录,并强制执行本地安全策略的重要角色。它负责处理用户的身份验证和授权操作。

Windows 8.1及以上版本引入了LSA保护功能,其主要目的是防止未受保护的进程读取内存和进行代码注入等恶意行为。通过实施LSA保护,可以增加系统的安全性,减少恶意程序对LSASS进程的攻击能力。

在注册表路径HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa中,存在一个名为LsaPid的键。该键所对应的值即为LSASS进程的PID(进程标识符)。通过查询LsaPid键的值,可以获取LSASS进程的PID信息,有助于进行系统监控、调试或其他相关操作。

1691142063_64ccc7afdfc7a894babaa.png!small?1691142064333

RegQueryValueExA函数主要作用就是去枚举注册表的键值对。

代码实现

#include <windows.h>
#include <malloc.h>
#include <iostream>

#define TOTALBYTES    8192
#define BYTEINCREMENT 4096

using namespace std;

int main(){
	HKEY lhKey = HKEY_LOCAL_MACHINE;
	LPCSTR pSubKey = "SYSTEM\\CurrentControlSet\\Control\\Lsa";
	HKEY hkResult;

	LSTATUS Open = RegOpenKeyExA(lhKey, pSubKey, 0, KEY_ALL_ACCESS, &hkResult);
	if (Open != ERROR_SUCCESS) {
		cout << "RegOpenKeyExA Error" << endl;

		return 1;
	}
	cout << "RegOpenKeyExA Success" << endl;
	DWORD BufferSize = TOTALBYTES;
	LPCSTR pValueName = "LsaPid";
	DWORD pType = REG_DWORD;
	DWORD pData;
	DWORD pcbData = sizeof(DWORD);
	

	LSTATUS Value = RegQueryValueExA(hkResult, pValueName, NULL, &pType, (LPBYTE)&pData, &pcbData);
	if (Value != ERROR_SUCCESS){
		cout << "RegOpenKeyExA Error" << endl;

		return 1;
	}
	cout << "RegQueryValueExA Success" << endl;
	cout << "lsass pid is:" << pData << endl;
	RegCloseKey(hkResult);
}

1691142225_64ccc85156954feaca76b.png!small?1691142225751

QueryServiceStatusEx

QueryServiceStatusEx函数主要根据指定的信息级别检索指定服务的当前状态,详细链接如下

https://learn.microsoft.com/zh-cn/windows/win32/api/winsvc/nf-winsvc-queryservicestatusex

LSASS提供的服务

  • CNG KeyIsolation (KeyIso)

  • Security Accounts Manager (SamSs)

  • Credential Manager (VaultSvc)

我们可以通过打开这些服务的任意一个,来获取进程的pid。我们来看看SERVICE_STATUS_PROCESS结构

https://learn.microsoft.com/zh-cn/windows/win32/api/winsvc/ns-winsvc-service_status_process

typedef struct _SERVICE_STATUS_PROCESS {
  DWORD dwServiceType;
  DWORD dwCurrentState;
  DWORD dwControlsAccepted;
  DWORD dwWin32ExitCode;
  DWORD dwServiceSpecificExitCode;
  DWORD dwCheckPoint;
  DWORD dwWaitHint;
  DWORD dwProcessId;
  DWORD dwServiceFlags;
} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;

其中的DWORD dwProcessId参数即可帮助我们获得进程的pid。

代码实现

#include <windows.h>
#include <iostream>

using namespace std;

int main() {

	SERVICE_STATUS_PROCESS ProcessInfo;
	DWORD bBufSize = sizeof(DWORD64);
	DWORD lpcbBytesNeeded;

	//建立与指定计算机上的服务控制管理器的连接,并打开指定的服务控制管理器数据库
	SC_HANDLE Handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (Handle == NULL) {
		cout << "OpenSCManagerA Error" << endl;

		return 1;
	}

	cout << "OpenSCManagerA Success" << endl;
	//打开现有服务
	SC_HANDLE Handle2 = OpenServiceW(Handle, L"VaultSvc", SC_MANAGER_ALL_ACCESS);
	if (Handle == NULL) {
		cout << "OpenServiceA Error" << endl;

		return 1;
	}

	cout << "OpenServiceA Success" << endl;
	//检索指定服务的当前状态,根据SERVICE_STATUS_PROCESS结构体获得进程信息
	BOOL Query_info = QueryServiceStatusEx(Handle2, SC_STATUS_PROCESS_INFO, (LPBYTE)&ProcessInfo, sizeof(SERVICE_STATUS_PROCESS), &lpcbBytesNeeded);
	if (Query_info == 0) {
		cout << "QueryServiceStatusEx Error" << endl;

		return 1;
	}
	cout << "QueryServiceStatusEx Success" << endl;
	cout << "lsass pid is:" << (DWORD)ProcessInfo.dwProcessId << endl;

	CloseServiceHandle(Handle2);
	CloseServiceHandle(Handle);
}

1691142575_64ccc9af0e5ce98a9f346.png!small?1691142575391

NtFsControlFile

NtFsControlFile例程将控制代码直接发送到指定的文件系统或文件系统筛选器驱动程序,导致相应的驱动程序执行指定的操作。

这里我们需要借助管道来获取进程的pid,管道的本质是用于进程间通信的共享内存区域,管道有两端,一个端口进程可以进行写入数据,另外一个端口进程可以进行读取数据。我们把创建管道的进行成为管道服务器,连接管道的进程成为管道客户端。

Windows管道分类

  • 匿名管道:只能本地实现,半双工通信(即单向通信)。

  • 命名管道:可以用于网络通信、可以对客户端连接、可以双向通信、可以在本机或者跨网络在不同进程间进行通信(即客户端可以是本地进程本地访问:.\pipe\PipeName或者远程访问远程:\ServerName\pipe\PipeName)

可以使用下面命令来查看本地管道

[System.IO.Directory]::GetFiles("\\.\pipe\")
Get-ChildItem \\.\pipe\

1691142824_64cccaa8bf289b7d7ba7f.png!small?1691142825176

或者使用Process Explorer,搜索\Device\NamedPipe也可以查看到

1691142924_64cccb0cd1516b2bf37ef.png!small?1691142925324

我们先连接管道,然后使用NtFsControlFile(或者GetNamedPipeServerProcessId)函数去获取指定命名管道进程的标识符。

代码实现

#include "function.h"
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

int main() {

	HANDLE lFileHandle;
	OBJECT_ATTRIBUTES Object;
	IO_STATUS_BLOCK Io;
	UNICODE_STRING lNtFileName;

	lRtlInitUnicodeString(&lNtFileName, L"\\Device\\NamedPipe\\lsass");
	InitializeObjectAttributes(&Object, &lNtFileName, OBJ_CASE_INSENSITIVE, 0, NULL);
	NTSTATUS Status = lNtOpenFile(&lFileHandle, FILE_READ_ATTRIBUTES, &Object, &Io, FILE_SHARE_READ, NULL);
	if (!NT_SUCCESS(Status)) {
		cout << "NtOpenFile Error" << endl;

		return 1;
	}

	cout << "NtOpenFile Success" << endl;
	LPCSTR InputBuffer = "ServerProcessId";
	DWORD OutputBuffer;								   
	lNtFsControlFile(lFileHandle, NULL, NULL, NULL, &Io, FSCTL_PIPE_GET_PIPE_ATTRIBUTE, (PVOID)InputBuffer, strlen(InputBuffer)+1, &OutputBuffer, sizeof(OutputBuffer));
	if (!NT_SUCCESS(Status)) {
		cout << "NtFsControlFile Error" << endl;

		return 1;
	}

	cout << "NtFsControlFile Success" << endl;
	cout << "lsass pid is:" << OutputBuffer << endl;
}

1691143197_64cccc1d6e9a2bb1411e3.png!small?1691143197830

安全事件日志

事件4608是Windows操作系统中的一个事件日志,它表示"Windows 正在启动"。当LSASS.EXE进程启动并初始化审核子系统时,会记录此事件,其中包含着lsass进程的pid。

这里我们很显然我们需要管理员权限,实现步骤如下

  • 使用EvtQuery函数进行事件查询:调用EvtQuery函数来查询事件,并获得一个事件查询的句柄(handle)作为返回值。

  • 使用EvtSeek函数在结果集中查找特定事件:使用EvtSeek函数传入上一步得到的事件查询句柄,可以在结果集中查找特定事件。

  • 利用EvtNext函数查询下一个事件:使用EvtNext函数传入事件查询句柄,可以获取查询结果集中的下一个事件。

  • 使用EvtCreateRenderContext函数创建呈现上下文:调用EvtCreateRenderContext函数创建一个上下文对象,用于指定要从事件中呈现的信息。

  • 使用EvtRender函数进行呈现:通过调用EvtRender函数并传入上述创建的呈现上下文,可以将事件呈现为可读的格式(如XML片段),即可获得进程pid。

代码实现

#include <windows.h>
#include <winevt.h>
#include <iostream>

using namespace std;
#pragma comment(lib, "Wevtapi.lib")

DWORD IsAdmin() {
    HANDLE TokenHandle;

    BOOL OpenToken = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &TokenHandle);
    if (OpenToken == 0) {
        cout << "OpenProcessToken Error" << endl;

        return 1;
    }
    TOKEN_ELEVATION TokenInformation;
    DWORD ReturnLength;
    GetTokenInformation(TokenHandle, TokenElevation, &TokenInformation, sizeof(TokenInformation), &ReturnLength);
    DWORD isadmin = TokenInformation.TokenIsElevated;
    if (isadmin == 0) {
        cout << "Please raise process permissions" << endl;

        return 1;
    }

    return 0;
}

DWORD GetLsaPidFromEventLogs(void) {
    if (IsAdmin() == 1) {
        return 1;
    }
    DWORD      dwProcessId = 0;
    EVT_HANDLE hResults = EvtQuery(NULL, L"Security", L"*/*[EventID=4608]", EvtQueryTolerateQueryErrors);
    if (hResults == NULL) {
        cout << "EvtQuery Error" << endl;

        return 1;
    }
    BOOL Result = EvtSeek(hResults, 0, NULL, 0, EvtSeekRelativeToLast);
    if (Result != TRUE) {
        cout << "EvtSeek Error" << endl;

        return 1;
    }
    DWORD dwReturned = 0;
    EVT_HANDLE hEvent;
    BOOL Result1 = EvtNext(hResults, 1, &hEvent, INFINITE, 0, &dwReturned);
    if (Result1 != TRUE) {
        cout << "EvtNext Error" << endl;

        return 1;
    }
    LPCWSTR ppValues[] = { L"Event/System/Execution/@ProcessID" };
    EVT_HANDLE hContext = EvtCreateRenderContext(ARRAYSIZE(ppValues), ppValues, EvtRenderContextValues);
    if (hContext == NULL) {
        cout << "EvtCreateRenderContext Error" << endl;

        return 1;
    }
    EVT_VARIANT pProcessId = { 0 };
    BOOL Result2 = EvtRender(hContext, hEvent, EvtRenderEventValues, sizeof(EVT_VARIANT), &pProcessId, &dwReturned, NULL);
    if (Result2 != TRUE) {
        cout << "EvtRender Error" << endl;

        return 1;
    }
    dwProcessId = pProcessId.UInt32Val;
   
    EvtClose(hEvent);
    EvtClose(hContext);
    EvtClose(hResults);

    cout << "lsass pid is:" << dwProcessId << endl;

    return dwProcessId;
}

int main() {

    GetLsaPidFromEventLogs();
}

1691143476_64cccd34c1839505a8e82.png!small?1691143477390


# 渗透测试 # web安全 # 网络安全技术
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 热烈的马 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
热烈的马 LV.6
这家伙太懒了,还未填写个人描述!
  • 45 文章数
  • 18 关注者
利用NtDuplicateObject进行Dump
2023-04-05
NTLM中继
2023-03-31
通过Hash查找API函数地址
2023-03-31
文章目录