android并没有实现linux的udev,其功能由vold进程实现,其包含VolumeManager,NetlinkManager,CommandListener等modules
vold工作机制:
kernel检测到硬件事件,然后广播出去,Native层的init进程通知vold(root权限)去处理该广播信息,vold再通知JNI层的MountService,其与Java应用层交互
vold进程可以将用户态数据copy到kernel,由kernel写入设备文件(恶意程序的依赖)
vold进程应该只处理kernel发送的device的NETLINK的socket消息,但实际上并未检测NETLINK的socket消息的来源,
这样可以广播add device的socket信息,触发硬件处理事件,将恶意代码传入kernel,由其写入设备文件
修改方法:
首先在代码中设置socket参数,可以让接受者知道发送者的uid和gid:
setsockopt(s,SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
我机器的源码,在system/core/libcutils/uevent.c 的uevent_open_socket函数中已经有了这个设置语句,是android打过patch的;
在/system/core/init/device.c中,修订版本的代码如下:
while((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {}
用了这个函数uevent_kernel_multicast_recv(),取代了之前的简单的recv函数。
在system/core/libcutils/uevent.c中,uevent_kernel_multicast_recv()代码实现了对socket消息的来源的判断
Myhook code:
打log至logcat
#include <cutils/log.h> #define LOG_TAG “netlink hooker” ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) { … … if (addr.nl_groups == 0 || addr.nl_pid != 0) { //判断socket来源的gid和uid LOGW(“non-kernel or unicast netlink message”); goto out; } struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { LOGW(“netlink message with no sender credentials”); goto out; } struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg); if (cred->uid != 0) { LOGW(“netlink message from non-root user”); goto out; } return n; out: /* clear residual potentially malicious data */ bzero(buffer, length); errno = EIO; return -1; }