PsSetCreateProcessNotifyRoutineEx 防多开

这个函数的功能是设置一个回调钩子,该钩子会在进程创建和进程销毁时被调用,钩子由用户提供

API格式

NTSTATUS PsSetCreateProcessNotifyRoutineEx(
  PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,
  BOOLEAN                           Remove
);

NotifyRoutine 是IN,传入用户指定的回调钩子
Remove 是IN ,创建回调钩子时传FALSE,创建了肯定需要在某一个时刻销毁,销毁回调钩子时传TRUE

ps:使用该函数前同样需要过掉驱动签名认证,过掉的方式和ObRegisterCallbacks一样

回调钩子的格式

void PcreateProcessNotifyRoutineEx(
  PEPROCESS Process,
  HANDLE ProcessId,
  PPS_CREATE_NOTIFY_INFO CreateInfo
)
{...}

Process 指向进程的EPROCESS的指针
ProcessId 该进程的PID
CreateInfo 是一个结构体

typedef struct _PS_CREATE_NOTIFY_INFO {
  SIZE_T              Size;
  union {
    ULONG Flags;
    struct {
      ULONG FileOpenNameAvailable : 1;
      ULONG IsSubsystemProcess : 1;
      ULONG Reserved : 30;
    };
  };
  HANDLE              ParentProcessId;
  CLIENT_ID           CreatingThreadId;
  struct _FILE_OBJECT *FileObject;
  PCUNICODE_STRING    ImageFileName;
  PCUNICODE_STRING    CommandLine;
  NTSTATUS            CreationStatus;
} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;

Size
该结构的大小(以字节为单位)。
Flags
保留。 请改用FileOpenNameAvailable成员。
FileOpenNameAvailable
一个布尔值,指定ImageFileName成员是否包含用于打开进程可执行文件的确切文件名。

IsSubsystemProcess
指示进程子系统类型的布尔值是Win32以外的子系统。

Reserved
保留供系统使用。

ParentProcessId
新进程的父进程的进程ID。 请注意,父进程不一定与创建新进程的进程相同。 新进程可以继承父进程的某些属性,如句柄或共享内存。 (进程创建者的进程ID由CreatingThreadId-> UniqueProcess给出。)

CreatingThreadId
创建新进程的进程和线程的进程ID和线程ID。 CreatingThreadId-> UniqueProcess包含进程ID,而CreatingThreadId-> UniqueThread包含线程ID。

FileObject
指向进程可执行文件的文件对象的指针。如果IsSubsystemProcess为TRUE,则此值可能为NULL。

ImageFileName
指向保存可执行文件的文件名的UNICODE_STRING字符串的指针。 如果FileOpenNameAvailable成员为TRUE,则该字符串指定用于打开可执行文件的确切文件名。 如果FileOpenNameAvailable为FALSE,则操作系统可能仅提供部分名称。如果IsSubsystemProcess为TRUE,则此值可能为NULL。

CommandLine
指向UNICODE_STRING字符串的指针,该字符串保存用于执行该过程的命令。 如果命令不可用,CommandLine为NULL。如果IsSubsystemProcess为TRUE,则此值可能为NULL。

CreationStatus
用于进程创建操作返回的NTSTATUS值。 驱动程序可以将此值更改为错误代码,以防止创建进程。

反调试
创建保护进程时让一个全局变量加一,保护进程退出时让那个全局变量减一,设置一个范围,当变量超出那个范围时让进程创建失败
代码实现

void PcreateProcessNotifyRoutineEx(
	PEPROCESS Process,
	HANDLE ProcessId,
	PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
	static int OpenCount = 0;
	PUCHAR ExeName =  PsGetProcessImageFileName(Process);
	//DbgPrint("ExeName=%s\n", ExeName);
	if (strcmp((PCCHAR)ExeName, "xxx.exe") == 0)
	{
		//目标进程创建时,且创建时已有此进程被创建
		if (OpenCount >= 1 && CreateInfo)
		{
			OpenCount++;
			//STATUS_UNSUCCESSFUL会在用户层弹出进程创建失败的提示框,而下面的参数不会
			CreateInfo->CreationStatus = STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY;
		}
		//目标进程创建时,且创建时没有此进程被创建
		else if (OpenCount == 0 && CreateInfo)
		{
			OpenCount++;
		}
		//目标进程退出时
		else if (NULL==CreateInfo)
		{
			OpenCount--;
		}
		DbgPrint("ExeName=%s,OpenCount=%d\n", ExeName,OpenCount);
	}
}

反反调试
系统有一个全局数组维护着所有注册的进程回调钩子,根据特征码搜索定位到该全局数组变量,遍历该数组,根据回调钩子的地址定位到所属模块,根据模块的名称判断是否是目标模块,然后卸载该模块下的所有进程回调钩子
代码实现

//==================================================================
//函数名: DeleteDriverProcessNotifyRoutine
//功能:删除指定驱动注册的进程回调钩子
//输入参数1:NotifyRoutineArray,进程回调钩子全局数组的基地址
//输入参数2:dllInfo,本驱动的pDriver->DriverSection
//输入参数3:DriverName,要删除的进程回调钩子所属的对象
//返回值:BOOLEAN,删除成功返回TRUE,删除失败返回FALSE
//==================================================================
BOOLEAN DeleteDriverProcessNotifyRoutine(PUINT64  NotifyRoutineArray, PLDR_DATA dllInfo, LPCWSTR DriverName)
{
	PEX_CALLBACK_ROUTINE_BLOCK pCallbackRoutineBlock;
	PEX_CALLBACK_FUNCTION pCallbackFunc;
	UNICODE_STRING uniDriverName;
	while (0 != *NotifyRoutineArray)
	{
		pCallbackRoutineBlock = (PEX_CALLBACK_ROUTINE_BLOCK)(*NotifyRoutineArray & (~0x000000000000000F));
		//DbgPrint("pCallbackRoutineBlock=%p\n", pCallbackRoutineBlock);
		pCallbackFunc = pCallbackRoutineBlock->Function;
		//查找API所在的内核模块
		PLDR_DATA TargetDllInfo = GetModuleByObCallbackAddr((PVOID)pCallbackFunc, dllInfo);
		//DbgPrint("pCallbackFunc=%p,DriverName=%wZ\n", pCallbackFunc, TargetDllInfo->name);
		//do something else ...
		RtlInitUnicodeString(&uniDriverName, DriverName);
		if (RtlCompareUnicodeString(&uniDriverName, &TargetDllInfo->name, TRUE) == 0)
		{
			DbgPrint("pCallbackFunc=%p,DriverName=%wZ\n", pCallbackFunc, TargetDllInfo->name);
			PsSetCreateProcessNotifyRoutineEx(pCallbackFunc, TRUE);
			return TRUE;
		}
		NotifyRoutineArray++;
	}
	return FALSE;
}
//==================================================================
//函数名: GetAddrByOffset
//功能:根据偏移获得地址
//输入参数1:Offset,偏移
//输入参数2:CodeStart,这条指令的开始地址
//输入参数3:CodeLength,这条指令的长度
//返回值:UINT64,地址
//==================================================================
UINT64 GetAddrByOffset(INT32 Offset,UINT64 CodeStart, UINT64 CodeLength)
{
	return (UINT64)((INT64)CodeLength + (INT64)CodeStart + Offset);
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值