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;
}