Basic Reverse Engineering Linux Binaries
Assalamualaikum. Saya akan sedikit membahas basic dalam melakukan reverse engineering elf binnary, reverse engineering sendiri sangat penting apalagi dalam challenge CTF yang biasa nya mempunyai poin yang besar.
The Code
root@kali:~# cat rev.c
#include
int main(){
int a,b,c,d;
a = 306;
b = 737;
c = 100 * a + b;
printf("Enter your passcode : ");
scanf("%d", &d);
if(d == c){
puts("Correct");
else{
puts("Incorrect");
return 0;
```
Proof Of Concept
====================================
``` c %}
root@kali:~# gcc rev.c -o rev
root@kali:~# gdb -q rev
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
0x0000555555554760 <+0>: push rbp
0x0000555555554761 <+1>: mov rbp,rsp
0x0000555555554764 <+4>: sub rsp,0x10
0x0000555555554768 <+8>: mov DWORD PTR [rbp-0x4],0x132
0x000055555555476f <+15>: mov DWORD PTR [rbp-0x8],0x2e1
0x0000555555554776 <+22>: mov eax,DWORD PTR [rbp-0x4]
0x0000555555554779 <+25>: imul edx,eax,0x64
0x000055555555477c <+28>: mov eax,DWORD PTR [rbp-0x8]
0x000055555555477f <+31>: add eax,edx
0x0000555555554781 <+33>: mov DWORD PTR [rbp-0xc],eax
0x0000555555554784 <+36>: lea rdi,[rip+0xd9] # 0x555555554864
0x000055555555478b <+43>: mov eax,0x0
0x0000555555554790 <+48>: call 0x555555554600 <printf@plt>
0x0000555555554795 <+53>: lea rax,[rbp-0x10]
0x0000555555554799 <+57>: mov rsi,rax
0x000055555555479c <+60>: lea rdi,[rip+0xd7] # 0x55555555487a
0x00005555555547a3 <+67>: mov eax,0x0
0x00005555555547a8 <+72>: call 0x555555554610 <__isoc99_scanf@plt>
0x00005555555547ad <+77>: mov eax,DWORD PTR [rbp-0x10]
0x00005555555547b0 <+80>: cmp eax,DWORD PTR [rbp-0xc]
0x00005555555547b3 <+83>: jne 0x5555555547c3 <main+99>
0x00005555555547b5 <+85>: lea rdi,[rip+0xc1] # 0x55555555487d
0x00005555555547bc <+92>: call 0x5555555545f0 <puts@plt>
0x00005555555547c1 <+97>: jmp 0x5555555547cf <main+111>
0x00005555555547c3 <+99>: lea rdi,[rip+0xbb] # 0x555555554885
0x00005555555547ca <+106>: call 0x5555555545f0 <puts@plt>
0x00005555555547cf <+111>: mov eax,0x0
0x00005555555547d4 <+116>: leave
0x00005555555547d5 <+117>: ret
End of assembler dump.
Command set disassembly-flavor intel
digunakan agar hasil
disassembly menggunakan syntax intel, karena terdapat 2 syntax pada gdb yaitu att dan intel.
Sedangkan disass main
merupakan command untuk melakukan disassembly pada fungsi main, apabila ingin melakukan disassembly pada fungsi lain bisa menggunakan disass nama_fungsi
.
Dari hasil disassembly diatas, terdapat instruksi yang akan memindahkan nilai hex 0x132 ke rbp-0x4 dan 0x2e1 rbp-0x8. Yang apabila 0x132 dan 0x2e1 dikonversi ke decimal akan menghasilkan 306 dan 737 sama seperti nilai dari variable a,b.
0x0000555555554768 <+8>: mov DWORD PTR [rbp-0x4],0x132
0x000055555555476f <+15>: mov DWORD PTR [rbp-0x8],0x2e1
Selanjutnya nilai 0x132 yang tersimpan pada rbp-0x4 akan dipindahkan ke register eax
0x0000555555554776 <+22>: mov eax,DWORD PTR [rbp-0x4]
Lalu terdapat intruksi imul, imul digunakan untuk melakukan operasi perkalian pada intruksi ini terdapat 0x64 ( konversi ke decimal = 100, sama seperti yang di source code), yang akan dikalikan dengan register eax ( nilai 0x132 ), 0x64 * 0x132 = 0x7788, dan hasil kali tersebut akan disimpan di register edx.
0x0000555555554779 <+25>: imul edx,eax,0x64
Selanjutnya nilai 0x2e1 yang tersimpan pada rbp-0x8 akan dipindahkan ke register eax
0x000055555555477c <+28>: mov eax,DWORD PTR [rbp-0x8]
Sehingga masuk pada intruksi add yang melakukan operasi pertambahan antara 0x2e1 + 0x7788 = 0x7a69 (31337), lalu akan memindahkan nilai 0x7a69 pada eax ke rbp-0xc, setelah itu baru memindahkan user input (scanf) yang tersimpan sementara di rbp-0x10 ke register eax.
0x000055555555477f <+31>: add eax,edx
0x0000555555554781 <+33>: mov DWORD PTR [rbp-0xc],eax
--- snip ---
0x00005555555547ad <+77>: mov eax,DWORD PTR [rbp-0x10]
Setelah itu intruksi cmp akan melakukan compare antara nilai 0x7a69 yang berada di rbp-0xc dan inputan passcode kita yang berada di eax.
0x00005555555547b0 <+80>: cmp eax,DWORD PTR [rbp-0xc]
Apabila hasil compare tidak sama (jne = jump if not equal), maka akan menuju offset 0x5555555547c3 dan mengeluarkan pesan “Incorrect”.
0x00005555555547b3 <+83>: jne 0x5555555547c3 <main+99>
--- snip ---
0x00005555555547ca <+106>: call 0x5555555545f0 <puts@plt>
Sedangkan apabila sama, akan melewati intruksi jne dan dan mengeluarkan pesan “Correct”.
0x00005555555547bc <+92>: call 0x5555555545f0 <puts@plt>
Dari pembahasan diatas, diketahui bahwa passcode yang valid adalah 31337 Apabila kita sudah mengerti uraian diatas, sebenar nya kita bisa mencari passcode yang tersimpan dalam register secara otomatis tanpa menghitung manual seperti diatas jika kita sudah sedikit mengerti intruksi assembly.
Proof Of Concept #2
Saya memasang breakpoint pada offset 0x00005555555547b0 #<+80>, untuk melihat nilai yang akan di compare dengan inputan kita yang telah tersimpan di rbp-0xc
(gdb) b *0x00005555555547b0
Breakpoint 1 at 0x5555555547b0
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: ~/rev
Enter your passcode : 123456
Breakpoint 1, 0x00005555555547b0 in main ()
(gdb) x $rbp-0xc
0x7fffffffe244: 0x00007a69
(gdb) print 0x00007a69
$1 = 31337
Karena passcode yang valid sudah diketahui, bisa langsung membuktikan dengan menjalankan program nya.
root@kali:~# ./rev
Enter your passcode : 31337
Correct
Akhir Kata…
Saya sendiri sebenarnya tidak terlalu ahli dalam reverse engineering, saya menulis posting ini ditujukan kepada siapa saja yang ingin belajar sehingga bisa mendapatkan pengetahuan dasar mengenai reverse engineering. Jadi semoga tulisan ini dapat bermanfaat bagi kita semua.