文章出處

  請想象一下這樣的情景:你可以任意偽造很多個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 DSFROM 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-POLLABLECF-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捕獲端口數據以后, 發現 數據格式有問題, 沒有發送成功....

  參考:

    from:https://zhoujianshi.github.io/articles/2016/%E6%9E%84%E9%80%A0%E5%B9%B6%E5%8F%91%E9%80%81Beacon%E5%B8%A7%E4%BB%A5%E4%BC%AA%E9%80%A0%E4%BB%BB%E6%84%8FWiFi%E7%83%AD%E7%82%B9/index.html

 

廈門點燃未來網絡科技有限公司, 是廈門最好的微信應用, 小程序, 微信網站, 公眾號開發公司


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()