跳至主要內容

进程监控


本篇教程主要介绍iMonitorSDK的进程监控相关功能。

进程监控全部消息

消息类型说明
emMSGProcessCreate进程创建事件,返回拦截可以阻止进程的创建
emMSGProcessExit进程退出事件
emMSGProcessOpen打开进程事件,可以拦截打开用于保护进程
emMSGThreadCreate线程创建事件
emMSGThreadExit线程退出事件
emMSGThreadOpen打开线程事件
emMSGImageLoad加载模块事件,返回拦截可以阻止某个模块、某个驱动的加载
emMSGProcessStart(模拟事件)进程启动事件
emMSGThreadStart(模拟事件)线程启动事件

emMSGProcessCreate

字段

struct ProcessCreate {
	Path                 : Path;	// 进程路径
	ProcessId            : ULONG;	// 进程PID
	Commandline          : String;	// 进程命令行
	CreateTime           : Time;	// 创建时间
	ParentPath           : Path;	// 父进程的路径(注意:父进程有可能不是当前操作进程)
	ParentProcessId      : ULONG;	// 父进程PID
	ParentCommandline    : String;	// 父进程命令行
	ParentCreateTime     : Time;	// 父进程创建时间
}

使用场景

进程创建事件是非常重要的一个事件,很多场景都会使用到。比如杀毒软件要拦截病毒进程启动、企业管理员禁止员工玩游戏、云游戏只允许信任的进程运行、EDR追踪进程创建信息等等。

class MonitorCallback : public IMonitorCallback
{
public:
	void OnCallback(IMonitorMessage* Message) override
	{
		if (Message->GetType() != emMSGProcessCreate)
			return;

		cxMSGProcessCreate* msg = (cxMSGProcessCreate*)Message;

		//
		// 禁止进程名 cmd.exe 的进程启动
		//

		if (msg->IsMatchPath(L"*\\cmd.exe")) {
			msg->SetBlock();
        }
	}
};

大部分场景,都是通过判断进程路径来做拦截,可以直接使用IsMatchPath来快速匹配。

如果需要获取进程相关的其他信息、比如进程签名、进程文件属性等,可以通过FindProcess查询到进程对应的信息来操作。

CComPtr<IMonitorProcess> process;

if (manager.FindProcess(msg->ProcessId(), &process)) {
	process->GetCompanyName();
}
interface IMonitorMessageProcess
{
	enum emProcessType {
		emProcessUnknown,
		emProcessServices,
		emProcessCsrss,
		emProcessSvchost,
		emProcessExplorer,
	};

	virtual ULONG			GetProcessId		(void) = 0;
	virtual ULONG			GetParentProcessId	(void) = 0;
	virtual	ULONG			GetCreatorProcessId	(void) = 0;
	virtual LPCWSTR			GetProcessName		(void) = 0;
	virtual LPCWSTR			GetProcessPath		(void) = 0;
	virtual LPCWSTR			GetCommandline		(void) = 0;
	virtual LPCWSTR			GetCompanyName		(void) = 0;
	virtual LPCWSTR			GetProductName		(void) = 0;
	virtual LPCWSTR			GetFileDescription	(void) = 0;
	virtual LPCWSTR			GetSigner			(void) = 0;
	virtual bool			IsSignerVerified	(void) = 0;
	virtual LPCWSTR			GetCatalogSigner	(void) = 0;
	virtual bool			IsCatalogSignerVerified(void) = 0;
	virtual ULONGLONG		GetCreateTime		(void) = 0;
	virtual emProcessType	GetProcessType		(void) = 0;
	virtual bool			GetMD5				(UCHAR Hash[16]) = 0;
	virtual LPCWSTR			GetMD5String		(void) = 0;
};
注意字段说明
GetSigner进程内嵌签名(不支持Catalog、没有验证签名的有效性),一般用于黑名单模式
IsSignerVerified进程内嵌签名是否有效,GetSigner只查询签名,如果还需要校验有效性需要配合这个使用
GetCatalogSigner对于Windows等文件,签名信息可能不是直接内嵌的,这时候可以通过这个接口获取签名,这个性能会比较差
IsCatalogSignerVerified判断GetCatalogSigner返回的签名是否有效,这个性能会比较差,而且可能存在联网查询,除非白名单模式,否则不建议使用
GetMD5、GetMD5String查询签名、查询MD5都是异步的,超时5秒,如果对应大文件或者存在其他安全软件拦截的情况,查询可能或返回失败。

emMSGProcessExit

字段

struct ProcessExit {
	Path                 : Path;
	ProcessId            : ULONG;
}

使用场景

如果使用者维持了所有活动进程的状态,那么可以根据进程退出事件来清理退出的进程。

emMSGProcessOpen

字段

struct ProcessOpen {
	Path                 : Path;
	ProcessId            : ULONG;
	ParentProcessId      : ULONG;
	DesiredAccess        : ProcessAccess;	// 打开的权限:这里可以判断是否存在结束、操作内存的权限
	Duplicate            : Bool;			// 是否通过DuplicateHandle的方式打开
}

使用场景

通过拦截进程打开,可以用于保护进程不被结束、防止进程被远程线程注入、防止内存被修改、禁止调试器调试进程等。

进程打开除了直接拦截以外,还可以返回修改后的权限。

	void OnCallback(IMonitorMessage* Message) override
	{
		if (Message->GetType() != emMSGProcessOpen)
			return;

		Message->SetGrantedAccess(PROCESS_QUERY_INFORMATION);
	}

emMSGThreadCreate

字段

struct ThreadCreate {
	Path                 : Path;
	ProcessId            : ULONG;
	ThreadId             : ULONG;
	StartAddress         : ULONGLONG;	// 线程的行入口
	RemoteThread         : Bool;		// 是否远程线程(表示创建者并不是当前进程)
}

使用场景

多用于判断是否远程线程注入。

emMSGThreadOpen

参考 emMSGProcessOpen,行为和使用场景比较类似

emMSGImageLoad

字段

struct ImageLoad {
	Path                 : Path;
	ProcessId            : ULONG;
	ImageBase            : ULONGLONG;
	ImageSize            : ULONGLONG;
	IsKernelImage        : Bool;		// 表示是否驱动(也可以通过进程来判断)
}

使用场景

模块加载事件,可以用于病毒分析、EDR检测。模块的加载支持返回SetBlock拦截,可以用于拦截动态库的加载、也可以拦截驱动(IsKernelImage = TRUE)的加载。

除了拦截,还可以通过SetInjectDll来注入动态库。为什么选择在ImageLoad的时候注入动态库,是因为注入动态库需要调用ntdll的某些函数,所以需要监控ntdll 模块加载完成后,才开始注入。(对于wow64的还需要监控wow64的ntdll,或者直接根据kernel32来判断。如果使用内置的自动注入功能,就不需要自己来判断这些逻辑)

void OnCallback(IMonitorMessage* Message) override
{
	if (Message->GetType() != emMSGImageLoad)
		return;

	cxMSGImageLoad* msg = (cxMSGImageLoad*)Message;

	if (!msg->IsMatchCurrentProcessName(L"notepad.exe"))
		return;

	if (msg->IsMatchPath(L"*\\kernel32.dll")) {
		msg->SetInjectDll(L"D:\\test.dll");
	}
}