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));
這裏說明一下:
- 第一個參數使用 PF_PACKET 可以操作鏈路層的數據
- 第二個參數 SOCK_RAW,它表示是包含了MAC層頭部信息的原始分組,當然這種類型的套接字在發送的時候需要自己加上一個MAC頭部
- 第三個參數 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 ")