获取进程用户名域名

前言

  本章使用c语言来获取进程所属的域名/用户名,并解决一系列问题。

遍历进程

  首先要遍历本机进程,这里使用的api是WTSEnumerateProcessesEx,所属头文件wtsapi32.h

BOOL WTSEnumerateProcessesEx(
    HANDLE hServer,			// 主机服务器句柄 本机填 WTS_CURRENT_SERVER_HANDLE
    DWORD  *pLevel,			// 值为1 返回WTS_PROCESS_INFO_EX结构体数组 值为0 返回WTS_PROCESS_INFO结构体数组
    DWORD  SessionId,		// 进程会话 枚举所有进程会话 填WTS_ANY_SESSION
    LPSTR  *ppProcessInfo,	// 接收指向WTS_PROCESS_INFO_EX或WTS_PROCESS_INFO结构体数组的指针
    DWORD  *pCount			// 得到进程数
);							// 成功返回非0 失败返回0

  以下是WTS_PROCESS_INFO_EX结构体定义,这里用不到WTS_PROCESS_INFO不讲解

typedef struct _WTS_PROCESS_INFO_EX {
  DWORD         SessionId;			// 进程会话
  DWORD         ProcessId;			// 进程PID
  LPSTR         pProcessName;		// 进程名
  PSID          pUserSid;			// SID
  DWORD         NumberOfThreads;	// 线程数
  DWORD         HandleCount;		// 句柄数
  DWORD         PagefileUsage;		// 页交换文件使用大小
  DWORD         PeakPagefileUsage;	// 历史页交换文件使用
  DWORD         WorkingSetSize;		// 工作集大小
  DWORD         PeakWorkingSetSize;	// 峰值工作集大小
  LARGE_INTEGER UserTime;			// 进程在用户模式下运行时间
  LARGE_INTEGER KernelTime;			// 进程在内核模式下运行时间
} WTS_PROCESS_INFO_EX, *PWTS_PROCESS_INFO_EX;

  释放内存函数WTSFreeMemoryEx,最后还要将指针设置成NULL

BOOL WTSFreeMemoryEx(
	WTS_TYPE_CLASS WTSTypeClass,	// 枚举类型 值为WTSTypeProcessInfoLevel1 代表释放WTS_PROCESS_INFO_EX指针
	PVOID          pMemory,			// 释放的指针
	ULONG          NumberOfEntries	// 释放的数量
);									// 成功返回非0 失败返回0

  示例

#include <stdio.h>
#include <windows.h>
#include <WtsApi32.h>
#include <sddl.h>
#pragma comment(lib,"Wtsapi32.lib")

int main()
{
    DWORD level = 1;
    // 保存进程信息
    PWTS_PROCESS_INFO_EX processList = NULL;
    // 进程数
    DWORD processCnt = 0;
    BOOL bRet = FALSE;
	
    // 查询进程
    bRet = WTSEnumerateProcessesEx(WTS_CURRENT_SERVER_HANDLE, &level, WTS_ANY_SESSION, (LPTSTR*)&processList, &processCnt);
    if (!bRet)
    {
        printf("error get process\n");
        return 0;
    }

    printf("Processes Found: %d\n\n", processCnt);
    printf("#\tPID\tHandles\tThreads\tProcess Name\t\n\n");
    
	// 保存结构体指针
    PWTS_PROCESS_INFO_EX tempList = processList;
    // 输出进程信息
    for (DWORD i = 0; i < processCnt; i++)
    {
        printf("%d\t", i + 1);
        printf("%d\t", tempList->ProcessId);
        printf("%d\t", tempList->HandleCount);
        printf("%d\t", tempList->NumberOfThreads);
        printf("%ws\t", tempList->pProcessName);
        printf("\n");
        // 移到下一个进程
        tempList++;
    }

    // 释放内存
    bRet = WTSFreeMemoryEx(WTSTypeProcessInfoLevel1, processList, processCnt);
    if (!bRet)
    {
        printf("error free memory\n");
        return 0;
    }
    processList = NULL;
    return 0;
}

  运行结果
  

获取sid

  这里我们要打印SID,如果直接printf("%d\t", tempList->pUserSid);是不行的。要用到将SID转换成字符串的函数ConvertSidToStringSid,所在头文件sddl.h

BOOL ConvertSidToStringSid(
    PSID  Sid,			// Sid
    LPWSTR *StringSid	// 接收SID字符串指针
);						// 成功返回非0 失败返回0

  示例

// sid字符串
LPWSTR stringSID = NULL;
bRet = ConvertSidToStringSid(tempList->pUserSid, &stringSID);

if (!bRet)
{
    printf("error sid\t");
}
else
{
    printf("%ws\t", stringSID);
    // 释放返回的缓冲区
    LocalFree((HLOCAL)stringSID);
}

获取域名\用户名

  用到查询函数为LookupAccountSid

BOOL LookupAccountSid(
    LPCSTR        lpSystemName,				// 本地填NULL
    PSID          Sid,						// SID
    LPSTR         Name,						// 接收用户名
    LPDWORD       cchName,					// 用户名缓冲区大小
    LPSTR         ReferencedDomainName,		// 接收域名
    LPDWORD       cchReferencedDomainName,	// 域名缓冲区大小
    PSID_NAME_USE peUse						// 指向SID_NAME_USE结构体指针
);											// 成功返回非0 失败返回0

  结合前面的获取sid的代码

#include <stdio.h>
#include <windows.h>
#include <WtsApi32.h>
#include <sddl.h>
#pragma comment(lib,"Wtsapi32.lib")

// 获取SID
void GetSID(PSID userSid)
{
	LPWSTR stringSID = NULL;

	// 成功获取
	if (ConvertSidToStringSid(userSid, &stringSID))
	{
		printf("%ws\t", stringSID);
		// 释放返回的缓冲区
		LocalFree((HLOCAL)stringSID);
		return;
	}

	printf("-\t");
}

// 获取域名\用户名
void GetAccount(PSID userSid)
{
	// 用户名
	TCHAR userName[255];
	DWORD bufferLen = 255;
	// 域名
	TCHAR domainName[255];
	DWORD domainNameBufferLen = 255;
	SID_NAME_USE peUse;

	// 成功获取
	if (LookupAccountSid(NULL, userSid, userName, &bufferLen, domainName, &domainNameBufferLen, &peUse))
	{
		printf("%ls\\%ls\t", domainName, userName);
		return;
	}
	printf("-\t");
}

int main()
{
	DWORD level = 1;
	// 保存进程信息
	PWTS_PROCESS_INFO_EX processList = NULL;
	// 进程数
	DWORD processCnt = 0;
	BOOL bRet = FALSE;

	bRet = WTSEnumerateProcessesEx(WTS_CURRENT_SERVER_HANDLE, &level, WTS_ANY_SESSION, (LPTSTR*)&processList, &processCnt);

	// 查询进程失败
	if (!bRet)
	{
		printf("error get process\n");
		return 0;
	}

	printf("Processes Found: %d\n\n", processCnt);
	printf("#\tPID\tHandles\tThreads\tProcess Name\tSID\tAccount\t\n\n");


	PWTS_PROCESS_INFO_EX tempList = processList;
	// 输出进程信息
	for (DWORD i = 0; i < processCnt; i++)
	{
		printf("%d\t", i + 1);
		printf("%d\t", tempList->ProcessId);
		printf("%d\t", tempList->HandleCount);
		printf("%d\t", tempList->NumberOfThreads);
		printf("%ws\t", tempList->pProcessName);

		// 获取SID
		GetSID(tempList->pUserSid);
		// 获取域名\用户名
		GetAccount(tempList->pUserSid);

		printf("\n");
		// 移到下一个进程
		tempList++;
	}

	// 释放内存
	bRet = WTSFreeMemoryEx(WTSTypeProcessInfoLevel1, processList, processCnt);
	if (!bRet)
	{
		printf("error free memory\n");
		return 0;
	}

	processList = NULL;
	return 0;
}

  使用管理员运行后的结果
  

进程提权

  可看到csrss.exe fontdrvhost.exe 等等程序都没有获取到对应的信息,很明显是没有对应的权限。这时就需要提升自己进程到系统权限。

  windows的每个用户登录系统后,系统会产生一个访问令牌(access token),其中关联了当前用户的权限信息,用户登录后创建的每一个进程都含有用户access token的拷贝,当进程试图执行某些需要特殊权限的操作或是访问受保护的内核对象时,系统会检查其acess token中的权限信息以决定是否授权操作。Administrator组成员的access token中会含有一些可以执行系统级操作的特权(privileges) ,如终止任意进程、关闭/重启系统、加载设备驱动和更改系统时间等,不过这些特权默认是被禁用的,当Administrator组成员创建的进程中包含一些需要特权的操作时,进程必须首先打开这些禁用的特权以提升自己的权限,否则系统将拒绝进程的操作。注意,非Administrator组成员创建的进程无法提升自身的权限,因此下面提到的进程均指Administrator组成员创建的进程。(命令行输入net user可查看或net user 具体用户名称)

  我们的程序只需要启用调试权限(SeDebugPrivilege),就可以调试其他程序

  使用process explorer查看我们写的程序拥有的权限,可看到SeDebugPrivilege未启用
  
  首先要获取到一个进程的访问令牌,函数为OpenProcessToken

BOOL OpenProcessToken(
    HANDLE  ProcessHandle,	// 进程句柄
    DWORD   DesiredAccess,	// 令牌的请求类型
    PHANDLE TokenHandle		// 返回令牌句柄
);							// 成功返回非0 失败返回0

  然后取得特权的LUID值。查询或更改特权的API需要LUID来引用相应的特权,LUID表示local unique identifier,它是一个64位值,在当前系统中是唯一的。为了提升进程权限到指定的特权,我们必须先找到该特权对应的LUID。使用LookupPrivilegeValue函数

BOOL LookupPrivilegeValue(
    LPCSTR lpSystemName,	// 系统名称 本地填NULL
    LPCSTR lpName,			// 权限名称
    PLUID  lpLuid			// 返回LUID标识
);							// 成功返回非0 失败返回0

  最后使用AdjustTokenPrivileges,来修改令牌权限

BOOL AdjustTokenPrivileges(
	HANDLE            TokenHandle,			// 令牌句柄
	BOOL              DisableAllPrivileges,	// 是否禁用令牌所有权限
	PTOKEN_PRIVILEGES NewState,				// 指向TOKEN_PRIVILEGES结构的指针
	DWORD             BufferLength,			// 指向PreviousState缓冲区大小 设为0即可
	PTOKEN_PRIVILEGES PreviousState,		// 存放修改前的TOKEN_PRIVILEGES结构信息 设为NULL即可
	PDWORD            ReturnLength			// 实际PRIVILEGES NewState结构返回的大小 设为NULL即可
);											// 成功返回非0 失败返回0

  这里先看TOKEN_PRIVILEGES结构体定义

typedef struct _TOKEN_PRIVILEGES {
  DWORD               PrivilegeCount;				// 数组元素个数
  LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];	// 指向LUID_AND_ATTRIBUTES结构体
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;

  再来查看LUID_AND_ATTRIBUTES结构体定义

typedef struct _LUID_AND_ATTRIBUTES {
  LUID  Luid;			// 指定一个LUID值
  DWORD Attributes;		// 特权属性
} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;

  特权属性Attributes有以下值

SE_PRIVILEGE_ENABLED 			// 使特权有效
SE_PRIVILEGE_ENABLED_BY_DEFAULT // 使特权默认有效
SE_PRIVILEGE_REMOVED 			// 移除该特权
SE_PRIVILEGE_USED_FOR_ACCESS 	// 取得对象或服务的访问权

  示例

#include <stdio.h>
#include <windows.h>
#include <WtsApi32.h>
#include <sddl.h>
#pragma comment(lib,"Wtsapi32.lib")

// 获取SID
void GetSID(PSID userSid)
{
	LPWSTR stringSID = NULL;

	// 成功获取
	if (ConvertSidToStringSid(userSid, &stringSID))
	{
		printf("%ws\t", stringSID);
		// 释放返回的缓冲区
		LocalFree((HLOCAL)stringSID);
		return;
	}

	printf("-\t");
}

// 获取域名\用户名
void GetAccount(PSID userSid)
{
	// 用户名
	TCHAR userName[255];
	DWORD bufferLen = 255;
	// 域名
	TCHAR domainName[255];
	DWORD domainNameBufferLen = 255;
	SID_NAME_USE peUse;

	// 成功获取
	if (LookupAccountSid(NULL, userSid, userName, &bufferLen, domainName, &domainNameBufferLen, &peUse))
	{
		printf("%ws\\%ws\t", domainName, userName);
		return;
	}
	printf("-\t");
}

// 启用调试权限
BOOL EnableDebugAbility()
{
	// 令牌句柄
	HANDLE hProcessToken = NULL;
	// 1.打开进程访问令牌
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
	{
		printf("error open process token\n");
		return FALSE;
	}

	// 2.取得SeDebugPrivilege特权的LUID值
	LUID luid;
	if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
	{
		printf("error lookup value\n");
		return FALSE;
	}
	
	// 3.调整访问令牌特权
	TOKEN_PRIVILEGES token;
	token.PrivilegeCount = 1;
	token.Privileges[0].Luid = luid;
	// 使特权有效
	token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	
	if (!AdjustTokenPrivileges(hProcessToken, FALSE, &token, 0, NULL, NULL))
	{
		printf("error adjust token\n");
		return FALSE;
	}
	
	CloseHandle(hProcessToken);
	return TRUE;
	
}

int main()
{
	// 提权失败
	if (!EnableDebugAbility()) return 0;

	DWORD level = 1;
	// 保存进程信息
	PWTS_PROCESS_INFO_EX processList = NULL;
	// 进程数
	DWORD processCnt = 0;
	BOOL bRet = FALSE;

	bRet = WTSEnumerateProcessesEx(WTS_CURRENT_SERVER_HANDLE, &level, WTS_ANY_SESSION, (LPTSTR*)&processList, &processCnt);

	// 查询进程失败
	if (!bRet)
	{
		printf("error get process\n");
		return 0;
	}

	printf("Processes Found: %d\n\n", processCnt);
	printf("#\tPID\tHandles\tThreads\tProcess Name\tSID\tAccount\t\n\n");

	// 保存结构体指针
	PWTS_PROCESS_INFO_EX tempList = processList;

	// 输出进程信息
	for (DWORD i = 0; i < processCnt; i++)
	{
		printf("%d\t", i + 1);
		printf("%d\t", tempList->ProcessId);
		printf("%d\t", tempList->HandleCount);
		printf("%d\t", tempList->NumberOfThreads);
		printf("%ws\t", tempList->pProcessName);

		// 获取SID
		GetSID(tempList->pUserSid);
		// 获取域名\用户名
		GetAccount(tempList->pUserSid);

		printf("\n");
		// 移到下一个进程
		tempList++;
	}

	// 释放内存
	bRet = WTSFreeMemoryEx(WTSTypeProcessInfoLevel1, processList, processCnt);
	if (!bRet)
	{
		printf("error free memory\n");
		return 0;
	}

	processList = NULL;
	return 0;
}

  成功获取所有进程信息
  

  查看SeDebugPrivilege也成功启用
  

提权问题

  在上述代码中,如果程序是以非管理员身份运行的,AdjustTokenPrivileges函数也会成功运行。可看出没打印error adjust token
  
  要想解决此问题,应该查询程序是否有SeDebugPrivilege权限选项。用到的函数为GetTokenInformation

BOOL GetTokenInformation(
    HANDLE                  TokenHandle,			// 令牌句柄 必须拥有TOKEN_QUERY访问权
    TOKEN_INFORMATION_CLASS TokenInformationClass,	// 指定TOKEN_INFORMATION_CLASS枚举类型的值
    LPVOID                  TokenInformation,		// 指向缓冲区的指针
    DWORD                   TokenInformationLength,	// 指向缓冲区的大小 如果缓冲区指针为NULL 此值设为0
    PDWORD                  ReturnLength			// 返回缓冲区所需的大小
);													// 成功返回非0 失败返回0

  TokenInformationClass,有非常多的值,这里传入TokenPrivileges值即可。之后进行依次的比较即可

#include <stdio.h>
#include <windows.h>
#include <WtsApi32.h>
#include <sddl.h>
#pragma comment(lib,"Wtsapi32.lib")

// 获取SID
void GetSID(PSID userSid)
{
	LPWSTR stringSID = NULL;

	// 成功获取
	if (ConvertSidToStringSid(userSid, &stringSID))
	{
		printf("%ws\t", stringSID);
		// 释放返回的缓冲区
		LocalFree((HLOCAL)stringSID);
		return;
	}

	printf("-\t");
}

// 获取域名\用户名
void GetAccount(PSID userSid)
{
	// 用户名
	TCHAR userName[255];
	DWORD bufferLen = 255;
	// 域名
	TCHAR domainName[255];
	DWORD domainNameBufferLen = 255;
	SID_NAME_USE peUse;

	// 成功获取
	if (LookupAccountSid(NULL, userSid, userName, &bufferLen, domainName, &domainNameBufferLen, &peUse))
	{
		printf("%ws\\%ws\t", domainName, userName);
		return;
	}
	printf("-\t");
}

// 启用调试权限
BOOL EnableDebugAbility()
{
	// 令牌句柄
	HANDLE hProcessToken = NULL;
	// 1.打开进程访问令牌
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
	{
		printf("error open process token\n");
		return FALSE;
	}

	// 2.取得SeDebugPrivilege特权的LUID值
	LUID luid;
	if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
	{
		printf("error lookup value\n");
		return FALSE;
	}


	// 3.是否有SeDebugPrivilege权限选项
	// 这里第一次调用GetTokenInformation是为了获取令牌信息需要的缓冲区的大小,
	DWORD realSize;
	GetTokenInformation(hProcessToken, TokenPrivileges, NULL, 0, &realSize);

	DWORD size;
	// 保存令牌信息
	PTOKEN_PRIVILEGES pToken = NULL;
	
	// 分配空间
	pToken = (PTOKEN_PRIVILEGES)malloc(realSize);
	if (!GetTokenInformation(hProcessToken, TokenPrivileges, pToken, realSize, &size))
	{
		printf("GetTokenInformation error");
		return FALSE;
	}

	BOOL bRet = FALSE;
	for (DWORD i = 0; i < pToken->PrivilegeCount; i++)
	{
		PLUID_AND_ATTRIBUTES runner = &pToken->Privileges[i];
		// 使用lowpart 和 highpart进行查询比较
		if ((runner->Luid.LowPart == luid.LowPart) && (runner->Luid.HighPart == luid.HighPart))
		{
			bRet = TRUE;
			break;
		}
	}

	if (!bRet)
	{
		printf("[-] SeDebugPrivilege unavailable\n");
		return FALSE;
	}

	// 4.调整访问令牌特权
	TOKEN_PRIVILEGES token;
	token.PrivilegeCount = 1;
	token.Privileges[0].Luid = luid;
	// 使特权有效
	token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (!AdjustTokenPrivileges(hProcessToken, FALSE, &token, 0, NULL, NULL))
	{
		printf("error adjust token\n");
		return FALSE;
	}

	CloseHandle(hProcessToken);
	return TRUE;

}

int main()
{
	// 提权失败
	if (!EnableDebugAbility())
	{
		printf("Could not get Debugging privilege\n");
		return 0;
	}

	printf("[+] SeDebugPrivilege available for enabling!\n");

	DWORD level = 1;
	// 保存进程信息
	PWTS_PROCESS_INFO_EX processList = NULL;
	// 进程数
	DWORD processCnt = 0;
	BOOL bRet = FALSE;

	bRet = WTSEnumerateProcessesEx(WTS_CURRENT_SERVER_HANDLE, &level, WTS_ANY_SESSION, (LPTSTR*)&processList, &processCnt);

	// 查询进程失败
	if (!bRet)
	{
		printf("error get process\n");
		return 0;
	}

	printf("Processes Found: %d\n\n", processCnt);
	printf("#\tPID\tHandles\tThreads\tProcess Name\tSID\tAccount\t\n\n");

	// 保存结构体指针
	PWTS_PROCESS_INFO_EX tempList = processList;

	// 输出进程信息
	for (DWORD i = 0; i < processCnt; i++)
	{
		printf("%d\t", i + 1);
		printf("%d\t", tempList->ProcessId);
		printf("%d\t", tempList->HandleCount);
		printf("%d\t", tempList->NumberOfThreads);
		printf("%ws\t", tempList->pProcessName);

		// 获取SID
		GetSID(tempList->pUserSid);
		// 获取域名\用户名
		GetAccount(tempList->pUserSid);

		printf("\n");
		// 移到下一个进程
		tempList++;
	}

	// 释放内存
	bRet = WTSFreeMemoryEx(WTSTypeProcessInfoLevel1, processList, processCnt);
	if (!bRet)
	{
		printf("error free memory\n");
		return 0;
	}

	processList = NULL;
	return 0;
}

  注意这里要生成64位的程序,再次使用非管理员权限运行,提示错误
  

管理员权限运行

  使用函数为ShellExecuteEx

BOOL ShellExecuteEx(
	SHELLEXECUTEINFOA *pExecInfo	// SHELLEXECUTEINFO结构体指针
);									// 成功返回非0 失败返回0

  SHELLEXECUTEINFO结构体,虽然有很多成员,但我们这里只关注几个

typedef struct _SHELLEXECUTEINFOA {
  DWORD     cbSize;			// 结构体大小
  ULONG     fMask;
  HWND      hwnd;
  LPCSTR    lpVerb;			// 指定执行的操作 值为runas以管理员身份启动
  LPCSTR    lpFile;			// 应用程序路径
  LPCSTR    lpParameters;
  LPCSTR    lpDirectory;
  int       nShow;			// 打开方式 值为SW_SHOWNORMAL 新窗口打开
  HINSTANCE hInstApp;
  void      *lpIDList;
  LPCSTR    lpClass;
  HKEY      hkeyClass;
  DWORD     dwHotKey;
  union {
    HANDLE hIcon;
    HANDLE hMonitor;
  } DUMMYUNIONNAME;
  HANDLE    hProcess;
} SHELLEXECUTEINFOA, *LPSHELLEXECUTEINFOA;

  具体值nShow  示例代码

#include <stdio.h>
#include <windows.h>
#include <WtsApi32.h>
#include <sddl.h>
#pragma comment(lib,"Wtsapi32.lib")

// 获取SID
void GetSID(PSID userSid)
{
	LPWSTR stringSID = NULL;

	// 成功获取
	if (ConvertSidToStringSid(userSid, &stringSID))
	{
		printf("%ws\t", stringSID);
		// 释放返回的缓冲区
		LocalFree((HLOCAL)stringSID);
		return;
	}

	printf("-\t");
}

// 获取域名\用户名
void GetAccount(PSID userSid)
{
	// 用户名
	TCHAR userName[255];
	DWORD bufferLen = 255;
	// 域名
	TCHAR domainName[255];
	DWORD domainNameBufferLen = 255;
	SID_NAME_USE peUse;

	// 成功获取
	if (LookupAccountSid(NULL, userSid, userName, &bufferLen, domainName, &domainNameBufferLen, &peUse))
	{
		printf("%ws\\%ws\t", domainName, userName);
		return;
	}
	printf("-\t");
}

// 启用调试权限
BOOL EnableDebugAbility()
{
	// 令牌句柄
	HANDLE hProcessToken = NULL;
	// 1.打开进程访问令牌
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
	{
		printf("error open process token\n");
		return FALSE;
	}

	// 2.取得SeDebugPrivilege特权的LUID值
	LUID luid;
	if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
	{
		printf("error lookup value\n");
		return FALSE;
	}


	// 3.是否有SeDebugPrivilege权限选项
	// 这里第一次调用GetTokenInformation是为了获取令牌信息需要的缓冲区的大小,
	DWORD realSize;
	GetTokenInformation(hProcessToken, TokenPrivileges, NULL, 0, &realSize);

	DWORD size;
	// 保存令牌信息
	PTOKEN_PRIVILEGES pToken = NULL;

	// 分配空间
	pToken = (PTOKEN_PRIVILEGES)malloc(realSize);
	if (!GetTokenInformation(hProcessToken, TokenPrivileges, pToken, realSize, &size))
	{
		printf("GetTokenInformation error");
		return FALSE;
	}

	BOOL bRet = FALSE;

	for (DWORD i = 0; i < pToken->PrivilegeCount; i++)
	{
		PLUID_AND_ATTRIBUTES runner = &pToken->Privileges[i];
		// 使用lowpart 和 highpart进行查询比较
		if ((runner->Luid.LowPart == luid.LowPart) && (runner->Luid.HighPart == luid.HighPart))
		{
			bRet = TRUE;
			break;
		}
	}

	if (!bRet)
	{
		printf("[-] SeDebugPrivilege unavailable\n");
		return FALSE;
	}

	// 4.调整访问令牌特权
	TOKEN_PRIVILEGES token;
	token.PrivilegeCount = 1;
	token.Privileges[0].Luid = luid;
	// 使特权有效
	token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (!AdjustTokenPrivileges(hProcessToken, FALSE, &token, 0, NULL, NULL))
	{
		printf("error adjust token\n");
		return FALSE;
	}

	CloseHandle(hProcessToken);
	return TRUE;

}

// 尝试以管理员身份运行
void RelaunchSelf()
{
	WCHAR fileName[MAX_PATH];
	// 获取当前程序路径
	GetModuleFileName(NULL, fileName, MAX_PATH);

	SHELLEXECUTEINFO sei;
	// 初始化
	memset(&sei, 0, sizeof(sei));
	sei.cbSize = sizeof(sei);
	// 以管理员身份运行
	sei.lpVerb = TEXT("runas");
	sei.lpFile = fileName;
	// 新窗口打开
	sei.nShow = SW_SHOWNORMAL;
	
	ShellExecuteEx(&sei);

}

int main()
{

	// 尝试提权
	if (!EnableDebugAbility())
	{
		printf("Could not get Debugging privilege\n");
		RelaunchSelf();
		return 0;
	}

	printf("[+] SeDebugPrivilege available for enabling!\n");

	DWORD level = 1;
	// 保存进程信息
	PWTS_PROCESS_INFO_EX processList = NULL;
	// 进程数
	DWORD processCnt = 0;
	BOOL bRet = FALSE;

	bRet = WTSEnumerateProcessesEx(WTS_CURRENT_SERVER_HANDLE, &level, WTS_ANY_SESSION, (LPTSTR*)&processList, &processCnt);

	// 查询进程失败
	if (!bRet)
	{
		printf("error get process\n");
		return 0;
	}

	printf("Processes Found: %d\n\n", processCnt);
	printf("#\tPID\tHandles\tThreads\tProcess Name\tSID\tAccount\t\n\n");

	// 保存结构体指针
	PWTS_PROCESS_INFO_EX tempList = processList;

	// 输出进程信息
	for (DWORD i = 0; i < processCnt; i++)
	{
		printf("%d\t", i + 1);
		printf("%d\t", tempList->ProcessId);
		printf("%d\t", tempList->HandleCount);
		printf("%d\t", tempList->NumberOfThreads);
		printf("%ws\t", tempList->pProcessName);

		// 获取SID
		GetSID(tempList->pUserSid);
		// 获取域名\用户名
		GetAccount(tempList->pUserSid);

		printf("\n");
		// 移到下一个进程
		tempList++;
	}

	// 释放内存
	bRet = WTSFreeMemoryEx(WTSTypeProcessInfoLevel1, processList, processCnt);
	if (!bRet)
	{
		printf("error free memory\n");
		return 0;
	}

	processList = NULL;

	system("pause");
	return 0;
}

  运行后弹出uac窗口,允许后得到正常信息
  

参考链接

Windows 用户、认证和对象安全
windows进程提权(C语言实现)
Windows API Exploitation

查看评论 -
评论