早期时候发过一个帖子 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);
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课