修改 WSARecv 数据

问题描述

需要在客户端收到之前修改服务器发送的未加密数据。

int WSAAPI __WSARecv(SOCKET s,LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesRecvd,LPDWORD lpFlags,LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
    {
        int result = _WSARecv(s,lpBuffers,dwBufferCount,lpNumberOfBytesRecvd,lpFlags,lpOverlapped,lpCompletionRoutine);
        // The original WSARecv must be called to fill the lpBuffers buffer
        
        if (lpBuffers->buf[0] == 0x09 and lpBuffers->buf[1] == 0x00 and lpBuffers->buf[2] == 0x12)
        {
            // 09 00 12 32 00 00 FFFFFFFD FFFFFF8D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     
            char replace[] = { 0x33,0x00,0x02,0x2F,0x7B,0x22,0x65,0x78,0x74,0x72,0x61,0x3A,0x5B,0x5C,0x75,0x30,0x33,0x63,0x58,0x70,0x6C,0x31,0x54,0x52,0x20,0x5D,0x2C,0x7D,0x01,0x00 };
     
            lpBuffers->buf = replace;
            lpBuffers->len = 64;
     
        cout << "replaced" << endl; // Writes to the console,which means it works,but the game still sees the same data
    }
 
    return result;
}

lpOverlapped 和 lpCompletionRoutine 为零。 TCP协议。

理想情况下,我希望能够防止不需要的数据到达客户端,但据我所知,如果不编写代理服务器或外部拦截器(例如 Wireshark),这是不可能的。

解决方法

WSARecv 逻辑比较复杂,最简单的使用场景是立即接收数据(重叠未使用),调用者只读取一个缓冲区(dwBufferCount == 1)。 该函数处理这种情况,重写接收缓冲区并相应地更新接收的字节数:

int WSAAPI __WSARecv(SOCKET s,LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesRecvd,LPDWORD lpFlags,LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
    int result = _WSARecv(s,lpBuffers,dwBufferCount,lpNumberOfBytesRecvd,lpFlags,lpOverlapped,lpCompletionRoutine);
    // The original WSARecv must be called to fill the lpBuffers buffer

    // Check WSARecv received the data,read was to one buffer,received data satisfies our pattern.
    if (!result && dwBufferCount == 1 && lpBuffers->buf[0] == 0x09 && lpBuffers->buf[1] == 0x00 && lpBuffers->buf[2] == 0x12)
    {
        // 09 00 12 32 00 00 FFFFFFFD FFFFFF8D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        char replace[] = { 0x33,0x00,0x02,0x2F,0x7B,0x22,0x65,0x78,0x74,0x72,0x61,0x3A,0x5B,0x5C,0x75,0x30,0x33,0x63,0x58,0x70,0x6C,0x31,0x54,0x52,0x20,0x5D,0x2C,0x7D,0x01,0x00 };
        if( lpBuffers->len >= sizeof( replace ) )
        {
            memcpy( lpBuffers->buf,replace,sizeof( replace ) );
            cout << "replaced" << endl; // Writes to the console,which means it works,but the game still sees the same data

            // Update lpNumberOfBytesRecvd
            if (lpNumberOfBytesRecvd)
                *lpNumberOfBytesRecvd = sizeof( replace );
        }
        else
          cout << "Cannot replace - buffer too small (" << lpBuffers->len << " bytes)" << endl;
    }

    return result;
}

调用者通常以这种方式调用和读取数据:

char buffer[DATA_BUFSIZE];
WSABUF DataBuf;
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer;

rc = WSARecv(...,&DataBuf,....);

data = buffer[0];

因此更改 lpBuffers->buf 的值无济于事,因为调用者不使用它。当调用者这样使用时

data = DataBuf.buf[0];

替换缓冲区地址将起作用。因此,替换缓冲区的内容是一个更好的解决方案。