My questions pertain to the actions that seem to happen between the line when context is changed especially concerning RSP and RBP.
Given this very simple program:
Reading symbols from ./function_call...done.
(gdb) disass main
Dump of assembler code for function main:
0x00000000004004d6 <+0>: push rbp
0x00000000004004d7 <+1>: mov rbp,rsp
0x00000000004004da <+4>: mov esi,0x2
0x00000000004004df <+9>: mov edi,0x1
0x00000000004004e4 <+14>: call 0x4004b6 <add_and_7>
0x00000000004004e9 <+19>: mov eax,0x0
0x00000000004004ee <+24>: pop rbp
0x00000000004004ef <+25>: ret
End of assembler dump.
(gdb) disass add_and_7
Dump of assembler code for function add_and_7:
0x00000000004004b6 <+0>: push rbp
0x00000000004004b7 <+1>: mov rbp,rsp
0x00000000004004ba <+4>: mov DWORD PTR [rbp-0x14],edi
0x00000000004004bd <+7>: mov DWORD PTR [rbp-0x18],esi
0x00000000004004c0 <+10>: mov DWORD PTR [rbp-0x4],0x7
0x00000000004004c7 <+17>: mov edx,DWORD PTR [rbp-0x14]
0x00000000004004ca <+20>: mov eax,DWORD PTR [rbp-0x18]
0x00000000004004cd <+23>: add edx,eax
0x00000000004004cf <+25>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004004d2 <+28>: add eax,edx
0x00000000004004d4 <+30>: pop rbp
0x00000000004004d5 <+31>: ret
End of assembler dump.
(gdb) list
1 int add_and_7( int num1, int num2 ) {
2 int seven = 7;
3 return num1 + num2 + seven;
4 }
5
6 int main() {
7 add_and_7( 1, 2 );
8 return 0;
9 }
All functions start off with push rbp which I as I understand it is preserving the parent context onto the stack. How does the parent function know how to rebuild itself? Are the necessary steps built into call and ret?
Then the rsp is always moved to rbp. As I have read this sets the new stack base to be in the context of the current function. What I can't seem to figure out is when or how stack pointer was set to that point in the first place. My best guess is the assembly function call does this, is that whats happening?
Lastly when a method returns it seems like eax is the register that is used for the parent function to utilize the return of its child function. Is eax explicitly used for this or is this just a convention with my compiler and architecture?
How does the parent function know how to rebuild itself ? Are the necessary steps built into call and ret?
Before calling a function, current status of registers are saved, as well as the return address. call instruction jumps to particular address, where the called function begins. The return address is pushed onto stack. When called function returns, ret instruction pops previously pushed return address and goes to that location.
Then the rsp is always moved to rbp
rbp is previously pushed onto stack to be able to restore rbp's value from caller's function. Then, rsp is moved to rbp to create a new stack frame for callee function. The new base pointer has been set up. So currently, rbp and rsp points to the same addresses. If there are other push instructions, esp is automatically adjusted. When function is done, the pop ebp instruction restores previously pushed stack base pointer address.
Push and Pop modify the stack pointer - SP.
Call pushes FLAGS - status register as well as the RA - return address.
Ret pops the FLAGS pops and jumps to the return address.
As rkhb said, the need to keep certain registers as they are comes from the calling conventions.
Related
VS2015 C++ / Windows7 SP1
Considering the following code:
CComPtr<IFontDisp> m_pFont;
::OleCreateFontIndirect(&fdesc,IID_IFontDisp,(void**)&m_pFont);
VARIANT var = m_pFont; // PSEUDO CODE
after this,
var.vt = 9; //VT_DISPATCH
var.DISPATCH = "oleaut32.dll/IFontDisp"
So all looks fine.
Now I call
::VariantClear(var);
And I debug into (ASM), I found this:
74CB2EA6 nop
CFont::Release:
--> 74CB2EA7 sub dword ptr [esp+4],4
74CB2EAC jmp CFont::Release (74CB2E79h)
74CB2EAE nop
74CB2EAF nop
74CB2EB0 nop
Following the code:
CFont::Release:
--> 74CB2E79 mov edi,edi
74CB2E7B push ebp
74CB2E7C mov ebp,esp
74CB2E7E push esi
74CB2E7F mov esi,dword ptr [ebp+8]
74CB2E82 push edi
74CB2E83 lea eax,[esi+0A8h]
74CB2E89 push eax
74CB2E8A call dword ptr [__imp__InterlockedDecrement#4 (74C91298h)]
74CB2E90 mov edi,eax
74CB2E92 test edi,edi
74CB2E94 je CFont::Release+261h (74CB30DAh)
74CB2E9A mov eax,edi
74CB2E9C pop edi
74CB2E9D pop esi
74CB2E9E pop ebp
74CB2E9F ret 4
So as I see, it releases the COM interface.
But if I see MSDN doc about VariantClear:
if the variant to be cleared is a COM object that is passed by
reference, the vtfield of the pvargparameter is VT_DISPATCH | VT_BYREF
or VT_UNKNOWN | VT_BYREF. In this case, VariantClear does not release
the object. Because the variant being cleared is a pointer to a
reference to an object, VariantClear has no way to determine if it is
necessary to release the object. It is therefore the responsibility of
the caller to release the object or not, as appropriate.
According to this, it should not call release on the IFontDisp.
Can anybody explain what is going on here?
Thanks.
I am using GDB on a x64 CPU. As you can see, I am trying to access the value of the rip register and for some reason the address of the instruction the register is pointing to is displayed using 5s instead of 0s as it should be.
Dump of assembler code for function main:
0x0000000000001139 <+0>: push rbp
0x000000000000113a <+1>: mov rbp,rsp
0x000000000000113d <+4>: sub rsp,0x10
0x0000000000001141 <+8>: mov DWORD PTR [rbp-0x4],0x0
0x0000000000001148 <+15>: mov DWORD PTR [rbp-0x4],0x0
0x000000000000114f <+22>: jmp 0x1161 <main+40>
0x0000000000001151 <+24>: lea rdi,[rip+0xeac] # 0x2004
0x0000000000001158 <+31>: call 0x1030 <puts#plt>
0x000000000000115d <+36>: add DWORD PTR [rbp-0x4],0x1
0x0000000000001161 <+40>: cmp DWORD PTR [rbp-0x4],0x9
0x0000000000001165 <+44>: jle 0x1151 <main+24>
0x0000000000001167 <+46>: mov eax,0x0
0x000000000000116c <+51>: leave
0x000000000000116d <+52>: ret
End of assembler dump.
(gdb) break main
Breakpoint 1 at 0x1141: file Desktop/myprogram.c, line 6.
(gdb) run
Starting program: /home/william/Desktop/a.out
Breakpoint 1, main () at Desktop/myprogram.c:6
6 int i = 0;
(gdb) info register rip
rip 0x555555555141 0x555555555141 <main+8>
As you can see, the rip register contains the address of the mov instruction listed above but for some reason has replaced all the 0s for 5s. Any idea why?
Before running a position-independent executable, there is no base address so gcc assumes 0. This matches what you'll see from objdump -drwC -Mintel /bin/ls or whatever.
On running the executable to create a process, the OS's program-loader maps it to an address. x86-64 Linux chooses a page address that starts with 0x0000555555555... when GDB disables ASLR.
If you run it outside GDB, or with set disable-randomization off, then the address will still start with 0x000055555, but be randomized in some range.
I've compiled this code with gcc (gcc -ggdb -mpreferred-stack-boundary=2 -o demo demo.c) and decompiled it to look at the assembly (I know it's using unsafe functions, this was for an exercise into buffer overflows):
#include<stdio.h>
CanNeverExecute()
{
printf("I can never execute\n");
exit(0);
}
GetInput()
{
char buffer[8];
gets(buffer);
puts(buffer);
}
main()
{
GetInput();
return 0;
}
Here is the assembly for the GetInput() Function:
(gdb) disas GetInput
Dump of assembler code for function GetInput:
0x08048432 <+0>: push ebp
0x08048433 <+1>: mov ebp,esp
0x08048435 <+3>: sub esp,0xc
=> 0x08048438 <+6>: lea eax,[ebp-0x8]
0x0804843b <+9>: mov DWORD PTR [esp],eax
0x0804843e <+12>: call 0x8048320 <gets#plt>
0x08048443 <+17>: lea eax,[ebp-0x8]
0x08048446 <+20>: mov DWORD PTR [esp],eax
0x08048449 <+23>: call 0x8048340 <puts#plt>
0x0804844e <+28>: leave
0x0804844f <+29>: ret
End of assembler dump.
Here is the assembly for the Main() Function:
(gdb) disas main
Dump of assembler code for function main:
0x08048450 <+0>: push ebp
0x08048451 <+1>: mov ebp,esp
0x08048453 <+3>: call 0x8048432 <GetInput>
0x08048458 <+8>: mov eax,0x0
0x0804845d <+13>: pop ebp
0x0804845e <+14>: ret
End of assembler dump.
I've set a breakpoint at line 13 (gets(buffer))
From Main(), I can see that the ebp value is pushed onto the stack. Then when GetInput() function is called the ret address is also pushed onto the stack. Once entered the GetInput function, the ebp value is pushed onto the stack again.
Now this is where I get confused:
0x08048435 <+3>: sub esp,0xc
The buffer variable is only 8 bytes, so 8 bytes should be subtracted from esp to allow for the buffer local variable.
The stack:
(gdb) x/8xw $esp
0xbffff404: 0x08048360 0x0804847b 0x002c3ff4 0xbffff418
0xbffff414: 0x08048458 0xbffff498 0x00147d36 0x00000001
(gdb) x/x &buffer
0xbffff408: 0x0804847b
0x08048458 is the ret address, 0xbffff418 is the old value of ebp, and 4 bytes of the buffer variable is in 0x0804847b, so I guess the other 4 bytes is 0x002c3ff4. But there seems to be another 4 bytes on the stack.
So my question is, why is it subtracting 12 bytes if only 8 bytes is needed? What's the extra 4 bytes for?
Thank you
It's because of the
mov DWORD PTR [esp],eax
Apparently, your puts and gets implementations require the argument to be pushed onto the stack.
Value [ebp-0xc] is actually [esp] now, that's why that dword is reserved ahead.
Why is it so? Doing it this way is more efficient, as you don't have to pop and push, but just move eax on [esp], so you spare at least one instruction. However, I guess this code has gone through some optimiation, because this one is clever.
I am trying to move stuff from a register to a variable in .CODE, but trying to do so makes my program start over in an infinite loop (no crash and no error message, but obviously broken). I don't understand what I'm doing wrong. Here is the beginning of my code where I am trying to move data; the program never even gets past this part when I include it:
.CODE
screenX DWORD 0
screenY DWORD 0
...
ProcName PROC
mov ebx, edx ;; Copy srcBitmap into ebx
mov eax, edi ;; Take given y-location (edi)
mov edx, (EECS205BITMAP PTR [ebx]).dwHeight
shr edx, 1 ;; Subtract dwHeight/2 to center
sub eax, edx
mov screenY, eax ;; Program jumps back to beginning with no error message
Seems like I'm missing something obvious, anyone have a clue?
Your application's code segment (which is actually it's .text section under Windows) isn't writable. If you want to modify these variables you need to put them in the data segment.
[SOLVED]
I'm trying to do my own assembly code to do what similar C code will do:
main()
{
scanf("%d",&integer_var); // here must be the address of the integer_var
printf("Your Value is:%d",integer_var);
}
Well this is in C, so I'm doing with NASM under linux with extern functions. scanf and printf and compile first with nasm and then with gcc.
Here's my code (is not right :D)
SECTION .text
argstr: db "%d",10,0
str: db "Your value is:%d",10,0
extern printf
extern scanf
SECTION .data
global main
main:
push ebp
mov esp,ebp
sub esp, 0x10 ;ok integer right?
mov [ebp-0x4],0x0 ;just put 0 number on our integer variable
mov eax,(ebp-0x4) ;here i don't know how to push the address of ebp-0x4
push ecx ;first push is last argument so here's our address to scanf
push argstr ;just the string format
call scanf ;call that to input something
;I have no idea how to do this
;but if i don't do this i get an error
;because the scanf won't clear the arguments on stack
;and what scanf can't return
pop edx ;maybe help here? but it works fine
pop edx
push [-0x4(ebp)] ;i want the value of our var :D
push str
call printf
pop edx ;clear the stack to avoid "segment fault" or something similar
pop edx
mov esp,ebp
pop ebp
ret ;the end :(
Compiler error:
a.asm:18: error: invalid operand type
a.asm:28: error: parser: expecting ]
Another thing: Do I need to align the stack on this case, by the way?
thanks guys ! :)
EDIT solved whole program!
well at least, I can print the variable with printf. scanf i will do later and then I will share here the last result:
SECTION .text
str: db "Value is:%d",10,0
extern printf
SECTION .data
global main
main:
push ebp ;the main function starts here.
mov ebp,esp
;
sub esp,4 ;we need 4bytes of space for the integer
and esp,0xfffffff0 ;align the stack
mov [esp-4], dword 0xff ;move the value 0xff to our var
mov eax,[esp-4] ;move our variable value to the eax
push eax ;second argument of printf
push str ;first argument of printf
call printf ;printf
;
add esp,16 ;this add to the stack pointer what we pushed basicly
mov ebp,esp ;if we don't do add 16 to esp it shows us
pop ebp ;a segment fault cuz ret doesnt pop saved ebp
ret ;of who whatever called this program :)
To load the address EBP-4 into EAX, use lea eax, [ebp-4]. (this is NOT the same as pushing the address.)
In order to push the value at memory location EBP-4, push dword [ebp-4] should work.
Then you need to specify operand size for one of your movs, too: mov [ebp-4], dword 0x0.
These will fix your current assembler errors, and make your program compile, but there are a few other errors in there that will probably prevent it from running.
Here's a working attempt that is close to yours:
;note the sections, the string literals are better in .rodata
;all code goes in .text
SECTION .rodata
;no newline after scanf string
argstr: db "%d",0
str: db "Your value is: %d",10,0
SECTION .text
extern printf
extern scanf
global main
main:
push ebp
mov ebp,esp ;move esp to ebp, NOT other way round!
sub esp, 4 ;4 bytes are enough for the local variable
;there are NO alignment requirements for this program
lea eax,[ebp-4]
push eax
push dword argstr
call scanf
add esp, 8 ;since we don't actually need the popped values
;we can increment esp instead of two pop edx
push dword [ebp-4]
push dword str
call printf
add esp, 8
mov esp,ebp
pop ebp
ret