你若安好
便是晴天

SYN无状态扫描程序开发(二)

构造报文,生成seq和sport

根据srcip和dstip生成一串16字节的校验码,校验码第一个4字节(uint32类型)当做seq,第二个4字节用于从指定sport范围(比如:32768~60000)中选择一个sport。剩下的8字节保留。

  1. 主要使用rijndael算法,参考AES。先初始化一个静态key,然后使用这个key和srcip,dstip生成一串16字节的密文。
#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);
}
  1. seq值的为validate前四个字节组成的uint32类型值。
tcp_header->th_seq = htonl(validation[0]); // 根据srcip dstip计算出来的密文前四个字节
  1. 我们先设定一个sport的范围,然后按取模的方式从这个范围中选出一个sport。
#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);
}
  1. 到这里,完整的报文就构造完成了,接下来就是sendto了,由于sendto不阻塞,发送太快会出现丢包情况,所以需要控制发包速率,请继续往下阅读。

发送syn包并自动调整发包速率,防止丢包

数据构造好了,只需要向原始套接字进行send动作。sockaddr填充网关的mac,数据包发送到网关进行路由。需要注意的是,sendto是网络io操作,虽然不阻塞,但会出现线程安全问题,如果是多线程则需要加线程锁。在这里单线程sendto就已经够用了。

int send_packet(int sock, void *buf, int len) {
    ++ssend.sent; // 记录发包数量
    return sendto(sock, buf, len, 0, (struct sockaddr *) &sockaddr, sizeof(struct sockaddr_ll));
}
  1. packet_streams是对同一目的端口发包的数量。while循环根据当前计算出来的速率,添加延时,使发送速率不超过设定的最大值rate。(速率表示每秒发送的数据包数量。)
#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);
}
  1. 计算系统最大发包速率。rate = bandwidth / pkt_len;,带宽除以一个数据包的总bits数,表示每秒发送的最大数据包个数
// 计算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;
}

libpcap嗅探网卡,过滤出目的ip回传的报文(根据ack和dport)

作者太懒,明天继续

原创作品未经允许不得转载:WTF博客 » SYN无状态扫描程序开发(二)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址