1. 程式人生 > >修改一個程序的許可權

修改一個程序的許可權

      要對一個任意程序(包括系統安全程序和服務程序)進行指定了寫相關的訪問權的OpenProcess操作,只要當前程序具有SeDeDebug許可權就可以了。要是一個使用者是Administrator或是被給予了相應的許可權,就可以具有該許可權。可是,就算我們用Administrator帳號對一個系統安全程序執行OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessID)還是會遇到“訪問拒絕”的錯誤。什麼原因呢?原來在預設的情況下程序的一些訪問許可權是沒有被使能(Enabled)的,所以我們要做的首先是使能這些許可權。與此相關的一些API函式有OpenProcessToken、LookupPrivilegevalue、AdjustTokenPrivileges。我們要修改一個程序的訪問令牌,首先要獲得程序訪問令牌的控制代碼,這可以通過OpenProcessToken得到,函式的原型如下: 
  BOOL OpenProcessToken(
  __in HANDLE ProcessHandle, //要修改訪問許可權的程序控制代碼
  __in DWORD DesiredAccess, //指定你要進行的操作型別
  __out PHANDLE TokenHandle //返回的訪問令牌指標
  );
  第一引數是要修改訪問許可權的程序控制代碼;第三個引數就是返回的訪問令牌指標;第二個引數指定你要進行的操作型別,如要修改訪問令牌的特權,我們要指定第二個引數為TOKEN_ADJUST_PRIVILEGES(其它一些引數可參考Platform SDK)。通過這個函式我們就可以得到當前程序的訪問令牌的控制代碼(指定函式的第一個引數為GetCurrentProcess()就可以了)。接著我們可以呼叫AdjustTokenPrivileges對這個訪問令牌進行修改。AdjustTokenPrivileges的原型如下:
  BOOL AdjustTokenPrivileges(
  HANDLE TokenHandle, // handle to token
  BOOL DisableAllPrivileges, // disabling option
  PTOKEN_PRIVILEGES NewState, // privilege information
  DWORD BufferLength, // size of buffer
  PTOKEN_PRIVILEGES PreviousState, // original state buffer
  PDWORD ReturnLength // required buffer size
  );
  第一個引數是訪問令牌的控制代碼;第二個引數決定是進行許可權修改還是除能(Disable)所有許可權;第三個引數指明要修改的許可權,是一個指向TOKEN_PRIVILEGES結構的指標,該結構包含一個數組,資料組的每個項指明瞭許可權的型別和要進行的操作; 第四個引數是結構PreviousState的長度,如果PreviousState為空,該引數應為NULL;第五個引數也是一個指向TOKEN_PRIVILEGES結構的指標,存放修改前的訪問許可權的資訊,可空;最後一個引數為實際PreviousState結構返回的大小。在使用這個函式前再看一下TOKEN_PRIVILEGES這個結構,其宣告如下:
  typedef struct _TOKEN_PRIVILEGES {
  DWORD PrivilegeCount;
  LUID_AND_ATTRIBUTES Privileges[];
  } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
  PrivilegeCount指的陣列元素的個數,接著是一個LUID_AND_ATTRIBUTES型別的陣列,再來看一下LUID_AND_ATTRIBUTES這個結構的內容,宣告如下:
  typedef struct _LUID_AND_ATTRIBUTES {
  LUID Luid;
  DWORD Attributes;
  } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES
  第二個引數就指明瞭我們要進行的操作型別,有三個可選項: SE_PRIVILEGE_ENABLED、SE_PRIVILEGE_ENABLED_BY_DEFAULT、SE_PRIVILEGE_USED_FOR_ACCESS。要使能一個許可權就指定Attributes為SE_PRIVILEGE_ENABLED。第一個引數就是指許可權的型別,是一個LUID的值,LUID就是指locally unique identifier,我想GUID大家是比較熟悉的,和GUID的要求保證全域性唯一不同,LUID只要保證區域性唯一,就是指在系統的每一次執行期間保證是唯一的就可以了。另外和GUID相同的一點,LUID也是一個64位的值,相信大家都看過GUID那一大串的值,我們要怎麼樣才能知道一個許可權對應的LUID值是多少呢?這就要用到另外一個API函式LookupPrivilegevalue,其原形如下:
  BOOL LookupPrivilegevalue(
  LPCTSTR lpSystemName, // system name
  LPCTSTR lpName, // privilege name
  PLUID lpLuid // locally unique identifier
  );
  第一個引數是系統的名稱,如果是本地系統只要指明為NULL就可以了,第三個引數就是返回LUID的指標,第二個引數就是指明瞭許可權的名稱,如“SeDebugPrivilege”。在Winnt.h中還定義了一些許可權名稱的巨集,如:
  #define SE_BACKUP_NAME TEXT("SeBackupPrivilege")
  #define SE_RESTORE_NAME TEXT("SeRestorePrivilege")
  #define SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege")
  #define SE_DEBUG_NAME TEXT("SeDebugPrivilege")
  這樣通過這三個函式的呼叫,我們就可以用OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessID)來打獲得任意程序的控制代碼,並且指定了所有的訪問權
  根據上面的內容,可獲得如下的應用:
  BOOL fResult;
  TOKEN_PRIVILEGES tkp;
  HANDLE hToken;
  if (!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hToken))
  {
  MessageBox("OpenProcessToken failed!"); //獲得程序控制代碼失敗
  }
  LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid); //獲得本地機唯一的標識
  tkp.PrivilegeCount = 1;
  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES) NULL, 0); //調整獲得的許可權
  if (GetLastError() != ERROR_SUCCESS)
  {
  MessageBox("AdjustTokenPrivileges enable failed!"); //修改許可權失敗
  }
  fResult =InitiateSystemShutdown(
  NULL, // 要關的計算機使用者名稱
  "由於系統不穩定,WINDOWS將在上面的時間內關機,請做好儲存工作!", // 顯示訊息
  0, // 關機所需的時間
  FALSE, // 是否提示使用者
  FALSE); //設為TRUE為重起,設為FALSE為關機