x64内核hook

x64中系统提供了api帮助我们进行hook,主要是如下三个函数

PsSetCreateProcessNotifyRoutineEx,这个函数的作用就是 当进程创建的时候会通知你.

PsSetCreateThreadNotifyRoutine,这个函数的作用是 当线程创建的时候会通知你。

PsSetLoadImageNotifyRoutine,这个函数的作用是 当模块加载的时候会通知你。

 

回调函数原型
VOID
  CreateProcessNotifyEx(
    __inout PEPROCESS  Process,          //进程的EPROCESS会提供给你.
    __in HANDLE  ProcessId,             //进程ID会提供给你.
    __in_opt PPS_CREATE_NOTIFY_INFO  CreateInfo  //进程信息的额外信息会提供给你. 注意参数是可操作的.也就是说可能为NULL
    );

NTSTATUS
  PsSetCreateThreadNotifyRoutine(
    IN PCREATE_THREAD_NOTIFY_ROUTINE  NotifyRoutine  //回调函数地址.当线程创建完毕,但还没运行的时候会调用你的回调.
    );

NTSTATUS
  PsSetLoadImageNotifyRoutine(
    IN PLOAD_IMAGE_NOTIFY_ROUTINE  NotifyRoutine
    );

进程和线程回调代码如下:

#include <ntddk.h>

NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);

/*typedef struct _PS_CREATE_NOTIFY_INFO {
SIZE_T              Size;
union {
ULONG  Flags;
struct {
ULONG FileOpenNameAvailable  :1;
ULONG Reserved  :31;
};
};
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;*/

/*VOID MyCreateProcessNotifyEx	//仅监视
(
__inout   PEPROCESS Process,
__in      HANDLE ProcessId,
__in_opt  PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
if(CreateInfo==NULL)
DbgPrint("[monitor_create_process_x64] Process exit: %s",PsGetProcessImageFileName(Process));
else
DbgPrint("[monitor_create_process_x64] Process create: %wZ",CreateInfo->CommandLine);
}*/

PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	PEPROCESS ProcessObj = NULL;
	PCHAR string = NULL;
	st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);
	if (NT_SUCCESS(st))
	{
		string = PsGetProcessImageFileName(ProcessObj);
		ObfDereferenceObject(ProcessObj);
	}
	return string;
}

VOID MyCreateProcessNotifyEx
(
__inout   PEPROCESS Process,
__in      HANDLE ProcessId,
__in_opt  PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
	char procName[16] = { 0 };
	if (CreateInfo != NULL)	//进程创建事件
	{
		DbgPrint("[monitor_create_process_x64][%ld]%s创建进程: %wZ",
			CreateInfo->ParentProcessId,
			GetProcessNameByProcessId(CreateInfo->ParentProcessId),
			CreateInfo->ImageFileName);
		strcpy(procName, PsGetProcessImageFileName(Process));
		DbgPrint("创建的进程名为:%p/n", procName);
		if (!_stricmp(procName, "calc.exe"))
		{
			DbgPrint("禁止创建计算器进程!");
			CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;	//禁止创建进程
		}
	}
	else
	{
		DbgPrint("[monitor_create_process_x64]进程退出: %s", PsGetProcessImageFileName(Process));
	}
}

VOID MyCreateThreadNotify
(
IN HANDLE  ProcessId,
IN HANDLE  ThreadId,
IN BOOLEAN  Create
)
{
	if (Create)
		DbgPrint("[monitor_create_process_x64]线程创建! PID=%ld;TID=%ld", ProcessId, ThreadId);
	else
		DbgPrint("[monitor_create_process_x64]线程退出! PID=%ld;TID=%ld", ProcessId, ThreadId);

	PEPROCESS Process = NULL;
	PETHREAD  Thread = NULL;
	UCHAR *pszImageName = NULL;
	NTSTATUS status;
	UCHAR *pWin32Address = NULL;

	status = PsLookupProcessByProcessId(ProcessId, &Process);//1.通过进程ID,获取EPROCESS
	if (!NT_SUCCESS(status))
		return;

	status = PsLookupThreadByThreadId(ThreadId, &Thread);//2.通过线程ID,获取ETHREAD

	pszImageName = PsGetProcessImageFileName(Process);//3.通过EPROCESS获取进程名


	if (Create)
	{
		//dprintf("[Hello] Create Thread pid=%d tid=%d ImageName=%s\r\n", ProcessId, ThreadId, pszImageName);
		if (strstr(pszImageName, "calc") != NULL)  //4.判断进程名是否是计算器
		{
			//KdBreakPoint();

			//修改回调函数代码
			pWin32Address = *(UCHAR**)((UCHAR*)Thread + 0x410);  //5.是的话.找到回调函数地址.并改为C3
			//KeAttachProcess();
			if (MmIsAddressValid(pWin32Address))
			{
				KdBreakPoint();
				//*pWin32Address = 0xC3;       //修改为C3,但是注意,你修改的是否内存保护属性需要去掉.但是在64位下,不允许使用内联汇编了.不过你可以写二进制进行更改.或者汇编生成obj,驱动来使用
				//这里直接手动更改为C3.
			}
			//KeUnstackDetachProcess();
		}
	}
	else
		//dprintf("[Hello] Exit Thread pid=%d tid=%d\r\n", ProcessId, ThreadId);


	if (Process)
		ObDereferenceObject(Process);     //引用计数--
	if (Thread)
		ObDereferenceObject(Thread);
}

VOID Monitor()
{
	// set create process/thread notify
	NTSTATUS st = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE);
	if (!NT_SUCCESS(st))
	{
		DbgPrint("PsSetCreateProcessNotifyRoutineEx return false");
	}
	
	st = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify);
	if (!NT_SUCCESS(st))
	{
		DbgPrint("PsSetCreateThreadNotifyRoutine return false");
	}
}

// 卸载驱动 一定要移除回调,否则蓝屏
VOID RemoveMonitor()
{
	//remove create process/thread notify
	PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE);
	PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify);
}

 模块加载回调函数如下

#include <ntddk.h>
#include <ntimage.h>

VOID UnicodeToChar(PUNICODE_STRING dst, char *src)
{
	ANSI_STRING string;
	RtlUnicodeStringToAnsiString(&string, dst, TRUE);
	strcpy(src, string.Buffer);
	RtlFreeAnsiString(&string);
}

PVOID GetDriverEntryByImageBase(PVOID pImageBase)
{
	PIMAGE_DOS_HEADER pDOSHeader;
	PIMAGE_NT_HEADERS64 pNTHeader;
	PVOID pEntryPoint;
	pDOSHeader = (PIMAGE_DOS_HEADER)pImageBase;
	pNTHeader = (PIMAGE_NT_HEADERS64)((ULONG64)pImageBase + pDOSHeader->e_lfanew);
	return (PVOID)((ULONG64)pImageBase + pNTHeader->OptionalHeader.AddressOfEntryPoint);
}

BOOLEAN VxkCopyMemory(PVOID pDestination, PVOID pSourceAddress, SIZE_T SizeOfCopy)
{
	PMDL pMdl = NULL;
	PVOID pSafeAddress = NULL;
	pMdl = IoAllocateMdl(pSourceAddress, (ULONG)SizeOfCopy, FALSE, FALSE, NULL);
	if (!pMdl) return FALSE;
	__try
	{
		MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		IoFreeMdl(pMdl);
		return FALSE;
	}
	pSafeAddress = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority);
	if (!pSafeAddress) return FALSE;
	RtlCopyMemory(pDestination, pSafeAddress, SizeOfCopy);
	MmUnlockPages(pMdl);
	IoFreeMdl(pMdl);
	return TRUE;
}

VOID DenyLoadDriver(PVOID DriverEntry)
{
	// “拒绝访问” 的机器码
	// mov eax, c0000022h
	// ret 
	// 机器码位 \xB8\x22\x00\x00\xC0\xC3

	UCHAR fuck[] = "\xB8\x22\x00\x00\xC0\xC3";
	VxkCopyMemory(DriverEntry, fuck, sizeof(fuck));
}

VOID LoadImageNotifyRoutine(
	_In_ PUNICODE_STRING FullImageName,
	_In_ HANDLE ProcessId,                // pid into which image is being mapped
	_In_ PIMAGE_INFO ImageInfo
	)
{
	PVOID pDrvEntry;
	char szFullImageName[260] = { 0 };
	if (FullImageName != NULL && MmIsAddressValid(FullImageName))
	{
		//KdPrint(("processid = %lld", ProcessId));
		// 根据回调函数 LoadImageNotifyRoutine 的第二个参数判断,如果 PID 是0,则表示加载驱动,如果 PID 位非零,则表示加载 DLL。

		if (0 == ProcessId)
		{ 
			pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase);
			UnicodeToChar(FullImageName, szFullImageName);
			KdPrint(("当前加载的模块是:%wZ", FullImageName));
			// strlwr函数的功能是将字符串中的S参数转换为小写形式。
			if (strstr(szFullImageName, "Win7_x64_SSDT_Hook.sys"))
			{
				DenyLoadDriver(pDrvEntry);
			}
		}
	}
}

VOID MonitorLoadModule()
{
	NTSTATUS nt = PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine);
	if (!NT_SUCCESS(nt))
	{
		KdPrint(("call PsSetLoadImageNotifyRoutine failed"));
	}
}

VOID RemoveMonitorLoadModule()
{
	PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine);
}

 

 

wow64_hook 源码历史更新 --------------------------------------------------------------- 2021/4/16  模块源码1.8.7 更新: 1:重新架构了穿插汇编指令,优化了一些代码和流程 2:在 远程hook64指令_安装()时新增可回调的3个自定义参数值,这些值在回调接口的[寄存器64.自定义值1;2;3]里可获取到该值 3:修复 寻找无效8字节指令地址()中一个重要BUG,此BUG极大可能导致之前版本在 远程hook64指令_安装()时即导致目标程序崩溃的现象 本次更新比较重要 建议使用者更新到此版本使用............ --------------------------------------------------------------- 2021/4/15  模块源码1.8.6 更新: 1:新增3组函数:X64_取模块代码区起始地址(),X64_取模块入口地址(),X64_取模块代码执行段大小() 2:自定义类型:模块信息64,的成员构成新增改动为 以下,在枚举模块中亦可直接取得 成员 模块基址, 长整数型, , , 模块映像的内存地址 也称为句柄 成员 模块长度, 整数型, , , 整个模块文件长度 成员 模块入口, 长整数型, , , 模块入口函数地址 如 Mian/DllMain 成员 模块代码入口, 长整数型, , , 模块代码执行区起始地址 成员 模块代码长度, 整数型, , , 模块代码执行区的长度 成员 模块名称, 文本型, , , 文件名称 成员 模块路径, 文本型, , , 完整的路径地址 3:新消息接口()远程返回_调用回调子程序()优化了代码严谨性,减少hook目标崩溃的可能性 4:寻找无效8字节指令地址()由之前的全模块查找 改动为 在模块代码执行区查找 5:改写模块实列为 一对多的模式 6:模块实列操作控件的方式由变量改为堆内存,避免引起多线程自身崩溃 7:模块实列 对 recv,recvfrom两个函数的hook方式由原先 在回调内 暂停recv-->recv_call-->恢复recv,的方式改为经过特殊改造的 recv_call,这个call经过特殊处理,在recv回调函数内调用,用来取得真实长度,这个调用会绕过hook位置,所以不会触发 recv回调,详见源码 8:修改了一些已知可能出现的问题 --------------------------------------------------------------- 2021/4/12 模块源码 v1.8.2更新 1:修复 x64_远调用函数()在 易语言 主线程调用时造成消息无法回调,导致易语言主线程窗口卡死的问题。      感谢楼下易友发现的BUG,已经第一时间更新 --------------------------------------------------------------- 2021/4/12 模块源码 v1.8.1更新 1:修复 hook全部卸载时的流程写法的一个错误,由于句柄的提前关闭导致多个hook点卸载不干净的问题 2:改写了消息回调时线程传参的代码优化,优化了其他一些小问题 3:  鉴于很多朋友需要,改写了模块自带实列,对TCP,UDP的两组封包函数做了hook实列写法 4:列子中同样增加对x64_远调用函数()的应用写了几个列子,如使用套接字取得本地或远端IP端口API调用的的应用实列 5:本hook模块不支持非模块内存区hook,如申请的动态分配页等,不是不能支持,只是觉得没有任何意义,对这方面有需求的,自行改写模块源码使用 提醒:hook回调函数中尽量减少耗时代码,时间越长返回越慢,回调中谨慎操作控件,如必须要用到可参考源码中实列写法采用线程操作 --------------------------------------------------------------- 2021/3/1   模块源码v1.6更新: 1:修复  x64_远程调用函数()命令,在没有提供 寄存器 参数时,没有返回值的BUG。 --------------------------------------------------------------- 2021/2/28 模块源码v1.5更新: 一:修复win7 64位系统下枚举模块 出现部分模块长度出现负数的问题,从而导致部分win7用户不能使用 二:强化 远程hook64指令_安装 的稳定性:        1,穿插代码中增加对标志位的保护,避免hook位置长度下一条指令为跳转时产生跳转错乱的问题,强化了hook任意位置的定位        2,因为穿插代码中会调用AP
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值