【发布时间】:2023-04-01 08:41:01
【问题描述】:
我正在 linux 64 位中构建没有 glibc 的简单应用程序。 但我不知道如何获得论点。
我搜索了一下,发现 RDI 是 argc,RSI 是 argv。但它没有用。
我在 _start 函数开始使用 gdb 时看到了寄存器,但 RDI 和 RSI 都是 0x0。 我还用最简单的组装应用程序进行了测试,但结果是一样的。 RDI 和 RSI 为 0x0。我相信即使我没有向程序传递任何参数,argc 也不应该是 0x0。
_start:
jmp $
这是我尝试过的 C 代码:
//Print Hello world WITHOUT standard library
//ONLY WITH SYSTEM CALL
#define ReadRdi(To) asm("movq %%rdi,%0" : "=r"(To));
#define ReadRsi(To) asm("movq %%rsi,%0" : "=r"(To));
void __Print(const char *str);
void __Exit();
void Test(const char *a){
long aL;
ReadRdi(aL);
char *aC = (int) aL;
__Print("Test argument: ");
__Print(aC);
__Print("\n");
}
void _start() {
long argcL;
long argvL;
ReadRdi(argcL);
ReadRsi(argvL);
int argc = (int) argcL;
char **argv = (char **) argvL;
__Print("Arguments: ");
for(int i = 0; i < argc; i ++) {
__Print(argv[i]);
__Print(", ");
}
__Print("\n");
Test("Hello, world!");
__Exit();
}
结果是:
Arguments:
Test argument: Hello, world!
我使用 gdb 检查了堆栈(RBP~RSP 中的内存值),但似乎什么都没有。
我尝试改变
void _start() {
long argcL;
long argvL;
ReadRdi(argcL);
ReadRsi(argvL);
int argc = (int) argcL;
char **argv = (char **) argvL;
__Print("Arguments: ");
到
void _start(int argc, char **argv) {
__Print("Arguments: ");
但我仍然看不到任何参数输出。
__Print 打印消息(使用 sys_write 系统调用),__Exit 退出程序(使用 sys_exit 系统调用)。
打印.asm:
section .text
global __Print
__Print:
mov rdx, 0
push rdi
jmp .count
.count:
add rdx, 1
add rdi, 1
cmp byte[rdi], 0
jne .count
.print:
pop rdi
mov rcx, rdi ;message to write
mov rbx, 1 ;file descriptor (stdout)
mov rax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
ret
退出.asm:
section .text
global __Exit
__Exit:
mov rax,1 ;system call number (sys_exit)
int 0x80
添加:我用这个命令链接:
gcc -o sysHello Exit.o Print.o sysHello.o -nostdlib -nodefaultlibs -g
【问题讨论】:
-
请注意,rdi, rsi 约定不适用于
_start(原始进程入口点)。此外,64 位系统调用与 32 位不同,因此所有这些都是错误的。此外,当您读取内联汇编中的寄存器时,编译器可能已经更改了它们,所以不要依赖这种行为。 -
标识符以
_开头,后跟一个大写字母或另一个下划线,保留用于实现。您不得在应用程序代码中使用它们。 -
如果你覆盖每个程序用于初始化的内部_start函数,你不会破坏控制流吗?有可能这样吗?