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 ")