欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]从应用层进程中KiFastSystemCall接管进入内核的一切

[原创]从应用层进程中KiFastSystemCall接管进入内核的一切

早期时候发过一个帖子 http://bbs.pediy.com/showthread.php?t=163658 介绍 HOOK KiFastSystemCall 这算是这帖子的延续 完整版.   
大致原理就是构造一个类似内核中SSDT表的HOOK数组存,当有函数通过KiFastSystemCall进入系统内核时 查找HOOK数组,看对应的Index中有无函数地址,有的话跳入新函数地址.  主要问题在于进入我们的函数时如果获取参数并且获取调用后函数返回!


DWORD	dwKiFastSystemCall;

DWORD	dwKiFastSystemCallReturn;

//HOOK后跳入的函数地址

DWORD dwHookSystemCall[0x2048*2];

//保存原始函数调用地址

DWORD *dwOrigSystemCall[0x2048*2];


dwHookSystemCall数组的组要目的就是为了保存被我们HOOK的函数新地址.类似SSDT表!
比如ZwAccessCheck 的index为1 那么查找dwHookSystemCall[1] 如果这地址不为空,那么就跳入这个新地址.

__declspec(naked) void KiFastSystemCallEx()

{

	__asm  {

		mov edx,esp

		__emit 0x0F

		__emit 0x34

		retn

	}

}

//EX SystemCall

__declspec(naked) void SystemCall()

{

	__asm  {

		__emit 0x90

		mov eax,0x11B2

		call KiFastSystemCallEx

		retn 0xFF

		__emit 0x90

	}

}


自己构造一个 KiFastSystemCall函数跟一个Zw原形,因为我们已经HOOK了系统的 KiFastSystemCall所以系统的函数我们不可能去调用必须自己构造.因为Shadow函数获取不到地址所以这我们必须自己先写个原形.

__declspec(naked) void KiFastSystemCall()

{

	__asm{

		pushad

			pushfd

			cmp         dword ptr [eax*4+dwHookSystemCall],0

			jz          Label

			popfd

			popad

			add         esp,4

			jmp         dword ptr [dwHookSystemCall+eax*4]

Label:

			popfd

			popad

			jmp         KiFastSystemCallEx

	}

}


这是我们的HOOK KiFastSystemCall后跳入的函数,KiFastSystemCall执行到这的时候EAX为函数INDEX ,判断INDEX 看我们的数组里是否存在 有的话就跳入我们的函数内.

add         esp,4


这个是整个HOOK中最重要的地方!
HOOK后正常流程: 调用Zw ->  Zw ->  MyKiFastSystemCall  ->MyZw ->OrgZw ->  OrgSystemCall 问题来了,Zw函数的参数传递到 MyZw的时候已经经过一次Zw了!正常直接获取是获取不到的!因此,为了让我们的函数可以正常获取到参数必须把ESP + 4  这样函数返回的时候是直接返回到"调用Zw"的地方,而不是NTDLL 中的ZW函数!即解决了函数返回的问题,也解决了MyZw获取参数的问题.


//初始化

BOOL HookSystemCall::Initialization()

{

	DWORD dwOldProtect;

	HMODULE hModule = GetModuleHandleA("NTDLL.dll");

	dwKiFastSystemCall = PtrToUlong(GetProcAddress(hModule, "KiFastSystemCall"));

	dwKiFastSystemCallReturn = dwKiFastSystemCall + 2;

	VirtualProtect(ULongToPtr(dwKiFastSystemCall - 0xA), 100, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	//保存原始代码

	memcpy(szCode,(VOID*)(dwKiFastSystemCall - 6),0xF);

	//	Hook KiFastSystemCall

	*(PWORD)ULongToPtr(dwKiFastSystemCall) = 0xF9EB;

	*(PBYTE)ULongToPtr(dwKiFastSystemCall - 0x5) = 0xE9;

	*(PDWORD)ULongToPtr(dwKiFastSystemCall - 0x4) = PtrToUlong(KiFastSystemCall) - (dwKiFastSystemCall - 0x5) - 5;

	return 0;

}


这主要是为了初始化 跟 HOOK KiFastSystemCall ;KiFastSystemCall函数太短,小JMP 后再大JMP到我们的HOOK

//调用函数GetDllFuncAddr以便获得服务号

int HookSystemCall::GetSysCallIndex( PCHAR FuncName )

{

	DWORD          FuncAddr;    //函数地址

	int            SysCallIndex;//服务号

	FuncAddr = (DWORD)GetProcAddress(GetModuleHandleA("ntdll.dll"),FuncName);

	SysCallIndex = *( (WORD*)(FuncAddr + 1) );

	return SysCallIndex;

}


获取ZW函数index,Zw函数开头都是mov eax,1 之类的,刚好将地址+1就获得了Index! 某些软件直接HOOK Zw函数头五字节 防止冲突,可以从文件获取.

// 安装钩子

BOOL HookSystemCall::InstallHook(PCHAR FunName,		//NTAPI

								 DWORD pHookFunc,	//HOOK的函数

								 DWORD * pOrigFunc)	//返回的函数

{

	DWORD dwOldProtect;

	DWORD dwIndex = GetSysCallIndex(FunName);

	DWORD	dwFunadd =PtrToUlong(GetProcAddress(GetModuleHandleA("NTDLL.dll"), FunName));

	//保存各种信息

	dwHookSystemCall[dwIndex] = pHookFunc;

	dwOrigSystemCall[dwIndex] = (PDWORD)malloc(0x10);

	//将原本的ZW函数保存进去

	memcpy(dwOrigSystemCall[dwIndex],(PVOID)dwFunadd,0x10);

	//野猪改造开始

	VirtualProtect(ULongToPtr(dwOrigSystemCall[dwIndex]), 0x10, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	*(PBYTE)((DWORD)dwOrigSystemCall[dwIndex]	+ 5)	=	 0xE8;

	*(PDWORD)((DWORD)dwOrigSystemCall[dwIndex] + 6)		=	 (DWORD)KiFastSystemCallEx - (DWORD)dwOrigSystemCall[dwIndex] -0xA;

	*(PWORD)((DWORD)dwOrigSystemCall[dwIndex]	+ 10)	=	 0x9090;

	* pOrigFunc = (DWORD)dwOrigSystemCall[dwIndex];

	return 0;

}


重新构造一个被我们HOOK的Zw函数,然后将

MOV EDX,7FFE0300

CALL DWORD PTR DS:[EDX]


修改为CALL KiFastSystemCallEx ;以便函数可以重新进入内核,实现我们函数内获取函数返回的功能.

BOOL HookSystemCall::InstallHookEx(DWORD dwIndex,		//NTAPI

								   WORD dwRetIndex,	//返回堆栈平衡

								   DWORD pHookFunc,		//HOOK的函数

								   DWORD * pOrigFunc)	//返回的函数

{

	DWORD dwOldProtect;

	//保存各种信息

	dwHookSystemCall[dwIndex] = pHookFunc;

	dwOrigSystemCall[dwIndex] = (PDWORD)malloc(0x10);

	//将原本的ZW函数保存进去

	memcpy(dwOrigSystemCall[dwIndex],(PVOID)SystemCall,0x10);

	//野猪改造开始

	VirtualProtect(ULongToPtr(dwOrigSystemCall[dwIndex]), 0x10, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	*(PDWORD)((DWORD)dwOrigSystemCall[dwIndex] + 2)		=	 (DWORD)dwIndex;

	*(PDWORD)((DWORD)dwOrigSystemCall[dwIndex] + 7)		=	 (DWORD)KiFastSystemCallEx - (DWORD)dwOrigSystemCall[dwIndex] - 0xB;

	*(PWORD)((DWORD)dwOrigSystemCall[dwIndex]	+ 12)	=	 dwRetIndex;

	* pOrigFunc = (DWORD)dwOrigSystemCall[dwIndex];

	return 0;

}


这个主要是为了HOOK 一些不是NTDLL 导出的函数,比如NtUserFindWindowEx之类的函数,因为Shadow SSDT也是经由KiFastSystemCall进入内核,原理大概一致.
不过因为没办法直接获取函数INDEX所以必须根据系统版本自己确定,而且也无法获取函数地址,所以使用我们之前构造的函数原形来处理.ret的平衡也只能手动了!

代码到此就已经接管了从KiFastSystemCall进入内核的入口.
主要代码基本这些,剩下看附件看源码.WIN7 32位测试通过!
例子

typedef NTSTATUS (NTAPI *_ZwOpenProcess)(OUT     PHANDLE ProcessHandle,

										 IN      ACCESS_MASK DesiredAccess,

										 IN      POBJECT_ATTRIBUTES ObjectAttributes,

										 IN  PCLIENT_ID ClientId

										 );

_ZwOpenProcess ZwOpenProcess;

NTSTATUS NTAPI MyZwOpenProcess(_Out_	PHANDLE ProcessHandle,

							   _In_	ACCESS_MASK DesiredAccess,

							   _In_	POBJECT_ATTRIBUTES ObjectAttributes,

							   _In_	PCLIENT_ID ClientId)

{

	NTSTATUS ntStatus = ZwOpenProcess(ProcessHandle,DesiredAccess,ObjectAttributes,ClientId);

	return ntStatus;

}

HookSystemCall Hook;

Hook.Initialization();

Hook.InstallHook("ZwOpenProcess",(DWORD)MyZwOpenProcess,(PDWORD)&ZwOpenProcess);

上传的附件 HookSystemCall.rar

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功