在枚举或结束系统进程,抑或是操作系统服务时,会出现自身进程权限不足而失败的情况,此时需要提升自身进程到系统权限,来完成这些特殊操作。

以下代码就是提升进程权限的一系列操作,具体的内容在《Windows核心编程》中有讲解,更多时候只需要使用就好。
- BOOL ImproveProcPriv()
- {
- HANDLE token;
-
- if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&token))
- {
- MessageBox(NULL,"打开进程令牌失败","错误",MB_ICONSTOP);
- return FALSE;
- }
- TOKEN_PRIVILEGES tkp;
- tkp.PrivilegeCount = 1;
- ::LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid);
- tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
- if(!AdjustTokenPrivileges(token,FALSE,&tkp,sizeof(tkp),NULL,NULL))
- {
- MessageBox(NULL,"调整令牌权限失败","错误",MB_ICONSTOP);
- return FALSE;
- }
- CloseHandle(token);
- return TRUE;
- }
但是每次提权都要调用这个函数有些不方便,使用Native API就能很快速的进行提权。
Native API中有一个非常好用的提权函数,包括打开其他进程,使用一些高权限的Native API都会使用RtlAdjustPrivilege进行权限提升。这个函数封装在NTDLL.dll中,该DLL在所有DLL加载前已经加载。
函数原型如下:
- NTSTATUS RtlAdjustPrivilege
- (
- ULONG Privilege,
- BOOLEAN Enable,
- BOOLEAN CurrentThread,
- PBOOLEAN Enabled
- )
各个参数的含义:
- Privilege [In] Privilege index to change.
- Enable [In] If TRUE, then enable the privilege otherwise disable.
- CurrentThread [In] If TRUE, then enable in calling thread, otherwise process.
- Enabled [Out] Whether privilege was previously enabled or disabled.
找出微软NTDLL.dll放入IDA进行逆向,可以看出大概的一个流程。
- NTSTATUS WINAPI
- RtlAdjustPrivilege(ULONG Privilege,
- BOOLEAN Enable,
- BOOLEAN CurrentThread,
- PBOOLEAN Enabled)
- {
- TOKEN_PRIVILEGES NewState;
- TOKEN_PRIVILEGES OldState;
- ULONG ReturnLength;
- HANDLE TokenHandle;
- NTSTATUS Status;
- TRACE("(%d, %s, %s, %p)\n", Privilege, Enable ? "TRUE" :
- "FALSE",
- CurrentThread ? "TRUE" : "FALSE", Enabled);
- if (CurrentThread)
- {
- Status = NtOpenThreadToken(GetCurrentThread(),
- TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
- FALSE,
- &TokenHandle);
- }
- else
- {
- Status = NtOpenProcessToken(GetCurrentProcess(),
- TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
- &TokenHandle);
- }
- if (!NT_SUCCESS(Status))
- {
- WARN("Retrieving token handle failed (Status %x)\n", Status);
- return Status;
- }
- OldState.PrivilegeCount = 1;
- NewState.PrivilegeCount = 1;
- NewState.Privileges[0].Luid.LowPart = Privilege;
- NewState.Privileges[0].Luid.HighPart = 0;
- NewState.Privileges[0].Attributes = (Enable) ? SE_PRIVILEGE_ENABLED : 0;
- Status = NtAdjustPrivilegesToken(TokenHandle,
- FALSE,
- &NewState,
- sizeof(TOKEN_PRIVILEGES),
- &OldState,
- &ReturnLength);
- NtClose (TokenHandle);
- if (Status == STATUS_NOT_ALL_ASSIGNED)
- {
- TRACE("Failed to assign all privileges\n");
- return STATUS_PRIVILEGE_NOT_HELD;
- }
- if (!NT_SUCCESS(Status))
- {
- WARN("NtAdjustPrivilegesToken() failed (Status %x)\n", Status);
- return Status;
- }
- if (OldState.PrivilegeCount == 0)
- *Enabled = Enable;
- else
- *Enabled = (OldState.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED);
- return STATUS_SUCCESS;
- }
根据WRK参考RtlAdjustPrivilege的源码,理解提权函数。
- NTSTATUS
- RtlAdjustPrivilege(
- ULONG Privilege,
- BOOLEAN Enable,
- BOOLEAN Client,
- PBOOLEAN WasEnabled
- )
-
- /*++
-
- Routine Description:
-
- This procedure enables or disables a privilege process-wide.
-
- Arguments:
-
- Privilege - The lower 32-bits of the privilege ID to be enabled or
- disabled. The upper 32-bits is assumed to be zero.
-
- Enable - A boolean indicating whether the privilege is to be enabled
- or disabled. TRUE indicates the privilege is to be enabled.
- FALSE indicates the privilege is to be disabled.
-
- Client - A boolean indicating whether the privilege should be adjusted
- in a client token or the process's own token. TRUE indicates
- the client's token should be used (and an error returned if there
- is no client token). FALSE indicates the process's token should
- be used.
-
- WasEnabled - points to a boolean to receive an indication of whether
- the privilege was previously enabled or disabled. TRUE indicates
- the privilege was previously enabled. FALSE indicates the privilege
- was previously disabled. This value is useful for returning the
- privilege to its original state after using it.
-
-
- Return Value:
-
- STATUS_SUCCESS - The privilege has been successfully enabled or disabled.
-
- STATUS_PRIVILEGE_NOT_HELD - The privilege is not held by the specified context.
-
- Other status values as may be returned by:
-
- NtOpenProcessToken()
- NtAdjustPrivilegesToken()
-
-
- --*/
-
- {
- NTSTATUS
- Status,
- TmpStatus;
-
- HANDLE
- Token;
-
- LUID
- LuidPrivilege;
-
- PTOKEN_PRIVILEGES
- NewPrivileges,
- OldPrivileges;
-
- ULONG
- Length;
-
- UCHAR
- Buffer1[sizeof(TOKEN_PRIVILEGES)+
- ((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))],
- Buffer2[sizeof(TOKEN_PRIVILEGES)+
- ((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))];
-
-
- RTL_PAGED_CODE();
-
- NewPrivileges = (PTOKEN_PRIVILEGES)Buffer1;
- OldPrivileges = (PTOKEN_PRIVILEGES)Buffer2;
-
- //
- // Open the appropriate token...
- //
-
- if (Client == TRUE) {
- Status = NtOpenThreadToken(
- NtCurrentThread(),
- TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
- FALSE,
- &Token
- );
- } else {
-
- Status = NtOpenProcessToken(
- NtCurrentProcess(),
- TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
- &Token
- );
- }
-
- if (!NT_SUCCESS(Status)) {
- return(Status);
- }
-
-
-
- //
- // Initialize the privilege adjustment structure
- //
-
- LuidPrivilege = RtlConvertUlongToLuid(Privilege);
-
-
- NewPrivileges->PrivilegeCount = 1;
- NewPrivileges->Privileges[0].Luid = LuidPrivilege;
- NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
-
-
-
- //
- // Adjust the privilege
- //
-
- Status = NtAdjustPrivilegesToken(
- Token, // TokenHandle
- FALSE, // DisableAllPrivileges
- NewPrivileges, // NewPrivileges
- sizeof(Buffer1), // BufferLength
- OldPrivileges, // PreviousState (OPTIONAL)
- &Length // ReturnLength
- );
-
-
- TmpStatus = NtClose(Token);
- ASSERT(NT_SUCCESS(TmpStatus));
-
-
- //
- // Map the success code NOT_ALL_ASSIGNED to an appropriate error
- // since we're only trying to adjust the one privilege.
- //
-
- if (Status == STATUS_NOT_ALL_ASSIGNED) {
- Status = STATUS_PRIVILEGE_NOT_HELD;
- }
-
-
- if (NT_SUCCESS(Status)) {
-
- //
- // If there are no privileges in the previous state, there were
- // no changes made. The previous state of the privilege
- // is whatever we tried to change it to.
- //
-
- if (OldPrivileges->PrivilegeCount == 0) {
-
- (*WasEnabled) = Enable;
-
- } else {
-
- (*WasEnabled) =
- (OldPrivileges->Privileges[0].Attributes & SE_PRIVILEGE_ENABLED)
- TRUE : FALSE;
- }
- }
-
- return(Status);
- }
贴了这么多源代码,具体该如何调用Native API,非常简单。
- BOOLEAN bPrev;
- RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &bPrev);
下面是一个调试通过的例子,可以看出Native API是如何使用的。
- #include <windows.h>
-
- const unsigned int SE_SHUTDOWN_PRIVILEGE = 0x13;
-
- typedef enum _SHUTDOWN_ACTION
- {
- ShutdownNoReboot,
- ShutdownReboot,
- ShutdownPowerOff
- } SHUTDOWN_ACTION, *PSHUTDOWN_ACTION;
-
- typedef int (_stdcall *_RtlAdjustPrivilege)(int, BOOL, BOOL, int *);
- typedef int (_stdcall *_ZwShutdownSystem)(SHUTDOWN_ACTION);
-
- void HookPatch(DWORD OldFunc, DWORD NewFunc)
- {
- DWORD Pro;
- VirtualProtect((LPVOID)OldFunc, 20, PAGE_READWRITE, &Pro);
- *(BYTE *)OldFunc = 0xE9;
- *(DWORD *)(OldFunc+1) = (NewFunc-OldFunc-5);
- }
-
- __stdcall void HookMessage(int)
- {
- MessageBox(NULL, "ZwShutdownSystem have been hook", "HookMessage", MB_OK);
- }
-
- int main()
- {
- HMODULE hNtDll = LoadLibrary("ntdll.dll");
- _RtlAdjustPrivilege pfnRtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(hNtDll, "RtlAdjustPrivilege");
- _ZwShutdownSystem pfnZwShutdownSystem = (_ZwShutdownSystem)GetProcAddress(hNtDll, "ZwShutdownSystem");
-
- int nEn = 0;
- int nResult = pfnRtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, TRUE, &nEn);
- if(nResult == 0x0c000007c)
- {
- nResult = pfnRtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &nEn);
- }
-
- HookPatch((DWORD)pfnZwShutdownSystem, (DWORD)HookMessage);
- system("pause");
- pfnZwShutdownSystem(ShutdownReboot);
- FreeLibrary(hNtDll);
- return 0;
- }
|