【问题标题】:How to use the function from a custom kernel module?如何使用自定义内核模块中的功能?
【发布时间】:2015-04-05 03:24:27
【问题描述】:

我已经成功实现了一个自定义系统调用getpuid(),现在我需要编写一个自定义的动态加载模块来导出一个与自定义系统调用getpeuid()功能完全相同的函数。 该系统调用用于获取调用进程的父进程的euid。以及自定义模块的段:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/printk.h>
#include <linux/rcupdate.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/cred.h>

static int *getpeuid(pid_t pid, uid_t *uid)
{
    // Code to get the parent process euid
    ......;
}

EXPORT_SYMBOL(getpeuid);

/* This function is called when the module is loaded. */
int getpeuid_init(void)
{
       printk(KERN_INFO "getpeuid() loaded\n");

       return 0;
}

/* This function is called when the module is removed. */
void getpeuid_exit(void) {
    printk(KERN_INFO "Removing getpeuid()\n");
}

/* Macros for registering module entry and exit points. */
module_init( getpeuid_init );
module_exit( getpeuid_exit );

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Return parent euid.");
MODULE_AUTHOR("CGG");

我已成功编译此自定义模块并将模块插入内核。然后,我写了一个测试来测试从实现的可加载内核模块导出的函数的功能:

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    pid_t pid;
    uid_t *uid;

    uid = (uid_t *)malloc(sizeof(uid_t));

    pid = getppid();
    int retval = getpeuid(pid, uid);

    if( retval < 0 )
    {
        perror("My system call returned with an error code.");
    }

    printf("My syscall's parameters: %ld \n", pid);
    printf("My system call returned %d.\n", retval);
    printf("Current values:  uid=%ld \n", *uid);
    return 0;
}

但是当我编译测试脚本时,它给了我以下错误:

/tmp/ccV8WTx0.o:在函数'main'中: hw5-test.c:(.text+0x33): undefined reference to `supermom' collect2: error: ld 返回 1 个退出状态

我使用cat /proc/kallsyms检查了系统中可用的符号,我导出的符号在那里:

fa0eb000 T getpeuid [getpeuid]

我只是不知道我应该如何使用我的自定义函数,因为我没有用于我的自定义模块的头文件以包含在我的测试脚本中。即使我需要写一个头文件,我也不知道如何为自定义内核模块写一个头文件。

有人可以帮帮我吗?

提前致谢!

编辑:

我只能使用可动态加载的内核模块来模拟系统调用的功能。

编辑:

模块初始化代码中不允许修改系统调用表。

我从其他人那里得到了以下链接作为提示:

https://www.linux.com/learn/linux-career-center/31161-the-kernel-newbie-corner-kernel-symbols-whats-available-to-your-module-what-isnt

【问题讨论】:

  • 您的直接问题是您试图链接一个不支持您的更改的 C 库 - 它缺少自定义系统调用的包装器。如果你真的设法添加了一个新的系统调用,你有它的编号,并且通常有一个 C 库函数可以让你通过编号而不是名称调用任意一个,你可以在开始修改 C 库之前开始对于您修改后的内核。当然,您是否真的完成了添加新的系统调用,这似乎仍然是一个悬而未决的问题。
  • @ChrisStratton 是的,我已经修改了我的问题,实际上我需要做的是通过可加载内核模块添加一个函数来模拟我之前编写的系统调用的行为。
  • 您不能在不修改系统调用表的情况下添加系统调用。因此,要么,您修改了它,您需要通过数字调用它或将包装器添加到您的库中,否则您没有修改它并且必须使用其他接口,或者您希望得到的东西只是'除非您花更多时间了解这些现实并根据它们调整目标,否则不会发生。最后,您必须停止以毫无意义的方式使用“模拟”一词。 准确地说你的意思。当你这样做的时候,别再把 C 程序称为“脚本”了。
  • ....通过模拟我的意思是从模块中导出的函数必须与我之前实现的系统调用具有完全相同的功能。
  • 这样的功能只导出到内核,而不是用户空间。如果您愿意,您将不得不使用内核用户空间接口之一,例如已经讨论过的。

标签: c linux-kernel system-calls kernel-module archlinux


【解决方案1】:

使用 sysfs

查看list of various Linux kernel <--> Userspace interfaces

要允许用户空间可加载内核模块交互,请考虑使用sysfs

要在可加载模块中添加对 sysfs 的支持,请查看 a sys-fs entry 的基础知识。

best practices of creating sysfs entries 的良好指南应该让您以正确的方式开始。

然后用户空间测试将从

int retval = getpeuid(pid, uid);

到使用openwrite()read()的东西
像普通文件一样与 sysfs 条目交互。
(为什么文件?因为everything is a file on UNIX。)

您可以进一步简化此操作,使用 shell 脚本,该脚本使用 echo/cat 命令通过 sysfs 条目从可加载内核模块传递/收集数据。


替代选项:漂亮/丑陋的黑客攻击

免责声明:我同意尝试在可加载内核模块中使用系统调用既不是正确的解决方案,也不能保证始终有效。我知道我在做什么。
(将鼠标悬停在以下块上,仅在您同意上述内容的情况下

查看此 answer 和相关的 code,它描述了一种潜在的“黑客”,允许在当前系统调用中任何未使用位置的可加载模块中实现自定义系统调用内核表。

还要仔细阅读这个question 的几个答案/cmets。他们处理克服无法修改系统调用表的问题。其中一个 cmets 还强调了这样一个事实,即实现自己的扩展的管理程序不太可能受到这种“利用”的影响,因为它们对系统调用表提供了更好的保护。

请注意,此类非标准接口可能并不总是有效,即使有效,它们也可能随时停止工作。坚持使用标准接口以提高可靠性。

【讨论】:

  • 感谢您的回复,但我只能使用可动态加载的内核模块来模拟系统调用的功能。
  • 更新了答案。出于教育目的,请查看丑陋的黑客部分。不过要小心,您已收到警告。
  • 再次抱歉,伙计,我不允许在模块初始化中修改系统调用表。不过还是谢谢你的帮助。
  • 没问题。这种特定限制的任何原因?这是一个代码挑战/作业问题吗?如果有,请分享原始问题陈述。然后有人可以帮助确定原始问题陈述正在寻找的“正确答案”...
  • 是的,这是一个代码挑战,我已经修改了我的问题。
【解决方案2】:

EXPORT_SYMBOL 在内核中导出符号,以便其他内核模块可以使用它。它不会使其对用户态程序可用。 似乎无法通过内核模块添加新的系统调用: https://unix.stackexchange.com/questions/47701/adding-a-new-system-call-to-linux-3-2-x-with-a-loadable-kernel-module

【讨论】:

  • @TheCodeArtist 抱歉,回滚您的编辑。你完全改变了我回答的意思。
  • 当然。没问题。我想我最好提供一个单独的答案,因为 OP 想要在可加载内核中调用一个函数并且采用系统调用方式是有问题的。
  • @MK。感谢您的回复,让我重新表述我的问题,我已经成功实现了一个 getpeuid 系统调用。但是我需要使用一个可加载的模块来模拟我实现的系统调用getpeuid()的功能,我该怎么做呢?
  • @JialunLiu 对不起,不明白你所说的“模拟”是什么意思。如果你想添加一个用户态可用的新系统调用,你需要重新编译内核。
  • 发帖者实际遇到的问题是 C 库对这种自定义一无所知 - 这是构建时问题,而不是运行时问题(目前)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-29
  • 1970-01-01
相关资源
最近更新 更多