Наконец-то все заработало. Вот код маленькой программы, которая отключает все привилегии процесса. Pid передается в командной строке. Я бы не справился с этим без помощи Эрика Сан (спасибо!). (См. ОБНОВЛЕНИЕ внизу.)
#include <windows.h>
#include <stdio.h>
#include <TlHelp32.h>
void print_privileges(HANDLE hToken)
{
DWORD size;
if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &size) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
PTOKEN_PRIVILEGES tp = malloc(size);
if (tp != NULL && GetTokenInformation(hToken, TokenPrivileges, tp, size, &size)) {
size_t i;
for (i = 0; i < tp->PrivilegeCount; ++i) {
char name[64];
DWORD NAME_SIZE = sizeof name;
LookupPrivilegeNameA(0, &tp->Privileges[i].Luid, name, &NAME_SIZE);
BOOL fResult;
PRIVILEGE_SET ps = {
1, PRIVILEGE_SET_ALL_NECESSARY, {
{ { tp->Privileges[i].Luid.LowPart, tp->Privileges[i].Luid.HighPart } }
}
};
PrivilegeCheck(hToken, &ps, &fResult);
printf("%-*s %s\n", 42, name, fResult ? "Enabled" : "Disabled");
}
}
free(tp);
}
}
BOOL set_privilege(HANDLE hToken, const char* privilege, BOOL bEnablePrivilege)
{
BOOL ok = FALSE;
LUID luid;
if (LookupPrivilegeValueA(NULL, privilege, &luid)) {
// first pass. get current privilege setting
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious = sizeof tpPrevious;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof tp, &tpPrevious, &cbPrevious);
if (GetLastError() == ERROR_SUCCESS) {
// second pass. set privilege based on previous setting
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tpPrevious.Privileges[0].Attributes |= SE_PRIVILEGE_ENABLED;
else
tpPrevious.Privileges[0].Attributes ^= SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes;
AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL);
ok = GetLastError() == ERROR_SUCCESS;
}
}
return ok;
}
BOOL get_current_token(HANDLE* phToken)
{
BOOL ok = FALSE;
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, phToken)) {
if (GetLastError() == ERROR_NO_TOKEN)
if (ImpersonateSelf(SecurityImpersonation))
if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, phToken))
ok = TRUE;
} else
ok = TRUE;
return ok;
}
BOOL impersonate_process(HANDLE hProcess)
{
BOOL ok = FALSE;
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken)) {
if (ImpersonateLoggedOnUser(hToken))
ok = TRUE;
CloseHandle(hToken);
}
return ok;
}
BOOL disable_all_privileges(HANDLE hProcess)
{
BOOL ok = FALSE;
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
puts("\nBefore:");
print_privileges(hToken);
/* disable all privileges */
if (AdjustTokenPrivileges(hToken, TRUE, NULL, 0, NULL, NULL))
/* AdjustTokenPrivileges() could succeed but still not disable all privileges */
if (GetLastError() != ERROR_NOT_ALL_ASSIGNED) {
puts("\nAfter:");
print_privileges(hToken);
ok = TRUE;
}
CloseHandle(hToken);
}
return ok;
}
BOOL reduce_privileges(DWORD pid)
{
BOOL ok = FALSE;
/* in case target process has higher integrity level than this process, e.g., system versus
* high integrity, enable SeDebugPrivilege and impersonate target process */
HANDLE hCurrentToken;
if (get_current_token(&hCurrentToken)) {
if (set_privilege(hCurrentToken, "SeDebugPrivilege", TRUE)) {
if (set_privilege(hCurrentToken, "SeImpersonatePrivilege", TRUE)) {
/* get process handle with only the least access needed */
const HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (hProcess) {
if (impersonate_process(hProcess))
/* now we should be able to adjust the privileges of any target process */
if (disable_all_privileges(hProcess))
ok = TRUE;
CloseHandle(hProcess);
}
/* disable privileges we no longer need */
set_privilege(hCurrentToken, "SeImpersonatePrivilege", FALSE);
}
set_privilege(hCurrentToken, "SeDebugPrivilege", FALSE);
}
CloseHandle(hCurrentToken);
}
return ok;
}
int main(int argc, char* argv[])
{
DWORD pid = 0;
if (argc > 1)
sscanf_s(argv[1], "%u", &pid);
return pid && reduce_privileges(pid) ? 0 : 1;
}
ОБНОВЛЕНИЕ: Вот улучшенный reduce_privilege()
с дальнейшими предложениями Эрика. Таким образом, мы включаем и используем привилегию олицетворения только в случае крайней необходимости.
int reduce_privileges(DWORD pid)
{
int ret = 1;
HANDLE hCurrentToken;
if (get_current_token(&hCurrentToken)) {
const BOOL debug_set = set_privilege(hCurrentToken, "SeDebugPrivilege", TRUE);
/* regardless of whether we got debug privilege, try to get process handle with the least access needed */
const HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (hProcess) {
/* see if we can disable privileges of target process without the impersonate privilege;
if we can't, enable that privilege, impersonate, and try again */
if (disable_all_privileges(hProcess))
ret = 0;
else
if (set_privilege(hCurrentToken, "SeImpersonatePrivilege", TRUE)) {
if (impersonate_process(hProcess))
/* now we should be able to adjust the privileges of any target process */
if (disable_all_privileges(hProcess))
ret = 0;
/* impersonate privilege no longer needed */
set_privilege(hCurrentToken, "SeImpersonatePrivilege", FALSE);
}
CloseHandle(hProcess);
}
/* debug privilege no longer needed */
if (debug_set)
set_privilege(hCurrentToken, "SeDebugPrivilege", FALSE);
CloseHandle(hCurrentToken);
}
return ret;
}