macos kext内核空间与用户空间通信例子一枚
kext内核空间与用户空间通讯例子(节选自OS-X-and-iOS-kernel-programming )
macos使用特殊的自定义的kext control sock协议(KEXT Controls and Notifications)去实现内核层与应用层通讯。使用方式类似linux的netlink,可以很方便的使用sock api收发数据。
HelloKernControl.c : 内核模块实现
// // HelloKernControl.c // HelloKernControl // // Created by Kiprey on 2021/8/10. // #include <libkern/libkern.h> #include <libkern/OSMalloc.h> #include <mach/mach_types.h> #include <sys/kern_control.h> #include <sys/proc.h> #include <sys/mbuf.h> #include "HelloKernControl.h" static char g_string_buf [256]; static int g_clients_connected = 0; static int hello_ctl_send_data_to_user(kern_ctl_ref kctlref, u_int32_t unit, void* buf, size_t len, int flags) { printf("hello_ctl_send_data_to_user called: \"%s\"", buf); ctl_enqueuedata(kctlref, unit, buf, len, 0); return 0; } static errno_t hello_ctl_recv_data_from_user(kern_ctl_ref kctlref, u_int32_t unit, void *unitinfo, mbuf_t m, int flags) { char buf[MAX_STRING_LEN]; const char* prefix = "recv from user: "; strcpy(buf, prefix, strlen(prefix)); mbuf_copydata(m, 0, MAX_STRING_LEN - strlen(prefix), buf + strlen(prefix)); printf("hello_ctl_recv_data_from_user: \"%s\"\n", buf); mbuf_freem(m); return hello_ctl_send_data_to_user(kctlref, unit, buf, MAX_STRING_LEN, flags); } static int hello_ctl_connect(kern_ctl_ref ctl_ref, struct sockaddr_ctl *sac, void** unitinfo) { printf("process with pid=%d connected\n", proc_selfpid()); return 0; } static errno_t hello_ctl_disconnect(kern_ctl_ref ctl_ref, u_int32_t unit, void* unitinfo) { printf("process with pid=%d disconnected\n", proc_selfpid()); return 0; } static int hello_ctl_get(kern_ctl_ref ctl_ref, u_int32_t unit, void *unitinfo, int opt, void *data, size_t *len) { int ret = 0; switch (opt) { case HELLO_CONTROL_GET_STRING: *len = min(MAX_STRING_LEN, *len); strncpy(data, g_string_buf, *len); break; default: ret = ENOTSUP; break; } return ret; } static int hello_ctl_set(kern_ctl_ref ctl_ref, u_int32_t unit, void* unitinfo, int opt, void* data, size_t len) { int ret = 0; switch (opt) { case HELLO_CONTROL_SET_STRING: strncpy(g_string_buf, (char*)data, min(MAX_STRING_LEN, len)); printf("HELLP_CONTROL_SET_STRING: new string set to: \"%s\"\n", g_string_buf); break; default: ret = ENOTSUP; break; } return ret; } static struct kern_ctl_reg g_kern_ctl_reg = { "com.osxkernel.HelloKernControl",//需要相同的bundleid 0, 0, CTL_FLAG_PRIVILEGED, MAX_CTL_SENDSIZE, MAX_CTL_RECVSIZE, hello_ctl_connect, hello_ctl_disconnect, hello_ctl_recv_data_from_user, hello_ctl_set, hello_ctl_get }; static boolean_t g_filter_registered = FALSE; static kern_ctl_ref g_ctl_ref; kern_return_t HelloKernControl_start(kmod_info_t * ki, void *d); kern_return_t HelloKernControl_stop(kmod_info_t *ki, void *d); kern_return_t HelloKernControl_start(kmod_info_t * ki, void *d) { strncpy(g_string_buf, DEFAULT_STRING, strlen(DEFAULT_STRING)); /* Register the control */ int ret = ctl_register(&g_kern_ctl_reg, &g_ctl_ref); if (ret == KERN_SUCCESS) { g_filter_registered = TRUE; return KERN_SUCCESS; } return KERN_FAILURE; } kern_return_t HelloKernControl_stop(kmod_info_t *ki, void *d) { if (g_clients_connected != 0) return KERN_FAILURE; if (g_filter_registered) ctl_deregister(g_ctl_ref); return KERN_SUCCESS; }
HelloKernControl.h : 内核头文件
// // HelloKernControl.h // HelloKernControl // // Created by Kiprey on 2021/8/10. // #ifndef HelloKernControl_h #define HelloKernControl_h #define BUNDLE_ID "com.osxkernel.HelloKernControl" #define HELLO_CONTROL_GET_STRING 1 #define HELLO_CONTROL_SET_STRING 2 #define DEFAULT_STRING "Hello World" #define MAX_STRING_LEN 256 #define MAX_CTL_SENDSIZE 4096 #define MAX_CTL_RECVSIZE 4069 #endif /* HelloKernControl_h */
main.cpp : 用户空间
// // user_kern_ctl.cpp // HelloKernControl // // Created by Kiprey on 2021/8/10. // #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <unistd.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/kern_control.h> #include <sys/sys_domain.h> #include "../HelloKernControl/HelloKernControl.h" int main(int argc, char* const*argv) { struct ctl_info ctl_info; struct sockaddr_ctl sc; char str[MAX_STRING_LEN]; int sock = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); //初始化KEXT_CONTROL if (sock < 0) return -1; bzero(&ctl_info, sizeof(struct ctl_info)); strcpy(ctl_info.ctl_name, "com.osxkernel.HelloKernControl"); //需要相同的bundleid if (ioctl(sock, CTLIOCGINFO, &ctl_info) == -1) return -1; bzero(&sc, sizeof(struct sockaddr_ctl)); sc.sc_len = sizeof(struct sockaddr_ctl); sc.sc_family = AF_SYSTEM; sc.ss_sysaddr = SYSPROTO_CONTROL; sc.sc_id = ctl_info.ctl_id; sc.sc_unit = 0; if (connect(sock, (struct sockaddr *)&sc, sizeof(struct sockaddr_ctl))) //与sock api相同 return -1; /* Get an existing string from the kernel */ unsigned int size = MAX_STRING_LEN; if (getsockopt(sock, SYSPROTO_CONTROL, HELLO_CONTROL_GET_STRING, &str, &size) == -1) //对应内核层实现hello_ctl_get return -1; printf("kernel string is: %s\n", str); /* Set a new string */ strcpy(str, "Hello Kernel, here's your new string, enjoy!"); if (setsockopt(sock, SYSPROTO_CONTROL, HELLO_CONTROL_SET_STRING, str, (socklen_t)strlen(str)) == -1) //会调用内核层的上面的hello_ctl_set return -1; /* echo a string */ int ret = 0; const char * msg = "hello kernel (from user)"; if((ret = send(sock, msg, strlen(msg), 0)) <= 0) //发送消息 perror("send: "); char buf[255]; if((ret = recv(sock, buf, 255, 0)) < 0) //接收消息 perror("recv: "); printf("from kernel: [%s]\n", buf); close(sock); return 0; }