首页
论坛
课程
招聘
[原创]发一个支持任意地点hook的类(包含驱动hook和应用层hook)
2013-3-3 23:58 23751

[原创]发一个支持任意地点hook的类(包含驱动hook和应用层hook)

2013-3-3 23:58
23751
这是以前练习写驱动类的一个产物,  有点早了,很简单.
写这个类也是方便自己绕过某些驱动的保护钩子.  当然这个也只支持x86, 没有做x64的拓展. 因为只是方便自己不需要每次都copy一大堆代码;
如果需要x64的拓展的,可以参考detours 或者是 EasyHook.   比较好的是EasyHook提供了驱动的hook;

貌似关于驱动写类论坛上比较少, 对于驱动,大家更倾向于直接用c写, 而不喜欢用c++写;  其实遇到大的工程,用c++有很多好处,而且微软也是有c++写驱动类的例子的.

废话不多说:  
关于驱动的,啰嗦一句, 不能定义全局的驱动对象, 具体为什么不能大家就自己baidu或者google;

直接上代码,代码里提供了例子;hook NtOpenProcess 和 NtReadVirtualMemory
驱动部分Hook.zip

应用层的:
应用层hook.zip

比较简单, 高手飘过. 这里只是给一个例子

//KernelDetours.h
#pragma once
#include <ntddk.h>
#include "ldasm.h"
//保存5字节代码的结构
#pragma pack(1)
typedef struct _TOP5CODE
{
	UCHAR  instruction;  //指令
	ULONG  address;    //地址
}TOP5CODE,*PTOP5CODE;
#pragma pack()



#ifdef __cplusplus
extern "C" {
#endif
class CKernelDetours
{
public:
	CKernelDetours();
	~CKernelDetours();

	BOOLEAN Hook(ULONG HookAddr,PULONG NakedFunc);
	void UnHook();
	void __stdcall CallJmpBack();

protected:
	VOID WPOFF();
	VOID WPON();

private:
	UCHAR m_Bak[5];
	ULONG m_hookAddr;
	ULONG m_HookCodeLen;//被hook指令的长度

	ULONG m_OldProtect;
	BOOLEAN  m_bHookSuccess;
	KIRQL  Irql;

	PVOID m_detoursFunc;//这个地方呢,是动态生成的. 他是保存hook前的指令, 可以执行到这里然后跳转到原函数下面继续执行

public:
	static void __cdecl operator delete(void* pointer) { ASSERT(NULL != pointer); if (NULL != pointer) ExFreePool(pointer); }
	static void * __cdecl operator new(size_t iSize,POOL_TYPE PoolType,unsigned int tag) {
		KdPrint(("global operator new --  Allocate size :%d \n",iSize));
		PVOID result; // [sp+0h] [bp-4h]@1
		result = ExAllocatePoolWithTag(PoolType, iSize, tag);
		if ( result )
			memset(result, 0, iSize);
		return result;
	}


};



#ifdef __cplusplus
}; // extern "C"
#endif


//KernelDetours.cpp
#include "KernelDetours.h"

#define  TAG 'liuq'


CKernelDetours::CKernelDetours(void)
{
	RtlZeroMemory(m_Bak,0,6);
	m_hookAddr =0;
	m_OldProtect = 0;
	m_bHookSuccess = FALSE;
	m_HookCodeLen = 0;
	m_detoursFunc = NULL;
	m_detoursFunc = ExAllocatePoolWithQuotaTag(NonPagedPool,0x50,TAG);
	memset(m_detoursFunc,0x90,0x50);
	KdPrint(("Enter CKernelDetours::CKernelDetours(void)   m_detoursFunc: %x",(ULONG)m_detoursFunc));
}

CKernelDetours::~CKernelDetours(void)
{
// if (m_bHookSuccess)
// {
// 	UnHook();
// }
KdPrint(("Enter CKernelDetours::~CKernelDetours(void)"));
}

void CKernelDetours::WPOFF()
{		//清除页面保护
	__asm
	{
			cli
			mov eax,cr0
			and eax,not 10000h
			mov cr0,eax
	}
}

void CKernelDetours::WPON()
{	//恢复页面保护
	__asm
	{
			mov eax,cr0
			or eax,10000h
			mov cr0,eax
			sti
	}
}


void CKernelDetours::UnHook()
{
	if (m_bHookSuccess)
	{
		ULONG a = m_hookAddr;
		WPOFF();
		Irql = KeRaiseIrqlToDpcLevel();
		RtlCopyMemory((void*)a,m_Bak,5);
		KeLowerIrql(Irql);
		WPON();

		m_bHookSuccess =FALSE;
	}
	if (m_detoursFunc != NULL)
	{
		KdPrint(("ExFreePoolWithTag(m_detoursFunc,TAG);"));
		ExFreePoolWithTag(m_detoursFunc,TAG);
		m_detoursFunc  =  NULL;
	}
}

BOOLEAN CKernelDetours::Hook(ULONG HookAddr,PULONG NakedFunc)
{
	if(m_bHookSuccess || NakedFunc==NULL || HookAddr == NULL)
	{
		return FALSE;
	}
	m_hookAddr = HookAddr; //保存被hook的地址
	unsigned char jmp[6]  ={0xe9};
	jmp[5] = 0x90;


	PUCHAR pcode = NULL;
	ULONG codelen =0;
	ULONG uSumCodeLen = 0;
	BOOLEAN bFind = FALSE;
	for (int j =0; j<0x30; j+=codelen)
	{
		codelen = 	SizeOfCode((void*)(HookAddr+j),&pcode);
		uSumCodeLen+=codelen;//计算总长度
		if (uSumCodeLen>=5)
		{
			bFind =TRUE; 
			break;
		}
	}

	if (!bFind)
	{
		KdPrint((" I'm sorry, Can Not Find Right Place to Hook\n"));
		return FALSE;
	}
	
	m_HookCodeLen = uSumCodeLen; //保存这个长度

	ULONG  JmpBack= HookAddr + uSumCodeLen;
	ULONG jmpDetoursAddr = ((ULONG)m_detoursFunc + uSumCodeLen);//在detours函数跳转到hook的地方
	ULONG b = JmpBack - jmpDetoursAddr  - 5;
	*(ULONG *)(jmp+1) = b;

	WPOFF();
	Irql = KeRaiseIrqlToDpcLevel();
	RtlCopyMemory((void*)m_detoursFunc,(void*)HookAddr,uSumCodeLen); //把这些数据保存到detours 函数里面,//然后在后面写上 jmp (HookAddr+uSumCodeLen)
	RtlCopyMemory((void*)jmpDetoursAddr,jmp,5);
	KeLowerIrql(Irql);
	WPON();

	ULONG a = HookAddr;  //hook的地址
	 b = (ULONG)NakedFunc - a - 5;
	*(ULONG *)(jmp+1) = b;
	
	
	RtlCopyMemory((void*)m_Bak,(void*)a,5);//保存hook的地方

	WPOFF();
	Irql = KeRaiseIrqlToDpcLevel();
	RtlCopyMemory((void*)a,jmp,5);
	KeLowerIrql(Irql);
	WPON();
	m_bHookSuccess = TRUE;
	return TRUE;
}



void CKernelDetours::CallJmpBack()
{
/*
__asm
{
	push ebp
	mov ebp,esp
	sub,esp,8
}*/

ULONG ebp1 = (ULONG)m_detoursFunc;
__asm
{
	mov eax,ebp1
	mov esp,ebp
	pop ebp
	add esp,8  ;//这里会有一个变量的堆栈空间 + 一个this指针的参数
	jmp eax
}

/*
__asm 
{
	mov esp,ebp
	pop ebp
	retn 4
}*/

}




至于用法就比较简单了;

CKernelDetours *g_p= NULL; //hook NtOpenProcess 定义一个全局的指针,是为了方便还原, 不需要还原hook的可以直接在函数内部定义对象


//NtOpenProcess的中继函数
__declspec(naked) void MyOpenProcess()
{
	__asm
	{
			pushad
			pushfd
	}

	ANSI_STRING  p_str1;
	RtlInitAnsiString(&p_str1,(PSTR)((ULONG)IoGetCurrentProcess()+0x174));
	//将我们要比对的进程名放入str2

	KdPrint(("访问OPenProcess的进程名为 %s\n", p_str1.Buffer));

	__asm
	{
			popfd
			popad
	}

	// call detours
	g_p->CallJmpBack();

}
CKernelDetours* Hook_NtOpenProcess()
{
	CKernelDetours *p =  new(NonPagedPool, 'RysI') CKernelDetours;
	ULONG addr= GetSsdtFuncAddr(0x7A) ;
	KdPrint(("NtOpenProcess Addr 0x%x\n",addr));
	p->Hook(addr,(PULONG)MyOpenProcess);//这里只需要填入要hook的地址,和中继函数的地址即可.很简单; 在函数头,函数尾都行;有了这个功能,大家就可以专心写中继函数了.不必考虑其他的事情;
	return p;
}

void UnHookNtOpenProcess(CKernelDetours* p)
{
	CKernelDetours *pthis = p;
	if (pthis != NULL)
	{
		pthis->UnHook();
		delete pthis;
	}
}



这里就是演示其用法. 作用就是可以在任意函数内部进行Hook, 专心写中继函数,也不需要你人为的去调用被覆盖的代码. 你只需要在中继函数的最后面调用一下        g_p->CallJmpBack();就是实现跳转的功能,即可跳转会原函数执行; 这个功能对绕过某些驱动保护应该足够了.而且用法也很简单.

[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (23)
雪    币: 822
活跃值: 活跃值 (143)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
yyyang 活跃值 2013-3-4 09:55
2
0
下来看看 学习学习
雪    币: 0
活跃值: 活跃值 (24)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
tihty 活跃值 2 2013-3-4 09:56
3
0
thanks for share
雪    币: 1587
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
目标无敌 活跃值 2013-3-4 10:25
4
0
非常感谢
雪    币: 226
活跃值: 活跃值 (28)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
鹿剑 活跃值 3 2013-3-4 21:27
5
0
楼主的new不行啊,都没有看到调用构造函数诶
雪    币: 27
活跃值: 活跃值 (25)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
liuqiangni 活跃值 2 2013-3-4 22:37
6
0
没调用?不会吧,应该有打印输出吧!

global operator new --  Allocate size :28
Enter CKernelDetours::CKernelDetours(void)   m_detoursFunc: 814c3040NtOpenProcess Addr 0x805cc3fc
global operator new --  Allocate size :28
Enter CKernelDetours::CKernelDetours(void)   m_detoursFunc: 81730008NtReadVirtualMem Addr 0x805b528a
访问OPenProcess的进程名为 svchost.exe
访问OPenProcess的进程名为 Dbgview.exe
访问OPenProcess的进程名为 InstDrv.exe
访问OPenProcess的进程名为 Dbgview.exe
访问OPenProcess的进程名为 svchost.exe

这部分是卸载的时候析构函数调用打印的调试信息!

ExFreePoolWithTag(m_detoursFunc,TAG);
Enter CKernelDetours::~CKernelDetours(void)
ExFreePoolWithTag(m_detoursFunc,TAG);
Enter CKernelDetours::~CKernelDetours(void)
雪    币: 12
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
safeboy 活跃值 2013-3-6 14:28
7
0
好贴,前排留名,学习了
雪    币: 270
活跃值: 活跃值 (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tianyahai 活跃值 2013-3-6 14:37
8
0
谢谢共享,学习学习!
雪    币: 1619
活跃值: 活跃值 (61)
能力值: ( LV15,RANK:440 )
在线值:
发帖
回帖
粉丝
hackerlzc 活跃值 10 2013-3-7 10:37
9
0
是不是要考虑一下函数头包含相对跳转指令的情况?
雪    币: 199
活跃值: 活跃值 (793)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
appview 活跃值 2013-3-7 14:41
10
0
学习下,谢谢分享
雪    币: 5
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
宁妖 活跃值 2013-3-8 17:14
11
0
楼主这种inline hook在搞频繁调用的函数,如IoCallDriver,很容易蓝屏的吧~建议利用微软的热补丁做,还是很稳定的,既可做好多核,也可做好多线程的保护,做法如,在待挂钩目标前后1字节寻址范围内搜索5个nop指令~如果是x64,可以在1字节寻址范围内搜索6+8个nop指令,我自己的inlinehook引擎就是这么做的,还是相当稳定的,仅供楼主参考
雪    币: 27
活跃值: 活跃值 (25)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
liuqiangni 活跃值 2 2013-3-8 20:18
12
0
多谢提醒, 其实当时写这个仅仅是方便绕过某些游戏的保护钩子, 纯粹是自用而已,不打算考虑这么多. 就是一句话,简单能用就ok;
雪    币: 65
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
su汪妮 活跃值 2013-3-9 16:26
13
0
哦,哦,原来如此
雪    币: 37481
活跃值: 活跃值 (154348)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
linhanshi 活跃值 2013-3-10 14:17
14
0
Thanks for share.
雪    币: 214
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
落叶随风 活跃值 2013-12-30 20:49
15
0
原来的参数怎么取呢?堆栈变了。。
雪    币: 59
活跃值: 活跃值 (409)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
iceway 活跃值 2014-1-7 18:14
16
0
求代码求代码
雪    币: 130
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
nfgfgo 活跃值 2014-1-7 19:07
17
0
我记得有位高人的INLINEHOOK PUSH_DATA_ARG那才叫爽 完全忽视跳转 如果要跳到其他地方修改返回地址 中间可以HOOK 完全实现代码自管理 遇到跳转也可以HOOK
雪    币: 110
活跃值: 活跃值 (184)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ling林 活跃值 2014-1-7 22:37
18
0
弱弱地问一下:
刚看了你用户层的hook代码,你是直接把hook程序都放到了测试程序中了。在我们实际分析程序的时候,是不是需要将hook代码写到一个dll中,然后注入到目标程序中? 还是 有其它用法?
雪    币: 742
活跃值: 活跃值 (631)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yllen 活跃值 2014-1-7 23:03
19
0
mark ,收藏下!
雪    币: 79
活跃值: 活跃值 (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cqzj70 活跃值 2014-1-22 08:18
20
0
mark
雪    币: 454
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ueoboy 活跃值 2014-1-22 16:29
21
0
下来学习学习
雪    币: 291
活跃值: 活跃值 (51)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
zfdyq 活跃值 1 2014-5-2 21:43
22
0
mark
雪    币: 9
活跃值: 活跃值 (43)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
yber 活跃值 2018-6-8 14:21
23
0
g_p->CallJmpBack中没有恢复原来的寄存器,导致继续执行原函数时候错误
雪    币: 190
活跃值: 活跃值 (85)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小青峰 活跃值 2020-9-24 16:54
24
0
calljmpBack一直出错
游客
登录 | 注册 方可回帖
返回