根据srcip和dstip生成一串16字节的校验码,校验码第一个4字节(uint32类型)当做seq,第二个4字节用于从指定sport范围(比如:32768~60000)中选择一个sport。剩下的8字节保留。
- 主要使用rijndael算法,参考AES。先初始化一个静态key,然后使用这个key和srcip,dstip生成一串16字节的密文。
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
|
#define AES_ROUNDS 10
#define AES_BLOCK_WORDS 4
#define AES_KEY_BYTES 16
static int inited = 0;
static uint32_t aes_input[AES_BLOCK_WORDS];
static uint32_t aes_sched[(AES_ROUNDS + 1) * 4];
void validate_init()
{
for (int i = 0; i < AES_BLOCK_WORDS; i++) {
aes_input[i] = 0;
}
uint8_t key[AES_KEY_BYTES];
if (!random_bytes(key, AES_KEY_BYTES)) {
log_fatal("validate", "couldn't get random bytes");
}
if (rijndaelKeySetupEnc(aes_sched, key, AES_KEY_BYTES * 8) !=
AES_ROUNDS) {
log_fatal("validate", "couldn't initialize AES key");
}
inited = 1;
}
void validate_gen(const uint32_t src, const uint32_t dst,
uint8_t output[VALIDATE_BYTES])
{
assert(inited);
aes_input[0] = src;
aes_input[1] = dst;
rijndaelEncrypt(aes_sched, AES_ROUNDS, (uint8_t *)aes_input, output);
}
|
- seq值的为validate前四个字节组成的uint32类型值。
1
|
tcp_header->th_seq = htonl(validation[0]); // 根据srcip dstip计算出来的密文前四个字节
|
- 我们先设定一个sport的范围,然后按取模的方式从这个范围中选出一个sport。
1
2
3
4
5
|
#define source_port_first 32768 // 源端口起始位置
#define source_port_last 61000 // 源端口结束位置
uint16_t get_src_port(int num_ports, int probe_num/* 对同一个目的端口发送多个syn包时,syn包的序号 */, uint32_t *validation) {
return source_port_first + ((validation[1] + probe_num) % num_ports);
}
|
- 到这里,完整的报文就构造完成了,接下来就是sendto了,由于sendto不阻塞,发送太快会出现丢包情况,所以需要控制发包速率,请继续往下阅读。
数据构造好了,只需要向原始套接字进行send动作。sockaddr
填充网关的mac,数据包发送到网关进行路由。需要注意的是,sendto是网络io操作,虽然不阻塞,但会出现线程安全问题,如果是多线程则需要加线程锁。在这里单线程sendto就已经够用了。
1
2
3
4
|
int send_packet(int sock, void *buf, int len) {
++ssend.sent; // 记录发包数量
return sendto(sock, buf, len, 0, (struct sockaddr *) &sockaddr, sizeof(struct sockaddr_ll));
}
|
- packet_streams是对同一目的端口发包的数量。while循环根据当前计算出来的速率,添加延时,使发送速率不超过设定的最大值rate。(速率表示每秒发送的数据包数量。)
1
2
3
4
5
6
7
8
|
#define VALIDATE_BYTES 16
for (int i = 0; i < packet_streams; ++i) {
uint32_t validation[VALIDATE_BYTES / sizeof(uint32_t)];
validate_gen((uint32_t) index, sconf.src_ip, dst_ip, (uint8_t *) validation);
synscan_make_packet(buf, 54, sconf.src_ip, dst_ip, dst_port, validation, i);
send_packet(sock, buf, length);
while (ssend.sent/*当前发包数量*/ / (now()/*当前时间*/ - ssend.start/*send开始时间*/) > sconf.rate) usleep(100);
}
|
- 计算系统最大发包速率。
rate = bandwidth / pkt_len;
,带宽除以一个数据包的总bits数,表示每秒发送的最大数据包个数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// 计算packet长度
size_t pkt_len = 54; // ether header + ip header + tcp header (bytes)
pkt_len *= 8; // bits
// 7 byte MAC preamble, 1 byte Start frame, 4 byte CRC, 12 byte
// inter-frame gap
pkt_len += 8 * 24;
// adjust calculated length if less than the minimum size of an
// ethernet frame
if (pkt_len < 84 * 8) {
pkt_len = 84 * 8;
}
// 计算带宽,例如带宽为bandwidth = 10m
uint64_t bandwidth *= 1000000;
// 计算速率
uint32_t rate = bandwidth / pkt_len;
if (rate > 0xFFFFFFFFu) { /* 防止溢出 */
rate = 0;
}
|
作者太懒,明天继续