Here i will reverse a simple c++ program that does basic math calculations
0000000000001149 <_Z5printv>:
1149: 55 push rbp
114a: 48 89 e5 mov rbp,rsp
114d: 48 83 ec 10 sub rsp,0x10
1151: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2
1158: c7 45 fc 04 00 00 00 mov DWORD PTR [rbp-0x4],0x4
115f: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8]
1162: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
1165: 01 d0 add eax,edx
1167: 89 c6 mov esi,eax
1169: 48 8d 05 d0 2e 00 00 lea rax,[rip+0x2ed0] # 4040 <_ZSt4cout@GLIBCXX_3.4>
1170: 48 89 c7 mov rdi,rax
1173: e8 c8 fe ff ff call 1040 <_ZNSolsEi@plt>
1178: be 0a 00 00 00 mov esi,0xa
117d: 48 89 c7 mov rdi,rax
1180: e8 ab fe ff ff call 1030 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@plt>
1185: 90 nop
1186: c9 leave
1187: c3 ret
0000000000001188 <main>:
1188: 55 push rbp
1189: 48 89 e5 mov rbp,rsp
118c: e8 b8 ff ff ff call 1149 <_Z5printv>
1191: b8 00 00 00 00 mov eax,0x0
1196: 5d pop rbp
1197: c3 retmain()
starting off with the main function we see push rbp which puts the rbp register onto the stack
then we move the rsp register into rbp to be a reference point to the beginning of a function
next it makes a function call to print
print()
on the print function we start off just like the main function with pushing the rbp register to the stack and moving rsp to rbp
The program then subtracts 0x10 which is 16 bytes to allocate space for local variables.
Now for the interesting part, on address 1151 we move the number 0x2 into the address value of [rbp-0x8]. this is most likely to initialize a variable in the function.
we do the same with 0x4 but at [rbp-0x4].
Notice how the offset is going down by -4 each time? This should indicate that we are initializing a int data type
At this point the registers look like so:
[rbp-0x8] = 2
[rbp-0x4] = 4
Going forward we move both of those values into a new register. 2 into edx and 4 into eax
115f: mov edx,DWORD PTR [rbp-0x8] ; move 2 into edx
1162: mov eax,DWORD PTR [rbp-0x4] ; move 4 into eaxThis will be used for calculation purposes between the registers like below
1165: add eax,edx ; add 2 + 4 = 6here we added the two registers so that the eax register will equal 2 + 4
next we move eax(6) into the esi register. The esi register is the second argument whenever a function call is being made.
System V ABI Calling Convention:
| Argument # | Register |
|---|---|
| 1st | RDI |
| 2nd | RSI |
| 3rd | RDX |
| 4th | RCX |
| 5th | R8 |
| 6th | R9 |
1167: mov esi,eax ; move eax into esiFor the final set of operations we load the address lea of cout , which is a function used to print to the console, into the rdi register which is the first argument to a function.
1169: lea rax,[rip+0x2ed0] ; 4040 <_ZSt4cout@GLIBCXX_3.4> get std::cout << address
1170: mov rdi,raxNow we can make a function call with the arguments in place rdi = std::cout << rsi = 6
1173: call 1040 <_ZNSolsEi@plt> ; std::cout << 6;The final set of instruction are making a new function call and we can see that it is using another set of arguments esi,rdi moving 0xa = \n into esi, which will be the second argument and rax = std::cout << into rdi.
Finally we make another function call to the << operator which will print a newline character to the end of the cout statement and end the program.
1178: mov esi,0xa
117d: mov rdi,rax
1180: call 1030 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@plt>
1185: nop
1186: leave
1187: retProgram reveal
#include <iostream>
void print()
{
int num1{2};
int num2{4};
std::cout << num1 + num2 << '\n';
}
int main(){
print();
}As we can see, the program initialized two int variables with 2 and 4 then proceeded to add them, with a newline ending.


