C语言在Windows中使用Hook抓包

  1. 使用C语言写服务端与客户端程序。
  2. 使用C语言写动态链接库实现抓包功能。
  3. 将动态链接库注入到客户端程序中。
  4. 此时客户端发送消息会被抓取。

1.使用C语言写服务端与客户端程序。

  1. LinServer.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    // LinServer.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h> // for close()
    #include <arpa/inet.h> // for sockaddr_in, inet_ntop
    #include <sys/socket.h> // for socket(), bind(), listen(), accept()

    #define SERVER_PORT 8888

    int main() {
    int serverSocket, clientSocket;
    struct sockaddr_in server, client;
    socklen_t clientSize;
    char buffer[1024];
    int recvSize;

    // 创建 socket
    serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket < 0) {
    perror("Could not create socket");
    return 1;
    }

    // 设置服务器信息
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的接口
    server.sin_port = htons(SERVER_PORT); // 服务器端口

    // 绑定 socket
    if (bind(serverSocket, (struct sockaddr*)&server, sizeof(server)) < 0) {
    perror("Bind failed");
    close(serverSocket);
    return 1;
    }

    // 开始监听
    if (listen(serverSocket, 3) < 0) {
    perror("Listen failed");
    close(serverSocket);
    return 1;
    }
    printf("Waiting for incoming connections...\n");

    clientSize = sizeof(client);

    // 循环接受连接
    while (1) {
    // 接受连接
    clientSocket = accept(serverSocket, (struct sockaddr*)&client, &clientSize);
    if (clientSocket < 0) {
    perror("Accept failed");
    close(serverSocket);
    return 1;
    }

    char clientIP[INET_ADDRSTRLEN]; // 用于存储客户端 IP 地址
    inet_ntop(AF_INET, &client.sin_addr, clientIP, sizeof(clientIP)); // 使用 inet_ntop
    printf("Connection accepted from %s:%d\n", clientIP, ntohs(client.sin_port));

    // 接收消息
    while ((recvSize = recv(clientSocket, buffer, sizeof(buffer) - 1, 0)) > 0) {
    buffer[recvSize] = '\0'; // 添加字符串结束符
    printf("Received message: %s\n", buffer);
    }

    if (recvSize < 0) {
    perror("Receive failed");
    }

    // 关闭客户端 socket
    close(clientSocket);
    printf("Client disconnected.\n");
    }

    // 关闭服务器 socket
    close(serverSocket);
    return 0;
    }

  2. WinClient.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    //WinClient.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <winsock2.h>
    #include <ws2tcpip.h>

    #pragma comment(lib, "ws2_32.lib") // 链接 Winsock 库

    #define SERVER_IP "10.0.3.1"
    #define SERVER_PORT 8888

    int main() {
    WSADATA wsaData;
    SOCKET sock;
    struct sockaddr_in server;
    char message[1024];
    int sendResult;

    // 初始化 Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
    printf("Failed to initialize Winsock. Error Code: %d\n", WSAGetLastError());
    return 1;
    }

    // 创建 socket
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
    printf("Could not create socket. Error Code: %d\n", WSAGetLastError());
    WSACleanup();
    return 1;
    }

    // 设置服务器信息
    server.sin_family = AF_INET;
    server.sin_port = htons(SERVER_PORT); // 服务器端口

    // 使用 inet_pton 将 IP 地址转换为网络字节顺序
    if (inet_pton(AF_INET, SERVER_IP, &server.sin_addr) <= 0) {
    printf("Invalid address/ Address not supported \n");
    closesocket(sock);
    WSACleanup();
    return 1;
    }

    // 连接到服务器
    if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
    printf("Connection failed. Error Code: %d\n", WSAGetLastError());
    closesocket(sock);
    WSACleanup();
    return 1;
    }

    // 发送多条消息
    while (1) {
    printf("Enter message (type 'exit' to quit): ");
    fgets(message, sizeof(message), stdin);
    message[strcspn(message, "\n")] = 0; // 去掉换行符

    // 检查是否退出
    if (strcmp(message, "exit") == 0) {
    break;
    }

    // 发送消息
    sendResult = send(sock, message, strlen(message), 0);
    if (sendResult == SOCKET_ERROR) {
    printf("Send failed. Error Code: %d\n", WSAGetLastError());
    break;
    }
    else {
    printf("Message sent: %s\n", message);
    }
    }

    // 关闭 socket
    closesocket(sock);
    WSACleanup();
    return 0;
    }

  3. 在Linux中编译并运行LinServer

  4. 与Windows中运行WinClien.exe

2. 使用C语言写动态链接库实现抓包功能。

  1. 使用VCPKG安装detours库。
  2. 在vs2022中新建DLL项目。
  3. 编写DLL项目代码。
  4. 编译DLL项目得到文件Capture.dll。

1.使用VCPKG安装detours库。

1
2
.\vcpkg.exe install detours:x86-windows
.\vcpkg.exe install detours:x64-windows

3. 编写DLL项目代码。

主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <windows.h>
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <detours/detours.h>

#pragma comment(lib, "ws2_32.lib") // 链接 Winsock 库

// 原始函数指针
typedef int (WINAPI* SEND_FUNC)(SOCKET, const char*, int, int);
typedef int (WINAPI* RECV_FUNC)(SOCKET, char*, int, int);
typedef int (WINAPI* SENDTO_FUNC)(SOCKET, const char*, int, int, const struct sockaddr*, int);
typedef int (WINAPI* RECVFROM_FUNC)(SOCKET, char*, int, int, struct sockaddr*, int*);

static SEND_FUNC original_send = NULL;
static RECV_FUNC original_recv = NULL;
static SENDTO_FUNC original_sendto = NULL;
static RECVFROM_FUNC original_recvfrom = NULL;

// 调试信息输出函数
void DebugPrint(const char* format, ...) {
char buffer[1024];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
OutputDebugStringA(buffer);
}

/// 将内存转为十六进制字符串
void PrintHex(const char* buf, int len) {
char hexString[3]; // 每个字节转换为2个16进制字符 + 1个结束符
char output[1024]; // 存储最终输出的字符串
size_t outputIndex = 0;
size_t count = 0; // 计数器,用于跟踪输出的字节数

for (size_t i = 0; i < len; ++i) {
// 将每个字节转换为16进制字符串
snprintf(hexString, sizeof(hexString), "%02X", (unsigned char)buf[i]);

// 将转换后的16进制字符串添加到输出中
output[outputIndex++] = hexString[0];
output[outputIndex++] = hexString[1];

// 添加空格分隔符
if (i < len - 1) {
output[outputIndex++] = ' ';
}

// 每16个字节添加换行符
count++;
if (count == 16) {
output[outputIndex++] = '\r'; // 添加回车符
output[outputIndex++] = '\n'; // 添加换行符
count = 0; // 重置计数器
}
}

// 添加字符串结束符
output[outputIndex] = '\0';

// 输出到调试窗口
OutputDebugStringA(output);
}
//----------------------------- TCP Hook -----------------------------
int WINAPI hooked_send(SOCKET s, const char* buf, int len, int flags) {
DebugPrint("[TCP SEND] Length: %d\n", len);
PrintHex(buf, len);
return original_send(s, buf, len, flags);
}

int WINAPI hooked_recv(SOCKET s, char* buf, int len, int flags) {
int result = original_recv(s, buf, len, flags);
DebugPrint("[TCP RECV] Length: %d\n", result);
PrintHex(buf, result);
return result;
}

//----------------------------- UDP Hook -----------------------------
int WINAPI hooked_sendto(SOCKET s, const char* buf, int len, int flags,
const struct sockaddr* to, int tolen) {
if (to->sa_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)to;
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, ip, INET_ADDRSTRLEN);
DebugPrint("[UDP SENDTO] IP: %s, Length: %d\n", ip, len);
PrintHex(buf, len);
}
return original_sendto(s, buf, len, flags, to, tolen);
}

int WINAPI hooked_recvfrom(SOCKET s, char* buf, int len, int flags,
struct sockaddr* from, int* fromlen) {
int result = original_recvfrom(s, buf, len, flags, from, fromlen);
if (from->sa_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)from;
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, ip, INET_ADDRSTRLEN);
DebugPrint("[UDP RECVFROM] IP: %s, Length: %d\n", ip, result);
PrintHex(buf, result);
}
return result;
}

//----------------------------- 安装所有Hook -----------------------------
BOOL InstallHook() {
HMODULE hWs2_32 = GetModuleHandleA("ws2_32.dll");
if (!hWs2_32) return FALSE;

// 获取所有函数地址
original_send = (SEND_FUNC)GetProcAddress(hWs2_32, "send");
original_recv = (RECV_FUNC)GetProcAddress(hWs2_32, "recv");
original_sendto = (SENDTO_FUNC)GetProcAddress(hWs2_32, "sendto");
original_recvfrom = (RECVFROM_FUNC)GetProcAddress(hWs2_32, "recvfrom");

if (!original_send || !original_recv || !original_sendto || !original_recvfrom) {
return FALSE;
}

// 修改内存保护权限(实际项目需使用Detours等库)
DWORD oldProtect;
VirtualProtect(original_send, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
VirtualProtect(original_recv, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
VirtualProtect(original_sendto, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
VirtualProtect(original_recvfrom, 5, PAGE_EXECUTE_READWRITE, &oldProtect);

// 此处应写入跳转指令(示例代码省略具体汇编操作)
// 实际开发请使用 Detours 库:

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)original_send, hooked_send);
DetourAttach(&(PVOID&)original_recv, hooked_recv);
DetourAttach(&(PVOID&)original_sendto, hooked_sendto);
DetourAttach(&(PVOID&)original_recvfrom, hooked_recvfrom);
DetourTransactionCommit();

return TRUE;
}

//----------------------------- 卸载Hook -----------------------------
BOOL UninstallHook() {
if (!original_send || !original_recv || !original_sendto || !original_recvfrom) {
return FALSE;
}

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)original_send, hooked_send);
DetourDetach(&(PVOID&)original_recv, hooked_recv);
DetourDetach(&(PVOID&)original_sendto, hooked_sendto);
DetourDetach(&(PVOID&)original_recvfrom, hooked_recvfrom);
DetourTransactionCommit();

return TRUE;
}
// DLL入口
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
InstallHook();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
UninstallHook();
break;
}
return TRUE;
}

3. 将Capture.dll注入进程WinClien.exe中。

  1. C语言写一个注入器代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    //Inject.c
    #include <windows.h>
    #include <tlhelp32.h>
    #include <stdio.h>
    #include <tchar.h> // 包含tchar.h以使用TEXT宏

    // 根据进程名获取PID
    DWORD GetProcessIdByName(const TCHAR* processName) {
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (Process32First(hSnapshot, &pe32)) {
    do {
    if (_tcscmp(pe32.szExeFile, processName) == 0) {
    CloseHandle(hSnapshot);
    return pe32.th32ProcessID;
    }
    } while (Process32Next(hSnapshot, &pe32));
    }
    CloseHandle(hSnapshot);
    return 0;
    }

    // 注入DLL到目标进程
    BOOL InjectDLL(DWORD pid, const TCHAR* dllPath) {
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!hProcess) return FALSE;

    // 在目标进程中分配内存
    LPVOID pRemoteMem = VirtualAllocEx(hProcess, NULL, (_tcslen(dllPath) + 1) * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE);
    if (!pRemoteMem) {
    CloseHandle(hProcess);
    return FALSE;
    }

    // 写入DLL路径
    WriteProcessMemory(hProcess, pRemoteMem, dllPath, (_tcslen(dllPath) + 1) * sizeof(TCHAR), NULL);

    // 调用LoadLibrary加载DLL
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
    (LPTHREAD_START_ROUTINE)LoadLibrary, pRemoteMem, 0, NULL);
    if (!hThread) {
    VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return FALSE;
    }

    WaitForSingleObject(hThread, INFINITE);
    VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return TRUE;
    }

    int main() {
    const TCHAR* targetProcess = TEXT("WinClient.exe"); // 目标进程名
    const TCHAR* dllPath = TEXT("D:\\test\\CC++\\C+Windows+Pragrma\\C+Socks+demo\\Debug\\capture.dll"); // DLL路径
    //const TCHAR* dllPath = TEXT("D:\\test\\CC++\\C+Windows+Pragrma\\C+Socks+demo\\x64\\Debug\\capture.dll"); // DLL路径

    DWORD pid = GetProcessIdByName(targetProcess);
    if (pid == 0) {
    _tprintf(TEXT("Process not found!\n"));
    return 1;
    }

    if (InjectDLL(pid, dllPath)) {
    _tprintf(TEXT("DLL injected successfully!\n"));
    }
    else {
    _tprintf(TEXT("Injection failed!\n"));
    }
    return 0;
    }

  2. 编译运行Inject.exe