当一个无符号整型指针指向它自己的地址时,你可以将返回地址递增10,以便在函数返回后跳过一行.示例:

#include <stdio.h>

void f()
{   
    unsigned int *array = (unsigned int *) &array;
    array[4] = array[4] + 10;
}

void main()
{
    f();
    printf("I am skipped\n");
}

为什么必须是10才能递增?这个数字的意义是什么,幕后正在发生什么.

加入时间:清华2007年01月25日下午3:33Via C v7.5.0-GCC 加入时间:清华2007年01月25日下午3:33

(无输出)因此,已跳过行.

推荐答案

代码具有未定义的行为:指针被初始化为指向自身,并且代码从此地址访问索引4处的内存,即:超出内存中指针地址的16个字节.

在目标体系 struct 上,幸运的是,该地址指的是函数f()的堆栈中的返回地址,并且代码将该地址修补到更远的10个字节,这当然非常危险,但似乎是在调用函数printf之后的可执行代码.

正如您所观察到的,结果是没有调用printf,程序正常退出.

请注意,此行为非常特定于所使用的编译器、体系 struct 、操作系统和配置,绝不能保证.

未定义的行为意味着任何事情都可能发生,包括任何可见的或可怕的副作用.

assembly output on godbolt.org可以看出,函数f()的堆栈帧确实具有距离编译器(GCC和Clang)分配array指针(rbp-8)的位置16字节的返回地址.

然而,GCC和Cang为printf("I am skipped\n")生成了不同的代码,即使没有打开任何优化,GCC也会将这个调用转换为puts("I am skipped").用10个字节修补返回地址可能只适用于一个编译器,而不适用于另一个编译器,如果启用了优化,则可能根本不起作用.

没有可移植的方法来确定如何修补内存以跳过给定的代码行,如果您设法做到了这一点,请注意这个结果非常脆弱,因为代码或环境中的任何更改都可能 destruct 它.

修补现有的可执行文件以跳过一些代码不那么脆弱,因为打补丁的代码在安装新版本之前不会更改,但会使其变得更难,例如经过身份验证的代码指针,即使偏移量正确,也会使发布的代码失败.

C++相关问答推荐

有什么方法可以检测SunOS上的SparcWorks吗?

如果实际的syscall是CLONE(),那么为什么strace接受fork()呢?

*p[num]和(*p)num的区别

为什么输出不是从上到下C

正在try 将文件/文件夹名从目录 struct 存储到链接列表

#If指令中未定义宏?

将常量转换为指针会增加.数据大小增加1000字节

非常大的数组的大小

进程在写入管道时挂起

在另一个函数中使用realloc和指针指向指针

如何使用_newindex数组我总是得到错误的参数

在C++中允许使用字符作为宏参数

C程序向服务器发送TCPRST

有没有办法减少C语言中线程的堆大小?

如何组合两个宏来初始化C语言中的字符串数组?

哪个首选包含第三个库S头文件?#INCLUDE;文件名或#INCLUDE<;文件名&>?

计算时出现奇怪的计算错误;N Select K;在C中

从系统派生线程调用CRT

C 程序不显示任何输出,但它接受 CS50 Lab1 的输入问题

设置具有非零终止字符串的大整数