当前位置:  开发笔记 > 编程语言 > 正文

为什么ebx保存在一个简单函数的堆栈框架中,调用得到?

如何解决《为什么ebx保存在一个简单函数的堆栈框架中,调用得到?》经验,为你挑选了1个好方法。

我正在尝试用c为学生写一个缓冲区溢出练习.

通常,堆栈帧由函数参数,返回地址,基指针和局部变量组成.但我发现,有时候附加的寄存器会与基指针一起保存.我从课堂上记得,calee保存的寄存器必须在使用之前保存.但有些情况下,C代码的编译会产生汇编,这会毫无目的地保存和使用寄存器.请向我解释这个行为.

假设主要功能

int main (int argc, char** argv) {
    func();

    return 0;
}

和功能

void func() {
    char buf[5];
    strcpy(buf,"AAAA");
    strcpy(buf,"BBBB");
}

如果我使用gdb调试生成的可执行文件

break func
run
info frame

一切都很好,堆栈框架只包含ebp和eip.

如果我使用

void func() {
    char buf[5];
    gets(buf);
}

我明白了

 Saved registers:
  ebx at 0xffffd1cc, ebp at 0xffffd1d0, eip at 0xffffd1d4

那么ebx是否另外保存在堆栈框架中?为什么?如果我跑

disas func

我明白了

Dump of assembler code for function func:
   0x56555730 <+0>: push   %ebp
   0x56555731 <+1>: mov    %esp,%ebp
   0x56555733 <+3>: push   %ebx
   0x56555734 <+4>: sub    $0x8,%esp
   0x56555737 <+7>: call   0x5655576e <__x86.get_pc_thunk.ax>
   0x5655573c <+12>:    add    $0x18c4,%eax
=> 0x56555741 <+17>:    lea    -0x9(%ebp),%edx
   0x56555744 <+20>:    push   %edx
   0x56555745 <+21>:    mov    %eax,%ebx
   0x56555747 <+23>:    call   0x56555590 
   0x5655574c <+28>:    add    $0x4,%esp
   0x5655574f <+31>:    nop
   0x56555750 <+32>:    mov    -0x4(%ebp),%ebx
   0x56555753 <+35>:    leave  
   0x56555754 <+36>:    ret    
End of assembler dump.

所以ebx得救了.好.但它用于什么?在调用gets()之前,eax在ebx中移动.但之后没有使用它.在离开和返回之前,旧的ebx刚从堆栈中恢复.这似乎毫无用处.顺便说一句.是什么call get_pc_thunk东西?

可比行为,如果我使用printf而不是gets:

void func() {
    char buf[5];
    strcpy(buf, "AAAA");
    printf("%s",buf);
}

gdb输出:

(gdb) info frame
Stack level 0, frame at 0xffffd1d8:
 eip = 0x56555741 in func (/home/mischa/stuff/test/test.c:35); saved eip = 0x56555779
 called by frame at 0xffffd1e0
 source language c.
 Arglist at 0xffffd1d0, args: 
 Locals at 0xffffd1d0, Previous frame's sp is 0xffffd1d8
 Saved registers:
  ebx at 0xffffd1cc, ebp at 0xffffd1d0, eip at 0xffffd1d4
(gdb) disas func
Dump of assembler code for function func:
   0x56555730 <+0>: push   %ebp
   0x56555731 <+1>: mov    %esp,%ebp
   0x56555733 <+3>: push   %ebx
   0x56555734 <+4>: sub    $0x8,%esp
   0x56555737 <+7>: call   0x56555780 <__x86.get_pc_thunk.ax>
   0x5655573c <+12>:    add    $0x18c4,%eax
=> 0x56555741 <+17>:    movl   $0x41414141,-0x9(%ebp)
   0x56555748 <+24>:    movb   $0x0,-0x5(%ebp)
   0x5655574c <+28>:    lea    -0x9(%ebp),%edx
   0x5655574f <+31>:    push   %edx
   0x56555750 <+32>:    lea    -0x17f0(%eax),%edx
   0x56555756 <+38>:    push   %edx
   0x56555757 <+39>:    mov    %eax,%ebx
   0x56555759 <+41>:    call   0x565555a0 
   0x5655575e <+46>:    add    $0x8,%esp
   0x56555761 <+49>:    nop
   0x56555762 <+50>:    mov    -0x4(%ebp),%ebx
   0x56555765 <+53>:    leave  
   0x56555766 <+54>:    ret    
End of assembler dump.

有人可以向我解释一下吗?

我使用cmake进行编译,使用以下CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)

# projectname is the same as the main-executable
project(test)

# compile with 32 bit
add_definitions('-m32')

# Disable compiler optimization
add_definitions('-O0')

# include debugging information
add_definitions('-g')

# Align items on the stack to 4 bytes. This makes stuff easier.
# See /sf/ask/17360801/
add_definitions('-mpreferred-stack-boundary=2')

# disable compiler buffer overflow protection
add_definitions('-z execstack -z norelro -fno-stack-protector')

# executable source code
add_executable(test test.c)

cmake似乎使用gcc.



1> R....:

您的编译器工具链已经配置(可能由您的发行版),以生成默认情况下位置无关的可执行文件(PIE).在32位x86上,为了使位置无关的代码能够调用可能与调用者位于不同库中的函数,必须ebx在调用时加载调用模块的GOT的地址.这是ABI的要求.由于ebx是x86 ABI中的调用保存寄存器,调用者必须先保存并稍后将其恢复,然后再返回自己的调用者.

我在前一段时间写的关于这个主题的文章可能是有用的:

https://ewontfix.com/18/

在最近的gcc版本中,新-fno-plt选项可以通过内联来自GOT的负载而不是使用依赖的PLT来避免此问题ebx.

推荐阅读
mobiledu2402852413
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有