首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC++

Windows 進程令牌信息查看工具源碼解析 (TokenInfor)

     編輯:VC++

工具定位:進程級 `whoami`

標準 `whoami.exe` 僅顯示當前進程令牌信息。本工具 `TokenInfor` 支持指定 PID 查看**任意進程**的訪問令牌完整信息:用戶 SID、群組成員、特權列表、令牌類型、模擬級別、令牌源。適用於權限審計、提權分析、服務帳號排查。

核心 API 鏈路

  1. `OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ)` → 進程句柄
  2. `OpenProcessToken(hProcess, TOKEN_QUERY)` → 令牌句柄
  3. `GetTokenInformation(hToken, TokenUser/Groups/Privileges/Statistics/Source, ...)` → 結構化信息
  4. `LookupAccountSid` / `LookupPrivilegeName` / `LookupPrivilegeDisplayName` → 友好名稱解析
  5. `GetModuleFileNameEx (Psapi.dll)` → 進程映像路徑

關鍵結構體定義

// 令牌用戶
typedef struct _TOKEN_USER { SID_AND_ATTRIBUTES User; } TOKEN_USER;
// 群組
typedef struct _TOKEN_GROUPS { DWORD GroupCount; SID_AND_ATTRIBUTES Groups[ANYSIZE_ARRAY]; } TOKEN_GROUPS;
// 特權
typedef struct _TOKEN_PRIVILEGES { DWORD PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; } TOKEN_PRIVILEGES;
// 統計信息 (類型/模擬級別/ID)
typedef struct _TOKEN_STATISTICS {
    LUID TokenId; LUID AuthenticationId; LARGE_INTEGER ExpirationTime;
    TOKEN_TYPE TokenType; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
    DWORD DynamicCharged, DynamicAvailable, GroupCount, PrivilegeCount;
    LUID ModifiedId;
} TOKEN_STATISTICS;
// 令牌源
typedef struct _TOKEN_SOURCE { CHAR SourceName[8]; LUID SourceIdentifier; } TOKEN_SOURCE;

完整源碼邏輯解析 (精簡版)

1. 主流程

int main(int argc, char* argv[]) {
    DWORD dwPid = (argc > 1) ? atoi(argv[1]) : GetCurrentProcessId();

    // 啟用 SeDebugPrivilege 以訪問系統進程 (如 csrss.exe)
    EnableDebugPriv(TRUE);

    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPid);
    if (!hProcess) { PrintError("OpenProcess"); return 1; }

    // 獲取進程映像路徑 (需 Psapi.dll)
    HMODULE hPsapi = LoadLibrary("psapi.dll");
    if (hPsapi) {
        auto pGetModName = (GetModuleFileNameExType*)GetProcAddress(hPsapi, "GetModuleFileNameExA");
        if (pGetModName) { char path[MAX_PATH]; pGetModName(hProcess, NULL, path, MAX_PATH); printf("Path: %s\n", path); }
        FreeLibrary(hPsapi);
    }

    HANDLE hToken;
    if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
        PrintUserName(hToken);
        PrintGroups(hToken);
        PrintPrivileges(hToken);
        PrintTokenType(hToken);
        PrintTokenSource(hProcess); // 需 TOKEN_QUERY_SOURCE
        CloseHandle(hToken);
    }
    CloseHandle(hProcess);
    EnableDebugPriv(FALSE);
    return 0;
}

2. 用戶名解析

void PrintUserName(HANDLE hToken) {
    BYTE buf[1024]; DWORD len;
    if (GetTokenInformation(hToken, TokenUser, buf, sizeof(buf), &len)) {
        TOKEN_USER* ptu = (TOKEN_USER*)buf;
        char name[256], domain[256]; DWORD n=256, d=256; SID_NAME_USE snu;
        if (LookupAccountSid(NULL, ptu->User.Sid, name, &n, domain, &d, &snu))
            printf("User: %s\\%s (SID: %s)\n", domain, name, SidToString(ptu->User.Sid));
    }
}

3. 群組枚舉

void PrintGroups(HANDLE hToken) {
    BYTE buf[4096]; DWORD len;
    if (GetTokenInformation(hToken, TokenGroups, buf, sizeof(buf), &len)) {
        TOKEN_GROUPS* ptg = (TOKEN_GROUPS*)buf;
        printf("Groups (%d):\n", ptg->GroupCount);
        for (DWORD i=0; iGroupCount; i++) {
            char name[256], domain[256]; DWORD n=256, d=256; SID_NAME_USE snu;
            if (LookupAccountSid(NULL, ptg->Groups[i].Sid, name, &n, domain, &d, &snu))
                printf("  [%d] %s\\%s (Attr: 0x%X)\n", i, domain, name, ptg->Groups[i].Attributes);
        }
    }
}

4. 特權枚舉 (核心亮點)

void PrintPrivileges(HANDLE hToken) {
    BYTE buf[4096]; DWORD len;
    if (GetTokenInformation(hToken, TokenPrivileges, buf, sizeof(buf), &len)) {
        TOKEN_PRIVILEGES* ptp = (TOKEN_PRIVILEGES*)buf;
        printf("Privileges (%d):\n", ptp->PrivilegeCount);
        for (DWORD i=0; iPrivilegeCount; i++) {
            LUID luid = ptp->Privileges[i].Luid;
            char pname[256], dname[512]; DWORD pn=256, dn=512;
            if (LookupPrivilegeName(NULL, &luid, pname, &pn)) {
                if (LookupPrivilegeDisplayName(NULL, pname, dname, &dn, NULL))
                    printf("  [%d] %s - %s %s\n", i, pname, dname, (ptp->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED) ? "(Enabled)" : "");
            }
        }
    }
}

5. 令牌類型與模擬級別

void PrintTokenType(HANDLE hToken) {
    BYTE buf[1024]; DWORD len;
    if (GetTokenInformation(hToken, TokenStatistics, buf, sizeof(buf), &len)) {
        TOKEN_STATISTICS* pts = (TOKEN_STATISTICS*)buf;
        printf("Token Type: %s\n", pts->TokenType == TokenPrimary ? "Primary" : "Impersonation");
        if (pts->TokenType == TokenImpersonation) {
            static const char* levels[] = {
                "Anonymous", "Identification", "Impersonation", "Delegation"
            };
            if (pts->ImpersonationLevel <= SecurityDelegation)
                printf("Impersonation Level: %s\n", levels[pts->ImpersonationLevel]);
        }
    }
}

6. 啟用 SeDebugPrivilege (關鍵)

BOOL EnableDebugPriv(BOOL fEnable) {
    HANDLE hToken;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE;
    TOKEN_PRIVILEGES tp = {1};
    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
    tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
    BOOL ok = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
    CloseHandle(hToken);
    return ok && GetLastError() != ERROR_NOT_ALL_ASSIGNED;
}

運行示例輸出

D:\> TokenInfor.exe 160
Token Information of Process ID = 160.
Execute File Path = \??\D:\WINNT\system32\csrss.exe.

User Name : NT AUTHORITY\SYSTEM (S-1-5-18)

Belong to 3 groups
[0] BUILTIN\Administrators (S-1-5-32-544) Attr: 0x7
[1] \Everyone (S-1-1-0) Attr: 0x7
[2] NT AUTHORITY\Authenticated Users (S-1-5-11) Attr: 0x7

Have 21 Privileges
[0] SeTcbPrivilege - Act as part of the operating system (Enabled)
[1] SeCreateTokenPrivilege - Create a token object (Enabled)
[2] SeTakeOwnershipPrivilege - Take ownership of files or other objects (Enabled)
...
[20] SeUndockPrivilege - Undock computer (Enabled)

Token Type : Primary Token
OpenProcessToken QUERY_SOURCE error : 5 (Access Denied)

編譯與部署注意事項

  • 編譯器: 原作者使用 LCC (輕量 C 編譯器),VC6/VC++ 2005+ 直接編譯需處理 `_CRT_SECURE_NO_WARNINGS`、字符集 (建議多字節集 MBCS)、鏈接 `Psapi.lib` (或動態加載 `psapi.dll` 兼容 XP/2003)。
  • 權限: 需管理員權限運行以查看系統進程 (如 `csrss.exe`, `lsass.exe`)。普通用戶僅能查看同會話進程。
  • 64 位兼容: 32 位程序無法正確打開 64 位進程 (`OpenProcess` 失敗)。需編譯為 x64 版本或使用 `IsWow64Process` 判斷並調用 64 位版本。
  • 錯誤 5 (Access Denied): `TOKEN_QUERY_SOURCE` 需 `SeTcbPrivilege` (TCB 特權),極高權限,通常僅 SYSTEM 擁有,普通管理員也無法獲取令牌源。

擴展方向

  • 輸出 JSON/CSV 供自動化分析
  • GUI 版本:進程樹視圖、令牌對比、特權過濾
  • 集成 `GetTokenInformation(TokenLinkedToken)` 檢測 UAC 虛擬化/提升對
  • 遠程查詢:配合 `OpenProcessToken` + `ImpersonateLoggedOnUser` 實現遠程令牌審計

總結

`TokenInfor` 以精簡的 C 代碼展示了 Windows 訪問令牌核心數據結構的枚舉與解析全流程。掌握 `TOKEN_QUERY` 信息類、`LookupAccountSid/Name/DisplayName` 解析鏈、`SeDebugPrivilege` 提權模式,是 Windows 安全開發、權限模型研究、滲透測試後利用階段的必備基本功。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved