1

開発中のネットワーク アプリケーションのデバッグに役立つ「中間者」プログラムを作成したいと考えています。Google で検索した後 (これを行う方法がまったくわかりませんでした)、フックを作成する必要があるというアイデアを思いつきました (以前に他の言語でフックを使用したことがありますが、フックははるかに簡単でした)。 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("SEND\tStart: %x\t Return: %x\n", 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()。と の呼び出しを削除するHookSendと、正常にUnhookSend動作します。呼び出しの順序を変更すると (WSAStartup後に配置するなどHookSend)、最初の WinSock 関数でクラッシュします。だから私はライブラリ全体か何かを壊しているようなものだと思いますが、本当によくわかりません。正直なところ、私は今日アセンブリを学習したばかりです (まあ、Visual Studio のバージョンです)。かなり握りやすく、このコードで組み立てが理解できると思います。私のコードは Web のコードとほぼ同じなので、何が悪いのかわかりません。それはOSの競合なのか、プロジェクトの設定なのか、それともまったく別のものなのか.

私はここで途方に暮れているので、正しい方向への指針は素晴らしいでしょう! また、これが完全に間違っている場合 (より良い方法があるなど) は、お気軽にお知らせください。Microsoft の Detoursについては知っていますが、このような単純なことには使用したくありません (また、非営利使用ライセンスが頭に残るという考えも好きではありません。これがおそらくリリースされることはないとしても)。 )。

4

2 に答える 2

2

実際の API 呼び出し自体をこのような強引な方法でデバッグする必要はほとんどありません。Wireshark のようなパケット スニファを使用して、送信されているネットワーク トラフィックをキャプチャして分析することができます。しかし、API 呼び出しをデバッグする必要がある場合は、基本的に Detour を手動で実装しようとしているので、Microsoft の Detours ライブラリを使用して実際の Detour を正しく実装する必要があります。より良いオプションは、WinSock の組み込みのデバッグ機能を使用し、API 関数をまったくフックしないことです。呼び出しとパラメーターのフックとログ記録を OS に処理させます。

于 2012-06-18T02:40:45.907 に答える
1

VirtualProtect への呼び出しの戻り値をチェックしていません。私の推測では、それは成功しておらず、必要な権利を持っていない可能性があります。このコードを「管理者」として実行していて、Windows UAC を無効にしていますか?

コードを更新して VirtualProtect の戻り値を調べ、何か明らかになった場合はお知らせください。

于 2012-06-18T01:59:49.247 に答える