前几天在论坛上看到一篇关于360HOOK框架的文章,写得不错。实际上,之前很多人都逆向破解了hookport.sys,而且教主还写了一套非常详细的文章。我才疏学浅,只是想补充一些科普知识,帮助像我这样的新手成长。这篇文章主要介绍了一些360的精妙应用,至于它Hook了哪些函数,大家可以去网上搜索教主的那篇文章。其实它就是一个结构。

亮点一:Hook KiFastCallEntry

科普一下KiFastCallEntry,快速系统调用SYSENTER。也就是说,每次系统调用的时候,最终都会通过KiFastCallEntry。我们来看看KiFastCallEntry函数的关键代码:

```assembly

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

```

这段代码展示了KiFastCallEntry函数的关键部分。首先,它获取当前线程(CurrentThread),然后将ebp寄存器的值设置为0FFFFFFFF。接下来,它将ebp的值存储到esi指向的内存地址中。最后,它检查ebp和esp的值,如果它们相等,则跳转到KiFastCallEntry2函数的末尾。

以下是重构后的代码段:

```plaintext

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 //判断是否为SSDTSHADOW或SSDT,要就是0 要就是0X10

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)

```

这段代码似乎是一段汇编语言的代码,但是没有明确的主题或功能,因此很难进行重构。然而,我可以为你提供一些基本的重构建议:

1. 保持一致性:尽量保持代码的格式和风格一致,例如缩进、空格和括号的使用等。

2. 提取函数:如果有多个相似的功能,可以考虑将它们提取为单独的函数,以提高代码的可读性和可维护性。

3. 注释:对于复杂的代码段,添加适当的注释可以帮助其他人理解你的代码的目的和工作方式。

4. 错误处理:考虑添加错误处理机制,以便在出现问题时能够给出有用的错误信息。

5. 变量命名:尝试使用具有描述性的变量名,以便其他人(以及你自己在未来)能够理解它们的用途。

由于缺乏具体的上下文信息,我无法提供更具体的重构建议。如果你能提供更多的信息,我会很乐意帮助你进一步重构这段代码。

在这段代码中,我们可以看到360的Hook机制。首先,我们需要了解一些寄存器的含义:EDI=SSDTaddr,EBX=系统服务地址,EAX=SSDTindex。有了这些寄存器,我们可以省去很多麻烦。

接下来,我们分析360是如何进行Hook的:

1. 将edi指向当前函数的ebp寄存器地址;

2. 保存ebp寄存器;

3. 将esp寄存器指向ebp寄存器;

4. 减少栈指针esp的值,为后续操作做准备;

5. 保存edi、esi、ebx这三个寄存器的值;

6. 保存ZwSetEvent的地址;

7. 初始化一个AnsiString类型的DestinationString;

8. 调用ds:RtlInitAnsiString进行初始化;

9. 获取ZwSetEventINDEXID = DB;

10. 保存ZwSetEventaddrid的值;

11. 初始化一个SpinLock。

通过以上步骤,360成功地实现了Hook机制。

```

; 以下是重构后的内容:

; 获取当前的CPL(Current Processor Level)值

mov eax, cr0 ; 将CR0寄存器的值放入EAX

and eax, 0FFFEFFFFh ; 与掩码进行位与操作以获取最低有效位(Least Significant Bit),即CPL值

push eax ; 将CPL值压入栈

; 初始化自旋锁

call ds:KeInitializeSpinLock

; 尝试获取自旋锁

lea ecx, [ebp+SpinLock] ; 将SpinLock地址加载到ECX寄存器

call edi ; 调用KfAcquireSpinLock函数,将返回的事件句柄存储在EDI寄存器中

; 如果成功获取到自旋锁,则继续执行后续操作

mov cl, al ; 将AL寄存器的值保存到CL寄存器中,用于判断是否需要释放自旋锁

push eax ; 将事件句柄压入栈

push ebx ; 将EBX寄存器清空,用于保存上一次的状态

push edx ; 将EDX寄存器清空,用于保存上一次的状态

cli ; 关闭中断

mov eax, cr0 ; 将CR0寄存器的值放入EAX

mov [ebp+var_C], eax ; 将CPL值保存到[ebp+var_C]内存位置

and eax, 0FFFEFFFFh ; 与掩码进行位与操作以获取最低有效位(Least Significant Bit),即CPL值

mov cr0, eax ; 将修改后的CPL值写回CR0寄存器

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 //将hook地址保存到EBX寄存器中

mov [edx+eax*4], ebx //更新服务描述表中的事件地址

mov eax, [ebp+var_C] //将CPL值加载到EAX寄存器中

mov cr0, eax //将修改后的CPL值写回CR0寄存器

sti //开启中断

pop edx //恢复EDX寄存器的值

pop ebx //恢复EBX寄存器的值

pop eax //恢复EAX寄存器的值

mov dl, cl //将CL寄存器的值复制到DL寄存器中,用于计算NewIrql的值

lea ecx, [ebp+SpinLock] //将SpinLock地址加载到ECX寄存器

call ds:KfReleaseSpinLock //释放自旋锁并将结果保存在esi和eax寄存器中

mov eax, DWORD PTR [ebp+1B850] //从内存中读取保存的事件句柄值(DWORD类型)到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函数

.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

重构后的内容如下:

```plaintext

.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

```

以下是重构后的代码:

```assembly

.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

```

以下是重构后的代码:

```assembly

.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 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:

```c

#include

#include

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;

p = code;

ps = code1;

addr = (ULONG_PTR)hand + 0x193CB; // 这里需要自己计算具体的地址

*(PULONG_PTR)(p) = (ULONG_PTR)ps + sizeof(code);

p += sizeof(ULONG_PTR);

*(PULONG_PTR)(p) = addr;

p += sizeof(ULONG_PTR);

*(PULONG_PTR)(p) = (ULONG_PTR)PreviousState + sizeof(PLONG);

p += sizeof(ULONG_PTR);

*(PULONG_PTR)(p) = &Irql;

p += sizeof(ULONG_PTR);

*(PULONG_PTR)(p) = (ULONG_PTR)ZwSetEventaddr + sizeof(void *); //这里是确定不是自己调用就调用原服务

p += sizeof(ULONG_PTR);

*(PULONG_PTR)(p) = (ULONG_PTR)&status;

}

```

以下是根据您提供的内容重构的代码:

```assembly

if (EventHandle != hand || ExGetPreviousMode() == UserMode) //判断一下是不是自己调用

{

status = OldZwSetEventEventHandle, PreviousState;

}

else

{

_asm

{

mov eax, [ebp + 4]

mov addrss, eax

}

DbgPrint("addrs is %08X

", addrss);

p = (ULONG)addrss;

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

", 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步是查看过滤函数。以下是过滤函数的代码:

```assembly

pushf //保护现场标志寄存器清零

pusha //数据栈指针寄存器清零

push edi //SSDTADDR寄存器清零

push ebx //服务函数地址寄存器清零

push eax //SSDTINDEX寄存器清零

call sub_191AC //调用过滤函数

mov [esp + 10h], eax //将结果压入堆栈顶(esp+10h)处

popa //清除数据栈指针寄存器中的内容,并弹出数据栈顶部的数据到eax、ebx、edi和esi中,恢复现场标志寄存器的值(因为在调用之前已经清零)

popf //清除现场标志寄存器中的信息,恢复现场标志寄存器的值(因为在调用之前已经清零)

sub esp, ecx //减少栈指针,为下一组指令腾出空间(ecx为5字节即5*4即为20字节的空间需求)

shr ecx, 2 //右移2位得到10字节的空间需求(因为每条跳转指令占用3字节空间,所以需要10条跳转指令的空间)

push addbp4addr5 //将计算出的跳转地址压入堆栈顶(esp+40h,即esp+28h+3*4)处,准备跳转执行过滤后的服务函数地址(MyHook1)处的代码段

retn //程序从过滤函数返回执行被过滤后的服务函数地址处的代码段

好的,我会尽力帮你完成这篇文章。你可以先提供一些关于你需要重构的文章的信息吗?例如,你需要重构哪些部分?你希望在文章中包含哪些内容?这些信息将有助于我更好地理解你的需求并为你提供帮助。