WinSock send() 钩子导致访问冲突

WinSock send() hook causing access violation

本文关键字:访问冲突 send WinSock      更新时间:2023-10-16

我想编写一个"中间人"程序来帮助我调试正在开发的网络应用程序。经过一些谷歌搜索(我根本不知道如何做到这一点),我想出了一个想法,我必须创建一个钩子(我以前在其他语言中使用过钩子,但它们要简单得多)来拦截 WinSock 的send。根据各种文章,我必须修改函数的前 5 个字节才能跳转到我的函数,做一些工作,然后跳回去。我在网上找到了这段代码,我在控制台应用程序中重写了它。这是我想到的:

#include "stdafx.h"
#include <iostream>
#include <WinSock2.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#define JMP(frm, to) (int)(((int)to - (int)frm) - 5);
#define DEF_PORT 27015
using namespace std;
bool HookSend();
void UnhookSend();
DWORD SendOriginal          = 0;
DWORD SendReturn            = 0;
DWORD *SendHookFunc         = 0;
DWORD OldProtection         = 0;
char* send_buffer;
int send_sizeofdata         = 0;
SOCKET send_s;
int send_flags              = 0;
HINSTANCE hWinSock          = 0;
void __declspec(naked) __stdcall SendHook()
{
    __asm
    { 
        mov     edi,edi
        push    ebp
        mov     ebp, esp
        mov     eax, [ebp+0x08]         /* Param 1 : Socket */
        mov     send_s, eax
        mov     eax, [ebp+0x0C]         /* Param 2 : buffer */
        mov     [send_buffer], eax
        mov     eax, [ebp+0x10]         /*Param 3 : Size*/
        mov     send_sizeofdata, eax
        mov     eax, [ebp+0x14]         /*Param 4 : flags*/
        mov     send_flags, eax
        jmp     SendReturn
    }
}
int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    SOCKET soket;
    sockaddr_in soketDesc;
    char buf[256];
    cout << "Packet Reader by Timothy Volpe" << endl;
    cout << "Program will read packets sent" << endl << endl;
    cout << "Loading WinSock" << endl;
    if((hWinSock = LoadLibrary("ws2_32.dll")) == NULL)
    {
        cout << "Failed to load WinSock library" << endl;
        return -1;
    }
    cout << "Starting WinSock API" << endl;
    if(WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR)
    {
        cout << "Failed to load WinSock API (Error:" << WSAGetLastError() << ")" << endl;
        cout << "Press enter to exit" << endl;
        cin.get();
        return -1;
    }
    cout << "Hooking send()" << endl;
    if(!HookSend())
    {
        cout << "Press enter to exit" << endl;
        cin.get();
        return -1;
    }
    cout << "Hooked successfully!" << endl;
    cout << "Creating socket" << endl;
    soket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(soket == INVALID_SOCKET)
    {
        cout << "Failed to create socket (Error:" << WSAGetLastError() << ")" << endl;
        UnhookSend();
        cout << "Press enter to exit" << endl;
        cin.get();
        return -1;
    }
    cout << "Connecting socket to port " << DEF_PORT << endl;
    soketDesc.sin_family        = AF_INET;
    soketDesc.sin_addr.s_addr   = inet_addr("127.0.0.1");
    soketDesc.sin_port          = htons(DEF_PORT);
    if(connect(soket, (SOCKADDR*)&soketDesc, sizeof(soketDesc)) == SOCKET_ERROR)
    {
        cout << "Failed to connect socket (Error:" << WSAGetLastError() << ")" << endl;
        UnhookSend();
        cout << "Press enter to exit" << endl;
        cin.get();
        return -1;
    }
    cout << "Enter some data to send (MAX 256):" << endl;
    cin.get(buf, 256); //Read 256 chars
    cout << "Sending "" << buf << """ << endl;
    //Send the data
    send(soket, buf, (int)sizeof(buf), 0);
    //Close
    closesocket(soket);
    WSACleanup();
    UnhookSend();
    cout << endl << "Press enter to exit" << endl;
    cin.clear();
    cin.ignore();
    cin.get();
    return 0;
}
bool HookSend()
{
    //Get the new function's address
    SendHookFunc = (DWORD*)SendHook;
    //Get the send() function's address
    SendOriginal = (DWORD)GetProcAddress(hWinSock, "send");
    //Get the return address, which is 5 bytes past the start
    SendReturn = SendOriginal + 5;
    printf("SENDtStart: %xt Return: %xn", SendOriginal, SendReturn);
    //Change protection for the first 5 bytes
    VirtualProtect((void*)SendOriginal, 0x05, PAGE_READWRITE, &OldProtection);
    *(BYTE*)(SendOriginal) = 0xe9;
    *(int*)(SendOriginal+1) = JMP(SendOriginal, SendHookFunc);
    return true;
}
void UnhookSend()
{
    cout << "Unhooking..." << endl;
    //Restore the old stuff
    *(WORD*)SendOriginal = 0xFF8B;
    *(BYTE*)(SendOriginal+1) = 0x55;
    *(WORD*)(SendOriginal+3) = 0xEC8B;
    //Restore protection
    VirtualProtect((void*)SendOriginal, 0x05, OldProtection, &OldProtection);
}

但是,它不起作用,因为我在socket()上遇到访问冲突.如果我删除对HookSendUnhookSend的调用,它运行良好。如果我更改调用的顺序(例如将WSAStartup放在HookSend之后),它会在第一个 WinSock 函数上崩溃。所以我觉得我就像破坏了整个图书馆什么的,我真的不太确定。老实说,我今天刚刚学会了汇编(嗯,Visual Studio的版本)。它很容易掌握,我相信我理解这段代码中的程序集。我的代码几乎与网络上的代码相同,所以我不知道出了什么问题。我想知道这是操作系统冲突,还是项目设置问题,或者完全不同的东西。

我在这里不知所措,所以任何正确方向的指针都会很棒!另外,如果我完全错误(就像有更好的方法一样),请随时告诉我。我确实知道Microsoft的Detours,但我宁愿不把它用于这么简单的事情(我也不喜欢非商业使用许可证的想法在我脑海中徘徊,即使这可能永远不会发布)。

很少需要以这种暴力方式调试实际的 API 调用本身。 您可以使用像Wireshark这样的数据包嗅探器来捕获和分析正在传输的网络流量。 但是,如果您必须调试 API 调用,那么您实际上是在尝试手动实现 Detour,因此您确实应该使用 Microsoft 的 Detours 库来正确实现真正的 Detour。 更好的选择是使用 WinSock 的内置调试功能,根本不挂钩 API 函数。 让操作系统为您处理调用和参数的挂钩和日志记录。

您没有检查对 VirtualProtect 的调用的返回值;我的猜测是它没有成功,您可能没有必要的权限。 您是否以"管理员"身份运行此代码,并且是否禁用了Windows UAC内容?

更新您的代码以检查 VirtualProtect 的返回值,并让我们知道这是否揭示了什么。