2012-06-18 70 views
1

我想編寫一個「中間人」程序來幫助我調試正在開發的網絡應用程序。經過一些谷歌(我根本不知道該怎麼做),我想出了一個想法,即我必須創建一個鉤子(我之前用其他語言的鉤子工作過,但它們更簡單)攔截WinSock的send。根據各種文章,我必須修改函數的前5個字節來跳轉到我的函數,做一些工作,然後跳回去。我在網上找到了this code,我在控制檯應用程序中重寫了它。以下是我想出了:WinSock send()掛鉤導致訪問衝突

#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()得到一個訪問衝突。如果我刪除對HookSendUnhookSend的呼叫,它運行良好。如果我圍繞調用順序進行更改(例如在HookSend之後放置WSAStartup),它將在第一個WinSock函數中崩潰。所以我覺得我很喜歡破壞整個圖書館或者什麼,我真的不太確定。說實話,我今天剛剛學會了彙編(好吧,Visual Studio的版本)。我相信這很容易抓握,並且我理解這個代碼中的程序集。我的代碼與網絡代碼幾乎相同,所以我不知道什麼是錯的。我想知道這是否是操作系統衝突,還是項目設置,或者完全不同。

我在這裏不知所措,所以任何指向正確方向的指針都會很棒!另外,如果我對這個完全錯誤的(如更好的方法),請隨時讓我知道。我確實知道Microsoft's Detours,但我寧願不將它用於這麼簡單的事情(我也不喜歡這種非商業用途許可證的想法,即使這個許可證可能永遠不會發布) 。

+0

這感覺就像病毒會使用的那種代碼。爲什麼你不能通過一些對操作系統和防病毒似乎合法的流量來傳輸流量? – Linuxios

+0

爲什麼不寫一條LSP或使用Detours進行注射? –

+0

你重寫和跳過原始函數的那5個字節,它們是否對應於鉤子例程的第一條指令? –

回答

1

您沒有檢查對VirtualProtect調用的返回值;我的猜測是它沒有成功,你沒有必要的權利可能。您是否以「管理員」身份運行此代碼,並且您是否禁用了Windows UAC?

更新您的代碼來檢查VirtualProtect的返回值,並讓我們知道是否顯示任何內容。

+0

我會檢查返回值。據我所知,它總是以管理員身份運行。我的機器上有1個帳戶,它是一個管理員帳戶。所有可執行文件都默認以admin身份運行。我很久以前就關閉了UAC,討厭那件事...... – smoth190

2

很少有必要以這種蠻力的方式調試實際的API調用。您可以使用Wireshark等數據包嗅探器來捕獲和分析正在傳輸的網絡流量。但是,如果您必須調試API調用,那麼您基本上正在嘗試手動實現Detour,因此您應該使用Microsoft的Detours庫來正確實施真正的Detour。更好的選擇是使用WinSock的內置調試功能,而不是掛鉤API函數。讓操作系統爲您處理掛接和記錄呼叫和參數。

+0

我承認,我確實是爲冒險而努力的。但我無法找到如何查看網絡上實時發送的所有數據包。似乎沒有必要讓Detours找到我可能永遠不會再做的事情。 – smoth190

+1

如果您只想查看傳輸的數據包,則使用Wireshark等數據包嗅探器。 –

+0

我會研究一下,對我的意圖看起來很不錯 – smoth190