0

我正在尝试 HookExecve 并打印参数。我可以很好地打印文件名。如何在内核模式下打印char *const argv[]的参数?我需要能够获得它们的数量并且能够打印它们。

因此,如果我运行 ls -lah 。我应该在 argv ["ls", "-lah"] 中看到;我可以在用户空间中执行此操作,但是所有将信息从用户空间复制到内核空间的尝试似乎都会导致崩溃。

    char CharBuffer [255] = {'\0'};
    char Argz       [255] = {'\0'};

    asmlinkage int (*origional_execve)(const char *filename, char *const argv[], char *const envp[]);
    asmlinkage int HookExecve(const char *filename, char *const argv[], char *const envp[]) {
       unsigned int len     = sizeof(CharBuffer) / sizeof(CharBuffer[0]);
       unsigned int Argzlen = sizeof(Argz)       / sizeof(Argz[0]);

        copy_from_user(&CharBuffer , filename , len );
        //copy_from_user(Argz , argv[0] , sizeof(argv[0]) );     
        //strncpy_from_user(Argz, argv[0], len ); // Might break stuff
        printk( KERN_INFO "Executable Name %s \n", CharBuffer  );
        memset(CharBuffer, 0 , len);

      return (*origional_execve)(filename, argv, envp);
    }
4

2 回答 2

0

sizeof(CharBuffer) / sizeof(CharBuffer[0])

没有。这只是sizeof(char*)/sizeof(char)

sizeof(Argz) / sizeof(Argz[0])

没有。这正好等于sizeof(char**)/sizeof(char*)

sizeof不会让你知道数组中元素的数量。sizeof返回基础类型的大小。

要获取字符串的长度(即找到终止零字符之前的字符数),请使用strlen. strlen返回size_t类型,sizeof.

argv数组以标记值 NULL结尾。要打印char **argv数组的内容,请迭代直到找到空值:

for (char *i = argv; *i != NULL; ++i) {
    printf("%s\n", *i);
}

或者:

for (size_t i = 0; argv[i] != NULL; ++i) {
    printf("%s\n", argv[i]);
}

char CharBuffer [255] = {'\0'}; &字符缓冲区

虽然等于值,&CharBuffer但不是char*类型。&CharBuffer具有类型char (*)[255]- 它是一个指向 255 个字符的数组的指针。启用编译器警告并解决它们 - 在内核中不要犯错误。只需使用CharBufferwhich 转换为char*type in strcpy-likeish 函数。

你似乎有点想要:

char char_buffer[255] = {0};
// Note: Do not name variables similar, especially globals.
// The argc <-> argz <-> argv differ only in one char.
// and 2d array to hold arguments strings
char argz[255][255] = {0};
// the count of arguments
size_t argc = 0;

asmlinkage int (*origional_execve)(const char *filename, char *const argv[], char *const envp[]);
asmlinkage int HookExecve(const char *filename, char *const argv[], char *const envp[]) {

    if (strlen_user(filename) + 1 > sizeof(char_buffer)) {
         // TODO: no memory for filename
         return -1;
    }
    copy_from_user(char_buffer, filename, strlen_user(filename) + 1);

    size_t i = 0;
    for (i = 0; argv[i] != 0; ++i) {

         // note: argz has type `char (*)[255][255]`
         // so: sizeof(argz) = 255 * 255 * sizeof(char)
         // *argz has type `char (*)[255]`
         // so: sizeof(*argz) = 255 * 255 * sizeof(char)
         // so: sizeof(argz)/sizeof(*argz) = 255
         // This would NOT work if `argz` would be a `char**`...
         // if (i > sizeof(argz)/sizeof(*argz)) {
         // but there is ARRAY_SIZE macro that does the same
         if (i > ARRAY_SIZE(argz)) {
              // TODO: not enough indexes
              return -1;
         }

         // sizeof(argz[i]) = sizeof(char(*)[255]) = 
         //                 = 255 * sizeof(char) = 255
         if (strlen_user(argv[i]) + 1 > sizeof(argz[i])) {
              // TODO: no memory to copy
              return -1;
         }
         copy_from_user(argz[i], argv[i], strlen_user(argv[i]) + 1);
    }
    argc = i;

    printk( KERN_INFO "Executable Name %s \n", char_buffer);
    for (size_t i = 0; i < argc; ++i) {
        printk( KERN_INFO "arg[i] = %s\n", argz[i]);            
    }

    return (*origional_execve)(filename, argv, envp);
}

我没有测试代码,很可能它包含许多错误。更好地处理错误并仅使用动态分配并处理分配错误也会更好。

于 2020-03-21T12:42:15.737 回答
0

我目前正在实现一个简单的 LSM(由于 rootkit 标签,不确定你是否想要这个),它execve()通过bprm_check_security()钩子钩子来检查签名。

为了了解如何访问字符串,argv[]envp[]使用 TOMOYO Linux 作为我需要的起点,因为我无法通过在struct linux_binprm. 参数计数argc始终可以通过bprm->argc但字符串本身不容易访问。

如果通过 bash 直接执行 bash 脚本,则此简化示例将打印参数和环境变量。

static int mylsm_bprm_check_security (struct linux_binprm *bprm){
    
    struct tomoyo_page_dump dump = { };

    if (strstr(bprm->filename, "bash")){ 
        printk(KERN_EMERG "<mylsm> %s\n", tomoyo_print_bprm(bprm, &dump));
    }
    return 0;
}

这让我们得到了这个格式很好的输出:

target_board:~# echo "echo hacked" > hacked.sh
target_board:~# bash hacked.sh
[   84.033006] <mylsm> argv[]={ "bash" "hacked.sh" } envp[]={ "BOARD=redacted" 
"ARCH=x86_64" "TARGET_SYS=redacted" "TZ=Universal" "HUSHLOGIN=FALSE" "USER=root" 
"TARGET_ARCH=x86_64" "PWD=/root" 
"HOME=/root" "HOST=redacted" "SHELL=/bin/sh" "TERM=vt100" "SHLVL=0" "LOGNAME=root" 
"PATH=/bin:/sbin:/usr/bin:/usr/sbin" "HISTFILESIZE=10" }
hacked

要了解它是如何工作的,您需要 TOMOYO Linux 本身的一些东西:

为了能够保存转储的内存页面:

要将包含所需字符串的内存页面转储到上述结构中:

这通过遍历内存并返回一些字符串来实现真正的魔力:

在将所有内容放在您自己的 LSM 中之后,您可以使用这些钩子来完成它或从中派生您自己的代码。

于 2022-02-17T13:51:34.777 回答