要对一个任意进程(包括系统安全进程和服务进程)进行指定了写相关的访问权的OpenProcess操作,只要当前进程具有SeDeDebug权限就可以了。要是一个用户是Administrator或是被给予了相应的权限,就可以具有该权限。可是,就算我们用Administrator帐号对一个系统安全进程执行OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessID)还是会遇到“访问拒绝”的错误。

什么原因呢?

      原来在默认的情况下进程的一些访问权限是没有被启用(Enabled)的,所以我们要做的首先是启用这些权限,windows提供与此相关的一些API函数有

      OpenProcessToken

      LookupPrivilegevalue

      AdjustTokenPrivileges

    要修改一个进程的访问令牌,

   首先要获得进程访问令牌的句柄,

   这可以通过OpenProcessToken得到,函数的原型如下

MSDN中可以看到

BOOL WINAPI OpenProcessToken(
  __in   HANDLE ProcessHandle,
  __in   DWORD DesiredAccess,
  __out  PHANDLE TokenHandle
);

Parameters

ProcessHandle [in]

A handle to the process whose access token is opened. The process must have the PROCESS_QUERY_INFORMATION access permission.

要访问的进程句柄,这个进程必须要有 PROCESS_QUERY_INFOMATION访问权限

DesiredAccess [in]

Specifies an access mask that specifies the requested types of access to the access token. These requested access types are compared with the discretionary access control list (DACL) of the token to determine which accesses are granted or denied.

For a list of access rights for access tokens, see Access Rights for Access-Token Objects


指定进行的操作类型

TokenHandle [out]

A pointer to a handle that identifies the newly opened access token when the function returns.

返回一个新的访问令牌指针

第一参数是要修改访问权限的进程句柄;

第二个参数指定你要进行的操作类型,如要修改访问令牌的特权,我们要指定第二个参数为TOKEN_ADJUST_PRIVILEGES(其它一些参数可参考Platform SDK)。通过这个函数我们就可以得到当前进程的访问令牌的句柄(指定函数的第一个参数为GetCurrentProcess()就可以了)

第三个参数就是返回的访问令牌指针;

 

 

接着我们可以调用AdjustTokenPrivileges对这个访问令牌进行修改。

BOOL WINAPI AdjustTokenPrivileges(
  __in       HANDLE TokenHandle,
  __in       BOOL DisableAllPrivileges,
  __in_opt   PTOKEN_PRIVILEGES NewState,
  __in       DWORD BufferLength,
  __out_opt  PTOKEN_PRIVILEGES PreviousState,
  __out_opt  PDWORD ReturnLength
);

 

Parameters

TokenHandle [in]

A handle to the access token that contains the privileges to be modified. The handle must have TOKEN_ADJUST_PRIVILEGES access to the token. If the PreviousState parameter is not NULL, the handle must also have TOKEN_QUERY access.

传入由OpenTokenProcess返回的新令牌句柄。

DisableAllPrivileges [in]

Specifies whether the function disables all of the token\’s privileges. If this value is TRUE, the function disables all privileges and ignores the NewState parameter. If it is FALSE, the function modifies privileges based on the information pointed to by the NewState parameter.

禁用或使用AdjustTokenPrivileges方法。

NewState [in, optional]

A pointer to a TOKEN_PRIVILEGES structure that specifies an array of privileges and their attributes. If the DisableAllPrivileges parameter is FALSE, the AdjustTokenPrivileges function enables, disables, or removes these privileges for the token. The following table describes the action taken by the AdjustTokenPrivileges function, based on the privilege attribute.

 NewState是一个TOKEN_PRIVILEGES 结构体 记录着权限和属性。如果参数DisableAllPrivileges为FALSE,方法AdjustTokenPrivileges,可用还是禁用或者用

移除这个进程令牌的权限

If DisableAllPrivileges is TRUE, the function ignores this parameter.

如果DisableAppPrivleges为TRUE 则忽略以下这些参数

Value Meaning
SE_PRIVILEGE_ENABLED

The function enables the privilege.

SE_PRIVILEGE_REMOVED

The privilege is removed from the list of privileges in the token. The other privileges in the list are reordered to remain contiguous.

SE_PRIVILEGE_REMOVED supersedes SE_PRIVILEGE_ENABLED.

Because the privilege has been removed from the token, attempts to reenable the privilege result in the warning ERROR_NOT_ALL_ASSIGNED as if the privilege had never existed.

Attempting to remove a privilege that does not exist in the token results in ERROR_NOT_ALL_ASSIGNED being returned.

Privilege checks for removed privileges result in STATUS_PRIVILEGE_NOT_HELD. Failed privilege check auditing occurs as normal.

The removal of the privilege is irreversible, so the name of the removed privilege is not included in thePreviousState parameter after a call to AdjustTokenPrivileges.

Windows XP with SP1:  The function cannot remove privileges. This value is not supported.
None

The function disables the privilege.

 

 

 

 

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)来打获得任意进程的句柄,并且指定了所有的访问权

 

  一次关机和重启动函数InitiateSystemShutdown: 

 

  BOOL InitiateSystemShutdown(lpszMachineName,lpszMessage,dwTimeout,fForce AppsClosed,fReboot)

 

  参数:lpszMachineName

 

  指定以MULL终止的用来指定要关机的网络名称的字符串。如果lpszMachineName为NULL,

 

  或指向一个空串,则该函数关掉本地计算机。

 

  LpszMessage

 

  指向一个以MULL终止的指定要显示在关机对话框中的消息的字符串。如果不需要消息,

 

  该参数可以为NULL。

 

  DwTimeout

 

  指定对话框应该显示的时间(按秒计)。在此对话框显示期间,可通过调用AbortSyste

 

  mShutdown函数将关机停止。

 

  如果DwTimeout不为零,则InitiateSystemShutdown在指定的计算机上显示的一个对

 

  话框。该对话框显示调用此函数的用户名,显示由lpszMessage参数指定的消息,并提示

 

  用户退出系统。当对话框被创建时发出嘟嘟声,并保持位于系统中其它窗口的上面。此

 

  对话框可以被移动,但不能被关闭。定时器倒记在强制关机之前剩余的时间。如果用户

 

  退出系统,系统立即关闭。否则,当定时器到时间计算机才关机。

 

  如果DwTimeout为零时,计算机关机,但不显示此对话框,并且不能用AbortSystem

 

  Shutdown函数来停止关机。

 

  fForce AppsClosed

 

  指定对变化未做保存的应用程序是否被强制关闭。如果这个参数为TRUE,则这样的

 

  应用程序被关闭。如果这个参数为FALSE,则显示一个对话框以提示用户关闭这些应用程

 

  序。

 

  FReboot

 

  指定计算机关机之后是否立即重启。如果这个参数为TRUE,则计算机重启。如果这

 

  个参数为FALSE,则系统将所有高速缓存刷新到磁盘上,清除屏幕,并且显示一条消息,

 

  指示关掉电源是安全的。

 

  返回值:如果函数成功,则返回值为TRUE;否则,返回值为FALSE。要获得扩展错误信息

 

  ,可使用GetLastError函数。

 

  注释:要关掉本地计算机,调用进程必须具有SE_SHUTDOWN_NAME特权。要关掉一台远程

 

  计算机,调用进程必须对此远程计算机具有的SE_REMOTE_SHUTDOWN_NAME特权。缺省情况

 

  下,用户具有对他们所有登录计算机的SE_SHUTDOWN_NAME特权,管理员具有对远程计算

 

  机的SE_REMOTE_SHUTDOWN_NAME特权。


 

  

function EnabledDebugPrivilege(const Enabled : Boolean) : Boolean;
var
  hTk : THandle; { 打开令牌句柄 }
  rtnTemp : Dword; { 调整权限时返回的值 }
  TokenPri : TOKEN_PRIVILEGES;
const
  SE_DEBUG = \'SeDebugPrivilege\'; { 查询值 }
begin
  Result := False;
  { 获取进程令牌句柄,设置权限 }
  if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,hTk)) then
  begin
    TokenPri.PrivilegeCount := 1;
    { 获取Luid值 }
    LookupPrivilegeValue(nil,SE_DEBUG,TokenPri.Privileges[0].Luid);

    if Enabled then
      TokenPri.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
    else
      TokenPri.Privileges[0].Attributes := 0;

    rtnTemp := 0;
    { 设置新的权限 }
    AdjustTokenPrivileges(hTk,False,TokenPri,sizeof(TokenPri),nil,rtnTemp);

    Result := GetLastError = ERROR_SUCCESS;
    CloseHandle(hTk);

  end;
end;

  


版权声明:本文为pengshaomin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/pengshaomin/archive/2012/02/29/2374709.html