;下面的代码其实就是为了找到360safebox的hook而已nt!KiFastCallEntry+0x89:804df77d mov dword ptr [ebp+4],edi804df780 sti804df781 mov edi,eax804df783 shr edi,8804df786 and edi,30h804df789 mov ecx,edi804df78b add edi,dword ptr [esi+0E0h]804df791 mov ebx,eax804df793 and eax,0FFFh804df798 cmp eax,dword ptr [edi+8]804df79b jae nt!KiBBTUnexpectedRange (804df4e2)804df7a1 cmp ecx,10h804df7a4 jne nt!KiFastCallEntry+0xcc (804df7c0)804df7a6 mov ecx,dword ptr ds:[0FFDFF018h]804df7ac xor ebx,ebx804df7ae or ebx,dword ptr [ecx+0F70h]804df7b4 je nt!KiFastCallEntry+0xcc (804df7c0)804df7b6 push edx804df7b7 push eax804df7b8 call dword ptr [nt!KeGdiFlushUserBatch (8055a6c4)]804df7be pop eax804df7bf pop edx804df7c0 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 sysenter804df7c8 mov ebx,dword ptr [edi+0Ch]804df7cb xor ecx,ecx804df7cd 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,2804df7da 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],edxnt!KiServiceExit:;...省略;接下来是360safebox的过滤函数的分析。;其中的push ret用的很邪恶。。;行号 ;OpCodef71b767c mov edi,edi ;其实就是2字节版本的NOP,而且比NOP更节约CPU。——应该是为了对齐吧?f71b767e pushfd ;保存现场f71b767f pushad ;保存现场f71b7680 push edi ;函数f71b6e40的参数5f71b7681 push ecx ;函数f71b6e40的参数4f71b7682 push esi ;函数f71b6e40的参数3f71b7683 push ebx ;函数f71b6e40的参数2f71b7684 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 :Df71b76b3 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的作品。
[课程]FART 脱壳王!加量不加价!FART作者讲授!
mov edi,esp ;把esp放到edi中