Assembly Linux内核如何知道应该从系统调用路径参数中读取多少字节?

Assembly Linux内核如何知道应该从系统调用路径参数中读取多少字节?,assembly,linux-kernel,parameter-passing,system-calls,abi,Assembly,Linux Kernel,Parameter Passing,System Calls,Abi,我在谷歌上搜索,发现Linux内核使用struct作为变量 #define EMBEDDED_LEVELS 2 struct nameidata { struct path path; struct qstr last; struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags; unsigned seq, m_seq;

我在谷歌上搜索,发现Linux内核使用struct作为变量

#define EMBEDDED_LEVELS 2
struct nameidata {
    struct path path;
    struct qstr last;
    struct path root;
    struct inode    *inode; /* path.dentry.d_inode */
    unsigned int    flags;
    unsigned    seq, m_seq;
    int     last_type;
    unsigned    depth;
    int     total_link_count;
    struct saved {
        struct path link;
        struct delayed_call done;
        const char *name;
        unsigned seq;
    } *stack, internal[EMBEDDED_LEVELS];
    struct filename *name;
    struct nameidata *saved;
    struct inode    *link_inode;
    unsigned    root_seq;
    int     dfd;
} __randomize_layout;
例如
execve
systeml调用(在此处找到)
此函数将文件名指针作为路径名传递给另一个函数,并将结构名设置为此路径名

static int __do_execve_file(int fd, struct filename *filename,
                struct user_arg_ptr argv,
                struct user_arg_ptr envp,
                int flags, struct file *file)
我这里的问题是如何计算从堆栈传递给这个函数的参数的长度(例如
“/bin/sh”

(编者按:
const char*pathname
arg to不必指向堆栈内存。我认为这个问题是假设在外壳代码用例中,您在用户空间堆栈上构造了一个路径,并传递指向该路径的指针。)


(我正在学习汇编,我被困在系统调用的参数传递部分)

Linux使用以零结尾的字符串,这是C的标准字符串格式。字符串的结尾用零字节标记,字符串中第一个零字节以外的任何字节都不是字符串的一部分。值得注意的是,这意味着文件名中不能有零字节。(出于同样的原因,大多数外壳代码不能有零字节,因为它们旨在利用某种字符串缓冲区溢出。)


实际上,内核通常不需要知道文件名的长度,而是使用像
strcmp
这样的函数逐字节比较字符串,在比较不同的第一个字节或遇到的第一个零字节处停止。但是,如果需要,可以使用类似于
strlen

的函数来计算字符串的长度。Linux使用以零结尾的字符串,这是C的标准字符串格式。字符串的结尾用零字节标记,字符串中第一个零字节以外的任何字节都不是字符串的一部分。值得注意的是,这意味着文件名中不能有零字节。(出于同样的原因,大多数外壳代码不能有零字节,因为它们旨在利用某种字符串缓冲区溢出。)


实际上,内核通常不需要知道文件名的长度,而是使用像
strcmp
这样的函数逐字节比较字符串,在比较不同的第一个字节或遇到的第一个零字节处停止。但是,如果有必要,可以使用类似于
strlen
的函数来计算字符串的长度。最后,我找到了我的答案
这是我找到的源代码

#include <stdio.h>

char hello[] = "HelloA\0AAABB";

int main( void )
{
   __asm__
   (
    "mov $hello , %eax;"
    "push %eax;"
    "call myprint;"
   );
}

void myprint(char input[])
{
    printf("%s" , input);
}
它实际上是这样编译的:

char hello[] = "Hello\0"
您问候的大小将从5改为6。

最后,在汇编程序设计中,我们必须考虑在系统调用中传递的参数的空终止符。只要linux内核是用C编程语言编写的,我们就必须接受C编程语言的规则


下面是charhello[]的Gdb结果

0x8049597 <hello>:      0x48    0x65    0x6c    0x6c    0x6f    0x41    0x00    0x41
0x804959f <hello+8>:    0x41    0x41    0x42    0x42    0x00    0x77    0x6f    0x72
0x8049597:0x48 0x65 0x6c 0x6c 0x6f 0x41 0x00 0x41
0x804959f:0x41 0x41 0x42 0x42 0x00 0x77 0x6f 0x72
0x8049597是字符串(“HelloA\0AAABB”)的起始地址。
我们将\0放在字符后面。字符“A”等于Ascii表中的0x41十六进制数。并且\0是0x00。

这就是为什么printf函数只显示字符串的前6个字符。

最后我找到了答案
这是我找到的源代码

#include <stdio.h>

char hello[] = "HelloA\0AAABB";

int main( void )
{
   __asm__
   (
    "mov $hello , %eax;"
    "push %eax;"
    "call myprint;"
   );
}

void myprint(char input[])
{
    printf("%s" , input);
}
它实际上是这样编译的:

char hello[] = "Hello\0"
您问候的大小将从5改为6。

最后,在汇编程序设计中,我们必须考虑在系统调用中传递的参数的空终止符。只要linux内核是用C编程语言编写的,我们就必须接受C编程语言的规则


下面是charhello[]的Gdb结果

0x8049597 <hello>:      0x48    0x65    0x6c    0x6c    0x6f    0x41    0x00    0x41
0x804959f <hello+8>:    0x41    0x41    0x42    0x42    0x00    0x77    0x6f    0x72
0x8049597:0x48 0x65 0x6c 0x6c 0x6f 0x41 0x00 0x41
0x804959f:0x41 0x41 0x42 0x42 0x00 0x77 0x6f 0x72
0x8049597是字符串(“HelloA\0AAABB”)的起始地址。
我们将\0放在字符后面。字符“A”等于Ascii表中的0x41十六进制数。并且\0是0x00。

这就是为什么printf函数只显示字符串的前6个字符。

路径名arg to
execve
指向
0
终止/隐式长度的C字符串。内核在用户空间内存上使用某种类型的strlen,就像将它传递给printf一样。这就是你要问的吗?您不需要知道内核内部,只需要使用系统调用,如
execve
open
,通过指针获取C字符串。是的,我知道我不需要知道内核内部。但我是一个发现者,如果我在编程生涯中错过了一些东西,我就不会有这个想法。这不仅仅是针对shellcdoe用例的。事实并非如此。这只是关于如何从堆栈计算参数lenght什么堆栈?系统调用参数在寄存器中传递。没有任何内容隐式使用用户空间堆栈。指向
execve
的路径名arg指向
0
终止/隐式长度的C字符串。内核在用户空间内存上使用某种类型的strlen,就像将它传递给printf一样。这就是你要问的吗?您不需要知道内核内部,只需要使用系统调用,如
execve
open
,通过指针获取C字符串。是的,我知道我不需要知道内核内部。但我是一个发现者,如果我在编程生涯中错过了一些东西,我就不会有这个想法。这不仅仅是针对shellcdoe用例的。事实并非如此。这只是关于如何从堆栈计算参数lenght什么堆栈?系统调用参数在寄存器中传递。任何内容都不会隐式使用用户空间堆栈。必须将系统调用参数复制到内核拥有的内存中,