zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C++实现inlinehook的原理及应用实例

C++实例应用原理 实现 InlineHook
2023-06-13 09:15:41 时间

本文实例简述了C++实现inlinehook的原理及应用,对于大家更好的理解inlinehook原理及其应用有很大的帮助。具体内容如下:

一、InlineHook简介:

1.INLINEHOOK原理:

InlineHook通过硬编码的方式向内核API的内存空间(通常是开始的一段字节,且一般在第一个call之前,这么做是为了防止堆栈混乱)写入跳转语句,这样,该API只要被调用,程序就会跳转到我们的函数中来,我们在自己写的函数里需要完成3个任务:

1)重新调整当前堆栈。程序流程在刚刚跳转的时候,内核API并没有执行完,而我们的函数需要根据其结果来进行信息过滤,所以我们需要保证内核API能在顺利执行完毕后返回到我们的函数中来,这就要求对当前堆栈做一个调整。

2)执行遗失的指令。我们向内核API地址空间些如跳转指令(jmpxxxxxxxx)时,势必要覆盖原先的一些汇编指令,所以我们一定要保证这些被覆盖的指令能够顺利执行(否则,你的及其就要BSOD了,呵呵,BlueScreenOfDeath)。关于这部分指令的执行,一般是将其放在我们的函数中,让我们的函数“帮助”内核API执行完被覆盖的指令,然后再跳回内核API中被覆盖内后后的地址继续执行剩余内容。跳回去的时候,一定要算好是跳回到什么地址,是内核API起始地址后的第几个字节。

3)信息过滤。这个就不用多说了,内核API顺利执行并返回到我们的函数中,我们自然要根据其结果做一些信息过滤,这部分内容因被hook的API以及Hook目的的不同而不同。

2.Inlinehook的工作流程:

1)验证内核API的版本(特征码匹配)。

2)撰写自己的函数,要完成以上三项任务。

3)获取自己函数的地址,覆盖内核API内存,供跳转。

简而言之,inlinehook的原理就是,修改函数,使其跳转到我们指定的地方。

常见的有改函数入口,也有改函数尾,函数中间的
比如,通常函数开头的汇编代码都是这样:movedi,edi;pushesp;movebp,esp,而我们便可以通过修改这里进行HOOK。

二、示例代码(该示例摘自看雪)

#include<ntifs.h>
#include<windef.h>
ULONGg_KiInsertQueueApc;
ULONGg_uCr0;
BYTEg_HookCode[5]={0xe9,0,0,0,0};//JMPNEAR
BYTEg_OrigCode[5]={0};//原函数的前字节内容
BYTEjmp_orig_code[7]={0xEA,0,0,0,0,0x08,0x00};//JMPFAR
BOOLg_bHooked=FALSE;
VOID
fake_KiInsertQueueApc(
PKAPCApc,
KPRIORITYIncrement
);
VOID
Proxy_KiInsertQueueApc(
PKAPCApc,
KPRIORITYIncrement
);
voidWPOFF()
{
ULONGuAttr;
_asm
{
pusheax;
moveax,cr0;
movuAttr,eax;
andeax,0FFFEFFFFh;//CR016BIT=0
movcr0,eax;
popeax;
cli
};
g_uCr0=uAttr;//保存原有的CRO?傩
}
VOIDWPON()
{
_asm
{
sti
pusheax;
moveax,g_uCr0;//恢?驮?CR0?傩
movcr0,eax;
popeax;
};
}
//
//停止inlinehook
//
VOIDUnHookKiInsertQueueApc()
{
KIRQLoldIrql;
WPOFF();
oldIrql=KeRaiseIrqlToDpcLevel();
RtlCopyMemory((BYTE*)g_KiInsertQueueApc,g_OrigCode,5);
KeLowerIrql(oldIrql);
WPON();
g_bHooked=FALSE;
}
//
//开始inlinehook--KiInsertQueueApc
//
VOIDHookKiInsertQueueApc()
{
KIRQLoldIrql;
if(g_KiInsertQueueApc==0){
DbgPrint("KiInsertQueueApc==NULL\n");
return;
}
//DbgPrint("开始inlinehook--KiInsertQueueApc\n");
DbgPrint("KiInsertQueueApc的地址t0x%08x\n",(ULONG)g_KiInsertQueueApc);
DbgPrint("fake_KiInsertQueueApc的地址t0x%08x\n",(ULONG)fake_KiInsertQueueApc);

//保存原函数的前字节内容
RtlCopyMemory(g_OrigCode,(BYTE*)g_KiInsertQueueApc,5);
//jmp指令,此处为短跳,计算相对偏移,同时,jmpxxxxxx这条指令占了5个字节
*((ULONG*)(g_HookCode+1))=(ULONG)fake_KiInsertQueueApc-(ULONG)g_KiInsertQueueApc-5;
//禁止系统写保护,提升IRQL到DPC
WPOFF();
oldIrql=KeRaiseIrqlToDpcLevel();
RtlCopyMemory((BYTE*)g_KiInsertQueueApc,g_HookCode,5);
*((ULONG*)(jmp_orig_code+1))=(ULONG)((BYTE*)g_KiInsertQueueApc+5);
RtlCopyMemory((BYTE*)Proxy_KiInsertQueueApc,g_OrigCode,5);
RtlCopyMemory((BYTE*)Proxy_KiInsertQueueApc+5,jmp_orig_code,7);
//恢复写保护,降低IRQL
KeLowerIrql(oldIrql);
WPON();
g_bHooked=TRUE;
}
//
//跳转到我们的函数里面进行预处理,裸函数,有调用者进行堆栈的平衡
//
__declspec(naked)
VOID
fake_KiInsertQueueApc(
PKAPCApc,
KPRIORITYIncrement
)
{
//去掉DbgPrint,不然这个hook会产生递归
//DbgPrint("inlinehook--KiInsertQueueApc成功\n");
__asm
{
jmpProxy_KiInsertQueueApc
}
}
//
//代理函数,负责跳转到原函数中继续执行
//
__declspec(naked)
VOID
Proxy_KiInsertQueueApc(
PKAPCApc,
KPRIORITYIncrement
)
{
__asm{//共字节
_emit0x90
_emit0x90
_emit0x90
_emit0x90
_emit0x90//前字节实现原函数的头字节功能
_emit0x90//这个填充jmp
_emit0x90
_emit0x90
_emit0x90
_emit0x90//这字节保存原函数+5处的地址
_emit0x90
_emit0x90//因为是长转移,所以必须是0x0080
}
}
ULONGGetFunctionAddr(INPCWSTRFunctionName)
{
UNICODE_STRINGUniCodeFunctionName;
RtlInitUnicodeString(&UniCodeFunctionName,FunctionName);
return(ULONG)MmGetSystemRoutineAddress(&UniCodeFunctionName);
}
//根据特征值,从KeInsertQueueApc搜索中搜索KiInsertQueueApc
ULONGFindKiInsertQueueApcAddress()
{
char*Addr_KeInsertQueueApc=0;
inti=0;
charFindcode[]={0xE8,0xcc,0x29,0x00,0x00};
ULONGAddr_KiInsertQueueApc=0;
Addr_KeInsertQueueApc=(char*)GetFunctionAddr(L"KeInsertQueueApc");
for(i=0;i<100;i++)
{
if(Addr_KeInsertQueueApc[i]==Findcode[0]&&
Addr_KeInsertQueueApc[i+1]==Findcode[1]&&
Addr_KeInsertQueueApc[i+2]==Findcode[2]&&
Addr_KeInsertQueueApc[i+3]==Findcode[3]&&
Addr_KeInsertQueueApc[i+4]==Findcode[4]
)
{
Addr_KiInsertQueueApc=(ULONG)&Addr_KeInsertQueueApc[i]+0x29cc+5;
break;
}
}
returnAddr_KiInsertQueueApc;
}
VOIDOnUnload(INPDRIVER_OBJECTDriverObject)
{
DbgPrint("MyDriverUnloaded!");
UnHookKiInsertQueueApc();
}
NTSTATUSDriverEntry(INPDRIVER_OBJECTtheDriverObject,INPUNICODE_STRINGtheRegistryPath)
{
DbgPrint("MyDriverLoaded!");
theDriverObject->DriverUnload=OnUnload;
g_KiInsertQueueApc=FindKiInsertQueueApcAddress();
HookKiInsertQueueApc();
returnSTATUS_SUCCESS;
}