前几天在论坛上看见一篇360HOOK框架的文章,写得不错,其实hookport.sys之前很多人都
逆过了,而且教主写了一套很详细的文章。本人才疏学浅,只是补充一些科普知识,好像一些像我一样
的菜鸟成长起来。这篇文章主要介绍一些360的比较精辟的应用,至于它HOOK了那些函数大家可以去网上
搜索教主那篇文章。其实就是一个结构。
亮点一:HOOK KiFastCallEntry 科普一下 KiFastCallEntry 快速系统调用SYSENTER
就是说每次系统调用的时候,最终都会通过KiFastCallEntry
我们来看看KiFastCallEntry函数比较关键代码
8054257d 8bb324010000 mov esi,dword ptr [ebx+124h] //获得CurrentThread
80542583 ff33 push dword ptr [ebx]
80542585 c703ffffffff mov dword ptr [ebx],0FFFFFFFFh
8054258b 8b6e18 mov ebp,dword ptr [esi+18h]
8054258e 6a01 push 1
80542590 83ec48 sub esp,48h
80542593 81ed9c020000 sub ebp,29Ch
80542599 c6864001000001 mov byte ptr [esi+140h],1
805425a0 3bec cmp ebp,esp
805425a2 758d jne nt!KiFastCallEntry2+0x49 (80542531)
805425a4 83652c00 and dword ptr [ebp+2Ch],0
805425a8 f6462cff test byte ptr [esi+2Ch],0FFh
805425ac 89ae34010000 mov dword ptr [esi+134h],ebp
805425b2 0f8538feffff jne nt!Dr_FastCallDrSave (805423f0)
805425b8 8b5d60 mov ebx,dword ptr [ebp+60h]
805425bb 8b7d68 mov edi,dword ptr [ebp+68h]
805425be 89550c mov dword ptr [ebp+0Ch],edx
805425c1 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h
805425c8 895d00 mov dword ptr [ebp],ebx
805425cb 897d04 mov dword ptr [ebp+4],edi
805425ce fb sti
805425cf 8bf8 mov edi,eax //eax=SSDTindex
805425d1 c1ef08 shr edi,8 //除以256
805425d4 83e730 and edi,30h //要就是0 要就是0X10为了判断是SSDTSHADOW还是SSDT
805425d7 8bcf mov ecx,edi
805425d9 03bee0000000 add edi,dword ptr [esi+0E0h] //通过esi=KTHREAD->ServiceTable获得
当前线程使用的ServiceTable;
805425df 8bd8 mov ebx,eax //SSDTID
805425e1 25ff0f0000 and eax,0FFFh
805425e6 3b4708 cmp eax,dword ptr [edi+8]
805425e9 0f8333fdffff jae nt!KiBBTUnexpectedRange (80542322)
805425ef 83f910 cmp ecx,10h //这里判断是SSDT还是SSDTSHADOW
805425f2 751b jne nt!KiFastCallEntry+0xcf (8054260f)
805425f4 648b0d18000000 mov ecx,dword ptr fs:[18h]
805425fb 33db xor ebx,ebx
805425fd 0b99700f0000 or ebx,dword ptr [ecx+0F70h]
80542603 740a je nt!KiFastCallEntry+0xcf (8054260f)
80542605 52 push edx
80542606 50 push eax
80542607 ff1548d75580 call dword ptr [nt!KeGdiFlushUserBatch (8055d748)]
8054260d 58 pop eax
8054260e 5a pop edx
8054260f 64ff0538060000 inc dword ptr fs:[638h] //使用计数
80542616 8bf2 mov esi,edx
80542618 8b5f0c mov ebx,dword ptr [edi+0Ch]
8054261b 33c9 xor ecx,ecx
8054261d 8a0c18 mov cl,byte ptr [eax+ebx]
80542620 8b3f mov edi,dword ptr [edi]
80542622 8b1c87 mov ebx,dword ptr [edi+eax*4]
80542625 e9d69c8d09 jmp 89e1c300 //这个就是360的HOOK了,这个就是360的亮点之处
上面 EDI=SSDTaddr EBX=系统服务地址 EAX=SSDTindex 有了这几个寄存器省了很多麻烦了。
8054262a 8bfc mov edi,esp
8054262c 3b3534315680 cmp esi,dword ptr [nt!MmUserProbeAddress (80563134)]
80542632 0f83a8010000 jae nt!KiSystemCallExit2+0x9f (805427e0)
80542638 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
8054263a ffd3 call ebx //服务调用
8054263c 8be5 mov esp,ebp
第二步,我们来看看360是怎么HOOK的
mov edi, edi
push ebp
mov ebp, esp
sub esp, 18h
push ebx
push esi
push edi
push offset aZwsetevent ; "ZwSetEvent"
lea eax, [ebp+DestinationString]
push eax
call ds:RtlInitAnsiString //初始化
lea eax, [ebp+DestinationString]
push eax
call sub_17A78
xor esi, esi
cmp eax, esi
jz loc_195B4
mov eax, [eax+1] //获取ZwSetEventINDEXID =DB
mov ZwSetEventaddrid, eax
lea eax, [ebp+SpinLock]
. push eax ; SpinLock
call ds:KeInitializeSpinLock
mov edi, ds:KfAcquireSpinLock
. lea ecx, [ebp+SpinLock] ; SpinLock
call edi ; KfAcquireSpinLock
mov cl, al
push eax
push ebx
push edx
cli
mov eax, cr0
mov [ebp+var_C], eax
and eax, 0FFFEFFFFh
mov cr0, eax
mov eax, ZwSetEventaddrid //EAX=INDEXID
mov edx, KeServiceDescriptorTableaddr//EDX=SSDTADDR
mov edx, [edx]
mov ebx, [edx+eax*4] //获取ZwSetEventADDR
mov ZwSetEventaddr, ebx //保持
mov ebx, offset sub_192A8//HOOK
mov [edx+eax*4], ebx
mov eax, [ebp+var_C]
mov cr0, eax
sti
pop edx
pop ebx
pop eax
mov dl, cl ; NewIrql
lea ecx, [ebp+SpinLock] ; SpinLock
call ds:KfReleaseSpinLock
mov eax, 288C58F1h //这个比较关键,主要是确定是不是自己调用
push esi ; PreviousState
push eax ; EventHandle
mov dword_1B850, eax //保存
call ds:ZwSetEvent
mov eax, fanhuiaddrs //调用后返回地址8054263c
cmp eax, esi
jnz short loc_19510
mov eax, P
cmp eax, esi
jmp loc_195AB
cmp addbp4addr5, esi//这个是HOOK返回地址8054262a
jnz loc_19629
第三步,我们来看下它的HOOK函数
mov edi, edi
.text:000192AA push ebp
.text:000192AB mov ebp, esp
.text:000192AD push ecx
.text:000192AE push ecx
.text:000192AF mov eax, [ebp+arg_0] //获取第一个参数EventHandle
.text:000192B2 cmp eax, dword_1B850 //看看是不是自己调用
.text:000192B8 push ebx
.text:000192B9 push esi
.text:000192BA push edi
.text:000192BB jnz loc_193CB //如果不是就跳到原服务调用
.text:000192C1 call ds:ExGetPreviousMode
.text:000192C7 test al, al
.text:000192C9 jnz loc_193CB//比较是不是USERMODULE调用是酒跳
.text:000192CF push 206B6444h ; Tag
.text:000192D4 push 5 ; NumberOfBytes
.text:000192D6 push 0 ; PoolType
.text:000192D8 call ds:ExAllocatePoolWithTag //声请一块5字节内存
.text:000192DE test eax, eax
.text:000192E0 mov P, eax //保存到全局变量
.text:000192E5 jz loc_193C7
.text:000192EB mov byte ptr [eax], 0E9h //第一个E9 JMP
.text:000192EE mov eax, ds:NtBuildNumber
.text:000192F3 cmp word ptr [eax], 1770h //比较版本号
.text:000192F8 mov eax, P
.text:000192FD mov ecx, offset byte_19284
.text:00019302 jge short loc_19309
.text:00019304 mov ecx, offset byte_19260 //取过滤函数地址
.text:00019309
.text:00019309 loc_19309:
.text:00019309 sub ecx, eax
.text:0001930B sub ecx, 5
.text:0001930E mov [eax+1], ecx //这几步都懂的,写跳转
.text:00019311 lea eax, [ebp+SpinLock]
.text:00019314 push eax ; SpinLock
.text:00019315 call ds:KeInitializeSpinLock
.text:0001931B lea ecx, [ebp+SpinLock] ; SpinLock
.text:0001931E call ds:KfAcquireSpinLock
.text:00019324 mov bl, al
.text:00019326 push eax
.text:00019327 push ecx
.text:00019328 push edx
.text:00019329 push esi
.text:0001932A push edi
.text:0001932B cli
.text:0001932C mov eax, cr0
.text:0001932F mov [ebp+arg_0], eax
.text:00019332 and eax, 0FFFEFFFFh
.text:00019337 mov cr0, eax
.text:0001933A mov eax, KeServiceDescriptorTableaddr
.text:0001933F mov eax, [eax] //这里先重新写回
.text:00019341 mov edx, ZwSetEventaddrid
.text:00019347 lea eax, [eax+edx*4]
.text:0001934A mov edx, ZwSetEventaddr
.text:00019350 mov [eax], edx
.text:00019352 mov eax, [ebp+4] //获取返回地址8054263c
.text:00019355 mov edx, eax
.text:00019357 mov fanhuiaddrs, edx//保存返回地址
.text:0001935D
.text:0001935D loc_1935D:
.text:0001935D mov edi, eax //EAX=返回地址
.text:0001935F lea esi, dword_1B680//特征码2be1c1e9 也就是sub esp, ecx shr ecx, 2
查找确定KiFastCallEntryHOOK位置
.text:00019365 mov ecx, 5
.text:0001936A repe cmpsb
.text:0001936C push eax
.text:0001936D setz al
.text:00019370 test al, al
.text:00019372 pop eax
.text:00019373 jnz short loc_19381
.text:00019375 dec eax
.text:00019376 push edx
.text:00019377 sub edx, eax
.text:00019379 cmp edx, 64h
.text:0001937C pop edx
.text:0001937D ja short loc_193B0
.text:0001937F jmp short loc_1935D
.text:00019381 ; ---------------------------------------------------------------------------
.text:00019381
.text:00019381 loc_19381:
.text:00019381 add eax, 5 //确定HOOK后返回地址
.text:00019384 mov addbp4addr5, eax //保存
.text:00019389 mov edx, P
.text:0001938F sub edx, eax
.text:00019391 lea eax, byte_1B688
.text:00019397 mov [eax+1], edx
.text:0001939A mov edi, addbp4addr5
.text:000193A0 sub edi, 5
.text:000193A3 lea esi, byte_1B688
.text:000193A9 mov ecx, 5
.text:000193AE rep movsb //这一段都知道的,HOOK
.text:000193B0
.text:000193B0 loc_193B0:
.text:000193B0 mov eax, [ebp+arg_0]
.text:000193B3 mov cr0, eax
.text:000193B6 sti
.text:000193B7 pop edi
.text:000193B8 pop esi
.text:000193B9 pop edx
.text:000193BA pop ecx
.text:000193BB pop eax
.text:000193BC mov dl, bl ; NewIrql
.text:000193BE lea ecx, [ebp+SpinLock] ; SpinLock
.text:000193C1 call ds:KfReleaseSpinLock
.text:000193C7
.text:000193C7 loc_193C7:
.text:000193C7 xor eax, eax
.text:000193C9 jmp short loc_193DF
.text:000193CB
.text:000193CB
.text:000193CB loc_193CB:
.text:000193CB
.text:000193CB push eax
.text:000193CC push [ebp+arg_4]
.text:000193CF push [ebp+arg_0]
.text:000193D2 call ZwSetEventaddr//这里是确定不是自己调用就调用原服务
.text:000193D8 mov [ebp+var_8], eax
.text:000193DB pop eax
.text:000193DC mov eax, [ebp+var_8]
.text:000193DF
.text:000193DF loc_193DF:
.text:000193DF pop edi
.text:000193E0 pop esi
.text:000193E1 pop ebx
.text:000193E2 leave
.text:000193E3 retn 8
.text:000193E3 sub_192A8 endp
这段给一段C代码,写得很乱,大家凑合看
HANDLE hand=(HANDLE)0x288C58F1;//这里的几个全局变量大家自己定义,
这里特别标出这个主要是考虑要判断
NTSTATUS
NewZwSetEvent (
HANDLE EventHandle,
PLONG PreviousState
)
{
NTSTATUS status;
char *p,*ps;
char code[5]={0xe9,0,0,0,0};
char code1[5]={0xe9,0,0,0,0};
ULONG addr,addr1,addr2;
KIRQL Irql;
if (EventHandle!=hand||ExGetPreviousMode()==UserMode) //判断一下是不是自己调用
{
status= OldZwSetEventEventHandle,PreviousState);
}
else{
_asm{
mov eax,[ebp+4]
mov addrss,eax
}
DbgPrint("addrs is %08X\n",addrss);
p=(ULONG)addrss;
//2be1c1e902
ps=(char*)ExAllocatePoolWithTag(0,5,0);//这里判断一下哈,免得失败了 本人很懒
while(1)
{
if(*p==0x2b) //这里大家多判断几个特征,本人很懒
{
hookaddrs=(ULONG)p;//HOOK地址
Ret=(ULONG)p+5;//返回地址
break;
}
p--;
}
DbgPrint("hookaddrs is %08X\n",hookaddrs);
addr1=(ULONG)ps;
addr=(ULONG)addr1-hookaddrs-5; //1个跳转
addr2=(ULONG)MyHook1-addr1-5; //2个跳转
*(ULONG*)(code+1)=addr;
*(ULONG*)(code1+1)=addr2;
WPOFF();
Irql=KeRaiseIrqlToDpcLevel();
RtlCopyMemory(hookaddrs,code,5);
RtlCopyMemory(addr1,code1,5);
KeLowerIrql(Irql);
WPON();
status=STATUS_SUCCESS;
}
return status;
}
第4步看下过滤函数
pushf
pusha
push edi //SSDTADDR
push ebx //服务函数地址
push eax //SSDTINDEX
call sub_191AC //过滤函数
mov [esp+10h], eax
popa
popf
sub esp, ecx
shr ecx, 2
push addbp4addr5
retn
由于时间和篇幅关系文章先讲这么多先,等那天有空的时候继续写下过滤函数的处理,和360的建自己的
服务表的结构处理,和监控恢复HOOK的处理。此文章只是为了学习交流,如果此文章有错误的希望大牛门
指出和更正。共同学习 共同进步嘛
参考文章 教主的 分析360安全卫士的HOOK
[注意]APP应用上架合规检测服务,协助应用顺利上架!