void fun_a(void) {
....
}
int main() {
int x = 5, y = 6;
fun_a();
printf("%d, %d", x, y);
return 0;
}
请问 fun_a 函数内部如何实现能够改变程序输入不为5,6?[一道笔试题,考虑了很久,感觉可以通过指针hack外部变量,但是没有进一步思路]
void fun_a(void) {
....
}
int main() {
int x = 5, y = 6;
fun_a();
printf("%d, %d", x, y);
return 0;
}
请问 fun_a 函数内部如何实现能够改变程序输入不为5,6?[一道笔试题,考虑了很久,感觉可以通过指针hack外部变量,但是没有进一步思路]
如果算偏移影响因素太多(机器字长、编译器优化),只是希望修改程序输出,那直接输出并退出就好了:
void func_a(void) {
printf("2, 3");
exit(0);
}
这种题目想从偏移上解决根本就是错误的。如果 func_a 没有副作用,可以直接把 func_a 优化没。即使没有把 func_a 优化没,也完全可以把 func_a 内联,此时根本就没有 push 进去的 return address。退一万步,如果 x, y 在寄存器里,你如何利用偏移修改?
而即使不开优化,编译器之间生成的代码也是不同的。我试图利用所谓偏移的办法解决问题:
#include <cstdint>
#include <cstdio>
void func_a()
{
int x = 0;
const auto px = reinterpret_cast<std::uintptr_t>(&x);
*reinterpret_cast<int*>(*reinterpret_cast<std::uintptr_t*>(px + sizeof(int)) - 8) = 100;
}
int main()
{
int x = 5, y = 6;
func_a();
printf("%d, %d", x, y);
}
我在 gcc.godbolt 里观察汇编代码时,GCC 7.0 把 func_a 里面的 x 放在 DWORD PTR [rbp-12],而 clang 3.8 放在 dword ptr [rbp - 4]。也就是说,上面的代码是不能在 clang 和 GCC 同时跑的。
我不认为这种题目有什么价值(当然,如果努力写出满篇 Undefined Behavior 以及不可移植的代码算是价值的话,那还是有价值的)。在这道题上,我赞同 exit(0) 的方案。
说时迟,那时快,我又想出一种办法:
#include <cstdio>
void func_a()
{
#define printf(...) puts("hahah")
}
int main()
{
int x = 5, y = 6;
func_a();
printf("%d, %d", x, y);
}
//通过溢出来破坏函数栈帧。
//这其实是一种非侵入debug技术的原型。
#include <stdio.h>
void func_a(void) {
int point = 0;
int *s = &point;
int *x = s+7;
int *y = x-1;
*x = 3;
*y = 4;
}
int main () {
int x=1,y=2;
func_a();
printf("%d,%d\n",x,y);
return 0;
}1 回答823 阅读
565 阅读
看上图,这是linux下的进程内存空间布局图,是32位的,64位也差不多,地址不一样而已。
首先要明确一点,就是函数的调用是栈式的,而栈空间的地址是向下的,也就是后入栈的内存地址反而小。并且入栈的地址是对齐的。(这不是绝对的。不同的体系结构下可能是完全不同的)
在
main函数里面先入栈了y,然后入栈了x,再入栈函数func_a这里要在
func_a里面修改x/y,就需要得到它们的地址,可以使用以下方式来达到目的。