禁止系统运行某个进程的简单方法(代码实现)

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家拍砖!

 

 

 

一、解决方法

1、理论

要对一个任意进程(包括系统安全进程和服务进程)进行指定了写相关的访问权的OpenProcess操作,只要当前进程具有SeDeDebug权限就可以了。

要是一个用户是Administrator或是被给予了相应的权限,就可以具有该权限。可是,就算我们用Administrator帐号对一个系统安全进程执行OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessID)还是会遇到“访问拒绝”的错误。

什么原因呢?原来在默认的情况下进程的一些访问权限是没有被启用(Enabled)的,所以我们要做的首先是启用这些权限。与此相关的一些API函数有OpenProcessToken、LookupPrivilegevalue、AdjustTokenPrivileges

GetCurrentProcess得到得到的称之为"伪句柄",只是一个标识,你可以发现,其实就是返回$FFFFFFFF。
每个进程得句柄都是一样的,只是实用于进程内部得使用.如果你想得到实际得句柄,在进程间进行通讯,必需要进行转化,
调用DuplicateHandle,注意,得实句柄使用完成以后,你必须要调用CloseHandle去关闭.
其实,你应该明白了为何"伪句柄"得存在,就是使用简单,不用关闭,不会造成内存泄漏.。

内核对象的句柄会在新进程中,产生一条记录,并且该内核对象计数增加。根据引用计数,这里会引出该函数的一种巧妙用法,文件锁定或者叫文件占坑,

原理如下:向系统进程中,复制打开的文件句柄,内核对象在所有引用未删除时不会销毁

2、代码实现
#include <Windows.h>


//提权函数  
void RaiseToDebugP()  
{  
	HANDLE hToken;  
	HANDLE hProcess = GetCurrentProcess();  
	if ( OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) )  
	{  
		TOKEN_PRIVILEGES tkp;  
		if ( LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid) )  
		{  
			tkp.PrivilegeCount = 1;  
			tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  

			BOOL bREt = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0) ;  
		}  
		CloseHandle(hToken);  
	}      
}  
BOOL OccupyFile( LPCTSTR lpFileName )  
{  
	BOOL    bRet;  
	//提升自身权限  
	RaiseToDebugP();  
	//打开一个pid为4的进程,只要是存在的进程,都可以  
	HANDLE hProcess = OpenProcess( PROCESS_DUP_HANDLE, FALSE, 4);    // 4为system进程号  

	if ( hProcess == NULL )  
	{            
		return FALSE;  
	}  

	HANDLE hFile;  
	HANDLE hTargetHandle;  
	//以独占模式打开目标文件  
	hFile = CreateFile( lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);      


	if ( hFile == INVALID_HANDLE_VALUE )  
	{  
		CloseHandle( hProcess );  
		return FALSE;  
	}  

	//把文件句柄复制到pid=4的进程中去,这样,只要pid=4的进程不退出,谁也动不了目标文件  
	bRet = DuplicateHandle( GetCurrentProcess(), hFile, hProcess, &hTargetHandle,   
		0, FALSE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE);  

	CloseHandle( hProcess );  

	return bRet;  
}  


//入口函数  
int main()  
{  
	OccupyFile("D:\\Program Files\\IDA\\idag.exe");  

	return 0;  
} 


 

二、API函数说明

1、 OpenProcessToken

 使用OpenProcessToken()用于得到指定进程的访问令牌,而第三个参数定义设置不正确可能导致该函数调用失败。
以下举例说明:

HANDLE hProc;
hProc = GetCurrentProcess();

 

// Method1 - Error(998)
HANDLE *hToken;
OpenProcessToken(hProc, TOKEN_ADJUST_PRIVILEGES, hToken);

 

// Method2 - Success
HANDLE hToken;
OpenProcessToken(hProc, TOKEN_ADJUST_PRIVILEGES, &hToken);

 

以上是获取访问令牌的调用,OpenProcessToken()函数原型如下:
 

BOOL OpenProcessToken(   
__in HANDLE ProcessHandle, //要修改访问权限的进程句柄   
__in DWORD DesiredAccess, //指定你要进行的操作类型   
__out PHANDLE TokenHandle //返回的访问令牌指针   
);

 方法1和方法2都使用HANDLE类型定义,方法1定义指针,方法2定义变量。
方法1调用函数返回失败(通过GetLastError()可知错误代码为998——拒绝访问);
方法2调用函数则能成功获取访问令牌。为什么出现这种情况呢?WinNT.h中相关的定义引起,如下:

 

#ifdef STRICT
typedef void *HANDLE;
#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif
typedef HANDLE *PHANDLE;

 由此可见可以把任意一种类型的指针赋值给PVOID型,因此PVOID*赋给PVOID型是可以的,而把PVOID型赋值给PVOID*型也可以。

2、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 


第一个参数就是指权限的类型,是一个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

其原形如下:

BOOL LookupPrivilegevalue(   
	LPCTSTR lpSystemName, // system name   
	LPCTSTR lpName, // privilege name   
	PLUID lpLuid // locally unique identifier   
); 


(1)第一个参数是系统的名称,如果是本地系统只要指明为NULL就可以了;

(2)第二个参数就是指明了权限的名称,如“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") 

(3)第三个参数就是返回LUID的指针;

4、DuplicateHandle

The DuplicateHandle  function creates a duplicate handle. The returned duplicate is in the caller's process space.(从当前进程复制句柄到其他进程空间)

BOOL DuplicateHandle(
HANDLE hSourceProcessHandle, // handle to source process
HANDLE hSourceHandle, // handle to duplicate
HANDLE hTargetProcessHandle, // handle to target process
LPHANDLE lpTargetHandle, // duplicate handle
DWORD dwDesiredAccess, // requested access
BOOL bInheritHandle, // handle inheritance option
DWORD dwOptions // optional actions
);


 

(1)在系统中,对象分两类:

核心对象和用户对象

如进程对象,线程对象,文件映射 对象等就是核心对象;

而向窗口,菜单等都是用户对象.。
(2)两者是有差别的

(2、1)用于标示用户对象的句柄是系统唯一的,也就是说,一个进程 完全可以对另外一个进程中的用户对象进行操作,比如两个进程间通信的方法之一, 就是发送消息.正是由于窗口是用户对象,所以句柄是系统唯一,通过FindWindow, 得到另外一个进程的窗口句柄,然后用SendMessage(),让hWnd的窗口过程来处理消 息,实现了进程间的通信.

因此,对于用户对象,你根本不用DuplicateHandle(),直接 把句柄拿来用就行了;
(2、2)而核心对象则不一样。核心对象是为了加强系统的稳定性,因此,核心对象句柄是 进程相关的,在每一个进程中都有一个核心对象表,每一个对象的索引(不完全是)作为内核对象的句柄,从而实现进程相关.同一个对象在不同的进程中可能有不 同的索引,即句柄.

对核心对象进行操作时,系统还要进行安全检验,看一下你是否有权来操作这个对象.因此你不能同用户对象一样,直接把句柄拿过来用.比方 说,你想操作另一个进程中的文件映射对象,这个文件映射对象句柄在那个进程中假设是0x000001,但在你的进程中,很有可能0x00000001时表 示另一个核心对象,此时的操作就永远不会成功,甚至会产生灾难性的后果.此时,就有必要用DuplicateHandle().

二、效果

展开阅读全文

没有更多推荐了,返回首页