手记式的东西。自认为ASM还算没白学。。大概可以及格啦
![]()
至少应该不会被打PP咯~!
![]()
引用:
;下面的代码其实就是为了找到360safebox的hook而已
nt!KiFastCallEntry+0x89:
804df77d mov dword ptr [ebp+4],edi
804df780 sti
804df781 mov edi,eax
804df783 shr edi,8
804df786 and edi,30h
804df789 mov ecx,edi
804df78b add edi,dword ptr [esi+0E0h]
804df791 mov ebx,eax
804df793 and eax,0FFFh
804df798 cmp eax,dword ptr [edi+8]
804df79b jae nt!KiBBTUnexpectedRange (804df4e2)
804df7a1 cmp ecx,10h
804df7a4 jne nt!KiFastCallEntry+0xcc (804df7c0)
804df7a6 mov ecx,dword ptr ds:[0FFDFF018h]
804df7ac xor ebx,ebx
804df7ae or ebx,dword ptr [ecx+0F70h]
804df7b4 je nt!KiFastCallEntry+0xcc (804df7c0)
804df7b6 push edx
804df7b7 push eax
804df7b8 call dword ptr [nt!KeGdiFlushUserBatch (8055a6c4)]
804df7be pop eax
804df7bf pop edx
804df7c0 inc dword ptr ds:[0FFDFF638h]
804df7c6 mov esi,edx ;edx中是参数(堆栈顶的指针),如下:
;ntdll!ZwOpenProcess:
;7c92dd7b mov eax,7Ah
;7c92dd80 mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
;7c92dd85 call dword ptr [edx] edx中是7ffe0300
;7c92dd87 ret 10h
;lkd> dd 7ffe0300
;7ffe0300 7c92eb8b ;省略...
;lkd> u 7c92eb8b
;ntdll!KiFastSystemCall:
;7c92eb8b mov edx,esp ;把当前堆栈放到edx中
;7c92eb8d sysenter
804df7c8 mov ebx,dword ptr [edi+0Ch]
804df7cb xor ecx,ecx
804df7cd mov cl,byte ptr [eax+ebx]
804df7d0 mov edi,dword ptr [edi]
804df7d2 mov ebx,dword ptr [edi+eax*4]
804df7d5 jmp f71b767c ;诺。转跳到360safebox的处理函数
;360safebox的jmp语句覆盖掉的代码:
;sub esp,ecx
;shr ecx,2
804df7da mov edi,esp ;把esp放到edi中
804df7dc cmp esi,dword ptr [nt!MmUserProbeAddress (80560034)]
804df7e2 jae nt!KiSystemCallExit2+0x9f (804df990)
804df7e8 rep movs dword ptr es:[edi],dword ptr [esi] ;把参数复制到内核态。
804df7ea call ebx ;ebx就是相应的服务历程地址了。
804df7ec mov esp,ebp ;恢复堆栈。(之前保存堆栈的代码自己用WinDBG找吧。。偶偷懒了。。)
804df7ee mov ecx,dword ptr ds:[0FFDFF124h] ;0FFDFF124h中储存着当前线程的ETHREAD这就是另外一个故事了 :)
804df7f4 mov edx,dword ptr [ebp+3Ch] ;似乎是在恢复现场吧。大概是KiServiceExit中的一些事情了。略。
804df7f7 mov dword ptr [ecx+134h],edx
nt!KiServiceExit:
;...省略
;接下来是360safebox的过滤函数的分析。
;其中的push ret用的很邪恶。。
;行号 ;OpCode
f71b767c mov edi,edi ;其实就是2字节版本的NOP,而且比NOP更节约CPU。应该是为了对齐吧?
f71b767e pushfd ;保存现场
f71b767f pushad ;保存现场
f71b7680 push edi ;函数f71b6e40的参数5
f71b7681 push ecx ;函数f71b6e40的参数4
f71b7682 push esi ;函数f71b6e40的参数3
f71b7683 push ebx ;函数f71b6e40的参数2
f71b7684 push eax ;函数f71b6e40的参数1
;这些寄存器中的内容:
;Eax: 服务ID
;Ebx: 服务历程地址(你可以把这个改成原始地址,这样就绕过了SSDT HOOK :P)
;Esi: KiFastCall之前的堆栈栈顶(我没验证,不过也差不离,有兴趣自己看下)
;Ecx: 参数长度
;Edi: 服务表基址(比如KiServiceTable、W32pServiceTable)
f71b7685 call f71b6e40 ;应该是个过滤函数吧。
f71b768a mov dword ptr [esp+10h],eax ;把过滤函数的返回值保存起来 - 似乎放到了ebx中??? 我这么写的时候直接就bsod了。。难道过滤函数中还。。。?
f71b768e popad ;恢复现场
f71b768f popfd ;恢复现场
f71b7690 sub esp,ecx ;应该是360safebox在自己实现覆盖掉的代码了。
f71b7692 shr ecx,2 ;同上
f71b7695 mov edi,esp ;自己实现的不仅仅是覆盖掉的代码了。。(参考前文KiFastCallEntry代码)
f71b7697 cmp esi,dword ptr ds:[0F71B9108h] ;在我的机器上,F71B9108h处的内容为80560034(MmUserProbeAddress)还是在自己实现。
;条件转移指令JB/JNAE
;格式: JB/JNAE 标号
;功能: 低于/不高于等于时转移
;也就是说如果参数比MmUserProbeAddress低就jmp(如果比MmUserProbeAddress高就是内核下的call,忽略之)
f71b769d jb f71b76b1 ;----------------------------------------------+
;条件转移指令JE/JZ |
;格式: JE/JZ标号 |
;功能: ZF=1,转至标号处执 |
;ZF(ZeroFlag) 零标志:当结果为负时,ZF=1否则为0。 |
f71b769f test byte ptr [ebp+6Ch],1 ;if (*(PBYTE)[ebp+6Ch] && 1) | -------------没看懂
f71b76a3 je f71b76b1 ; { __asm jmp f71b76b1; } ------+ -------------没看懂,似乎是在检测高2G什么的。。也可能是判断之前过滤函数的返回值吧。。
f71b76a5 mov eax,0C0000005h ;0xC0000005 = 内存分配访问无效。 |
;把0xC0000005放到eax的意思就是返回值为0xC0000005,看下来的东西: |
f71b76aa push dword ptr ds:[0F71C01B0h] ; 真淫荡啊。。。。 |
f71b76b0 ret ;ret了 其实是ret到了dword ptr ds:[0F71C01B0h]处这是|
;堆栈的一些事情,这是另外一个故事了 :D。[0F71C01B0h]=804df7ec(nt!KiFastCa|
;llEntry+0xf8)。在这儿直接越过了调用服务函数还有复制参数的一些事情, |
;返回了用户态。:D |
f71b76b1 rep movs dword ptr es:[edi],dword ptr [esi] ;<------------------+ 还是在自己实现KiFastCallEntry
;难道所有系统的KiFastCallEntry都一样么?不过这种ASM函数换谁也不愿意没事干就重写一次的,何况是M$那群BT :D
f71b76b3 push dword ptr ds:[0F71C01B0h] ;精妙!! 之后的ret首先call到了服务函数(ebx中)
f71b76b9 push ebx ;然后服务函数的ret就call到了[0F71C01B0h](=nt!KiFastCallEntry+0xf8(804df7ec))处
f71b76ba ret ;ret到了ebx
;几次push ret最后都有804df7ec处的一句 mov esp,ebp 来帮忙完成了所有的堆栈平衡 :D
;做的真的很漂亮,不愧是doskey的作品。