我想編寫一個「中間人」程序來幫助我調試正在開發的網絡應用程序。經過一些谷歌(我根本不知道該怎麼做),我想出了一個想法,即我必須創建一個鉤子(我之前用其他語言的鉤子工作過,但它們更簡單)攔截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()
得到一個訪問衝突。如果我刪除對HookSend
和UnhookSend
的呼叫,它運行良好。如果我圍繞調用順序進行更改(例如在HookSend
之後放置WSAStartup
),它將在第一個WinSock函數中崩潰。所以我覺得我很喜歡破壞整個圖書館或者什麼,我真的不太確定。說實話,我今天剛剛學會了彙編(好吧,Visual Studio的版本)。我相信這很容易抓握,並且我理解這個代碼中的程序集。我的代碼與網絡代碼幾乎相同,所以我不知道什麼是錯的。我想知道這是否是操作系統衝突,還是項目設置,或者完全不同。
我在這裏不知所措,所以任何指向正確方向的指針都會很棒!另外,如果我對這個完全錯誤的(如更好的方法),請隨時讓我知道。我確實知道Microsoft's Detours,但我寧願不將它用於這麼簡單的事情(我也不喜歡這種非商業用途許可證的想法,即使這個許可證可能永遠不會發布) 。
這感覺就像病毒會使用的那種代碼。爲什麼你不能通過一些對操作系統和防病毒似乎合法的流量來傳輸流量? – Linuxios
爲什麼不寫一條LSP或使用Detours進行注射? –
你重寫和跳過原始函數的那5個字節,它們是否對應於鉤子例程的第一條指令? –