Through this paper I am not encouraging people to hack, destroy or steal anything, you must comply with laws and you shall take entire responsibility if you use this knowledge for bad behavior. With great power comes great responsibilities. Reverse engineering is not always legal, check EULA/laws in your country.
Alright I think this will be the last of my "Reverse Engineering a Compiled C++ Binary" papers as there is only so many very simple examples we can go through.
Below is the source code for the binary we will attempt to reverse engineer.
$ cat sample.c++
#include <iostream>
int main() {
using namespace std;
int a;
int b;
int c;
int d;
int e;
b = 24;
c = 10;
d = 12;
e = b - c + d;
cout << "Please enter your code: ";
cin >> a;
if ( a == e )
{
cout << "Correct!\n";
} else {
cout << "Incorrect...\n";
}
return 0;
}
Just like last time this binary has all symbols in place.
$ file sample sample: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped
Like always lets check this binary for anything promising in the binary.
$ strings sample /lib64/ld-linux-x86-64.so.2 "+xO CyIk libstdc++.so.6 __gmon_start__ _Jv_RegisterClasses _ZNSt8ios_base4InitD1Ev __gxx_personality_v0 _ZSt3cin _ZNSirsERi _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc _ZSt4cout _ZNSt8ios_base4InitC1Ev libm.so.6 libgcc_s.so.1 libc.so.6 __cxa_atexit __libc_start_main GLIBC_2.2.5 CXXABI_1.3 GLIBCXX_3.4 fff. fffff. l$ L t$(L |$0H Please enter your code: Correct! Incorrect...
Lets get down to it and start by disassembling the main portion.
$ gdb -q sample Reading symbols from /home/jness/tm/sample...done. (gdb) disass main Dump of assembler code for function main: 0x0000000000400844 <main+0>: push %rbp 0x0000000000400845 <main+1>: mov %rsp,%rbp 0x0000000000400848 <main+4>: sub $0x20,%rsp 0x000000000040084c <main+8>: movl $0x18,-0x8(%rbp) 0x0000000000400853 <main+15>: movl $0xa,-0xc(%rbp) 0x000000000040085a <main+22>: movl $0xc,-0x10(%rbp) 0x0000000000400861 <main+29>: mov -0xc(%rbp),%eax 0x0000000000400864 <main+32>: mov -0x8(%rbp),%edx 0x0000000000400867 <main+35>: mov %edx,%ecx 0x0000000000400869 <main+37>: sub %eax,%ecx 0x000000000040086b <main+39>: mov %ecx,%eax 0x000000000040086d <main+41>: add -0x10(%rbp),%eax 0x0000000000400870 <main+44>: mov %eax,-0x14(%rbp) 0x0000000000400873 <main+47>: mov $0x400a0c,%esi 0x0000000000400878 <main+52>: mov $0x601180,%edi 0x000000000040087d <main+57>: callq 0x400730 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x0000000000400882 <main+62>: lea -0x4(%rbp),%rax 0x0000000000400886 <main+66>: mov %rax,%rsi 0x0000000000400889 <main+69>: mov $0x601060,%edi 0x000000000040088e <main+74>: callq 0x400740 <_ZNSirsERi@plt> 0x0000000000400893 <main+79>: mov -0x4(%rbp),%eax 0x0000000000400896 <main+82>: cmp -0x14(%rbp),%eax 0x0000000000400899 <main+85>: jne 0x4008ac <main+104> 0x000000000040089b <main+87>: mov $0x400a25,%esi 0x00000000004008a0 <main+92>: mov $0x601180,%edi 0x00000000004008a5 <main+97>: callq 0x400730 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x00000000004008aa <main+102>: jmp 0x4008bb <main+119> 0x00000000004008ac <main+104>: mov $0x400a2f,%esi 0x00000000004008b1 <main+109>: mov $0x601180,%edi 0x00000000004008b6 <main+114>: callq 0x400730 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x00000000004008bb <main+119>: mov $0x0,%eax 0x00000000004008c0 <main+124>: leaveq 0x00000000004008c1 <main+125>: retq End of assembler dump.
Right off the bat we see what appears to be some hex values being added to a portion of memory, and then below we can see a cmp which leads to our jne. Lets take a better look at this cmp.
0x0000000000400896 <main+82>: cmp -0x14(%rbp),%eax
0x000000000040084c <main+8>: movl $0x18,-0x8(%rbp) 0x0000000000400853 <main+15>: movl $0xa,-0xc(%rbp) 0x000000000040085a <main+22>: movl $0xc,-0x10(%rbp) 0x0000000000400861 <main+29>: mov -0xc(%rbp),%eax 0x0000000000400864 <main+32>: mov -0x8(%rbp),%edx 0x0000000000400867 <main+35>: mov %edx,%ecx 0x0000000000400869 <main+37>: sub %eax,%ecx 0x000000000040086b <main+39>: mov %ecx,%eax 0x000000000040086d <main+41>: add -0x10(%rbp),%eax 0x0000000000400870 <main+44>: mov %eax,-0x14(%rbp) ## 0x0000000000400896 <main+82>: cmp -0x14(%rbp),%eax
(gdb) print 0x18 $1 = 24 (gdb) print 0xa $2 = 10 (gdb) print 0xc $3 = 12
Looking at this snippet above we notice two new function which we havent went over before, sub and add, we can look these up at http://flip-edesign.com/basics_of_assembler.pdf
*SUB* is the opposite of the ADD command. It subtracts the value of src from the value of dest and stores the result in dest. *ADD* instruction adds a value to a register or a memory address. It can be used in these ways
Alright we have to do some basic math.
0x000000000040084c <main+8>: movl $0x18,-0x8(%rbp) ; value = 24 0x0000000000400853 <main+15>: movl $0xa,-0xc(%rbp) ; value = 10 0x000000000040085a <main+22>: movl $0xc,-0x10(%rbp) ; value = 12 0x0000000000400861 <main+29>: mov -0xc(%rbp),%eax ; move int 10 to %eax 0x0000000000400864 <main+32>: mov -0x8(%rbp),%edx ; move int 24 to %edx 0x0000000000400867 <main+35>: mov %edx,%ecx ; move %edx to %ecx (int 24) 0x0000000000400869 <main+37>: sub %eax,%ecx ; subtract %ecx (24) from %eax (10) 0x000000000040086b <main+39>: mov %ecx,%eax ; move result of subtraction (int 14) to %eax 0x000000000040086d <main+41>: add -0x10(%rbp),%eax ; add -0x10(%rbp) (int 12) to %eax (int 14)
So if our theroy is correct the "code" should be 26, lets give this a try.
$ ./sample Please enter your code: 26 Correct!
Date: 2010-03-23 15:40:57 CDT
HTML generated by org-mode 6.21b in emacs 23