[轉自“看雪論壇”]RtlAdjustPrivliege (http://bbs.pediy.com/showthread.php?t=76552)

前言:
今天逆向一個非常實用的函數RtlAdjustPrivliege

這個函數封裝在NtDll.dll中(在所有DLL加載之前加載),被微軟嚴格保密,就是說你在MSDN上查不到關於他的任何信息。

先來看看這個函數的定義(Winehq給出):

引用:
 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;
}

看看吧,這個提權快要累死了...
但是 如果有這個函數就不一樣了,你可以只用一個函數就實現這個功能,甚至功能遠多於上面的代碼...
通過恰當的IDE設置和必要的Defination,上面這個函數的功能你完全可以通過一行代碼來實現。

代碼:
RtlAdjustPrivilege(SE_DEBUG_NAME,1,0,NULL);

 
正文:
下面我們看一下這個函數是怎麼運行的,順便學習下強大的IDA
IDA 載入ntdll.dll (我這裏載入的是 WinDBG自動下載的 Symbol裏面的英文版本 可能不同的Windows版本略有不同)

先把函數的原型給輸入IDA 方便一下閱讀,然後開始閱讀彙編代碼了(黨和國家考驗我們的時候到了)。
看看Graph View 真的是很牛啊... 
看看函數最開頭...

引用:
mov     edi, edi        ; 這句話是廢指令
push    ebp
mov     ebp, esp
sub     esp, 30h        ; 48個字節的子過程域Auto變量
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  ;  調用 AdjustPrivilegesToken提權
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 = -1073741727;
    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;
}

 

發佈了46 篇原創文章 · 獲贊 6 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章