zoukankan      html  css  js  c++  java
  • 班门弄斧一次~~VC++.NET 2008写的HOOK RECV代码

    确实是班门弄斧,因为我是学VB的,虽然VB6和.NET都算入门吧,可真正拿VC++写程序还是第一次。学B/C/D的就莫看了。

    由于是用上两篇写的远线程调用,所以很多参数等等都是传入的,而不是直接获取的。

    下面就一个用VB的人能理解为目标做简单说明和注释:

    1、VC里面声明变量和VB不一样:类型在前,名称在后,例如:

    BYTE B ;

    等同于:

    dim B as byte

    VC用分号来识别行结束,而VB用回车;VB用圆括号表示数组,VC用方括号……不多说了,我也不是很清楚,总之习惯就好

    2、VC里面的函数、变量等等区分大小写,VB不区分,这个一定要注意!

    3、类型转换:VB有显式或隐式的,而VC必须用显式的——即时内存里面存的就是那个东东你也不能直接拿来用,够严格,不过指针够灵活也够太灵活……转换时使用的方法就是在圆括号里面写入你要的类型,后面跟着你的参数就可以。例如:

    (DWORD)LEN;就是把LEN转换成DWORD类型,据我理解要么是UINTEGER,要么是INTEGER,可能不同API要求不同,我一直都是按INTEGER处理的,也就是说,前面那句相当于CTYPE(LEN,INTEGER)了。

    4、&将会吧后面的变量变为指针,*将会把指针变成相应的数据……说的不确切,应该是转换吧

    5、其他看看就知道了,VC用API、常数等根本不声明……而是导入.H文件吧(头文件,而且里面可以用宏来改变API书写的名称,就像VB声明API时那个别名吧),反正这个工程里没用到。

    以上言论如有误导,请尽快指出………………以免造孽更深…………谢谢………………

    //自定义APIHOOK结构
    typedef struct
    {
        FARPROC funcaddr;  //RECV函数所在地址
     BYTE    olddata[8];      //RECV函数开头的8个字节原始数据
     BYTE    newdata[8];    //我们要写入的数据,汇编代码在下面,利用了EAX存储要跳转的地址,并且FF E0这个操作是绝对地址的
    }HOOKSTRUCT;
    //0-4 : move eax 0x00000000 //绝对地址,避免了一个换算,其实这个换算也简单,ADDRESS-MFUNADDRESS-5即是
    //5-7 : jmp eax

    HWND  _fHandle ;  //接收消息窗口句柄
    DWORD _dwIdNew ;  //PID
    HANDLE hProc;  //进程内核句柄,就是OPENPROCESS打开PID得到的那个东东——读写内存用到
    ////////////////////////////////////////
    DWORD _ws2_RecvIndex = 1 ;  //ws2_32.dll RECVHOOK标志,实际和HOOK没关系,但是当你HOOK的函数多起来,你得区分是哪个函数发来的消息啊!
    HOOKSTRUCT _ws2_RecvHook; //ws2_32.dll HOOK结构
    ////////////////////////////////////////

    //这下面的这些家伙,就是一个定义,和下面写的函数实体一样声明即可,VB里不用这么干编译器也能识别都有哪些函数,可VC不成

    int WINAPI m_ws2_recv(SOCKET s, char FAR *buf, int len, int flags);

    void SendMsg(char *buf,DWORD len,DWORD Index);
    void HookOnOne(HOOKSTRUCT *hookfunc);
    void HookOffOne(HOOKSTRUCT *hookfunc);
    bool HOOKAPI(DWORD Address,HOOKSTRUCT *hookfunc,DWORD mFncAddress);

    //下面传入了一些参数:被注入进程PID,要接收返回信息窗口的句柄

    extern "C" _declspec(dllexport) void __stdcall Init(DWORD PID ,HWND FormHandle)
    {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());   //这个……VC里叫宏吧,哎……说来话长,这句去掉也无所谓的

     _dwIdNew = PID ;            //备份PID,其实后面代码没用到,主要是为了识别注入了多个程序的话,是哪个程序发回来的信息呢
     _fHandle = FormHandle ;  //接收消息的窗口句柄,SendMsg函数用到
     hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, _dwIdNew);   //打开进程,进一步操作他
    }

    //下面这个用来:记录被HOOK函数前8字节原始数据、构建新的8字节代码、将新代码替换RECV函数原来代码的

    //说明全部都是以RECV函数为例,其实这个函数是一个通用函数

    bool HOOKAPI(DWORD Address,HOOKSTRUCT *hookfunc,DWORD mFncAddress)
    {
     hookfunc->funcaddr = (FARPROC)Address;  //这个地方用了一个转换,把传入值转换成何结构当中声明的一样
        memcpy(hookfunc->olddata, hookfunc->funcaddr, 8); //记录RECV函数前8字节原始数据
     //下面开始构建汇编代码{0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00 }
     hookfunc->newdata[0]=0xB8;   //MOVE EAX (EAX后面的数就是下面4字节了)
     hookfunc->newdata[1]=0x00;
     hookfunc->newdata[2]=0x00;
     hookfunc->newdata[3]=0x00;
     hookfunc->newdata[4]=0x00; //到这里是 MOVE EAX 00000000 ,这里的地址,将会在下面被替换(注意是绝对地址)
     hookfunc->newdata[5]=0xFF; 

     hookfunc->newdata[6]=0xE0;//到这里是 JMP EAX
     hookfunc->newdata[7]=0xCC;//填充一个INT3 ,其实NOP(&h90)啥的也行,换句话说这个不改、不理他都可以,不过翻看帖子时看到一些人说CPU识别错代码,谁知道真假,还是放个显眼的比较保险……
     memcpy(&hookfunc->newdata[1], &mFncAddress, 4);  //这里替换了上面提到的 00000000 为我们用来代替RECV的函数的地址
     HookOnOne(hookfunc);  //将RECV函数前8字节替换为我们构建的代码——开始HOOK RECV
     return true;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    //recv,这个函数将实现RECV函数的替换:只需执行一次即可
    ////////////////////////////////////////////////////////////////////////////////////
    extern "C" _declspec(dllexport) bool __stdcall Hook_ws2_Recv(DWORD RecvAddress)
    {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     HOOKAPI(RecvAddress,&_ws2_RecvHook,(DWORD)m_ws2_recv); //这里,(DWORD)m_ws2_recv的返回值就是m_ws2_recv函数的地址!你看人家VC多方便……
        return true;
    }

    //显然了,这个事解除RECV HOOK的
    extern "C" _declspec(dllexport) bool __stdcall UnHook_ws2_Recv()
    {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     HookOffOne(&_ws2_RecvHook);
        return true;
    }
    //---------------------------------------------------------------------------

    //这个函数,就是我们用来替换RECV函数的家伙了,必须让这个家伙和RECV函数声明相同,这么相同呢,很简单,VC++.NET也有类似VB的代码提示(虽然少点),找一个适当的地方打上recv(小写)然后打圆括号……看到了?
    int WINAPI m_ws2_recv(SOCKET s, char FAR *buf, int len, int flags)
    {
        int nReturn = 0;  //这个家伙记录了RECV的返回值,-1为错误,我们不处理,0为断开,其他嘛,就是实际从SOCKET缓冲区复制了多少字节到RECV缓冲区。简单的说,-1,0都不处理,其他值就是我们要从BUF里面读多少字节了!

     HookOffOne(&_ws2_RecvHook); //先关闭HOOK,因为已经进入我们的函数了
        nReturn = recv(s, buf, len, flags);  //先运行原来的RECV,否则我们不能得到或不能得到全部被复制的内容
     HookOnOne(&_ws2_RecvHook); //继续HOOK

     //sndmsg((DWORD)len,(DWORD)nReturn);

     if ( nReturn == SOCKET_ERROR )
     return -1 ;
     
     SendMsg(buf ,nReturn,_ws2_RecvIndex); //发送BUF里面NRETURN个字节到接收窗体
      
        return(nReturn);
    }
    /////////////////////////////////////////////////////////////////////////////
    //WSARECV

    /////////////////////////////////////////////////////////////////////////////////

    //将原来的函数内容写回——关闭HOOK
    void HookOffOne(HOOKSTRUCT *hookfunc)
    {
     WriteProcessMemory(hProc, hookfunc->funcaddr, hookfunc->olddata, 8, 0);
    }
    //---------------------------------------------------------------------------

    //将我们构建的代码写入——打开HOOK
    void HookOnOne(HOOKSTRUCT *hookfunc)
    {
     WriteProcessMemory(hProc, hookfunc->funcaddr, hookfunc->newdata, 8, 0);
    }

    //发送WM_COPYDATA消息到指定窗口
    void SendMsg(char *buf,DWORD len,DWORD Index)
    {
              COPYDATASTRUCT cds;  
              cds.dwData = 0;//sizeof(COPYDATASTRUCT);    //有的说这个参数影响发送,还没用到,本来是存PID的,个人觉得应该不会影响
              cds.cbData = len;    //LPDATA里面的字节数
              cds.lpData = buf;   //实际数据
              SendMessage(_fHandle,WM_COPYDATA,(WPARAM)Index,(LPARAM)&cds);  //发到INIT里面指定的那个窗口
    }

    今天来编辑一下帖子,因为用VB.NET完成了HOOK工作。

    其实用VB.NET HOOK本进程API早就弄好了,并且用了一段时间,感觉还是满意的。但是注入其他进程一直在用VC,写起来代码真的不如VB.NET顺手,于是想到注入VB.NET的DLL到其他进程,其实这件事很早就有人在做,主要是给对方进程加载CLR的问题,其实这项工作都是由mscoree.dll完成的,本想用VB.NET写一段汇编代码注入到对方进程然后远线程,可看了看工作量确实不小,于是还是使用了一个VC写的DLL,导出了一个函数,然后远线程调用它用以加载CLR和VB.NET的DLL,这个VC的DLL很简单,只是调用CorBindToRuntimeEx函数来加载CLR,并由之调用VB.NET写的DLL(就是一个正常的类库,无需任何处理)中的方法。这样既然可以调用VB.NET的DLL中的方法了,那么接下来只需要在VB.NET的DLL中进行处理就行了。总结起来,主要是3块:

    1、VC的DLL:导出一个函数,用来加载CLR而后加载VB.NET的DLL并调用其指定方法,十来行代码而已。

    2、注入器:远线程运行API的能力(这个代码很久之前也发过了),当然,根据需要还可能需要设置一些进程访问权限或者更底层的东西。

    3、VB.NET的DLL:公开一个方法,使得CLR能够找到并直接调用它。接下来的事情都在这个DLL里面处理就行了,爱启动线程启动线程,想读啥就读啥,想写啥就写啥吧。结合以前完成的HOOK本进程API的一些方法,完全可以用托管代码来HOOK和处理了。

    具体实现就不贴了,毕竟不该归为原创,网上已经有很多类似代码了,只是直接调用CorBindToRuntimeEx,使用远线程的并不是很多,之所以使用远线程是因为可以在VC的DLL的导出函数那里传入一些参数。例如:加载的CLR版本,标志,要调用的托管DLL,类,方法,参数等都可以由注入器传递,从而提高了通用性和控制的灵活性。毕竟咱还没到纠结到底几个DLL的境界,当CLR加载完成并运行托管DLL时对方进程还不知道多出来多少DLL呢。

  • 相关阅读:
    Kmeans中文聚类
    第四周周总结
    数据清洗第一天
    第三周周总结
    关于sqoop上传mysql数据到hive报错的问题
    天津东软实训第十一天——Hive连接JDBC
    天津东软实训第十天——Hive配置
    天津东软实训第九天——MapReduce实战
    天津东软实训第八天------倒排索引
    Intellij IDEA 创建maven项目,利用API操作HDFS
  • 原文地址:https://www.cnblogs.com/zcsor/p/1439792.html
Copyright © 2011-2022 走看看