System Call Di Windows


Pada artikel Memakai System Call Linux Di Assembly, saya membahas tentang system call di Linux. Pada kesempatan ini, saya akan mencoba melihat system call di sistem operasi Microsoft Windows.

Apa itu system call? Pada saat saya sedang berada di sebuah sistem operasi, pada dasarnya ada 2 jenis kode program (dalam bentuk instruksi mesin) yang sedang bekerja, yaitu kernel milik sistem operasi dan aplikasi. Contoh aplikasi adalah Notepad, Paint, Office, Java, Photoshop, dan sebagainya. Agar dapat bekerja, aplikasi perlu memakai layanan dari kernel sistem operasi. System call adalah kejadian dimana kode program aplikasi memanggil kode program kernel sistem operasi. Salah satu mekanisme yang umum dipakai untuk system call adalah dengan menggunakan software interrupt. Saya membahas tentang interrupt di artikel Memahami Interrupt Di Komputer.

Programmer assembly di zaman MS-DOS (Disk Operating System) sering kali melakukan pemanggilan interrupt 0x21. Yup, MS-DOS API memang di-ekspos melalui vector number tersebut. Sebelum memanggil interrupt 0x21, programmer mengisi nilai register AH dengan sebuah angka yang mewakili jenis layanan yang diinginkan. Halaman http://en.wikipedia.org/wiki/MS-DOS_API berisi daftar yang lengkap. Sebagai contoh, beberapa MS-DOS API di interrupt 0x21 yang sering dipakai adalah:

  1. AH=0x01 untuk membaca sebuah huruf dari keyboard.
  2. AH=0x02 untuk mencetak sebuah huruf ke layar.
  3. AH=0x09 untuk mencetak sebuah kalimat ke layar.
  4. AH=0x0F untuk membaca file.
  5. AH=0x10 untuk menutup file.
  6. AH=0x13 untuk menghapus file.
  7. AH=0x2A untuk mendapatkan tanggal di sistem komputer.
  8. AH=0x30 untuk mendapatkan versi DOS.
  9. AH=0x36 untuk mendapatkan free space.

Lalu bagaimana dengan Microsoft Windows? System call pada sistem operasi Windows dapat dilakukan dengan memanggil interrupt 0x2e. Nilai register EAX berisi nomor layanan yang hendak diakses. Nilai register EDX merujuk pada argumen yang akan diberikan pada layanan yang dituju. Nomor layanan kini dapat berubah fungsinya seiring dengan perubahan pada versi Windows. Btw, mereka tidak lagi untuk dipakai secara langsung seperti pada zaman DOS.

Saya dapat memakai perintah !idt 2e untuk melihat handler pada interrupt 0x2e, seperti yang terlihat pada gambar berikut ini:

Melihat interrupt handler untuk vector number 0x2e

Melihat interrupt handler untuk vector number 0x2e

Pertanyaannya yang muncul adalah mengapa tidak melakukan system call dengan pemanggilan biasa seperti pada instruksi call atau jmp? Alasan utamanya adalah berkaitan dengan keamanan. Pada sistem operasi modern, kode program kernel berjalan pada ‘wilayah’ ring 0 yang tidak dapat diakses/dipanggil secara langsung oleh kode program aplikasi di ring 3. Bila kode program aplikasi dapat men-call kode program kernel dengan mudah, maka bukankah keamanan yang timbul dari pemisahanan wilayah eksekusi akan hilang?

P.S.: Kode program yang berjalan di ring 0 tidak selalu kode program sistem operasi. Pada arsitektur komputer PC, kode program yang pertama kali di-eksekusi oleh CPU selalu berjalan di ring 0. Yang pertama kali dijalankan adalah kode program BIOS yang kemudian akan membaca kode program MBR yang selanjutnya mengerjakan sistem operasi. Itu adalah alur eksekusi yang normal dan diharapkan. Bila sebelum sistem operasi terdapat kode program berbahaya yang dijalankan terlebih dahulu (misalnya dari BIOS atau MBR yang telah disusupi rootkit), maka kode program tersebut akan berjalan di ring 0.

Sebenarnya interrupt pada awalnya tidak ditujukan untuk kebutuhan system call, tetapi sistem operasi memanfaatkannya sebagai cara mudah untuk memanggil layanan kernel dari aplikasi. Untuk mengatasi hal tersebut, prosesor Pentium II memperkenalkan instruksi sysenter dan sysexit (yang kemudian diikuti oleh AMD). Selanjutnya, sebagai pelopor CPU 64-bit, AMD memperkenalkan instruksi syscall dan sysret (yang kemudian diikuti oleh Intel). Jadi, pada CPU x86 (32-bit), instruksi sysenter/sysexit pasti ada; sementara itu pada CPU x64 (64-bit), instruksi syscall/sysret pasti ada.

System call dengan menggunakan instruksi sysenter lebih cepat dibandingkan dengan system call menggunakan software interrupt. Sistem operasi modern seperti Windows dan Linux juga sudah memakai instruksi sysenter atau syscall. Untuk menggunakan instruksi sysenter, sistem operasi perlu mengisi nilai MSR (Machine Specific Register) berupa IA32_SYSENTER_CS (0x174), IA32_SYSENTER_ESP (0x175), dan IA32_SYSENTER_EIP (0x176). Nilai dari IA32_SYSENTER_CS (16-bit pertama) dan IA32_SYSENTER_EIP merujuk pada lokasi kode program di ring 0 yang akan dikerjakan pada saat CPU memproses instruksi sysenter. Untuk menulis dan membaca MSR, dibutuhkan instruksi WDMSR dan RDMSR yang hanya dapat dikerjakan dari ring 0. Dengan demikian, hanya sistem operasi yang bisa menentukan apa yang akan dikerjakan saat instruksi sysenter diproses. Sebaliknya, instruksi sysenter dirancang untuk dipanggil dari ring 3.

Sebagai contoh, pada Windows 7 32-bit, kode program yang akan dikerjakan saat CPU memproses instruksi sysenter adalah nt!KiFastCallEntry seperti yang terlihat pada gambar berikut ini:

Instruksi yang akan dikerjakan oleh SYSENTER

Instruksi yang akan dikerjakan oleh SYSENTER

Pertanyaan berikutnya adalah kapan instruksi sysenter diberikan? Pada zaman DOS, untuk melakukan system call, saya boleh memanggil instruksi INT sesuka hati. Lalu bagaimana dengan Windows? Programmer disarankan memakai Windows API yang terdokumentasi dengan baik. Mereka selanjutnya akan memanggil Ntdll.dll, Gdi32.dll dan User32.dll yang akan mengerjakan instruksi sysenter.

Sebagai contoh, saya akan memeriksa fungsi NtDisplayString() dari Ntdll.dll. Fungsi ini dipakai untuk menampilkan tulisan ke layar (seperti pada saat bluescreen). Isinya terlihat seperti berikut ini:

77b158b8 b86d000000      mov     eax,6Dh
77b158bd ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
77b158c2 ff12            call    dword ptr [edx]
77b158c4 c20400          ret     4
77b158c7 90              nop

Kode program NtDisplayString() di atas akan melakukan system call dengan nomor layanan 0x6D. Pada dasarnya SystemCallStub adalah pointer ke KiFastSystemCall(), seperti yang terlihat pada hasil berikut ini:

0: kd> dd SharedUserData!SystemCallStub l1
7ffe0300  77b170f0

0: kd> u 77b170f0
ntdll!KiFastSystemCall:
77b170f0 8bd4            mov     edx,esp
77b170f2 0f34            sysenter

Daftar nomor layanan yang disediakan oleh kernel Windows disimpan di struktur KeServiceDescriptorTable dan KeServiceDescriptorTableShadow (berkaitan dengan GUI). Untuk melihat struktur array yang berisi daftar layanan kernel, saya dapat menggunakan perintah debugger berupa dds seperti yang terlihat pada hasil berikut ini:

0: kd> dds KiServiceTable l 0xff
8329743c  83492fbf nt!NtAcceptConnectPort
83297440  832da855 nt!NtAccessCheck
83297444  83422d47 nt!NtAccessCheckAndAuditAlarm
83297448  8323e897 nt!NtAccessCheckByType
8329744c  83494895 nt!NtAccessCheckByTypeAndAuditAlarm
83297450  83317112 nt!NtAccessCheckByTypeResultList
83297454  835050d7 nt!NtAccessCheckByTypeResultListAndAuditAlarm
83297458  83505120 nt!NtAccessCheckByTypeResultListAndAuditAlarmByHandle
8329745c  83417563 nt!NtAddAtom
...

Saya bisa mendapatkan kode program kernel yang dikerjakan dengan memberikan perintah debugger seperti berikut ini:

0: kd> ln poi(KiServiceTable + 6d * 4)
(8351cc5b)   nt!NtDisplayString   |  (83420ea0)   nt!ExSystemExceptionFilter
Exact matches:
    nt!NtDisplayString = 
0: kd> u nt!NtDisplayString
nt!NtDisplayString:
8351cc5b 6a28            push    28h
8351cc5d 68e82f2783      push    offset nt! ?? ::FNODOBFM::`string'+0x6e48 (83272fe8)
8351cc62 e8d9b5d7ff      call    nt!_SEH_prolog4 (83298240)
8351cc67 33db            xor     ebx,ebx
8351cc69 895de4          mov     dword ptr [ebp-1Ch],ebx
8351cc6c 64a124010000    mov     eax,dword ptr fs:[00000124h]
8351cc72 8a803a010000    mov     al,byte ptr [eax+13Ah]
8351cc78 8845e0          mov     byte ptr [ebp-20h],al
...

Walaupun Windows sudah memakai intruksi sysenter, ia masih memberikan handler pada interrupt vector number 0x2e untuk menjaga kompatibilitas dengan program lama. Bila saya melihat handler untuk interrupt tersebut, saya akan menemukan output seperti pada gambar berikut ini:

Isi interrupt handler untuk vector number 0x2e

Isi interrupt handler untuk vector number 0x2e

Terlihat bahwa interrupt handler akan memanggil nt!KiFastCallEntry tetapi men-skip awalnya dan langsung ke offset 0x8f. nt!KiFastCallEntry juga adalah target untuk perintah sysenter (isi MSR 0x174 dan 0x176 merujuk pada lokasi nt!KiFastCallEntry).

Perihal Solid Snake
I'm nothing...

One Response to System Call Di Windows

  1. Bagus ni Artikel Gan.. ijin baca Gan ???

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: