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

 

可以看到kext control与linux 的netlink 非常相似。相关文档链接

源码连接