-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtest.cpp
302 lines (250 loc) · 8.54 KB
/
test.cpp
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#pragma comment(lib,"ws2_32.lib")
// IP header structure
typedef struct _iphdr
{
//Suppose the BYTE_ORDER is LITTLE_ENDIAN
//unsigned char version : 4; // Version of IP
//unsigned char h_len : 4; // Length of the header
unsigned char ver_len; // version and length
unsigned char tos; // Type of service
unsigned short total_len; // Total length of the packet
unsigned short id; // Unique identification
unsigned short frag_offset; // Fragment offset
unsigned char ttl; // Time to live
unsigned char protocol; // Protocol (TCP, UDP etc)
unsigned short checksum; // IP checksum
unsigned int sourceIP; // Source IP
unsigned int destIP; // Destination IP
} IpHeader, *PIpHeader;
#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0
#define ICMP_MIN 8 // Minimum 8-byte ICMP packet (header)
// ICMP header structure
// This is not the standard header, but we reserve space for time
typedef struct _icmphdr
{
BYTE i_type;
BYTE i_code; // Type sub code
USHORT i_cksum;
USHORT i_id;
USHORT i_seq;
} IcmpHeader;
// 这句话得写,不然IpSROption会在ptr之后自动帮你填充一个0x00字节
#pragma pack(1)
typedef struct _ipSROption // source route option
{
unsigned char type; // Option type
unsigned char len; // Length of option hdr
unsigned char ptr; // Offset into options
unsigned long addr[9]; // List of IP addrs
} IpSROption, *PIpSROption;
#define DEF_PACKET_SIZE 32 // Default packet size
#define MAX_PACKET 1024 // Max ICMP packet size
#define MAX_IP_HDR_SIZE 60 // Max IP header size w/options
int ICMPTotalSize = 1600;
// Function: checksum
// Description:
// This function calculates the 16-bit one's complement sum
// of the supplied buffer (ICMP) header
// ICMP校验和计算
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum = 0;
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (USHORT)(~cksum);
}
int main(int argc, char **argv) {
// 设置IP_HDRINCL选项,则发送的数据需要包括IP头部和数据
// 内核只负责填充两个域: 如果将IP数据包的标识域设置为0,内核将设置这个域
// 内核总是计算和填充IP数据包首部的校验和
// IP数据包首部各个域的内容都是网络字节顺序
char* fullPacket_1; // 包含Ip头部,Ip选项,ICMP头部,ICMP数据的空间
char* fullPacket_2;
PIpHeader ipHeaderPtr;
//PIpSROption ipSROptionPtr;
int ipSROptionLengthWithPad;
char icmp_HeaderAndData[4096] = { 0 };
IcmpHeader *icmp_hdr = NULL;
char *datapart = NULL;
int seq_no = 0;
WSADATA wsaData;
SOCKET sockRaw = INVALID_SOCKET;
struct sockaddr_in dest;
int bread, bwrote;
int timeout = 1000;
int ret = -1;
BOOL Flag;
/*
ipSROptionPtr = (PIpSROption)malloc(sizeof(IpSROption));
memset(ipSROptionPtr, 0, sizeof(IpSROption));
lpdest = argv[1];
for (int i = 2; i < argc; i++) {
inet_pton(AF_INET, argv[i], &ipSROptionPtr->addr[i - 2]);
}
ipSROptionPtr->len = 3 + (4 * (argc - 2));
ipSROptionPtr->type = 131; // loose source route;
ipSROptionPtr->ptr = 4;
ipSROptionLengthWithPad = ipSROptionPtr->len + 1;
*/
char optionPacket_1[] = {
'\x82', '\x0b', '\x41', '\x41',
'\x41', '\x08', '\x05', '\x41',
'\xC0', '\xA8', '\x7E', '\x01' // 最后一个字节用来填充
};
/*
char optionPacket_1[] = {
'\x82', '\x0b', '\x41', '\x41',
'\x41', '\x09', '\x06', '\x41',
'\x41', '\xC0', '\xA8', '\x01', // 最后一个字节用来填充
};
*/
char optionPacket_2[] = {
'\x01', '\x01', '\x01', '\x01',
'\x83', '\x07', '\x08', '\xC0',
'\xA8', '\x7E', '\x80', '\x00'
};
int optionLength = 12;
// 构造ip头部
// length 字段都是需要自己填的
ipHeaderPtr = (PIpHeader)malloc(sizeof(IpHeader));
memset(ipHeaderPtr, 0, sizeof(IpHeader));
//ipHeaderPtr->version = 0x40 + ((20 + ipSROptionLengthWithPad) / 4); // version and size
ipHeaderPtr->ver_len = 0x40 + ((20 + optionLength) / 4);
ipHeaderPtr->tos = 0;
// 总长度填完 icmp 再算
//ipHeaderPtr->id = 0; // 标识内核可以分配
ipHeaderPtr->id = 0x5555; // 指定一下,不然两次sendto就是两个id的包了
ipHeaderPtr->frag_offset = 0x0020; // 网络序,要倒过来
ipHeaderPtr->ttl = 120; // 填个不是128的数测试一下
ipHeaderPtr->protocol = 1; // ICMP
ipHeaderPtr->checksum = 0; // 校验和内核会算
ipHeaderPtr->sourceIP = 0x017EA8C0; // 网络序, 192.168.126.1
//ipHeaderPtr->sourceIP = 0x077EA8C0;
ipHeaderPtr->destIP = 0x807EA8C0; // 网络序, 192.168.126.128
char lpdest[] = "192.168.126.128";
// ICMP数据包
icmp_hdr = (IcmpHeader*)icmp_HeaderAndData;
icmp_hdr->i_type = ICMP_ECHO; // Request an ICMP echo, type is 8
icmp_hdr->i_code = 0; // code is 0
icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
icmp_hdr->i_seq = seq_no++;
datapart = icmp_HeaderAndData + sizeof(IcmpHeader);
memset(datapart, 'E', ICMPTotalSize - sizeof(IcmpHeader)); // 整个ICMP数据包(包括头部)32个字节(写死的)
// 校验和是包括数据部分的
icmp_hdr->i_cksum = 0;
icmp_hdr->i_cksum = checksum((USHORT*)icmp_HeaderAndData, ICMPTotalSize);
// 到这里全部设置完毕了,把包组合起来
// 第一个包(要保证数据是8的倍数)
// 让第一个分片为不超过1500的8的倍数
// frag_offset 字段是数据部分的(不包括IP头)
fullPacket_1 = (char *)malloc(4096);
memset(fullPacket_1, 0, 4096);
/* 一共就两个包,第一个包直接写到上限 */
// 正常来说这里应该写点代码保证分片是8的倍数
int ICMPToSendSize_1 = 1500 - (ipHeaderPtr->ver_len - 0x40) * 4;
if (ICMPToSendSize_1 % 8 != 0) {
ICMPToSendSize_1 = ICMPToSendSize_1 - (ICMPToSendSize_1 % 8);
}
int total_len1 = (ipHeaderPtr->ver_len - 0x40) * 4 + ICMPToSendSize_1;
ipHeaderPtr->total_len = total_len1;
memcpy(fullPacket_1, ipHeaderPtr, 20);
memcpy(fullPacket_1 + 20, optionPacket_1, optionLength);
memcpy(fullPacket_1 + 20 + optionLength, icmp_HeaderAndData, ICMPToSendSize_1);
// 第二个包
fullPacket_2 = (char *)malloc(4096);
memset(fullPacket_2, 0, 4096);
int ICMPToSendSize_2 = ICMPTotalSize - ICMPToSendSize_1;
int total_len2 = ICMPToSendSize_2 + (ipHeaderPtr->ver_len - 0x40) * 4;
ipHeaderPtr->total_len = total_len2;
// 我们的包不会超过一个字节表示的范围
// 所以这么写就行
ipHeaderPtr->frag_offset = (ICMPToSendSize_1 / 8) * 0x100;
ipHeaderPtr->ttl = 112; // 填个不是128的数测试一下
memcpy(fullPacket_2, ipHeaderPtr, 20);
memcpy(fullPacket_2 + 20, optionPacket_2, optionLength);
memcpy(fullPacket_2 + 20 + optionLength, icmp_HeaderAndData + ICMPToSendSize_1, ICMPToSendSize_2);
// 开始进行 socket 设置
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("WSAStartup() failed: %d \n", GetLastError());
return -1;
}
// Create a raw socket with IPPROTO_ICMP protocol
sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockRaw == INVALID_SOCKET)
{
printf("WSASocket() failed: %d \n", WSAGetLastError());
return -1;
}
// 设置超时
timeout = 1000;
bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
if (bread == SOCKET_ERROR)
{
printf("setsockopt(SO_RCVTIMEO) failed: %d \n", WSAGetLastError());
return -1;
}
timeout = 1000;
bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
if (bread == SOCKET_ERROR)
{
printf("setsockopt(SO_SNDTIMEO) failed: %d \n", WSAGetLastError());
return -1;
}
// 准备发送数据包
// 说明我们要自己构造IP头
Flag = TRUE;
ret = setsockopt(sockRaw, IPPROTO_IP, IP_HDRINCL, (char *)&Flag, sizeof(Flag));
if (ret == SOCKET_ERROR)
{
printf("setting IP_HDRINCL flag\n");
printf("setsockopt(IP_OPTIONS) failed: %d \n", WSAGetLastError());
return -1;
}
// 使用SOCK_RAW时,如果没有用connect函数绑定对方地址
// 则应使用sendto或sendmsg函数发送数据包,在函数参数中指定对方地址
// 如用connect函数绑定了对方地址,则可以直接使用send,write来发送数据包
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
inet_pton(AF_INET, lpdest, (void *)&dest.sin_addr.S_un);
// 发送第一个包
bwrote = sendto(sockRaw, fullPacket_1, total_len1, 0, (struct sockaddr*)&dest, sizeof(dest));
if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("timed out \n");
return 0;
}
printf("sendto() failed: %d \n", WSAGetLastError());
return 0;
}
// 发送第二个包
bwrote = sendto(sockRaw, fullPacket_2, total_len2, 0, (struct sockaddr*)&dest, sizeof(dest));
if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("timed out \n");
return 0;
}
printf("sendto() failed: %d \n", WSAGetLastError());
return 0;
}
}