Assembly Linux内核如何知道应该从系统调用路径参数中读取多少字节?
我在谷歌上搜索,发现Linux内核使用struct作为变量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;
#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什么堆栈?系统调用参数在寄存器中传递。任何内容都不会隐式使用用户空间堆栈。必须将系统调用参数复制到内核拥有的内存中,