c - 返回两次的 C 函数。(甚至与 fork() 几乎不相似)
问题描述
所以我在做一个项目,有点无聊,想着如何真正难以破解 C: 是否有可能欺骗编译器使用跳转(goto)进行函数调用?-也许,我回答自己。因此,经过一番工作和实践后,我意识到,一些指针的东西不能正常工作,但以一种(至少对我而言)意想不到的方式:goto 无法按预期工作。经过一些试验,我想出了这个东西(注释被删除,因为我有时会在其中保留未使用的代码,在测试时):
//author: me, The Array :)
#include <stdio.h>
void * func_return();
void (*break_ptr)(void) = (void *)func_return;
void * func_return(){
printf("ok2\n");
break_ptr = &&test2;
return NULL;
if(0 == 1){
test2:
printf("sh*t\n");
}
}
void scoping(){
printf("beginning of scoping\n");
break_ptr();
printf("after func call #1\n");
break_ptr();
printf("!!!YOU WILL NOT SEE THIS!!!!\n");
}
int main(){
printf("beginning of programm\n");
scoping();
printf("ending programm\n");
}
我使用 gcc 来编译它,因为我不知道任何其他支持使用 && 的编译器!我的平台是 Windows 64 位,我使用最基本的方法来编译它:
gcc.exe "however_you_want_to_call_it.c" -o "however_you_want_to_call_it.exe"
在查看该代码时,我希望它能够将“sh*t\n”打印到控制台窗口(当然,\n 将是不可见的)。但事实证明 gcc 对我来说有点太聪明了?我想这是在试图破坏某些东西时出现的。事实上,正如标题所说,它返回两次:
beginning of programm
beginning of scoping
ok2
after func call #1
ok2
ending programm
它不会像 fork 函数那样返回两次,并且可能会打印两次以下内容,不,它会从函数和调用它的函数中返回。因此,在第二次调用之后,它不会在控制台打印“!!!你不会看到这个!!!!\n”,而是“结束程序”,因为它返回了两次。(我试图放大事实,即打印“结束程序”,因为程序不会崩溃)
所以,为什么我在这里发布它的原因如下:我的问题..
- 为什么它不去/跳转到/调用实际的 test2 标签而是去那个函数的开头?
- 我将如何实现我的第一个问题?
- 为什么会返回两次?我认为它可能是编译器而不是运行时的东西,但我想我会等待某人的回答
- 第一次调用函数“break_ptr”而不是第二次可以实现相同的事情(返回两次)吗?
我不知道也不关心这是否也适用于 c++。
现在我可以看到很多有用的方法,有些是恶意的,有些实际上是好的。例如,您能否编写一个返回您的函数的企业函数。企业对问题的解决方案往往很奇怪,所以为什么不创建一个返回代码的函数,idk.. 然而,它可能是恶意的,例如,当某些代码意外返回或什至没有返回值时......我可以想象这存在在一个 dll 文件和一个简单地读取“extern void *break_ptr();”的头文件中 或者……没有测试它。(然而,还有更残酷的方式来惹某人......)
我在互联网上的任何地方都找不到此文档。请给我一些关于此的链接或参考,如果您找到一些,我想了解更多。
如果这“只是”一个错误并且 gnu/gcc 的某个人正在阅读此内容:请不要删除它,因为使用这些东西太有趣了。
提前感谢您的回答和您的时间,我很抱歉这么久。我想确保收集到的所有内容都集中在一个地方。(但如果我错过了什么,我仍然很抱歉..)
解决方案
来自关于值标签的gcc 文档:
您不能使用此机制跳转到不同函数中的代码。如果你这样做,就会发生完全不可预测的事情。
您看到的行为已正确记录。检查生成的程序集以真正了解编译器生成的代码。
break_ptr:
.quad func_return
.LC0:
.string "ok2"
func_return:
push rbp
mov rbp, rsp
.L2:
mov edi, OFFSET FLAT:.LC0
call puts
mov eax, OFFSET FLAT:.L2
mov QWORD PTR break_ptr[rip], rax
mov eax, 0
pop rbp
ret
.LC1:
.string "beginning of scoping"
.LC2:
.string "after func call #1"
.LC3:
.string "!!!YOU WILL NOT SEE THIS!!!!"
scoping:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC1
call puts
mov rax, QWORD PTR break_ptr[rip]
call rax
mov edi, OFFSET FLAT:.LC2
call puts
mov rax, QWORD PTR break_ptr[rip]
call rax
mov edi, OFFSET FLAT:.LC3
call puts
nop
pop rbp
ret
.LC4:
.string "beginning of programm"
.LC5:
.string "ending programm"
main:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC4
call puts
mov eax, 0
call scoping
mov edi, OFFSET FLAT:.LC5
call puts
mov eax, 0
pop rbp
ret
表明.L2
label 被放置在 function 之上,并且if (0 == 1) { /* this */ }
被编译器优化了。当您跳转时,.L2
您会跳转到函数的开头,除了堆栈设置不正确,因为push rbp
被省略了。
推荐阅读
- java - 如何使用 Java 在 selenium 中滚动网页?
- c# - 谷歌地图截图黑色
- css - 我是新来的反应,我正在尝试用反应引导,但我收到了这个错误?
- java - JAVA PROGRAMMING: Linkedlist question Collection c
- sql-server - 登录 SQL Server Management Studio 2008 时如何查看本地服务器的名称?
- python - 逐行提取符号
- android - 无法将数据从片段传递到片段
- java - 必须有 Java 运行时环境 (JRE) 或 Java 开发工具包 (JDK) 才能运行 Eclipse 安装程序
- angular - 如何在angular6中重复表格
- r - 将列条目转换为具有条目计数的列标题