C如何存储函数,何时转换为机器代码?
所以我最近问了这个问题 我必须创建一个环境变量MYENV并在其中存储一些东西,这样我才能成功地运行这段代码C如何存储函数,何时转换为机器代码?,c,assembly,shellcode,C,Assembly,Shellcode,所以我最近问了这个问题 我必须创建一个环境变量MYENV并在其中存储一些东西,这样我才能成功地运行这段代码 #include <stdio.h> #include <stdlib.h> int main(){ int (*func)(); func = getenv("MYENV"); func(); } returnVar有9900台吗 如果是的话,我怎么知道那个字符串是什么 我很难对这件事保持
#include <stdio.h>
#include <stdlib.h>
int main(){
int (*func)();
func = getenv("MYENV");
func();
}
returnVar有9900台吗
如果是的话,我怎么知道那个字符串是什么
我很难对这件事保持清醒 您必须用目标机器的操作码填写环境变量。我做了一个小实验:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int (*f)();
f = getenv("VIRUS");
(*f)();
printf("Haha, it returned\n");
return 0;
}
然后我写了一些汇编程序:
mov %rbp, %rsp
pop %rbp
ret
它模仿了尾声的功能。编辑它:
$ cc -c t.s
查看操作码:
$ objdump -D t.o
...
0: 48 89 ec mov %rbp,%rsp
3: 5d pop %rbp
4: c3 retq
设置环境:
$ export VIRUS=$(printf "\\x48\\x89\\xec\\x5d\\xc3")
然后运行程序:
$ ./a.out
它什么也没说,这清楚地表明printf生产线被跨过了。但是,为了核实,我试着:
$ export VIRUS=$(printf "\\xc3")
$ ./a.out
Haha, it returned
这是在ubuntu-18.04上使用amd64指令集运行的。如果这恰好是学校的作业,您应该以加分为目标,并找出如何让它执行包含空0字节的操作码。您必须用目标机器的操作码填充环境变量。我做了一个小实验:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int (*f)();
f = getenv("VIRUS");
(*f)();
printf("Haha, it returned\n");
return 0;
}
然后我写了一些汇编程序:
mov %rbp, %rsp
pop %rbp
ret
它模仿了尾声的功能。编辑它:
$ cc -c t.s
查看操作码:
$ objdump -D t.o
...
0: 48 89 ec mov %rbp,%rsp
3: 5d pop %rbp
4: c3 retq
设置环境:
$ export VIRUS=$(printf "\\x48\\x89\\xec\\x5d\\xc3")
然后运行程序:
$ ./a.out
它什么也没说,这清楚地表明printf生产线被跨过了。但是,为了核实,我试着:
$ export VIRUS=$(printf "\\xc3")
$ ./a.out
Haha, it returned
这是在ubuntu-18.04上使用amd64指令集运行的。如果这恰好是学校的作业,你应该争取加分,并弄清楚如何让它执行包含空0字节的操作码。一般来说不是这样。但是,它可能在某些平台上工作,但根据C标准,它是UB。在通用平台上,您必须至少使包含代码(即unixish系统上的mprotect)的页面可执行。您可以通过编译一个执行所需操作的程序,然后查看生成的机器代码来确定字符串是什么。@Barmar:更正:编译一个函数,而不是整个程序。e、 g./ls不是一个函数,而是一个命令。qsort是一种标准的C函数。更一般的说法是,编译语言通常对C:函数中的代码和C:变量(包括数组)中的数据进行严格区分。在现代系统中,程序不能自我修改,即使这很酷;除非您跳过上面链接中的环,否则它无法执行数据。比如说,在20世纪70年代,这两种方法都比较容易实现,有时也会得到很好的利用。但一般来说,您会使用解释语言来实现这一点,其中一些Lisp根本没有做任何区分。一般来说不会。但是,它可能在某些平台上工作,但根据C标准,它是UB。在通用平台上,您必须至少使包含代码(即unixish系统上的mprotect)的页面可执行。您可以通过编译一个执行所需操作的程序,然后查看生成的机器代码来确定字符串是什么。@Barmar:更正:编译一个函数,而不是整个程序。e、 g./ls不是一个函数,而是一个命令。qsort是一种标准的C函数。更一般的说法是,编译语言通常对C:函数中的代码和C:变量(包括数组)中的数据进行严格区分。在现代系统中,程序不能自我修改,即使这很酷;除非您跳过上面链接中的环,否则它无法执行数据。比如说,在20世纪70年代,这两种方法都比较容易实现,有时也会得到很好的利用。但一般来说,你会使用解释语言,其中一些Lisp根本没有区别。asm绝对值得评论。您正在拆下调用方的堆栈帧,因为该函数没有按%rbp/mov%rsp,%rbp生成自己的堆栈帧。所以ret将main的返回地址弹出到RIP中。它并不是完全超越printf,它更像是一个长jmp。当然,这取决于编译器生成的调试模式代码!它将恰好返回零,因为您将函数指针的arg类型声明为not void,因此编译器将通过调零EAX将AL归零。而且进程退出状态只捕获retval的低位字节。另外,更简单的方法是gcc-zexecstack ge.c,将execstack选项传递给链接器,而不是随后修改二进制文件。但是,无论哪种方式,都可以执行所有页面,包括但不限于env变量所在的初始堆栈指针上方的区域。这可能是一个家庭作业。给予太多…我有点融合了“任意约束”==分配asm绝对值得评论。您正在拆下调用方的堆栈帧,因为该函数没有按%rbp/mov%rsp,%rbp生成自己的堆栈帧。所以ret将main的返回地址弹出到RIP中。它并不是完全超越printf,它更像是一个长jmp。当然了
它取决于编译器生成的调试模式代码!它将恰好返回零,因为您将函数指针的arg类型声明为not void,因此编译器将通过调零EAX将AL归零。而且进程退出状态只捕获retval的低位字节。另外,更简单的方法是gcc-zexecstack ge.c,将execstack选项传递给链接器,而不是随后修改二进制文件。但是,无论哪种方式,都可以执行所有页面,包括但不限于env变量所在的初始堆栈指针上方的区域。这可能是一个家庭作业。给出的太多了……我有点融合了“任意约束”==赋值