中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > 综合其它
二、防火墙原理及实现 (3)
作者:未知 时间:2005-07-27 23:20 出处:CSDN 责编:chinaitpower
              摘要:二、防火墙原理及实现 (3)

    有了上面的知识,让我们回顾一下先前讨论的问题,首先,用户调用APIrecv函数,程序运行到recv的入口地址处,此时堆栈中拥有用户调用recv的参数和用户代码中CALL [recv]的下一条指令的地址。堆栈如下图:

堆栈指针

[ESP]

堆栈的内容

堆栈内容的含义

0x00000100

0

参数

0x000000fc

len

参数

0x000000f8

buf

参数

0x000000f4

S

参数

0x000000f0

RetUserAddress

用户调用recv的下一条指令的地址

 

 

然后程序指针EIP被修改为recv入口处的地址,而入口地址处有一条简单的CALL指令,它使程序将recv的第6个字节的地址压入栈中(因为CALL XXXX占用5个字节,第六个字节被认为为返回地址),然后跳转到我们的无参数无返回值的通用替换函数中去了,好了看看现在堆栈中都有些什么?如图:

堆栈指针

[ESP]

堆栈的内容

堆栈内容的含义

0x00000100

0

参数

0x000000fc

len

参数

0x000000f8

buf

参数

0x000000f4

s

参数

0x000000f0

RetUserAddress

用户调用recv的下一条指令的地址

0x00000ec

RetrecvAddress

recv的第六个字节的地址

 

 

首先是参数,其次是用户调用recv后的返回值,然后是recv调用我们的替换函数中的返回值,紧接着就像刚才提到的那样,程序将EBP当前内容压入栈中。如图

堆栈指针[ESP]

堆栈的内容

堆栈内容的含义

0x00000100

0

参数

0x000000fc

len

参数

0x000000f8

buf

参数

0x000000f4

S

参数

0x000000f0

RetUserAddress

用户调用recv的下一条指令的地址

0x00000ec

RetrecvAddress

recv的第六个字节的地址

0x000000e8

OldEBP

保存的旧的EBP的内容,然后[EBP]= 0x000000e8

0x000000e4

OldEBX

保存的旧的EBX的内容

0x000000e0

OldESI

保存的旧的ESI的内容

0x000000dc

OldEDI

保存的旧的EDI的内容,此时[ESP]=0x000000dc

 

 

此时我们可以看到,[EBP]为保存的ebp的值,现在对我们没有用处,函数返回前用于恢复EBP的值,[EBP+4]recv函数的CALL XXXX后面指令的地址(也就是第六个字节的地址),我们可以通过将此值减去5来得到recv的入口地址,这样在我们所有hookapi函数的列表中进行检索,就可以匹配出用户调用的是哪一个API函数,从而为后面恢复和再次改变该API的入口5字节做准备,因为调用任何我们需要HOOKAPI程序都会进入到这个无返回值无参数的函数,所以通过这种方法找到当前HOOK的是哪一个API,从而可以区分不同的API进行特殊的处理。[EBP+8]保存的是用户调用recv后的返回地址,由于我们执行完替换函数后,应该返回到这个地址,而不应该返回到recv的第6个字节处执行,所以我们还是需要保存下这个值,以便在我们用ret返回前把它压栈从而使程序返回到用户调用recv的下一条指令处继续运行。

       

        我们先定义如下函数:

        void CommonFunc(void);

        我们现在实现它,请注意参考上面堆栈表格。

        void CommonFunc(void)

        {

            DWORD pdwCall;      // recv入口地址

            DWORD dwRtAddr;     // 我们的函数真正要返回的地址

            DWORD* pdwParam;        // 第一个参数的地址

            DWORD dwParamCount; // 参数个数

            DWORD dwParamSize;  // 所有参数所占用的大小应该=4* dwParamCount

DWORD dwRt;         // 返回值

            _asm

            {

                lea EAX,[EBP+4]     // recv入口处第6个字节的地址

                mov [pdwCall],EAX

                mov EAX,[EBP+8]     // 用户调用recv(call XXXX)后面一条指令的地址

                mov [dwRtAddr],EAX

                lea EAX, [EBP+12]   // 第一个参数的地址!

                mov [pdwParam],EAX

            }

            (*pdwCall) -= 5;    // 获得recv入口地址

            HOOKINFO *hi = findHookInfo(pdwCall);   // 通过原始API的入口地址获得此API的相关信息

            memcpy(pdwCall,hi->OrgApi5bytes,5);     // 恢复被调用API的前5个字节,使下面的代码可以正常调用

            // 下面准备进入用户针对此API的替换函数,现准备参数

            dwParamCount = hi->ParamCount;  // 得到本API的参数个数

            dwParamSize = 4*dwParamCount;       // 计算参数所占用大小

            DWORD pdwESP;

            _asm

            {

                sub esp,[dwParamSize]   // 将栈增加,可以容纳参数

                mov [pdwESP],esp            // 保存当前栈的地址

            }

            memcpy(pdwESP,pdwParam,dwParamSize);//将用户传递的参数拷贝到栈中

            hi->myAPIFunc();    // 调用用户针对此API的替换函数

            _asm

            {

                mov [dwRt],eax  // 保存返回值

            }

            // 如果是CreateProcess,那么继续hook

            pPi = (PROCESS_INFORMATION*)pdwParam[9];

    if(strcmpi(pai->szOrgApiName,"CreateProcessA") != 0 || strcmpi(pai->szOrgApiName,"CreateProcessW") != 0)

            {

                InjectDll(pPi->dwProcessId,m_szDllPathName);

            }

            // 下面再次修改原始API的前5个字节

            memcpy(pdwCall,JMPCODE,5);  // #define JMPCODE 0xE8

            DWORD* pdwapi = pdwCall[1];

            pdwapi[0] = (DWORD) CommonFunc – (DWORD)pdCall – 5;   // CommonFunc函数地址的偏移

 

 

            // 下面准备返回的操作

            _asm

{

                add esp,[dwParamSize] // 清理我们为了调用真正的替换函数而分配的堆栈里的参数

                // 下面弹出所有保存的寄存器值(按照入栈的逆顺序)

                pop EDI // 恢复EDI

                pop ESI // 恢复ESI

                pop EBX // 恢复EBX

                // 我们没有改动过EBP的值,所以EBP指向堆栈中OldEBP的位置

                mov ESP,EBP

                pop EBP // 恢复EBP

                // 由于堆栈中还剩下参数和两个返回地址(我们真正要返回的地址和原始API中的第6个字节的地址),所以我们把这些数据也清除出堆栈

                add ESP,8 // 清除两个返回地址

                mov ECX,[dwParamSize]   // 获得参数的大小

                add ESP,ECX // 清除参数

                mov EAX,[dwRt]  // 设置返回值

                // 由于调用ret返回时,程序先从堆栈中取出返回地址,所以我们把要真正返回的地址压入堆栈中

                mov EDX,[dwRtAddr] // 设置返回地址

                push EDX

                ret // 返回

}

}

       最后要注意得一点是,如果要执行得API函数是CreateProcess,那么应该把它新开启得进程也HOOK掉。以上我们了解了通用替换函数的原理,那么让我们深入的讨论CHookApi类,并且实现它。

 

--------------------------------------

联系我(liutao_free@sohu.com)

--------------------------------------

联系我(liutao_free@sohu.com)

联系我(liutao_free@sohu.com)


关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有