我正在尝试使用以下简单代码返回到libc技巧:
#define SYSTEM_CALL_ADDR 0xb7ec5e50 /*my system call addr*/ #define EXIT_CALL_ADDR 0xb7ebbb80 /*my exit call addr*/ char shell[] = "/bin/sh"; int main(){ int* p; p = (int*)&p + 2; *p = SYSTEM_CALL_ADDR; p = (int*)&p + 3; *p = EXIT_CALL_ADDR; p = (int*)&p + 4; *p = shell; return 1; }
有趣的是,当我运行这个程序时,它以“分段错误”结束,但如果我使用gdb进行debugging并逐步运行,那就完全正常了,产生一个shell然后退出程序。 有人遇到这种情况? 或者有人请指导我如何纠正这一点? 首先感谢。 我在ArchLinux内核上:2.6.33,gcc 4.5.0。
gdb禁用一些缓冲区溢出攻击缓解技术,如ProPolice和地址空间布局随机化(ASLR)。
gdb设置ADDR_NO_RANDOMIZE 个性(2)以便于调试。
发生崩溃是因为变量'p'的声明只为您的堆栈分配了足够的空间来存放单个(int *)指针,但是您将值填入(&p + 2)等等中,因此最终会覆盖who-know-什么。 不管有没有gdb,它的行为都是不同的,因为谁知道哪个部分在一个环境中可能并不重要,但在另一个环境中却是至关重要的。
您可能可以通过确保您即将被垃圾回收的空间不是至关重要的,来“修复”您的程序。
int main(){ int* p; char junk[100]; /* ADD THIS */
现在,分配给'p'的存储之后的空间(很有可能)就是垃圾字符数组,你可以通过覆盖来获得。 (至少这个“修复”我的特定Linux系统上的程序,当我用“gcc -O0 -g”编译)。
所有这一切说 – 你在那里使用的方法是不是便携式或安全的。 编译器没有保证它将按照我们声明的特定顺序来安排存储分配。 另外,编译不同的优化开关可能会改变行为。 所以虽然这可能是一个有趣的练习,但是你绝对不应该期望这种方法能够一致地工作。
用这种方式手动操作C栈是一个糟糕的想法!