<![CDATA[Latest posts for the topic "CHECKSUM trong gói tin TCP UDP ICMP"]]> /hvaonline/posts/list/36.html JForum - http://www.jforum.net CHECKSUM trong gói tin TCP UDP ICMP sai cơ bản dẫn tới checksum không đúng. Gói tin bị DROP và không có reply từ server mặc dù em không spoof IP. Sau đây em xin được chia sẽ những cái ngu cơ bản của em để lần sau các bạn có tìm hiểu về cái này cũng không tốn kém nhiều thời gian. Các header em sử dụng: Code:
#include <stdlib.h>
#include <pcap.h>
#include <sys/types.h>
#include <string.h>

#define __FAVOR_BSD 1;

#include <netinet/ether.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <time.h>
1. Hàm tính checksum. Cụ thể thế nào các bạn cứ mỗ xẽ nó ra, và nghiền ngẫm. Nó dễ hiểu và không khó với ai có căn bản. Nếu không hiểu các bạn có thể reply bên dưới mình sẽ trả lời. Code:
uint16_t checksum(const uint16_t* buf, unsigned int nbytes){

  uint32_t sum = 0;

  for (; nbytes > 1; nbytes -= 2){
    sum += *buf++;
  }

  if (nbytes == 1)
  {
    sum += *(unsigned char*) buf;
  }

  sum  = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);

  return ~sum;

}
2. Tính checksum cho gói IP. Check sum gói IP sẽ bao gồm toàn bộ nội dung gói IP. Chúng ta chuyển gói IP bào buffer và gọi hàm checksum để tính toán. Code:
uint16_t check_ip(iphdr ipheader){
    uint16_t *ptr;
    uint16_t sum = 0;
    size_t ip_s = sizeof(iphdr);
    ptr = new uint16_t [ip_s/2];
    memcpy(ptr, &ipheader, ip_s);

    sum = checksum(ptr, ip_s);
    delete ptr;
    return sum;

}
3. Tính checksum cho gói ICMP. Gói ICMP khác với gói IP vì nó có chứa thêm data field lúc trước đã có lần em tính thiểu cái data field kết quả là checksum bị sai, nhưng thêm data field vào vẫn bị sai em cảm thấy bức bách lắm, nhưng để ý kỹ lại sẽ thấy như sau memcpy(ptr + icmp_s, dat, dat_size); bởi vì ptr là con trỏ uint16_t nó là 2 bytes nên dịch nó phải công cho icmp_s/2 nhưng xuống đoạn tính checksum không phải là sum = checksum(ptr, pkt_s/2); mà là sum = checksum(ptr, pkt_s); vì ở đây nó tính bằng bytes nếu tính toán máy móc chút là sẽ sai checksum. Code:
uint16_t check_icmp(icmphdr icmpheader, char *dat, size_t dat_size){
    uint16_t *ptr;
    uint16_t sum = 0;

    size_t icmp_s = sizeof(icmphdr);
    size_t pkt_s = sizeof(iphdr) + icmp_s + dat_size;

    if(sizeof(iphdr)%2 == 1 || dat_size%2 == 1)return 0x0000;

    ptr = new uint16_t [pkt_s/2];
    memcpy(ptr, &icmpheader, icmp_s);
    memcpy(ptr + icmp_s/2, dat, dat_size);

    sum = checksum(ptr, pkt_s);
    delete ptr;
    return sum;

}
4. Tính checksum cho gói TCP Đoạn này thì em mới bộc lộ cái ngu ra, em tính mỗi tcpheader và data, vì ăn quen mà tưởng nó giống như ICMP header. Sau này đọc tài liệu mới biết. Trước đây người ta không phân chia ra TCP hay IP và UDP mà chỉ có TCP kể từ năm 1970 mới tách ra để làm cấu trúc của gói tin minh bạch hơn. Để tính toán checksum cần phải tính cả những thứ "nguyên thuỷ" thuộc về nó pseudo header và cấu trúc của nó đây: Code:
struct pseudo_header
{
    unsigned int   s_addr;
    unsigned int   d_addr;
    char           reserved;
    unsigned char  protocol;
    unsigned short length;
};
Và đây là mã của tính toàn TCP: Code:
uint16_t check_tcp_syn(iphdr ipheader, tcp_syn tcpheader, char* opt, size_t opt_size){
    uint16_t *ptr;
    uint16_t sum = 0;
    pseudo_header psdheader;
    size_t psd_s = sizeof(pseudo_header);
    size_t tcp_s = sizeof(tcp_syn);
    size_t pkt_s = psd_s + tcp_s + opt_size;

    ptr = new uint16_t [pkt_s/2];

    psdheader.s_addr = ipheader.saddr;
    psdheader.d_addr = ipheader.daddr;
    psdheader.reserved = 0;
    psdheader.protocol = ipheader.protocol;
    psdheader.length = htons(tcp_s+opt_size);

    memcpy(ptr, &psdheader, psd_s);
    memcpy(ptr + psd_s/2 , &tcpheader, tcp_s);
    memcpy(ptr + psd_s/2 + tcp_s/2, opt, opt_size);
    sum = checksum(ptr, pkt_s);

    delete ptr;
    return sum;

}
Ở trên là gói SYN không có data field em chưa viết phần đó. Để hôm nào phân tích bằng WireShark đã rồi tính tiếp. Nhưng về cơ bản ta sẽ đưa data field vào và tăng length lên thôi. Cái opt là TCP options. 5. Tính toán checksum với gói UDP Việc tính toán check sum gói tin này sẽ giống với TCP tức là tính cả pseudo header. Dưới đây là mã tính toán nó. Code:
uint16_t check_udp(iphdr ipheader, udphdr udpheader, char* dat, size_t dat_size){
    uint16_t *ptr;
    uint16_t sum = 0;
    pseudo_header psdheader;
    size_t psd_s = sizeof(pseudo_header);
    size_t udp_s = sizeof(udphdr);
    size_t pkt_s = psd_s + udp_s + dat_size;

    ptr = new uint16_t [pkt_s/2];

    psdheader.s_addr = ipheader.saddr;
    psdheader.d_addr = ipheader.daddr;
    psdheader.reserved = 0;
    psdheader.protocol = ipheader.protocol;
    psdheader.length = htons(udp_s + dat_size);

    memcpy(ptr, &psdheader, psd_s);
    memcpy(ptr + psd_s/2 , &udpheader, udp_s);
    memcpy(ptr + psd_s/2 +  udp_s/2, dat, dat_size);

    sum = checksum(ptr, pkt_s);
    delete ptr;
    return sum;
}
6. Kết bài Viết tới đây chắc cũng có bạn thắc mắc sao đoạn này em rườm rà thế. Code:
sum = checksum(ptr, pkt_s);
    delete ptr;
    return sum;
Em làm thế vì nếu return ngay con trỏ ptr sẽ không được giải phóng. Từng đó thôi cũng đủ làm em tốn mất 2 ngày, đúng là sự thiếu hiểu biết thật đáng sợ. Lần sau em sẽ tìm hiểu một vấn đề kỹ càng hơn trước khi làm và đặt câu hỏi, cảm ơn anh conmale và bạn vd_ đã trả lời những thẳng mắc hồn nhiên của em :">. P/S: các bạn có thể sử dụng các hàm trên để: - Thử nghiệm việc gửi nhận các gói tin. - Viết module tự correct checksum trước khi gửi đi. - Bắt phân tích các gói checksum bị lỗi. Mà nguồn trên được em viết bằng C++ có tham khảo thêm các bài viết từ: http://tcpdump.org/ http://www.winpcap.org/ http://www.cplusplus.com/ http://yuba.stanford.edu/~casado/pcap/section1.html Một số trang khác nữa khi nào nhớ em sẽ bổ sung.]]>
/hvaonline/posts/list/41759.html#260060 /hvaonline/posts/list/41759.html#260060 GMT