Why is ebx stored in the stack frame of the simple function the call receives?

I am trying to write a buffer overflow exercise in c for students.

Typically, a stack frame consists of function parameters, return address, base pointer, and local variables. But I found that sometimes additional registers are stored along with the base pointer. I remember from the class that cached registers must be saved before they are used. But there are times when compiling C code creates an assembly that saves and uses registers apperently for no purpose. Please explain this behavior to me.

Assume the main function

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

and function

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

If I debug the resulting executable with gdb

 break func run info frame 

everything is fine and the stack frame contains only ebp and eip.

If i use

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

I get

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

So, ebx is additionally stored in the stack frame? What for? If I run

 disas func 

I get

 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 < gets@plt > 0x5655574c <+28>: add $0x4,%esp 0x5655574f <+31>: nop 0x56555750 <+32>: mov -0x4(%ebp),%ebx 0x56555753 <+35>: leave 0x56555754 <+36>: ret End of assembler dump. 

Thus, ebx is saved. OK. But what is it used for? eax moves to ebx before gets () is called. But it is not used afterwards. The old ebx has just been rebuilt from the stack before leaving and returning. It seems useless. Btw. What is all the call get_pc_thunk ?

Comparable behavior if I use printf instead of get:

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

Gdb output:

 (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 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 < printf@plt > 0x5655575e <+46>: add $0x8,%esp 0x56555761 <+49>: nop 0x56555762 <+50>: mov -0x4(%ebp),%ebx 0x56555765 <+53>: leave 0x56555766 <+54>: ret End of assembler dump. 

Can someone explain this to me?

I use cmake to compile with the following 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 https://stackoverflow.com/questions/1061818/stack-allocation-padding-and-alignment 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 seems to be using gcc.

+6
source share
1 answer

Your compiler program chain has been tuned (possibly by your distribution) to create independent default executable files (PIEs). In the x86 32-bit version, in order for the position-independent code to call functions that may be in another library from the caller, the address of the calling GOT module must be loaded into ebx during the call; This is an ABI requirement. Since ebx is a call-saving register in x86 ABI, the caller must save and restore it later before returning to his caller.

This article I wrote on the topic may be informative:

https://ewontfix.com/18/

In the latest versions of gcc, the new option -fno-plt can avoid this problem by inserting the load from the GOT rather than using the PLT, which depends on ebx .

+10
source

Source: https://habr.com/ru/post/1014338/


All Articles