RtlAdjustPrivilege一個實用的函式。這個函式封裝在NtDll.dll中(在所有用戶態DLL載入之前,被核心中的PsLocateSystemDll函式載入),沒有被微軟的MSDN公開。函式的定義(Winehq給出):NTSTATUS RtlAdjustPrivilege(ULONG Privilege,BOOLEAN Enable,BOOLEAN CurrentThread,PBOOLEAN Enabled)。
基本介紹
- 中文名:RtlAdjustPrivilege函式
- 對比:BOOL ImproveProcPriv()
- 參數的含義:Privilege [In] Privilege index
- 函式定義:NTSTATUS RtlAdjustPrivilege
信息介紹,定義及參數,函式定義,參數的含義,對比,套用示例,函式的運行,
信息介紹
這個函式封裝在NtDll.dll中,MSDN沒有專門的文檔介紹它,就是說你在MSDN上查不到關於它的任何信息,但是可以在微軟官方的WRK(Windows研究核心)里找到它的原始碼。
定義及參數
函式定義
NTSTATUS RtlAdjustPrivilege(
ULONG Privilege,
BOOLEAN Enable,
BOOLEAN CurrentThread,
PBOOLEAN Enabled)
參數的含義
Privilege [In] Privilege index to change.
// 所需要的許可權名稱,可以到MSDN查找關於Process Token & Privilege內容可以查到
Enable [In] If TRUE, then enable the privilege otherwise disable.
// 如果為True 就是打開相應許可權,如果為False 則是關閉相應許可權
CurrentThread [In] If TRUE, then enable in calling thread, otherwise process.
// 如果為True 則僅提升當前執行緒許可權,否則提升整個進程的許可權
Enabled [Out] Whether privilege was previously enabled or disabled.
// 輸出原來相應許可權的狀態(打開 | 關閉)
對比
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); // 獲得 SE_DEBUG_NAME 特權
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;
}
這個提權很複雜。
但是 如果有這個函式就不一樣了,你可以只用一個函式就實現這個功能,甚至功能遠多於上面的代碼,由於它的簡潔性,WinLogon和Lsass進程也用這個函式來提權。
通過恰當的IDE設定和必要的Defination,上面這個函式的功能你完全可以通過一行代碼來實現。
RtlAdjustPrivilege(SE_DEBUG_NAME,1,0,NULL);
套用示例
這個函式可用於快速關機(不保存,通知核心驅動後直接關電源)的提權
Public Declare Function RtlAdjustPrivilege& Lib "ntdll" (ByVal Privilege&, ByVal NewValue&, ByVal NewThread&, OldValue&) '聲明本函式,用來提權
Public Declare Function NtShutdownSystem& Lib "ntdll" (ByVal ShutdownAction&)‘用於快速關機,這個函式也未被MSDN文檔化
Public Const SE_SHUTDOWN_PRIVILEGE& = 19
Public Const shutdown& = 0
Public Const RESTART& = 1
Public Const POWEROFF& = 2
實現代碼:
Private Sub jinzhenhuaychhsh_Click()
RtlAdjustPrivilege SE_SHUTDOWN_PRIVILEGE, 1, 0, 0 '******提權(),關鍵,很簡單******
NtShutdownSystem poweroff
End Sub
若不快速關機的話
NtShutdownSystem restart 是重啟
NtShutdownSystem poweroff 是關機
函式的運行
下面我們看一下這個函式是怎么運行的,順便學習下強大的IDA
IDA 載入ntdll.dll (我這裡載入的是 WinDBG自動下載的 Symbol裡面的英文版本 可能不同的Windows版本略有不同)
先把函式的原型給輸入IDA 方便一下閱讀,然後開始閱讀彙編代碼了(黨和國家考驗我們的時候到了)
看看函式最開頭...
mov edi, edi ; 這句話是廢指令
push ebp
mov ebp, esp
cmp [ebp+CurrentThread], 1 ; 判斷CurrentThread參數是否被指定為1
mov eax, dword_7C97B0C8
mov [ebp+var_4], eax
mov eax, [ebp+Enabled]
mov [ebp+IsEnabled], eax ; BOOL *IsEnabled = Enabled;
lea eax, [ebp+var_28]
push eax
jz loc_7C93378B
判斷是調整進程許可權還是執行緒許可權,
CurrentThread == TRUE
push 0
push 28h ; TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
push 0FFFFFFFEh ; GetCurrentThread()
call ZwOpenThreadToken
jmp loc_7C929A7A
CurrentThread == FALSE
push 28h ; TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
push 0FFFFFFFFh ; GetCurrentProcess()
call NtOpenProcessToken
然後兩個代碼塊同時指向這裡
loc_7C929A7A: ; 很明白了吧 判斷進程/執行緒令牌是否成功被打開
test eax, eax
jl short loc_7C929AE4 ; 沒成功則跳
若 執行成功
mov eax, [ebp+Privilege]
mov [ebp+dwPrivilege], eax
mov al, [ebp+Enable]
xor ecx, ecx ; ecx清零
neg al
push esi
mov [ebp+NewState], 1
mov [ebp+var_C], ecx
sbb eax, eax
and eax, 2
mov [ebp+var_8], eax
lea eax, [ebp+ReturnLength] ; 實際返回長度
push eax
lea eax, [ebp+OldState]
push eax ; 舊的特權 指針
push 10h ; sizeof(TOKEN_PRIVILEGES)
lea eax, [ebp+NewState]
push eax ; 新的特權 指針
push ecx ; FALSE 因為上面有xor ecx,ecx
push [ebp+TokenHandle]
call NtAdjustPrivilegesToken ; 調用 NtAdjustPrivilegesToken提權
push [ebp+TokenHandle]
mov esi, eax ; eax備份
call ZwClose ; 關閉 核心對象句柄
cmp esi, 106h ; 判斷NtAdjustPrivilege執行情況 106h = STATUS_NOT_ALL_ASSIGNED
jz loc_7C947DF2
判斷是否執行成功之後,開始輸出最後一個參數
cmp [ebp+OldState], 0
mov ecx, [ebp+IsEnabled]
jnz loc_7C929E99
若 OldState != 0 則
mov al, [ebp+Enable] ; 應該很明顯了 把Enable變數賦給al 也就是eax最後兩位
若 OldState == 0 則
mov eax, [ebp+var_18]
shr eax, 1
and al, 1
jmp loc_7C929ADF
這個函式大致流程就是這樣。
到這裡差不多可以按一下傳說中的F5了
int __stdcall RtlAdjustPrivilege(int Privilege, char Enable,char CurrentThread, int Enabled)
{
int result; // eax@2
signed int AdjustResult; // esi@4
char returnValue; // al@7
int v7; // [sp+2Ch] [bp-4h]@1
int IsEnabled; // [sp+4h] [bp-2Ch]@1
int TokenHandle; // [sp+8h] [bp-28h]@2
int dwPrivilege; // [sp+20h] [bp-10h]@4
signed int NewState; // [sp+1Ch] [bp-14h]@4
int v12; // [sp+24h] [bp-Ch]@4
int v13; // [sp+28h] [bp-8h]@4
int OldState; // [sp+Ch] [bp-24h]@4
char ReturnLength; // [sp+0h] [bp-30h]@4
unsigned int v16; // [sp+18h] [bp-18h]@11
v7 = dword_7C97B0C8;
IsEnabled = Enabled;
if ( CurrentThread == 1 )
result = ZwOpenThreadToken(-2, 40, 0, &TokenHandle);
else
result = NtOpenProcessToken(-1, 40, &TokenHandle);
if ( result >= 0 )
{
dwPrivilege = Privilege;
NewState = 1;
v12 = 0;
v13 = -(Enable != 0) & 2;
AdjustResult = NtAdjustPrivilegesToken(TokenHandle, 0, &NewState, 16, &OldState, &ReturnLength);
ZwClose(TokenHandle);
if ( AdjustResult == 262 )
AdjustResult = ;
if ( AdjustResult >= 0 )
{
if ( OldState )
returnValue = (v16 >> 1) & 1;
else
returnValue = Enable;
*(_BYTE *)IsEnabled = returnValue;
}
result = AdjustResult;
}
return result;
}
可讀性好像仍然不高,看看這個...
/******************************************************************************
* RtlAdjustPrivilege [NTDLL.@]
*
* Enables or disables a privilege from the calling thread or process.
*
* PARAMS
* Privilege [I] Privilege index to change.
* Enable [I] If TRUE, then enable the privilege otherwise disable.
* CurrentThread [I] If TRUE, then enable in calling thread, otherwise process.
* Enabled [O] Whether privilege was previously enabled or disabled.
*
* RETURNS
* Success: STATUS_SUCCESS.
* Failure: NTSTATUS code.
*
* SEE ALSO
* NtAdjustPrivilegesToken, NtOpenThreadToken, NtOpenProcessToken.
*
*/
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;
}
haras
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()
--*/
{
NTSTATUSStatus,TmpStatus;
HANDLEToken;
LUIDLuidPrivilege;
PTOKEN_PRIVILEGESNewPrivileges,OldPrivileges;
ULONGLength;
UCHARBuffer1[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);
}