1. 程式人生 > >C#(禁止系統執行某個程序的簡單方法)

C#(禁止系統執行某個程序的簡單方法)

一、解決方法

1、理論

要對一個任意程序(包括系統安全程序和服務程序)進行指定了寫相關的訪問權的OpenProcess操作,只要當前程序具有SeDeDebug許可權就可以了。

要是一個使用者是Administrator或是被給予了相應的許可權,就可以具有該許可權。可是,就算我們用Administrator帳號對一個系統安全程序執行OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessID)還是會遇到“訪問拒絕”的錯誤。

什麼原因呢?原來在預設的情況下程序的一些訪問許可權是沒有被啟用(Enabled)的,所以我們要做的首先是啟用這些許可權。與此相關的一些API函式有OpenProcessToken、LookupPrivilegevalue、AdjustTokenPrivileges

GetCurrentProcess得到得到的稱之為"偽控制代碼",只是一個標識,你可以發現,其實就是返回$FFFFFFFF。
每個程序得控制代碼都是一樣的,只是實用於程序內部得使用.如果你想得到實際得控制代碼,在程序間進行通訊,必需要進行轉化,
呼叫DuplicateHandle,注意,得實控制代碼使用完成以後,你必須要呼叫CloseHandle去關閉.
其實,你應該明白了為何"偽控制代碼"得存在,就是使用簡單,不用關閉,不會造成記憶體洩漏.。

核心物件的控制代碼會在新程序中,產生一條記錄,並且該核心物件計數增加。根據引用計數,這裡會引出該函式的一種巧妙用法,檔案鎖定或者叫檔案佔坑,

原理如下:向系統程序中,複製開啟的檔案控制代碼,核心物件在所有引用未刪除時不會銷燬

2、程式碼實現
  1. #include <Windows.h>
  2. //提權函式  
  3. void RaiseToDebugP()    
  4. {    
  5.     HANDLE hToken;    
  6.     HANDLE hProcess = GetCurrentProcess();    
  7.     if ( OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) )    
  8.     {    
  9.         TOKEN_PRIVILEGES tkp;    
  10.         if ( LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid) )    
  11.         {    
  12.             tkp.PrivilegeCount = 1;    
  13.             tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;    
  14.             BOOL bREt = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0) ;    
  15.         }    
  16.         CloseHandle(hToken);    
  17.     }        
  18. }    
  19. BOOL OccupyFile( LPCTSTR lpFileName )    
  20. {    
  21.     BOOL    bRet;    
  22.     //提升自身許可權  
  23.     RaiseToDebugP();    
  24.     //開啟一個pid為4的程序,只要是存在的程序,都可以  
  25.     HANDLE hProcess = OpenProcess( PROCESS_DUP_HANDLE, FALSE, 4);    // 4為system程序號  
  26.     if ( hProcess == NULL )    
  27.     {              
  28.         return FALSE;    
  29.     }    
  30.     HANDLE hFile;    
  31.     HANDLE hTargetHandle;    
  32.     //以獨佔模式開啟目標檔案  
  33.     hFile = CreateFile( lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);        
  34.     if ( hFile == INVALID_HANDLE_VALUE )    
  35.     {    
  36.         CloseHandle( hProcess );    
  37.         return FALSE;    
  38.     }    
  39.     //把檔案控制代碼複製到pid=4的程序中去,這樣,只要pid=4的程序不退出,誰也動不了目標檔案  
  40.     bRet = DuplicateHandle( GetCurrentProcess(), hFile, hProcess, &hTargetHandle,     
  41.         0, FALSE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE);    
  42.     CloseHandle( hProcess );    
  43.     return bRet;    
  44. }    
  45. //入口函式  
  46. int main()    
  47. {    
  48.     OccupyFile("D:\\Program Files\\IDA\\idag.exe");    
  49.     return 0;    
  50. }   

二、API函式說明

1、 OpenProcessToken

 使用OpenProcessToken()用於得到指定程序的訪問令牌,而第三個引數定義設定不正確可能導致該函式呼叫失敗。
以下舉例說明:

  1. HANDLE hProc;  
  2. hProc = GetCurrentProcess();  
  3. // Method1 - Error(998)
  4. HANDLE *hToken;  
  5. OpenProcessToken(hProc, TOKEN_ADJUST_PRIVILEGES, hToken);  
  6. // Method2 - Success
  7. HANDLE hToken;  
  8. OpenProcessToken(hProc, TOKEN_ADJUST_PRIVILEGES, &hToken);  

以上是獲取訪問令牌的呼叫,OpenProcessToken()函式原型如下:
 

  1. BOOL OpenProcessToken(     
  2. __in HANDLE ProcessHandle, //要修改訪問許可權的程序控制代碼   
  3. __in DWORD DesiredAccess, //指定你要進行的操作型別   
  4. __out PHANDLE TokenHandle //返回的訪問令牌指標   
  5. );  

 方法1和方法2都使用HANDLE型別定義,方法1定義指標,方法2定義變數。
方法1呼叫函式返回失敗(通過GetLastError()可知錯誤程式碼為998——拒絕訪問);
方法2呼叫函式則能成功獲取訪問令牌。為什麼出現這種情況呢?WinNT.h中相關的定義引起,如下:

  1. #ifdef STRICT
  2. typedefvoid *HANDLE;  
  3. #define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
  4. #else
  5. typedefPVOIDHANDLE;  
  6. #define DECLARE_HANDLE(name) typedef HANDLE name
  7. #endif
  8. typedefHANDLE *PHANDLE;  

 由此可見可以把任意一種型別的指標賦值給PVOID型,因此PVOID*賦給PVOID型是可以的,而把PVOID型賦值給PVOID*型也可以。

2、AdjustTokenPrivileges

AdjustTokenPrivileges的原型如下:

  1. BOOL AdjustTokenPrivileges  
  2. (     
  3. HANDLE TokenHandle, // handle to token   
  4. BOOL DisableAllPrivileges, // disabling option   
  5. PTOKEN_PRIVILEGES NewState, // privilege information   
  6. DWORD BufferLength, // size of buffer   
  7. PTOKEN_PRIVILEGES PreviousState, // original state buffer   
  8. PDWORD ReturnLength // required buffer size   
  9. );   


第一個引數是訪問令牌的控制代碼;

第二個引數決定是進行許可權修改還是喪失(Disable)所有許可權

第三個引數指明要修改的許可權,是一個指向TOKEN_PRIVILEGES結構的指標,該結構包含一個數組,資料組的每個項指明瞭許可權的型別和要進行的操作;

第四個引數是結構PreviousState的長度,如果PreviousState為空,該引數應為NULL;

第五個引數也是一個指向TOKEN_PRIVILEGES結構的指標,存放修改前的訪問許可權的資訊,可空;

最後一個引數為實際PreviousState結構返回的大小。

在使用這個函式前再看一下TOKEN_PRIVILEGES這個結構,其宣告如下:

  1. typedefstruct _TOKEN_PRIVILEGES   
  2. {     
  3. DWORD PrivilegeCount;     
  4. LUID_AND_ATTRIBUTES Privileges[];     
  5. } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;   


PrivilegeCount指的陣列元素的個數,接著是一個LUID_AND_ATTRIBUTES型別的陣列,再來看一下LUID_AND_ATTRIBUTES這個結構的內容,宣告如下:

  1. typedefstruct _LUID_AND_ATTRIBUTES  
  2. {     
  3.     LUID Luid;     
  4.     DWORD Attributes;     
  5. } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES   


第一個引數就是指許可權的型別,是一個LUID的值,LUID就是指locally unique identifier,我想GUID大家是比較熟悉的,和GUID的要求保證全域性唯一不同,LUID只要保證區域性唯一,就是指在系統的每一次執行期間保證是唯一的就可以了。另外和GUID相同的一點,LUID也是一個64位的值,相信大家都看過GUID那一大串的值,我們要怎麼樣才能知道一個許可權對應的LUID值是多少呢?

第二個引數就指明瞭我們要進行的操作型別,有三個可選項: SE_PRIVILEGE_ENABLED、SE_PRIVILEGE_ENABLED_BY_DEFAULT、SE_PRIVILEGE_USED_FOR_ACCESS。要使能一個許可權就指定Attributes為SE_PRIVILEGE_ENABLED。

3、LookupPrivilegevalue

其原形如下:

  1. BOOL LookupPrivilegevalue(     
  2.     LPCTSTR lpSystemName, // system name   
  3.     LPCTSTR lpName, // privilege name   
  4.     PLUID lpLuid // locally unique identifier   
  5. );   


(1)第一個引數是系統的名稱,如果是本地系統只要指明為NULL就可以了;

(2)第二個引數就是指明瞭許可權的名稱,如“SeDebugPrivilege”。

在Winnt.h中還定義了一些許可權名稱的巨集,如:

  1. #define SE_BACKUP_NAME TEXT("SeBackupPrivilege")   
  2. #define SE_RESTORE_NAME TEXT("SeRestorePrivilege")   
  3. #define SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege")   
  4. #define SE_DEBUG_NAME TEXT("SeDebugPrivilege") 

(3)第三個引數就是返回LUID的指標;

4、DuplicateHandle

The DuplicateHandle  function creates a duplicate handle. The returned duplicate is in the caller's process space.(從當前程序複製控制代碼到其他程序空間)

  1. BOOL DuplicateHandle(  
  2. HANDLE hSourceProcessHandle, // handle to source process
  3. HANDLE hSourceHandle, // handle