我的方法:
通过CreateRemoteThread的方法注入麻烦,所以直接用hook,写线程钩子.
再将recv的地址前5个字节保存到old
再将recv地址前5字节改成jmp xxxxxx的机器码(xxxxxx为自己的函数newRecv)
再到自己newRecv把buf数据写a.txt文件中,再newRecv结尾将old还原到recv函数前5字节,并调用recv
上面是我的想法.不知能否实现?
但问题就是,最后recv函数还原了.也就是recv不会再跳到newRecv中去了.那下次有数据来就不会先执行newRecv再执行recv了那怎么办?
我可是想一直截取recv的数据哦..

解决方案 »

  1.   

    因为是截游戏包.所以在newRecv执行将结束,转向recv执行.
      

  2.   

    你用的方法叫Detour,但做法不完善。
    应该反汇编recv的入口指令,取出不少于5字节的完整指令,将其搬移的自己的内存中,并在后面接上一条jmp指令,跳回原本的下一条指令。
    你可以在网上搜一下,有更详细的说明和源代码。
      

  3.   

    问题很明显:
    同步问题,当你正在修改recv前5个字节,线程被切换出去了怎么办?况且函数头部5字节不一定是整数条指令(虽然xp sp2版本的recv是),所以这就存在了同步以及指令碎片问题.//前5字节不是整数条指令
    ntdll!NtCreateFile:
    7c92d682 b825000000      mov     eax,25h
    7c92d687 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
    7c92d68c ff12            call    dword ptr [edx]
    7c92d68e c22c00          ret     2Ch用Microsoft出的Detours吧..或者改用IAT钩子。
    LZ觉得CreateRemoteThread麻烦就给你一个类吧.
    ////////////////////////////////////////////////
    // RemThreadInjector.h文件
    #include <windows.h>class CRemThreadInjector
    {
    public:
    CRemThreadInjector(LPCTSTR pszDllName);
    ~CRemThreadInjector(); // 注入DLL到指定的进程空间
    BOOL InjectModuleInto(DWORD dwProcessId); // 从指定的进程空间卸载DLL
    BOOL EjectModuleFrom(DWORD dwProcessId);protected:
    char m_szDllName[MAX_PATH]; // 调整特权级别
    static BOOL EnableDebugPrivilege(BOOL bEnable);
    };////////////////////////////////////////////////
    // RemThreadInjector.cpp文件
    #include "stdafx.h"
    #include "RemThreadInjector.h"
    #include <tlhelp32.h>CRemThreadInjector::CRemThreadInjector(LPCTSTR pszDllName)
    {
    strncpy(m_szDllName, pszDllName, MAX_PATH);
    EnableDebugPrivilege(TRUE);
    }CRemThreadInjector::~CRemThreadInjector()
    {
    EnableDebugPrivilege(FALSE);
    }BOOL CRemThreadInjector::EnableDebugPrivilege(BOOL bEnable) 
    {
    // 附给本进程特权,以便访问系统进程
    BOOL bOk = FALSE; 
    HANDLE hToken;

    // 打开一个进程的访问令牌
    if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) 
    {
    // 取得特权名称为“SetDebugPrivilege”的LUID
    LUID uID;
    ::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID); // 调整特权级别
    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = uID;
    tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
    ::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
    bOk = (::GetLastError() == ERROR_SUCCESS); // 关闭访问令牌句柄
    ::CloseHandle(hToken);
    }
    return bOk;
    }BOOL CRemThreadInjector::InjectModuleInto(DWORD dwProcessId)
    {
    if(::GetCurrentProcessId() == dwProcessId)
    return FALSE; // 首先查看目标进程是否加载了这个模块
    BOOL bFound = FALSE;
    MODULEENTRY32 me32 = { 0 };
    HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
    me32.dwSize = sizeof(MODULEENTRY32);
    if(::Module32First(hModuleSnap, &me32))
    {
    do
    {
    if(lstrcmpiA(me32.szExePath, m_szDllName) == 0)
    {
    bFound = TRUE;
    break;
    }
    }
    while(::Module32Next(hModuleSnap, &me32));
    }
    ::CloseHandle(hModuleSnap); // 如果能够找到,就不重复加载了(因为重复加载没有用,Windows只将使用计数加1,其它什么也不做)
    if(bFound)
    return FALSE;
    // 试图打开目标进程
    HANDLE hProcess = ::OpenProcess(
    PROCESS_VM_WRITE|PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION, FALSE, dwProcessId);
    if(hProcess == NULL)
    return FALSE;
    // 在目标进程中申请空间,存放字符串pszDllName,作为远程线程的参数
    int cbSize = (strlen(m_szDllName) + 1);
    LPVOID lpRemoteDllName = ::VirtualAllocEx(hProcess, NULL, cbSize, MEM_COMMIT, PAGE_READWRITE);
    ::WriteProcessMemory(hProcess, lpRemoteDllName, m_szDllName, cbSize, NULL); // 取得LoadLibraryA函数的地址,我们将以它作为远程线程函数启动
    HMODULE hModule=::GetModuleHandle("kernel32.dll");
    LPTHREAD_START_ROUTINE pfnStartRoutine = 
    (LPTHREAD_START_ROUTINE)::GetProcAddress(hModule, "LoadLibraryA");
    // 启动远程线程
    HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnStartRoutine, lpRemoteDllName, 0, NULL);
    if(hRemoteThread == NULL)
    {
    ::CloseHandle(hProcess);
    return FALSE;
    }
    // 等待目标线程运行结束,即LoadLibraryA函数返回
    ::WaitForSingleObject(hRemoteThread, INFINITE);

    ::CloseHandle(hRemoteThread);
    ::CloseHandle(hProcess);
    return TRUE;
    }BOOL CRemThreadInjector::EjectModuleFrom(DWORD dwProcessId)
    {
    if(::GetCurrentProcessId() == dwProcessId)
    return FALSE; // 首先查看目标进程是否加载了这个模块
    BOOL bFound = FALSE;
    MODULEENTRY32 me32 = { 0 };
    HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
    me32.dwSize = sizeof(MODULEENTRY32);
    if(::Module32First(hModuleSnap, &me32))
    {
    do
    {
    if(lstrcmpiA(me32.szExePath, m_szDllName) == 0)
    {
    bFound = TRUE;
    break;
    }
    }
    while(::Module32Next(hModuleSnap, &me32));
    }
    ::CloseHandle(hModuleSnap); // 如果找不到就返回出错处理
    if(!bFound)
    return FALSE; // 试图打开目标进程
    HANDLE hProcess = ::OpenProcess(
    PROCESS_VM_WRITE|PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION, FALSE, dwProcessId);
    if(hProcess == NULL)
    return FALSE;
    // 取得LoadLibraryA函数的地址,我们将以它作为远程线程函数启动
    HMODULE hModule=::GetModuleHandle("kernel32.dll");
    LPTHREAD_START_ROUTINE pfnStartRoutine = 
    (LPTHREAD_START_ROUTINE)::GetProcAddress(hModule, "FreeLibrary");
    // 启动远程线程
    HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnStartRoutine, me32.hModule, 0, NULL);
    if(hRemoteThread == NULL)
    {
    ::CloseHandle(hProcess);
    return FALSE;
    }
    // 等待目标线程运行结束,即FreeLibrary函数返回
    ::WaitForSingleObject(hRemoteThread, INFINITE);

    ::CloseHandle(hRemoteThread);
    ::CloseHandle(hProcess);
    return TRUE;
    }使用方法:
    CRemThreadInjector m_injector(dll的路径);
    m_injector.InjectModuleInto(要注入的进程ID);
      

  4.   

    #include "stdafx.h"
    #include "replace.h"#pragma data_seg("publicdata") 
    HHOOK hhook=NULL; 
    HINSTANCE hInstance=NULL;
    #pragma data_seg()BYTE oldData[5];
    BYTE newData[5];
    HANDLE hProc;
    FARPROC recvAdd;BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
    {
        switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
    hInstance = GetModuleHandle(NULL);
    break;
    }
    case DLL_THREAD_ATTACH:
    break;
    case DLL_THREAD_DETACH:
    break;
    case DLL_PROCESS_DETACH:
    {
    UnHook();
    }
        }
        return TRUE;
    }void WINAPI InstallHook(DWORD threadId)
    {
    hhook = SetWindowsHookEx(WH_GETMESSAGE, HookProc, hInstance, threadId);
    Record();
    SetHookStart();
    }void WINAPI UnHook()
    {
    UnhookWindowsHookEx(hhook);
    }LRESULT CALLBACK HookProc(int code, WPARAM wparam, LPARAM lparam)
    {
    return CallNextHookEx(hhook, code, wparam, lparam);
    }void WINAPI Record()
    { newData[0] = 0xe9;
    hProc = ::GetCurrentProcess();
    recvAdd = GetProcAddress(LoadLibrary("ws2_32.dll"), "recv");
    memcpy(oldData, (char *)recvAdd, 5);
    DWORD *p = (DWORD*)&newData[1];
    *p = (DWORD *)recvAll-(DWORD *)recvAdd-5;
    }void WINAPI SetHookStart()
    {
    ::WriteProcessMemory(hProc, recvAdd, newData, 5, NULL);
    }void WINAPI SetHookEnd()
    {
    ::WriteProcessMemory(hProc, recvAdd, oldData, 5, NULL);
    }int WINAPI recvAll(SOCKET s, char *str, int len, int flag)
    {
    fstream file;
    file.open("C:\\recv.txt", ios::app|ios::out|ios::in);
    file.write(str, strlen(str));
    file.close();
    SetHookEnd();
    recv(s, str, len, flag);
    SetHookStart();
    return 0;
    }
      

  5.   

    程序直接调用上面代码(dll)中的InstallHook(线程ID)
      

  6.   

    我又想了一下,SetWindowsHook还是不行,因为这种Hook最多只能Hook到同一桌面的所有线程,其它桌面的Hook不到。
    我提一种新的方法,你可以试试。
    在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows下面,修改AppInit_DLLs值,给出一个dll的名称及路径,所有程序启动时会自动加载该dll。顺便提两点你的代码中与SetWindowsHook有关的问题:
    GetModuleHandle(NULL)返回的是exe的句柄,你需要的是dll的句柄,直接用DllMain的第1个参数即可。
    SetWindowsHookEx的最后参数给0才能Hook所有线程。
      

  7.   

    谢谢你.你提出的第一个问题我刚才在找BUG时找出来了..不过你提出的第二个问题本身就有问题.如果Set...最后参数为0就是全局钩子了.会钩住系统所有进程的.意思就是会改掉所有recv.修改后.
    #include "stdafx.h"
    #include "replace.h"HHOOK hhook=NULL; 
    HINSTANCE hInstance=NULL;BYTE oldData[5];
    BYTE newData[5];
    HANDLE hProc;
    FARPROC recvAdd;
    HANDLE hDebug;
    DWORD pid;BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
    {
        switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
    //hInstance = GetModuleHandle(NULL);
    hInstance = (HINSTANCE)hModule;
    break;
    }
    case DLL_THREAD_ATTACH:
    break;
    case DLL_THREAD_DETACH:
    break;
    case DLL_PROCESS_DETACH:
    {
    UnHook();
    }
        }
        return TRUE;
    }void WINAPI InstallHook(DWORD threadId)
    {
    hhook = SetWindowsHookEx(WH_CALLWNDPROC, HookProc, hInstance, threadId);
    hDebug = ::CreateFile( "C:\\Trace.log", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
    Record();
    SetHookStart();
    }void WINAPI UnHook()
    {
    UnhookWindowsHookEx(hhook);
    }LRESULT CALLBACK HookProc(int code, WPARAM wparam, LPARAM lparam)
    {
    return CallNextHookEx(hhook, code, wparam, lparam);
    }void WINAPI Record()
    {
    newData[0] = 0xe9;
    hProc = ::GetCurrentProcess();
    pid = ::GetCurrentProcessId();
    OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    recvAdd = GetProcAddress(LoadLibrary("WS2_32.dll"), "WSARecv");
    memcpy(oldData, (char *)recvAdd, 5);
    DWORD *p = (DWORD*)&newData[1];
    *p = (DWORD *)recvAll-(DWORD *)recvAdd-5;
    }void WINAPI SetHookStart()
    {
    ::WriteProcessMemory(hProc, recvAdd, newData, 5, NULL);
    }void WINAPI SetHookEnd()
    {
    ::WriteProcessMemory(hProc, recvAdd, oldData, 5, NULL);
    }int WINAPI recvAll(SOCKET s, char *str, int len, int flag)
    {
    MessageBox(NULL, str, "内容", MB_OK);
    DWORD dwSize;
    ::WriteFile( hDebug, str, strlen( str ), &dwSize, 0 );  SetHookEnd();
    recv(s, str, len, flag);
    SetHookStart();
    return 0;
    }存在问题就是不执行recvAll.也就是没执行自己的函数.但改成jmp好像成功了..
    始终找不出问题.真郁闷. 因为是dll.所以也不能单步调.
    谁有这样的经验的请指教.
      

  8.   

    #define REPLACE_API extern "C" __declspec(dllexport)#include "fstream.h"
    #include "winsock2.h"
    #include "stdio.h"
    #pragma comment(lib,"ws2_32.lib")typedef unsigned int    SOCKET;
    REPLACE_API void WINAPI InstallHook(DWORD);
    REPLACE_API void WINAPI UnHook();
    LRESULT CALLBACK HookProc(int, WPARAM, LPARAM);
    int WINAPI recvAll(SOCKET, char *, int, int);
    void WINAPI Record();
    void WINAPI SetHookStart();
    void WINAPI SetHookEnd();我把.h也贴上来吧.大家好方便测试
      

  9.   

    代码中还有很多问题,我没有一一提,先提的事比较关键的部分。
    SetWindowsHookEx的Hook所有线程是相对而言的,前提是同一桌面,言外之意就是那些不同桌面的线程Hook不到。MSDN中SetWindowsHookEx的说明部分有明确指出。
      

  10.   

    哎!帮你把错误指出来吧。不过即使这些错误都改好也达不到预期的效果。
    #include "stdafx.h"
    #include "replace.h"
    // 以下大部分数据须定义在共享数据段
    HHOOK hhook=NULL;
    HINSTANCE hInstance=NULL;BYTE oldData[5];
    BYTE newData[5];
    HANDLE hProc;
    FARPROC recvAdd;
    HANDLE hDebug;
    DWORD pid;BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
    {
        switch (ul_reason_for_call)
        {
            case DLL_PROCESS_ATTACH:
            {
                    //hInstance = GetModuleHandle(NULL);
                    hInstance = (HINSTANCE)hModule; // 注意,每个进程都会执行
                    break;
            }
            case DLL_THREAD_ATTACH:
                    break;
            case DLL_THREAD_DETACH:
                    break;
            case DLL_PROCESS_DETACH:
            {
                    UnHook(); // 每个进程都会执行,不能这样UnHook
            }
        }
        return TRUE;
    }
    // 导出函数需要加__declspec(dllimport)
    void WINAPI InstallHook(DWORD threadId) // Hook全局要传0进来
    {
        hhook = SetWindowsHookEx(WH_CALLWNDPROC, HookProc, hInstance, threadId);
        hDebug = ::CreateFile( "C:\\Trace.log", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
        Record();
        SetHookStart(); // 要先调试验证此时各进程都已LoadLibrary
    // 要调试验证修改的recv函数入口代码是否对所有进程有效
    }void WINAPI UnHook()
    {
        UnhookWindowsHookEx(hhook);
    }LRESULT CALLBACK HookProc(int code, WPARAM wparam, LPARAM lparam)
    {
        return CallNextHookEx(hhook, code, wparam, lparam);
    }void WINAPI Record()
    {
        newData[0] = 0xe9;
        hProc = ::GetCurrentProcess(); // 记得关句柄
        pid = ::GetCurrentProcessId();
        OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); // 不要返回值还执行它干嘛?
        recvAdd = GetProcAddress(LoadLibrary("WS2_32.dll"), "WSARecv");
        memcpy(oldData, (char *)recvAdd, 5);
        DWORD *p = (DWORD*)&newData[1];
        *p = (DWORD *)recvAll-(DWORD *)recvAdd-5; // DWORD 改成 BYTE
    }void WINAPI SetHookStart()
    {
        ::WriteProcessMemory(hProc, recvAdd, newData, 5, NULL); // 内存保护,代码段默认只读
    }void WINAPI SetHookEnd()
    {
        ::WriteProcessMemory(hProc, recvAdd, oldData, 5, NULL);
    }int WINAPI recvAll(SOCKET s, char *str, int len, int flag)
    {
        MessageBox(NULL, str, "内容", MB_OK); // 服务进程无法弹出
        DWORD dwSize;
        ::WriteFile( hDebug, str, strlen( str ), &dwSize, 0 ); // 除非本进程,否则hDebug不能用    SetHookEnd(); // 从End到Start过程必须同步完成
        recv(s, str, len, flag);
        SetHookStart();
        return 0; // 每次接收数据都是0!
    }
    // 整个程序都没有异常判断处理,很容易死机
      

  11.   

    呵呵.谢谢你.不过我的程序只针对某一个进程的.
    某游戏进程.所以你提出的问题
    ::WriteProcessMemory(hProc, recvAdd, newData, 5, NULL); // 内存保护,代码段默认只读这个问题才是问题.呵呵.太谢谢你了.等我测一下.如果成功了.我马上结贴给你加分.
      

  12.   


    void WINAPI SetHookStart()
    {
    DWORD dwOld;
    ::VirtualProtect(recvAdd, 5, PAGE_READWRITE, &dwOld);
    ::WriteProcessMemory(hProc, recvAdd, newData, 5, NULL);
    ::VirtualProtect(recvAdd, 5, PAGE_READONLY, &dwOld);
    }void WINAPI SetHookEnd()
    {
    DWORD dwOld;
    ::VirtualProtect(recvAdd, 5, PAGE_READWRITE, &dwOld);
    ::WriteProcessMemory(hProc, recvAdd, oldData, 5, NULL);
    ::VirtualProtect(recvAdd, 5, PAGE_READONLY, &dwOld);
    }
    这样还是不行
      

  13.   

    为什么一定要用 hook? SPI 不是很好吗。
      

  14.   

    线程同步调用recv函数问题这个先不考虑.现在最主要就是,修改了代码段权限还是改写不了(如17楼代码)
      

  15.   

    在recv刚执行时做跳转.肯定得不到buf数据. 应该把jmp xxx写到recv函数的结尾处.这个问题也先不考虑.最重要的是.现在为什么修改不了recv前5字节...这问题缠绕我多天了,我也看了不少别人写的hook recv的代码,大半是delphi的. 不过感觉和我写的是一样啊. 真奇怪
      

  16.   

    你说代码段默认为只读你看http://blog.csdn.net/raul_qu/archive/2007/12/18/1945926.aspx
    还有<windows核心编程应用程序捆定>书上.例子全是直接WriteProcMemory()之前并没virtualProtect修改权限,况且我修改了还是不行.
      

  17.   

    WriteProcessMemory返回失败吗?错误码是什么?另外,代码内存默认应该是PAGE_EXECUTE_READ,不是PAGE_READONLY,设置完后最好恢复为dwOld里面的值。用MessageBox把dwOld的值打出来就知道默认值是什么了。
      

  18.   

    最终问题还是自己解决了:有两个问题.问题1, Record();
        SetHookStart();不能紧跟着SetWindowsHookEx()后面.
    因为当挂钩没挂住时,dll还没有映射到被挂进程的空间里.
    问题2, 参数问题..
             recv函数与WSARecv参数是完全不同的..而我将WSARect前5字节改成转跳recv参数的函数,当然有问题
      

  19.   

        hDebug = ::CreateFile( "C:\\Trace.log", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
        Record();
        SetHookStart(); // 要先调试验证此时各进程都已LoadLibrary问题1我已经给你指出来了。
    问题2是没想到你会写不同的函数,所以没注意看。顺便再提一下,上面这3行代码应该加到DllMain或者HookProc里面并判断只在目标进程执行一次。
    另外,你的程序中应该还存在其它问题,等你遇到问题的时候可以参考我前面标出的那些注释。
      

  20.   

    问题在于你的HOOK方法,使用以下方法就不会存在你的问题
    Recv : ws_32.dll 导出地址
    MyRecv:新的Recv函数
    CodeBuff:代码中转(别名OrgionRecv)将 Recv的前段指令(使用指令测长代码)大于5字节[长度:codelen],复制到CodeBuff,CodeBuff未层加到Jmp Recv+codelen;
    将Recv的首指令改成Jmp MyRecv.MyRecv(...)
    {
     puts(buff);
     return OrgionRecv(...);
    }这样就不知道切换了,另外切换在多线程中是不安全的
      

  21.   

    关于MyRecv()部分写反了,写成Send了
    //--------------------
    问题在于你的HOOK方法,使用以下方法就不会存在你的问题 
    Recv : ws_32.dll 导出地址 
    MyRecv:新的Recv函数 
    CodeBuff:代码中转(别名OrgionRecv) 将 Recv的前段指令(使用指令测长代码)大于5字节[长度:codelen],复制到CodeBuff,CodeBuff未尾加到Jmp Recv+codelen; 
    将Recv的首指令改成Jmp MyRecv. MyRecv(...) 

     int ret=OrgionRecv(...); 
    puts(buff);
    return ret;
    } 这样就不需要切换了,另外切换在多线程中是不安全的