Android設備被搜索之RawSocket

背景,初衷

1.需要完成一個設備搜索功能,及通過PC工具可以搜索到局域網中的設備
2.就算IP衝突也能搜索到設備
3.有Android系統源碼可以進行系統開發條件。

Raw Socket

raw socket,就是原始套接字,可以接收本機網卡上的數據幀或者數據包,相比正常使用的socket來說,它更底層,數據更原始。

編譯環境

在Android系統源碼根目錄下創建一個文件夾rawSocket,在其中新建兩個文件,一個是rawSocket.c (源碼),一個是Android.mk(編譯腳本)。
文件夾及文件
想要編譯代碼,就需要編譯mk文件。如下是我的配置:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
#編譯後的模塊名稱
LOCAL_MODULE := rawSocket
#源碼全稱,有多少都要加上
LOCAL_SRC_FILES := rawSocket.c
LOCAL_MODULE_TAGS := optional
#此處是代碼導入的必需包,用來獲取系統版本等相關信息
LOCAL_STATIC_LIBRARIES :=libcutils 
#編譯成可執行文件
include $(BUILD_EXECUTABLE)

需要編譯代碼前,還需要建立編譯環境,及在根目錄下執行下面代碼:

$  . build/envsetup.sh 

然後切換到rawSocket目錄下,輸入 mm 即可進行模塊編譯,編譯後的文件會生成到system/bin/路徑下,名字就是LOCAL_MODULE對應名字。

RawSocket.c重點代碼描述

建立raw socket服務

使用如下代碼實現:

int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

這裏說明一下:

  1. 第一個參數使用 PF_PACKET 可以操作鏈路層的數據
  2. 第二個參數 SOCK_RAW,它表示是包含了MAC層頭部信息的原始分組,當然這種類型的套接字在發送的時候需要自己加上一個MAC頭部
  3. 第三個參數 htons(ETH_P_ALL) (ETH_P_ALL宏定義爲0)時表示收發所有的協議

接收socket數據

使用如下代碼實現:

#define BUF_SIZE 2048

char buf[BUF_SIZE]while (1)
{
		int len = recvfrom(sockfd, buf, BUF_SIZE, 0, NULL, NULL);
		//解析buf
		//todo somthing
}

發送socket數據

使用如下代碼實現:

ret = send(sockfd, (const char*)buf , len , MSG_NOSIGNAL | MSG_DONTWAIT);

其中 buf 就是要發送的數據,len 表示發送的數據長度,
ret > 0 表示數據發送成功,ret 結果是多少,表示發送成功多少長度,如果ret比len小,則需要補充發送。
封裝後大致如下:

int socket_send(int sockfd, const void *buf, int len)
{
    int sent = 0, ret;
    while (sent < len)
    {
        ret = send(sockfd, (const char*)buf + sent, 
			len - sent, MSG_NOSIGNAL | MSG_DONTWAIT);
        if (ret > 0)
        {
            sent += ret;
        }
    }
}

注意:最好設置一個超時時間在裏面,避免在while裏面死循環

獲取本機的IP、MAC

這裏是使用了一個內部循環的socket來獲取ip及mac的。

int get_addr(uint32_t *ip, char *mac)
{
    int sockfd;
	struct ifreq ir;
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
		return -1;
    strcpy(ir.ifr_name, "eth0");//eth0
	ioctl(sockfd, SIOCGIFHWADDR, &ir);
	memcpy(mac, ir.ifr_hwaddr.sa_data, 6);
	ioctl(sockfd, SIOCGIFADDR, &ir);
	close(sockfd);
	*ip = ((struct sockaddr_in*)&ir.ifr_addr)->sin_addr.s_addr;
	return 0;
}

如果要獲取 wifi 的IP,則可以替換 “eth0” -> “wlan0”

設置開機自啓動

想要讓系統開機就啓動你自己的可執行文件,可以進行如下配置:
找到 源碼根目錄\system\core\rootdir\init.rc 文件
追加如下代碼即可:

service rawSocket /system/bin/rawSocket
    class main
    user root
	oneshot

到此就基本可以滿足你的設備開機就能收到指定的 buf ,然後發送自身的 IP 和 MAC 出去的基本功能需求了。


順便學習記錄以下函數:

memcpy(buf + 6, mac, 6);//拷貝,將mac拷貝到指針第6個位置開始,長度爲6
memset(buf + 18, 0, 60);//數據填充,用0,將buf指針第16個位置開始填充60個長度
strcpy(buf + 78, prop); //字符串拷貝,將指定字符串拷貝到從buf第78開始位置
sprintf(buf + 78 + len, "test = %s", "xxxx");//格式化字符串拷貝,最後一個參數填充到%s位置,再拷貝到buf指針位置

//下面是和Android系統相關
system(" reboot "); 相當於系統執行 reboot,就是重啓功能
//使用system基本可以參考adb shell 後的指令
//例如使用 adb 發送廣播 adb shell am broadcast -a com.android.test 對應代碼如下:
system(" am broadcast -a com.android.test ")


參考資料:
百度百科
鏈路層套接字PF_PACKET簡介
PF_PACKET說開去

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章