求Windows 8+下用Win32 API(ISO C)实现从管理员切换至指定账户的示例
Win32 API 实现管理员进程切换至指定账户(含SYSTEM)
需求:编写适用于Windows 8及以上系统、采用ISO C编写的Win32 API示例,将当前拥有管理员权限的进程切换至指定账户(如FOOBAR、chicken.monster、MYFOOBAR、SYSTEM等),仅使用原生Win32 API。
原代码问题:使用LogOnUserA()调用SYSTEM这类特殊本地账户时失败,代码如下:
#include <stdio.h> #include <windows.h> #include <iostream> #include <string> void get_file_size(std::string path) { struct _stat fileinfo; _stat(path.c_str(), &fileinfo); std::cout << "file size is " << fileinfo.st_size << std::endl; } int main() { HANDLE tokenHandle; puts("#mark0"); bool returnValue = LogonUserA("SYSTEM", NULL, NULL, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &tokenHandle); if (false == returnValue) { return 1; } puts("#mark1"); bool res = ImpersonateLoggedOnUser(tokenHandle); if (res) { std::string path = "C:\cygwin64\bin\getent.exe"; puts("#mark2"); get_file_size(path); RevertToSelf(); } puts("#mark3"); CloseHandle(tokenHandle); return 0; }
以管理员身份运行时,LogOnUserA()调用失败。
问题分析
LogOnUserA()仅适用于普通用户的交互式登录,SYSTEM是系统内置特权账户,无可用的交互式登录凭据,无法通过该API直接获取令牌。- 原代码混合C++语法(如
std::string),不符合ISO C标准要求。
解决方案
核心思路
- 普通本地/域账户:使用
LogOnUserA获取令牌后模拟登录,需提供正确凭据。 - SYSTEM账户:通过获取系统进程(如
services.exe,始终以SYSTEM身份运行)的令牌来模拟身份。
以下是符合ISO C标准、兼容Windows 8+的完整示例:
#include <stdio.h> #include <windows.h> #include <tlhelp32.h> #include <sys/stat.h> #include <string.h> // 获取文件大小(ISO C实现) void get_file_size(const char* path) { struct _stat fileinfo; if (_stat(path, &fileinfo) == 0) { printf("file size is %lld\n", (long long)fileinfo.st_size); } else { printf("Failed to get file size, error: %lu\n", GetLastError()); } } // 获取指定进程名的进程ID DWORD get_process_id(const char* process_name) { DWORD pid = 0; HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot != INVALID_HANDLE_VALUE) { PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hSnapshot, &pe32)) { do { if (_stricmp(pe32.szExeFile, process_name) == 0) { pid = pe32.th32ProcessID; break; } } while (Process32Next(hSnapshot, &pe32)); } CloseHandle(hSnapshot); } return pid; } // 获取指定账户的令牌 BOOL get_user_token(const char* username, const char* password, HANDLE* out_token) { if (!username || !out_token) return FALSE; // 处理SYSTEM账户 if (_stricmp(username, "SYSTEM") == 0) { DWORD services_pid = get_process_id("services.exe"); if (services_pid == 0) { printf("Failed to find services.exe\n"); return FALSE; } HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, services_pid); if (hProcess == NULL) { printf("OpenProcess failed, error: %lu\n", GetLastError()); return FALSE; } BOOL result = OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_IMPERSONATE, out_token); CloseHandle(hProcess); return result; } // 处理普通账户 return LogonUserA( username, NULL, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out_token ); } int main() { HANDLE token_handle = NULL; const char* target_user = "SYSTEM"; // 可替换为FOOBAR、chicken.monster等 const char* user_password = ""; // 普通账户需替换为实际密码 // 获取目标用户令牌 if (!get_user_token(target_user, user_password, &token_handle)) { printf("get_user_token failed, error: %lu\n", GetLastError()); return 1; } // 模拟登录目标用户 if (ImpersonateLoggedOnUser(token_handle)) { printf("Impersonated as %s successfully\n", target_user); const char* test_path = "C:\\cygwin64\\bin\\getent.exe"; get_file_size(test_path); // 恢复原身份 RevertToSelf(); printf("Reverted to original identity\n"); } else { printf("ImpersonateLoggedOnUser failed, error: %lu\n", GetLastError()); } // 关闭令牌句柄 if (token_handle != NULL) { CloseHandle(token_handle); } return 0; }
关键说明
- SYSTEM账户处理:通过枚举进程找到
services.exe,打开其进程令牌用于身份模拟,无需密码。 - 普通账户处理:需传入正确的用户名和密码,
LogOnUserA才能成功获取令牌。 - 权限要求:进程必须以管理员身份运行,否则无法打开系统进程或调用特权API。
- 代码完全采用ISO C编写,无C++语法,兼容Windows 8及以上系统。
内容的提问来源于stack exchange,提问作者Roland Mainz
相关产品推荐
相关产品推荐

