請想象一下這樣的情景:你可以任意偽造很多個WiFi熱點, 這個技術只能在linux上使用,而且對無線網卡也有一定的挑剔,具體的下面會講~
階段一:基本原理
首先需要搞清楚的是,手機、電腦等支持WiFi的設備,是如何得知周圍無線熱點的存在的?無線熱點(通常就是無線路由器)會周期性地向外發送Beacon幀,中文名為信標幀。信標幀一個最重要的作用就是宣示無線網絡的存在(但不僅僅這個作用)。
信標幀里面包含了該無線熱點的一些基本信息,比如ESSID(也就是常說的網絡名稱,比如上圖中的“1.此廣告位招租”)、BSSID(接入點的MAC地址)、加密方式(比如開放無密碼、WEP加密或者WPA/WPA2加密)、支持的傳輸速率等等。
當無線設備接收到信標幀之后,就能得知周圍這個接入點的存在。那么,如果我能夠構造一個Beacon幀并且發送出去,那么無線設備收到以后也一樣會認為存在這么一個熱點。那么當我構造很多個Beacon幀,每個幀都宣示一個接入點,并且周期性地發送這些幀,那么無線設備就會以為周邊存在很多個接入點。
這就是偽造任意WiFi熱點的基本原理。
階段二:熟悉Beacon幀格式
那么接下來的目標就很明晰了——構造Beacon幀。要構造Beacon幀,就得知道一個Beacon幀的格式。而在給出Beacon幀的格式之前,需要先說明一下802.11管理幀的通用格式:
格式中每一個字段上面的數字是指該字段占用的字節數,比如Frame Control占用2字節,Destination Address占用6字節等等。
一、Frame Control,中文名為幀控制字段,占2個字節,各個位的定義如下:
(1)Protocol字段由兩位構成,用以顯示該幀所使用的版本號。目前802.11只有一個版本,編號為0。所以Protocol的值是00。
(2)Type字段由兩位構成,Sub type字段由4位構成。802.11幀共有三種類型的幀,分別是管理幀、控制幀與數據幀。而每種類型又分為多種子類型。Beacon幀的Type字段值為00(管理幀),而Sub type字段值為1000。
(3)To DS位與From DS位用來指示幀的目的地是否為分布式系統,定義如下:
TO DS | FROM DS | 含義 |
---|---|---|
0 | 0 | 所有管理與控制幀、非基礎結構型數據幀 |
0 | 1 | 基礎結構型網絡里無線工作站所收到的數據幀 |
1 | 0 | 基礎結構型網絡里無線工作站所傳送的數據幀 |
1 | 1 | 無線橋接器上的數據幀 |
由于Beacon幀屬于管理幀,所以To DS與From DS都是0。
(4)Mor Fragments位,類似于IP分包的more fragments位。因為802.11幀對負載有長度限制,所以當上層傳入很大的數據時,需要分段傳送。Beacon幀不需要分段,所以該位為0。
(5)Retry位。有時需要重傳幀,任何重傳的幀都要將此位置為1,否則為0。Beacon幀不存在重傳的情況,所以該位為0。
(6)Power Management位。很多無線設備是以電池供電的,比如手機。當沒有數據流量時,關閉無線發射器可以延長電池的使用時間。如果無線設備要把該位置為1,那么就意味著這個幀(或者這次數據交換)傳送完成之后,無線設備將進入省電模式。由于接入點是不 允許進入省電模式的,所以Beacon幀的該位為0。
(7)More data位。為了服務處于省電模式中的無線設備,接入點會將那些要傳給無線設備的幀加以緩存。如果無線設備從省電模式中醒來之后,收到一個幀中發現該位置1,說明接入點還有更多的緩存的數據要發送給無線設備。如果只是為了偽造Beacon幀的話,那么這一位一直為0即可。
(8)Protected Frame位如果被置為1的話,則說明該幀是受到鏈路層安全協議的保護的,比如WEP和WPA/WPA2。Beacon管理幀是不需要加密的,所以為0。
(9)Order位如果被置位,那么就表明幀進入了嚴格依次傳送模式,不過發送端與接收端必須付出額外的代價。Beacon幀的該位為0。
由此可得,我們構造的Beacon幀的Frame Control字段為0×80 0×00(按圖中的順序就是00000001,00000000,考慮高位在后,那么就是二進制的10000000,00000000,即0×80 0×00)。
二、Duration字段在802.11幀中用來預約媒介占用時間。簡單來說就是,每一個幀都會通過Duration字段來告知所有的無線設備:“我還要占用媒介多長時間!”。Duration字段保障了一系列原子操作不被打斷,當然,前提是大家都遵守802.11協議~而Beacon幀屬于廣播,沒有后續數據交互,所以其Duration為0,即0×00 0×00。
三、Destination Address即為目的地址,為接收端的MAC地址。由于Beacon幀是廣播幀,所以目的地址就是廣播地址,即FF:FF:FF:FF:FF:FF。
四、Source Address即為源地址,為發送端的MAC地址。發送端地址通常就是接入點的MAC地址,但是也有例外,比如中間加了一個中繼器,那么發送端的MAC地址就是中繼器的地址了。
五、BSSID就是接入點的MAC地址了。
六、Seq-ID(Sequence Control)字段中文名為順序控制字段,它的低4位是分段編號,而高12位為順序編號。幀片段之間的差異在于分段編號。第一個片段的編號為0,其后每個片段的分段編號依次加以,而它們的順序編號相同。除了最后一個分段,所有分段的More data位都置位。由于Beacon幀通常不分段,所以低4位為0000,高12位為順序編號。
七、Frame body即為幀主體。如果該幀是數據幀,那么幀主題就是數據的有效載荷,如果是管理幀,那么通常是各種信息元素(將在下面講解)。
八、FCS,中文名幀校驗序列,通常就是循環冗余校驗碼CRC。
階段三:熟悉Beacon幀主體Frame Body
從上面的分析中可以看出,最重要的內容還是包含在Frame body中。管理幀的Frame body有若干信息元素構成。信息元素有固定長度的信息元素與可變長度的信息元素構成。固定長度的信息元素占用的字節數固定,比如Timestamp固定占用8字節。而可變長度的信息元素占用的字節數不確定,比如Beacon幀中表示網絡名稱的ESSID。信息元素也可分為必選信息元素與可選信息元素,Beacon幀的格式詳見下圖(引用自《802.11無線網絡權威指南 第二版》):
很復雜不是嗎?既然我們只是要構造一個符合條件的,那么就從簡處理,構造最簡的Frame body即可,那么只需要包含必選的四個信息元素即可:Timestamp、Beacon interval、Capability info和SSID。
一、Timestamp,占用8字節的時間戳可用來同步BSS中的無線設備。BSS的主定時器會定期傳送目前已運行的微秒數。當計數器達到最大值時就會從0開始計數。對于長64位、可計數超過58萬年的計數器,要從頭開始計數,呵呵。
二、Beacon interval,占用2字節,用來設定Beacon信號之間相隔多少時間單位。時間單位通常縮寫為TU,代表1024微秒。Beacon interval通常會被設定為100個TU,大約每0.1秒發送一次Beacon信號。
三、Capability info共16位,用來告知網絡具備何種性能。每一個位各自代表一個標記,對應到網絡所具備的某種特殊功能。工作站會使用這些通告數據來判斷自己是否支持該BSS所有的功能。未實現性能通告中所有功能的工作站就無法加入該BSS。各位的定義如下(引用自《802.11無線網絡權威指南 第二版》):
(1)ESS置位則表示該網絡是一個擴展服務集的基本結構型,也就是接入點通常創建的網絡。IBSS與ESS互斥,如果IBSS置位,則該網絡是獨立基本服務器網絡,也就是常說的無線網卡直連。
(2)CF-Pollable與CF-Poll request為無競爭-輪詢位,表示與省電模式相關的功能。工作站從省電模式醒來之后,可以向工作站輪詢是否有緩存的幀。Poll即輪詢的意思。對于接入點而言,這兩位的組合代表的含義如下表:
CF-POLLABLE | CF-POLL REQUEST | 含義 |
---|---|---|
0 | 0 | 接入點并未支持點協調功能(point coordination function) |
0 | 1 | 接入點使用PCF來傳遞,但并不支持輪詢 |
1 | 0 | 接入點使用PCF來傳遞與輪詢 |
1 | 1 | 保留,尚未使用 |
(3)Privacy,保密性。如果將Privacy位設定為0,并且接下來沒有WPA信息元素,那么該無線網絡即為開放無密碼。如果將該為設定為1,代表需要使用WEP以維持機密性。
(4)Short Preamble,短前導碼,802.11b規范新增此字段是為了支持高速直接序列擴頻物理層。設定為1,代表此網絡目前使用短前導碼。0代表不使用此選型,并且在該BSS中禁止使用短前導碼。802.11g規定使用短前導碼,因此在依據802.11g標準所構建的網絡中,此字段必須為1。
(5)PBCC,封包二進制回旋碼,802.11b規范新增此字段是為了支持高速直接序列擴頻物理層。設定為1,代表此網絡目前使用封包二進制回旋碼調制機制,0代表不使用此選項并且在該BSS中禁止使用封包二進制回旋碼。
(6)Channel Agility,機動信道轉換,此字段加入802.11b規范是為了支持高速直接序列擴頻物理層。設定為1,代表此網絡使用機動信道轉換選項,0代表不使用此選項并且在該BSS中禁止使用機動信道轉換。
(7)Short Slot Time,該選項是802.11g規范新增的,設定為1代表使用較短的時隙。
(8)DSSS-OFDM,該選項是802.11g規范新增的,設定為1代表使用DSSS-OFDM幀構造。
如果我們要構造一個最簡的Beacon幀,Capability info字段可以設為0×01 0×00,如果要變成WEP加密的,那么就可以設為0×11 0×00,當然,很多選項選不選無所謂。
四、SSID,服務集標識符,是一個可變長的信息元素,也就是通常說的網絡名稱。可變長的信息元素的通用格式為:
而SSID的Element ID是0。有些文檔將SSID視為網絡名稱,因為網管人員通常以字符串來指定SSID。其實,SSID不過是由字節所形成的字符串,用來標示所屬網絡的BSSID。有些產品要求此字符串必須是以null(即0)結尾的ASCII字符串,雖然標準對此并無特別規范。SSID的長度介于0至32個字節之間。假如要偽造的熱點的名詞為“hello”,那么這個元素就應該為0,5,’h’,’e’,’l’,’l’,’o’。
階段四:編寫代碼構造Beacon幀
由此,我們已經弄清了一個最簡Beacon幀的每個細節,除了最后的校驗碼FCS。讓我們來總結一下最后的Beacon幀的樣子吧~舉個例子,我想偽造一個開放的、名為”hello,carrot!”的、接入點MAC地址為ec:17:2f:2d:b6:b8的接入點,那么最簡的Beacon幀應該這樣:
字段 | 值 |
---|---|
Frame Control | 0×80 0×00 |
Duration | 0×00 0×00 |
Destination Address | 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF |
Source Address | 0xEC 0×17 0x2F 0x2D 0xB6 0xB8 |
BSSID | 0xEC 0×17 0x2F 0x2D 0xB6 0xB8 |
Seq-ID | 低4位:0×0,高12位:幀序號 |
Timestamp | 當前運行的微秒數 |
Beacon interval | 0×64 0×00(100) |
Capability info | 0×01 0×00 |
SSID | 0×00 0x0D ‘h’ ‘e’ ‘l’ ‘l’ ‘o’ ‘,’ ‘c’ ‘a’ ‘r’ ‘r’ ‘o’ ‘t’ ‘!’ |
FCS | 循環冗余校驗碼,幸好驅動程序會自行計算 |
很簡潔吧~
為了實現一定的動態性,有必要定義一個結構體,那么就來看看代碼的實現吧~
首先是一個接入點結構體的定義:
struct ap { uint8 bssid[6]; uint16 seq_id; uint8 essid_len; char essid[32]; };
然后是struct ap的初始化函數:
void init_ap(struct ap* p_ap,uint8* p_bssid,char* p_essid) { memcpy(p_ap->bssid,p_bssid,6); p_ap->seq_id=0; uint32 t_len=strlen(p_essid); if(t_len>32) t_len=32; p_ap->essid_len=t_len; memcpy(p_ap->essid,p_essid,t_len); }
最后是根據struct ap來構造beacon幀的函數:
uint16 create_beacon_frame(uint8* p_buffer,struct ap* p_ap) { memcpy(p_buffer,"x80x00x00x00xFFxFFxFFxFFxFFxFF",10); memcpy(p_buffer+10,p_ap->bssid,6); memcpy(p_buffer+16,p_ap->bssid,6); p_buffer[22]=(uint8)(p_ap->seq_id&0xFF); p_buffer[23]=(uint8)((p_ap->seq_id>>8)&0xFF); p_ap->seq_id+=0x10; struct timeval t_time; gettimeofday(&t_time,0); uint64 t_timestamp=((uint64)t_time.tv_sec)*1000000+t_time.tv_usec; uint8 t_i; for(t_i=0;t_i<8;t_i++) p_buffer[24+t_i]=(uint8)((t_timestamp>>(t_i<<3))&0xFF); memcpy(p_buffer+32,"x64x00x01x00",4); p_buffer[36]=0; p_buffer[37]=p_ap->essid_len; memcpy(p_buffer+38,p_ap->essid,p_ap->essid_len); return 38+p_ap->essid_len; }
至于使用方法嘛,如下:
struct ap t_ap; init_ap(&t_ap,(uint8*)"xECx17x2Fx2DxB6xB8","zjs"); uint8 t_buffer[256]; uint16 t_len=create_beacon_frame(t_buffer,&t_ap);
這樣就能在t_buffer中生成一個MAC地址為ec:17:2f:2d:b6:b8、名為zjs的beacon幀了。
階段五:發送Beacon幀
構造了beacon幀,就差發送了。你以為直接創建一個socket就能發送了?太天真了!發送這一步真不簡單啊,我也是看了aircrack-ng套件中的源碼才知道怎么發送的。要讓無線網卡發送原始的802.11幀,首先需要把無線網卡設置為monitor模式。把無線網卡設置為monitor模式可以使用ifconfig與iwconfig命令,當然,如果你想自己代碼實現,可以參考ifconfig與iwconfig命令的源碼。
ifconfig wlan0 down
iwconfig wlan0 mode monitor
ifconfig wlan0 up
以上三句命令,依次是關閉wlan0、將wlan0設置為monitor模式和開啟wlan0,記得要有管理員權限。
設置為monitor模式之后,需要在自己的代碼中創建綁定了wlan0的鏈路層原始套接字,次序依次為創建鏈路層套接字、找出wlan0的網卡編號、將原始套接字與wlan0綁定、將原始套接字設置為混雜模式,代碼如下:
int32 create_raw_socket(char* p_iface) { /* new raw socket */ int32 t_socket=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); if(t_socket<0) { perror("<create_raw_socket> socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)) failed!"); return -1; } /* get the index of the interface */ struct ifreq t_ifr; memset(&t_ifr,0,sizeof(t_ifr)); strncpy(t_ifr.ifr_name,p_iface,sizeof(t_ifr.ifr_name)-1); if(ioctl(t_socket,SIOCGIFINDEX,&t_ifr)<0) { perror("<create_raw_socket> ioctl(SIOCGIFINDEX) failed!"); return -1; } /* bind the raw socket to the interface */ struct sockaddr_ll t_sll; memset(&t_sll,0,sizeof(t_sll)); t_sll.sll_family=AF_PACKET; t_sll.sll_ifindex=t_ifr.ifr_ifindex; t_sll.sll_protocol=htons(ETH_P_ALL); if(bind(t_socket,(struct sockaddr*)&t_sll,sizeof(t_sll))<0) { perror("<create_raw_socket> bind(ETH_P_ALL) failed!"); return -1; } /* open promisc */ struct packet_mreq t_mr; memset(&t_mr,0,sizeof(t_mr)); t_mr.mr_ifindex=t_sll.sll_ifindex; t_mr.mr_type=PACKET_MR_PROMISC; if(setsockopt(t_socket,SOL_PACKET,PACKET_ADD_MEMBERSHIP,&t_mr,sizeof(t_mr))<0) { perror("<create_raw_socket> setsockopt(PACKET_MR_PROMISC) failed!"); return -1; } return t_socket; }
參數里的p_iface通常就是 wlan0
開啟了monitor模式,又創建了原始套接字,是不是就能直接發送t_buffer了呢?too young too simple!還差一個radiotap頭!我當時就被這個坑的不輕啊,后來看了aireply-ng的源碼,又抓包,又百度,才大致明白radiotap的作用。在抓包的過程中,無線網卡會附上一個radiotap頭,以展現與物理層有關的信息,比如功率、速率。而在發包的過程中,radiotap給無線網卡一定的參考信息。
附加radiotap頭并且發送數據包的函數如下:
int32 send_80211_frame(int32 p_socket,uint8* p_buffer,uint32 p_size) { uint8 t_buffer[4096]; uint8* t_radiotap=(uint8*)"x00x00x0dx00x04x80x02x00x02x00x00x00x00"; memcpy(t_buffer,t_radiotap,13); memcpy(t_buffer+13,p_buffer,p_size); p_size+=13; int32 t_size=write(p_socket,t_buffer,p_size); if(t_size<0) { perror("<send_80211_frame> write() failed!"); return -1; } return t_size; }
可以看到就是在生成的beacon幀之前加了13個字節。
階段六:整合、檢驗成果
至此所有代碼完成,以下是一份完整的代碼:
#include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <netpacket/packet.h> #include <linux/if_ether.h> #include <sys/ioctl.h> #include <arpa/inet.h> #include <sys/time.h> #include <net/if.h> typedef unsigned char bool; typedef signed char int8; typedef unsigned char uint8; typedef signed short int16; typedef unsigned short uint16; typedef signed int int32; typedef unsigned int uint32; typedef signed long long int64; typedef unsigned long long uint64; struct ap { uint8 bssid[6]; uint16 seq_id; uint8 essid_len; char essid[32]; }; void init_ap(struct ap* p_ap,uint8* p_bssid,char* p_essid) { memcpy(p_ap->bssid,p_bssid,6); p_ap->seq_id=0; uint32 t_len=strlen(p_essid); if(t_len>32) t_len=32; p_ap->essid_len=t_len; memcpy(p_ap->essid,p_essid,t_len); } uint16 create_beacon_frame(uint8* p_buffer,struct ap* p_ap) { memcpy(p_buffer,"x80x00x00x00xFFxFFxFFxFFxFFxFF",10); memcpy(p_buffer+10,p_ap->bssid,6); memcpy(p_buffer+16,p_ap->bssid,6); p_buffer[22]=(uint8)(p_ap->seq_id&0xFF); p_buffer[23]=(uint8)((p_ap->seq_id>>8)&0xFF); p_ap->seq_id+=0x10; struct timeval t_time; gettimeofday(&t_time,0); uint64 t_timestamp=((uint64)t_time.tv_sec)*1000000+t_time.tv_usec; uint8 t_i; for(t_i=0;t_i<8;t_i++) p_buffer[24+t_i]=(uint8)((t_timestamp>>(t_i<<3))&0xFF); memcpy(p_buffer+32,"x64x00x01x00",4); p_buffer[36]=0; p_buffer[37]=p_ap->essid_len; memcpy(p_buffer+38,p_ap->essid,p_ap->essid_len); return 38+p_ap->essid_len; } int32 create_raw_socket(char* p_iface) { /* new raw socket */ int32 t_socket=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); if(t_socket<0) { perror("<create_raw_socket> socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)) failed!"); return -1; } /* get the index of the interface */ struct ifreq t_ifr; memset(&t_ifr,0,sizeof(t_ifr)); strncpy(t_ifr.ifr_name,p_iface,sizeof(t_ifr.ifr_name)-1); if(ioctl(t_socket,SIOCGIFINDEX,&t_ifr)<0) { perror("<create_raw_socket> ioctl(SIOCGIFINDEX) failed!"); return -1; } /* bind the raw socket to the interface */ struct sockaddr_ll t_sll; memset(&t_sll,0,sizeof(t_sll)); t_sll.sll_family=AF_PACKET; t_sll.sll_ifindex=t_ifr.ifr_ifindex; t_sll.sll_protocol=htons(ETH_P_ALL); if(bind(t_socket,(struct sockaddr*)&t_sll,sizeof(t_sll))<0) { perror("<create_raw_socket> bind(ETH_P_ALL) failed!"); return -1; } /* open promisc */ struct packet_mreq t_mr; memset(&t_mr,0,sizeof(t_mr)); t_mr.mr_ifindex=t_sll.sll_ifindex; t_mr.mr_type=PACKET_MR_PROMISC; if(setsockopt(t_socket,SOL_PACKET,PACKET_ADD_MEMBERSHIP,&t_mr,sizeof(t_mr))<0) { perror("<create_raw_socket> setsockopt(PACKET_MR_PROMISC) failed!"); return -1; } return t_socket; } int32 send_80211_frame(int32 p_socket,uint8* p_buffer,uint32 p_size) { uint8 t_buffer[4096]; uint8* t_radiotap=(uint8*)"x00x00x0dx00x04x80x02x00x02x00x00x00x00"; memcpy(t_buffer,t_radiotap,13); memcpy(t_buffer+13,p_buffer,p_size); p_size+=13; int32 t_size=write(p_socket,t_buffer,p_size); if(t_size<0) { perror("<send_80211_frame> write() failed!"); return -1; } return t_size; } int32 main() { struct ap t_ap1,t_ap2; init_ap(&t_ap1,(uint8*)"xECx17x2Fx2DxB6xB8","zjs ap 1"); init_ap(&t_ap2,(uint8*)"xECx17x2Fx2DxB6xB9","zjs ap 2"); uint8 t_buffer[1024]; int32 t_socket=create_raw_socket("wlan0"); while(1) { uint16 t_len=create_beacon_frame(t_buffer,&t_ap1); printf("%dn",send_80211_frame(t_socket,t_buffer,t_len)); t_len=create_beacon_frame(t_buffer,&t_ap2); printf("%dn",send_80211_frame(t_socket,t_buffer,t_len)); usleep(100000); } return 0; }
保存為~/beacon.c
編譯:
gcc beacon.c -o beacon
執行(要有管理員權限):
ifconfig wlan0 down
iwconfig wlan0 mode monitor
ifconfig wlan0 up
./beacon
執行后,控制臺不斷輸出該幀實際寫出長度,應該是不停地輸出59。如果修改熱點名字應該會相應改變。
此時,打開手機,可以搜索到wifi熱點“zjs ap 1”和“zjs ap 2”。如果沒有,請耐心等待一會兒。 目前在K-Touch W68a(Android 4.2.2)、iPhone 6、vivo Y22L(Android 4.3)上都能搜索到熱點,以圖為證:
當然,想玩狠一點的,比如這樣:
#include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <netpacket/packet.h> #include <linux/if_ether.h> #include <sys/ioctl.h> #include <arpa/inet.h> #include <sys/time.h> #include <net/if.h> typedef unsigned char bool; typedef signed char int8; typedef unsigned char uint8; typedef signed short int16; typedef unsigned short uint16; typedef signed int int32; typedef unsigned int uint32; typedef signed long long int64; typedef unsigned long long uint64; #define AP_COUNT 8 struct ap { uint8 bssid[6]; uint16 seq_id; uint8 essid_len; char essid[32]; }; void init_ap(struct ap* p_ap,uint8* p_bssid,char* p_essid) { memcpy(p_ap->bssid,p_bssid,6); p_ap->seq_id=0; uint32 t_len=strlen(p_essid); if(t_len>32) t_len=32; p_ap->essid_len=t_len; memcpy(p_ap->essid,p_essid,t_len); } uint16 create_beacon_frame(uint8* p_buffer,struct ap* p_ap) { memcpy(p_buffer,"x80x00x00x00xFFxFFxFFxFFxFFxFF",10); memcpy(p_buffer+10,p_ap->bssid,6); memcpy(p_buffer+16,p_ap->bssid,6); p_buffer[22]=(uint8)(p_ap->seq_id&0xFF); p_buffer[23]=(uint8)((p_ap->seq_id>>8)&0xFF); p_ap->seq_id+=0x10; struct timeval t_time; gettimeofday(&t_time,0); uint64 t_timestamp=((uint64)t_time.tv_sec)*1000000+t_time.tv_usec; uint8 t_i; for(t_i=0;t_i<8;t_i++) p_buffer[24+t_i]=(uint8)((t_timestamp>>(t_i<<3))&0xFF); memcpy(p_buffer+32,"x64x00x01x00",4); p_buffer[36]=0; p_buffer[37]=p_ap->essid_len; memcpy(p_buffer+38,p_ap->essid,p_ap->essid_len); return 38+p_ap->essid_len; } int32 create_raw_socket(char* p_iface) { /* new raw socket */ int32 t_socket=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); if(t_socket<0) { perror("<create_raw_socket> socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)) failed!"); return -1; } /* get the index of the interface */ struct ifreq t_ifr; memset(&t_ifr,0,sizeof(t_ifr)); strncpy(t_ifr.ifr_name,p_iface,sizeof(t_ifr.ifr_name)-1); if(ioctl(t_socket,SIOCGIFINDEX,&t_ifr)<0) { perror("<create_raw_socket> ioctl(SIOCGIFINDEX) failed!"); return -1; } /* bind the raw socket to the interface */ struct sockaddr_ll t_sll; memset(&t_sll,0,sizeof(t_sll)); t_sll.sll_family=AF_PACKET; t_sll.sll_ifindex=t_ifr.ifr_ifindex; t_sll.sll_protocol=htons(ETH_P_ALL); if(bind(t_socket,(struct sockaddr*)&t_sll,sizeof(t_sll))<0) { perror("<create_raw_socket> bind(ETH_P_ALL) failed!"); return -1; } /* open promisc */ struct packet_mreq t_mr; memset(&t_mr,0,sizeof(t_mr)); t_mr.mr_ifindex=t_sll.sll_ifindex; t_mr.mr_type=PACKET_MR_PROMISC; if(setsockopt(t_socket,SOL_PACKET,PACKET_ADD_MEMBERSHIP,&t_mr,sizeof(t_mr))<0) { perror("<create_raw_socket> setsockopt(PACKET_MR_PROMISC) failed!"); return -1; } return t_socket; } int32 send_80211_frame(int32 p_socket,uint8* p_buffer,uint32 p_size) { uint8 t_buffer[4096]; uint8* t_radiotap=(uint8*)"x00x00x0dx00x04x80x02x00x02x00x00x00x00"; memcpy(t_buffer,t_radiotap,13); memcpy(t_buffer+13,p_buffer,p_size); p_size+=13; int32 t_size=write(p_socket,t_buffer,p_size); if(t_size<0) { perror("<send_80211_frame> write() failed!"); return -1; } return t_size; } int32 main() { struct ap t_aps[AP_COUNT]; uint32 t_i; for(t_i=0;t_i<AP_COUNT;t_i++) { uint8 t_mac[6]; char t_essid[32]; memcpy(t_mac,"xECx17x2Fx2DxB6xB0",6); memcpy(t_essid,"zjs ap 0",9); t_mac[5]+=t_i; t_essid[7]+=t_i; init_ap(&t_aps[t_i],t_mac,t_essid); } int32 t_socket=create_raw_socket("wlan0"); while(1) { for(t_i=0;t_i<AP_COUNT;t_i++) { uint8 t_buffer[1024]; uint16 t_len=create_beacon_frame(t_buffer,t_aps+t_i); printf("%dn",send_80211_frame(t_socket,t_buffer,t_len)); } usleep(100000); } return 0; }
結果可想而知,就是被刷屏咯
kali系統親測無效, 用wireshark捕獲端口數據以后, 發現 數據格式有問題, 沒有發送成功....
參考:
廈門點燃未來網絡科技有限公司, 是廈門最好的微信應用, 小程序, 微信網站, 公眾號開發公司
文章列表