Category Archives: bahasa

Bahasa Pemrograman BASIC

Setelah membahas Forth dan juga Lua, kali ini saya akan membahas bahasa BASIC (Beginners’ All-purpose Symbolic Instruction Code). Bahasa BASIC ini merupakan bahasa yang sudah ada lama sekali (sejak 1964), tapi ada beberapa hal yang baru-baru ini terjadi yang membuat saya ingin menuliskan tentang bahasa BASIC:

Tiga hal pertama tersebut menunjukkan bahwa:

  • Minat terhadap bahasa lama (BASIC dan COBOL) ternyata masih cukup besar
  • Bahasa-bahasa lama ini masih terpakai (tapi dalam artikel ini saya hanya membahas BASIC)

Setelah membaca mengenai SmileBASIC 4, saya segera membeli softwarenya dari Nintendo Shop. Harga softwarenya 25 USD (masih lebih murah dari kebanyakan game di Nintendo Shop), plus harus membeli slot upload seharga 5 USD jika kita ingin mengupload program kita ke server SmileBASIC.

Setelah mencoba-coba lagi Smile BASIC, ternyata memang bahasa BASIC ini masih bisa membuat saya tersenyum Saya masih bisa membuat program dalam bahasa ini dengan cepat, dan bisa membuat animasi sederhana untuk Joshua dalam waktu sangat sebentar (dalam hitungan menit). Saya juga teringat dulu masa-masa saya belajar sendiri bahasa BASIC. Jadi saya ingin bercerita lebih jauh mengenai bahasa BASIC ini.

Animasi huruf yang dibuat hanya dalam beberapa menit

Di masa kejayaannya, BASIC ada di mana-mana:

  • Ada novel petualangan yang di dalamnya ada kode bahasa BASIC (pernah saya tulis di sini)
  • Berbagai majalah memberikan listing program bahasa BASIC
  • Berbagai buku teks mahasiswa juga diberi kode dalam bahasa BASIC untuk bisa langsung dicoba

Banyak orang menghindari bahasa BASIC karena sifatnya yang tidak terstruktur. Ini memang benar untuk BASIC generasi pertama. Ilmuan komputer Djikstra sering dikutip karena mengkritik BASIC, begini katanya:

“It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration”,

Edsger W. Dijkstra – “How do we tell truths that might hurt” . Selected Writings on Computing: A Personal Perspective

Tapi sebenarnya di tulisan lengkapnya, Djikstra tidak cuma mengkritik BASIC, tapi juga bahasa lain seperti FORTRAN dan COBOL yang sampai saat ini masih dipakai.

Setelah kritiknya itu, sebenarnya bahasa BASIC masih berkembang dan generasi berikutnya sebenarnya sudah cocok menjadi bahasa pemula dan bisa digunakan untuk mengajarkan berbagai konsep pemrograman yang baik.

Beberapa Generasi

Bahasa generasi pertama memakai nomor baris, konsep fungsi dan subrutinnya terbatas, dan sulit membuat struktur data kompleks. Contoh keterbatasan adalah: fungsi hanya sekedar formula, tidak bisa membuat fungsi rekursif. Subrutin memakai nomor baris (tidak memakai nama) dan tidak memiliki variabel lokal. Beberapa implementasi bahkan tidak memiliki DEF untuk membuat fungsi. Contoh versi BASIC ini adalah yang ada pada ROM PC XT, dan ROM berbagai hardware komputer rumah tahun 80an.

Seperti apa sih program BASIC ini? ini adalah program sangat sederhana yang tidak berhenti menampilkan “HELLO WORLD”. Programnya mudah dimengerti: cetak tulisan hello world, dan berikutnya GOTO 10, artinya ulangi lagi baris 10, dst.

10 PRINT "HELLO WORLD"
20 GOTO 10

BASIC sangat populer sehingga pernah dibuat dua standard (ISO/ANSI) tentang bahasa BASIC (yang core dan yang full). Kedua standardnya hanya mencakup generasi pertama yang masih menggunakan nomor baris. Bahasa BASIC juga masuk ke banyak device yang bukan PC, misalnya game console dan juga kalkulator.

20161127_220343
Saya program HP 39gs untuk menampilkan hari pasaran dari sebuah tanggal

BASIC generasi kedua (Structured Basic) tidak lagi memakai nomor baris (walau beberapa versi secara opsional membolehkan nomor baris), mendukung fungsi dan subrutin dengan variabel lokal, dan mendukung user defined type (struct/record). Contoh BASIC generasi kedua adalah Microsoft QuickBasic.

Tanpa nomor baris, program hello world bisa mirip Python 2 (Python 3 mewajibkan kurung). Apalagi BASIC ini sifatnya tidak case sensitive, jadi PRINT sama dengan print.

print "hello world"

BASIC generasi ketiga sudah seperti bahasa pemrograman lainnya yang berbasis objek, hanya saja syntax-nya meniru BASIC yang lama. Contoh BASIC generasi ketiga ini adalah Visual Basic .NET. Tapi saat ini minat bahasa BASIC generasi terbaru sudah mulai berkurang, bahkan Microsoft sudah tidak akan menambah lagi fitur di Visual Basic.NET.

Menurut saya sendiri, BASIC generasi kedua ini sudah cukup ideal untuk berbagai aplikasi kecil dan program ukuran kecil yang fun. Jika ingin memprogram berorientasi objek, sekalian saja pakai Java/C#/Python atau bahasa lain.

Sederhana

Kenapa bahasa BASIC sempat populer dibandingkan bahasa lain? menurut saya ini gabungan beberapa hal. Masalah utama adalah Keterbatasan hardware: sulit membuat compiler dengan memori terbatas di hardware awal masa komputer personal (awal 1980an). Jadi alternatifnya adalah: memprogram dalam assembly, atau dalam bahasa lain yang sederhana. BASIC menang dibandingkan bahasa lain (misalnya FORTH yang pernah saya bahas di sini), karena sangat mudah dimengerti walaupun baik BASIC maupun FORTH keduanya merupakan bahasa yang interaktif.

Cassette Basic. Pengguna PC XT dulu akan ingat ini. Memori yang bisa diakses kurang dari 64kb

Kesederhanaan Bahasa BASIC ini bisa dilihat dari jumlah statement dasar yang dimiliki. Versi awal yang dikembangkan pencipta BASIC: John G. Kemeny dan Thomas E. Kurtz (disebut Darmouth BASIC) hanya punya 15 statement (LET, PRINT, END, DATA, READ, GOTO, IF, FOR/NEXT, GOSUB, RETURN, DIM, REM, STOP, DEF). Plus ada beberapa fungsi built in seperti misalnya ABS untuk mendapatkan nilai absolute atau SIN/COS/TAN untuk trigonomoetri.

Dengan kesederhanaan ini, mengimplementasikan interpreter BASIC bisa dilakukan dengan memori kecil. Salah satu kesuksesan awal Microsoft (waktu itu namanya masih Micro Soft, bukan satu kata) adalah produk interpeter BASIC yang ditulis oleh Bill Gates dalam assembly (kodenya sudah dibuka, ada di github). Bahkan di kode assembly yang relatif kecil, Bill Gates bisa menyembunyikan Easter Egg.

Kesederhanaan bahasa BASIC juga bisa digunakan sebagai latihan ketika mengimplementasikan sebuah interpreter. Saya sendiri pernah mengimplementasikan interpreter BASIC dalam JavaScript 10 tahun yang lalu (masih berfungsi ada sampai sekarang).

Instan

Kebanyakan implementasi BASIC, bahkan dari jaman PC XT dulu, eksekusinya hampir instan. Dari mengetik kode hingga berjalan tidak dibutuhkan waktu berdetik-detik atau bermenit-menit. Selain itu hampir semua implementasi BASIC menggabungkan editor dan interpreter, jadi mengetik, menjalankan program, dan bahkan mendebug bisa dilakukan di environment yang sama.

Experience memprogram BASIC ini menurut saya masih sulit ditemui di sistem lain, kecuali jika kita mendownload IDE super lengkap dengan ukuran besar. Bahkan berbagai IDE modern dengan fitur lengkap sering sulit dikonfigurasi.

Meskipun cycle untuk mengedit dan menjalankan program bisa cepat, tapi program yang ditulis dalam BASIC relatif lambat. Untungnya sejak awal sebagian besar implementasi BASIC bisa memanggil kode mesin.

Kode dalam bahasa BASIC umumnya dijalankan dalam sebuah interpreter, tapi sejak puluhan tahun yang lalu sudah ada banyak compiler Bahasa BASIC yang bisa menghasilkan native code. Jadi tidak benar jika bahasa BASIC pasti harus diinterpretasi.

Menengok implementasi BASIC Modern

Saat ini ada banyak implementasi BASIC modern, dan saya sudah mencoba cukup banyak. Saya tidak akan membahas semuanya, dan hanya yang menurut saya menarik saja.

QB64

QB64 ini open source dan meniru user interface QBasic/QuickBasic. QB64 tersedia untuk multi platform. Meskipun meniru QBasic, fitur-fitur baru yang dimiliki sangat banyak, termasuk untuk membuat aplikasi serius atau game 3D dan bahkan aplikasi Android. Sayangnya saya tidak suka implementasinya: kode ditranslasikan ke C, alalu dicompile dengan compiler C. Hasilnya: waktu kompilasinya lama, tidak bisa menjalankan program dengan instan seperti QBasic/QuickBasic. Bahkan saat ini versi terbaru dari github yang saya download gagal menjalankan aplikasi hello world.

Just BASIC

Just BASIC ini hanya tersedia di Windows, gratis tapi tidak open source. Kelebihannya dibandingkan solusi yang lain adalah: bisa digunakan untuk membuat aplikasi Windows yang memakai native control di Windows. Just BASIC memakai pendekatan interpreter, dan eksekusinya instan, ketika mengklik RUN, program akan langsung berjalan.

AppGameKit Classic

AppGameKit Classic adalah implementasi BASIC berbayar untuk membuat game. Saya mengenal AppGameKit ini dari Humble Bundle beberapa tahun lalu dan ternyata sampai saat ini masih terus diupdate. Harga penuh aplikasi AppGameKit relatif mahal, tapi sering bisa dibeli murah dengan berbagai diskon dan bundle dari berbagai situs. AppGameKit dapat dipakai untuk membuat game, baik desktop, web, maupun mobile.

Engine utama AppGameKit ini ditulis dalam C++, dan bisa dipanggil langsung dari C++ jika kita mau, tapi mode untuk menjalankan kode BASIC adalah interpretasi. Bahasa BASIC yang didukung adalah generasi 2, jadi kita bisa membuat tipe data sendiri, tapi tidak bisa memakai OOP.

Beberapa tahun lalu ketika mencoba ini, rasanya cukup fun. Cukup mudah membuat game sederhana. Bahkan ada mode sangat bagus untuk membuat game mobile: mode broadcast. Asalkan device Android kita ada di subnet yang sama, kita cukup menjalankan AppGameKit Player di Android, tekan Broadcast di PC, dan akan muncul gamenya di device Androidnya, instan.

SmileBASIC 4

Saya mendengar berita rilis SmileBASIC di Hacker News, jadi ini relatif baru buat saya. SmileBASIC ini juga tersedia untuk game console Nintendo 3DS. Walaupun sulit sekali memprogram dengan layar kecil 3DS, ternyata aplikasi ini cukup populer sehingga mereka membuat versi Nintendo Switchnya.

Bandingkan jumlah memorinya dengan PC XT

Nintendo Switch bisa dihubungkan ke monitor dengan HDMI dan bisa dihubungkan juga ke keyboard dan mouse USB (dan bahkan juga headset USB). Tanpa keyboard USB, kita bisa memprogram dengan keyboard di layar sentuh, tapi ini kurang efisien. Jadi selama saya mencoba SmileBASIC, saya selalu menggunakan keyboard dan mouse dan layar besar (SmileBASIC mendukung sampai dengan resolusi 1280×720).

AppGameKit mudah diprogram, tapi memiliki kelemahan: kita harus mencari sendiri asset (gambar, musik, efek suara) untuk game atau membeli pack terpisah. Sedangkan SmileBASIC memiliki ratusan sprite built in, puluhan music background built in, dan juga puluhan sound effect. Tersedia juga editor bitmap built in jika gambar yang ada tidak seperti yang kita mau.

Tapi SmileBASIC memiliki kelemahan dari segi fitur bahasa BASIC-nya. Meskipun menggunakan BASIC generasi 2 (tanpa line number), tapi SmileBASIC tidak mendukung user defined type.

Penutup

Bahasa BASIC memang sudah tidak terlalu banyak dipakai dibandingkan masa kejayaannya, tapi belajar bahasa ini cukup menyenangkan. Masih ada orang yang bisa mendapatkan uang dari memprogram BASIC. Beberapa game ditulis dalam bahasa BASIC, demikian juga banyak aplikasi sederhana ditulis dalam BASIC. Beberapa aplikasi dalam berbagai bank masih ditulis dengan Visual Basic 6. Meski demikian, jika Anda pemula dan baru mulai belajar pemrograman sebaiknya belajar bahasa lain saja, seperti Python atau PHP.

Belajar bahasa BASIC bisa dipelajari sekedar untuk iseng. Bahasanya sederhana, tidak butuh banyak waktu untuk belajar, tidak seperti belajar Rust, Haskell atau Scala. Bisa juga belajar BASIC supaya bisa belajar membuat interpreter atau compilernya.

Multithreading dan Multiprocessing di Python

Python adalah salah satu bahasa yang sangat saya sukai, tapi bukan berarti bahasa ini tidak memiliki kekurangan, di tulisan ini saya ingin menunjukkan kekurangan Python dalam memproses data kompleks dengan Python murni (pemrosesan tidak dilakukan di modul native). Kompleks di sini maksudnya pemrosesannya CPU bound (batasan programnya adalah kemampuan CPU, bukan memori atau I/O).

Kelebihan python jelas sangat banyak: mudah dipakai, librarynya banyak, sintaksnya bersih dan mudah dibaca. Library Python untuk berbagai hal (terutama AI) sudah sangat banyak dan banyak yang ditulis dalam native code (C/C++). Dengan library eksternal yang ditulis dalam bahasa C/C++ (kebanyakan library AI), kita tidak perlu memikirkan apapun dalam hal pemrosesan data besar dan kompleks, karena itu dilakukan di level native code.

Multithreading dan Global Interpreter Lock

Dalam banyak bahasa (misalnya C atau Java): jika saya punya algoritma yang lambat untuk data besar, dan datanya masih masuk memori, saya biasanya bisa menyelesaikannya dengan thread. Saya hanya perlu mempartisi data, membuat banyak thread dan mengolah tiap potongan data di thread yang berbeda. Partisi data di sini biasanya mudah: jika bentuknya array, tinggal dibagi saja per N element.

Di Python, solusi dengan multithreading tidak akan berjalan baik karena adanya Global Interpreter Lock. Atau tepatnya lagi: bisa jalan, tapi tidak lebih cepat dari single thread. Adanya GIL berarti di kebanyakan kasus beberapa thread di Python tidak bisa jalan berbarengan. Threading di Python hanya berguna untuk proses yang menunggu I/O (akses file, akses socket, dsb). Jadi jika kita punya prosessor 32 core sekalipun, Python tidak bisa optimal memakai semua core tersebut dengan module threading.

Kadang-kadang mengganti interpreter dengan pypy sudah cukup menyelesaikan masalah komputasi yang berat. Pypy adalah implementasi interpreter Python yang memakai teknologi JIT (Just In Time Compiler), jadi lebih efisien walaupun tetap satu thread saja. Tapi Pypy ini tidak selalu berhasil:

  • Pypy tidak 100% kompatibel dengan Python
  • Ada modul-modul yang tidak bisa dipakai di Pypy
  • Versi Pypy ketinggalan dibandingkan versi Python resmi

Solusi lain seperti Cython juga bisa dipakai, tapi lebih rumit dari sekedar mengganti interpreter.

Multiprocessing

Python memiliki modul multiprocessing, artinya kita bisa menjalankan sebuah fungsi di subproses baru. Overhead untuk memulai proses baru cukup besar, jadi ada kita bisa juga memakai process Pool supaya lebih optimal. Di titik ketika kita membuat subprocess, isi memori saat ini di proses utama akan di-clone ke memori yang sifatnya copy-on-write (COW). Artinya: selama memori tersebut tidak diubah, maka tidak akan dibuat salinannya, hanya akan dishare (dipakai bersama).

Lalu bagaimana kita bisa memanggil fungsi di proses baru dan mendapatkan hasilnya di proses utama? jawabannya adalah: setiap parameter akan di-serialize dan dikirim ke subproses baru. Hasil kembalian fungsi diserialize oleh subproses dan dikembalikan ke proses utama. Ini cukup untuk menyelesaikan banyak masalah. Tapi pendekatan ini tidak sempurna. Proses serialization dilakukan menggunakan modul pickle. Ini titik yang harus diwaspadai, perhatikan contoh kecil ini di mana kita memproses XML dengan minidom.

from xml.dom import minidom
import pickle

myxml = """<?xml version="1.0"?>
<test>
	<item id="11">
		<name>red thing </name>
		<price>100</price>
	</item>
	<item id="12">
		<name>blue thing</name>
		<price>200</price>
	</item>
</test>
"""

doc = minidom.parseString(myxml)

items = doc.getElementsByTagName("item")

firstitem = items[0]

print(firstitem.toxml())

with open("test.bin", "wb") as f:
    f.write(pickle.dumps(firstitem))

Hasil dari pickle bisa dilihat di file test.bin dan jika dilihat dengan editor akan terlihat bahwa ada ‘blue thing’, padahal tidak ada di firstitem. Modul pickle akan melihat semua atribut yang dimiliki oleh sebuah node dan mempickle semuanya, dalam hal ini: parent-nya juga, dan karena parent memiliki semua child, maka semua child-nya juga ikut.

Jika kita menggunakan modul multiprocessing dan mengirimkan satu node ke subprocess, maka yang niatnya hanya mengirimkan satu node, ternyata mengirimkan seluruh DOM ke subprocess. Secara umum, ketika mengirimkan objek apapun, maka kita mulai harus memikirkan: ketika serialisasi dilakukan, apa saja yang akan dibawa oleh objek tersebut.

Objek yang diserialize ini tidak masuk ke memori COW, tapi Python akan mengalokasikan memori baru. Bayangkan jika kita mengirimkan copy seluruh DOM ke semua subprocess (misalnya ada 10 subprocess), hasilnya: penggunaan memori jadi sangat besar. Jika DOM-nya besar, proses serialisasi dan deserialisasi ini juga sangat lambat.

Dalam kasus tertentu, cara yang lebih efisien adalah: kita letakkan apa yang ingin kita proses di objek global, objek global ini akan di-clone ke proses baru (tidak melalui serialization). Ketika kita memanggil subprocess, kita hanya mengirimkan string atau bilangan yang berupa ID. Subprocess kemudian bisa mengakses variabel global berdasarkan ID tersebut. Cara ini efisien dalam mentransfer data dari proses utama ke subprocess, tapi tidak bisa dilakukan untuk sebaliknya. Ketika mengembalikan hasil proses, kita tetap harus memikirkan objek yang kita kembalikan. Untuk nilai kembalian ini, tidak ada trik yang mudah, kita harus berhati-hati agar serialisasi tidak berjalan liar.

Modul multiprocessing juga memiliki beberapa kelas khusus supaya kita bisa mentransfer data dengan lebih efisien antar proses (Queue, Pipe, Manager), tapi penggunaanna cukup rumit dan tidak intuitif, dan rawan salah. Ini juga tidak menyelesaikan masalah serialisasi data kompleks.

Mungkin sebagian orang akan bilang: ah kan gampang, cukup kita lihat setiap kelas yang kita miliki dan cek satu persatu membernya supaya jelas apa yang diserialisasi. Ada beberapa masalah:

  • Kode kadang ditulis anggota team lain, dan tidak jelas tipe data sebuah variabel member, lebih sulit lagi jika ada list atau dictionary yang tipe datanya tidak jelas
  • Kadang kita memakai library orang lain (contoh kecil: XML, Graph), yang tidak mudah untuk dimodifikasi. Andaikan bisa dimodifikasi: kita harus memaintain library itu jika ada versi baru

Untuk masalah pertama: Python saat ini memiliki type hint, kita bisa memberikan hint apa tipe sebuah variabel/member. Tipe data tidak akan dicek oleh interpreter python, tapi IDE yang kita pakai bisa membantu mengecek tipe data. Untuk masalah kedua: ini lebih sulit, kita bisa menulis serializer/pickler khusus untuk object tertentu (bisa dibaca di dokumentasi Python), atau menggunakan setstate__ dan __getstate untuk memproses secara khusus member variable tertentu.

Solusi Third Party

Dulu orang menyarankan: pakai saja Jython (implementasi Python dalam Java) atau IronPython (implementasi Python dalam .NET), keduanya tidak memakai Global Interpreter Lock, jadi thread bisa berjalan cepat. Tapi masalah dengan solusi dari pihak lain adalah: maintenance. Sekarang ini baik Jython maupun IronPython tidak dimaintain lagi. Belum lagi dukungan modul yang kurang lengkap.

Saat ini ada beberapa pendekatan lain untuk memudahkan masalah multithread/multiprocess, misalnya Ray, tapi ini masih kurang populer dan kode kita harus diubah untuk mengikuti model komputasi Ray, library ini pun punya masalahnya sendiri. Tentunya saya tetap khawatir dengan library apa saja dari 3rd party bahwa proyek semacam ini tidak bertahan lama.

Data Ukuran Sedang

Masalah yang saya contohkan di atas itu adalah kasus yang sudah saya temui beberapa kali. Jika dari awal diketahui bahwa data akan sangat besar, saya akan memecah data menjadi banyak bagian, lalu membuat proses khusus yang bisa memproses data yang sudah dipecah, atau bahkan menggunakan sistem terdistribusi. Jika data sangat kecil, saya juga tidak akan menggunakan threading sama sekali, cukup satu proses.

Perlu dicatat bahwa jika kita terlalu buru-buru berusaha memakai sistem terdistribusi untuk data yang masih muat di memori, performancenya justru tidak bagus (belum lagi biaya ekstra yang dikeluarkan untuk banyak server). Artikel tahun 2014 ini merupakan contoh yang bagus bahwa kadang command line tools bisa 235 kali lebih cepat dari Hadoop. Ini kasus yang sangat ekstrem, tapi pelajaran yang bisa dipetik adalah: kalau datanya kurang besar, overhead memakai Hadoop atau sistem lain kadang sangat besar.

Contoh yang biasa saya temui adalah: ada program yang hanya memproses sebuah file, biasanya ukurannya puluhan megabyte, tapi kemudian filenya bertumbuh jadi beberapa gigabyte, nilai ini besar (beberapa puluh kali lebih besar dari input biasanya) tapi masih jauh di bawah memori yang tersedia (misalnya server dengan memori 16-256 GB). Program yang tadinya butuh beberapa detik menjadi beberapa menit atau bahkan puluhan menit, sedangkan ketika diubah menjadi multiprocess, tiba-tiba pemakaian memori Python menjadi beberapa puluh gigabyte (karena masalah serialization) dan malah lebih lambat lagi. Memperbaiki skrip seperti ini bisa butuh waktu yang cukup lama.

Secara umum saya tidak suka dengan pendekatan multiprocessing ini: saya memakai bahasa yang managemen memorinya otomatis (contoh: java, C#, Python, Javascript) karena ingin memori diatur otomatis oleh sistem. Kalau memang ingin mengatur memori manual sendiri, lebih baik saya memakai C/C++ sekalian.

Penutup

Python tetap merupakan salah satu bahasa yang saya sukai dan saya tetap akan memakainya untuk berbagai hal. Posting ini hanya untuk menunjukkan bahwa Python juga memiliki keterbatasan. Dengan memahami batasan ini, kita bisa mempertimbangkan:menggunakan pendekatan lain dalam memproses data dengan Python atau memakai bahasa lain yang lebih cocok untuk masalah tersebut.

Perlu dicatat juga bahwa tiap bahasa bisa berubah, baik sintaksnya maupun implementasinya. Contohnya: NodeJS baru punya Worker Thread di Node JS 10 sebagai fitur experimental dan harus diaktifkan dengan flag khusus. Baru pada Node JS 12 yang baru dirilis April tahun 2019 API Worker Thread dianggap stabil dan layak dipakai produksi. Jadi mungkin saja suatu saat masalah multithread ini akan berubah di Python.

Mengenal Bahasa Pemrograman Lua

Di kesempatan kali ini saya akan memperkenalkan bahasa Lua. Bahasa ini sebenarnya sudah ada cukup lama (sejak 1993), tapi kurang terkenal secara umum. Lua lebih sering dijadikan bahasa scripting untuk aplikasi dan sering kali nama Lua tidak disebut sama sekali. Lua artinya “Bulan” (moon) dalam bahasa portugis. Lua diciptakan oleh Roberto Ierusalimschy, Luiz Henrique de Figueiredo, dan Waldemar Celes, anggota dari Computer Graphics Technology Group (Tecgraf) di Pontifical Catholic University of Rio de Janeiro, di Brazil.

Lua bisa dipakai untuk membuat program/aplikasi seperti bahasa lain, tapi kelebihan bahasa ini  adalah dari awal dirancang untuk diembed dalam sebuah aplikasi lain (terutama dalam bahasa sejenis C). Di-embed ini artinya bisa  bisa dipakai untuk sistem plugin atau bisa dipakai untuk mengontrol bagian business/game/app logic sementara bagian yang butuh performance tinggi memakai C atau bahasa lain. Lua juga bisa jadi file konfigurasi yang sangat fleksibel.

Lua dipakai di banyak software misalnya Adobe Lightroom, Nmap, Wireshark, dan lighttpd. Seperti saya sebutkan di awal, sering kali berbagai aplikasi menyebutkan bahasa Lua ini hanya sekilas, dan fitur aplikasinya yang lebih ditekankan dengan nama lain, misalnya dokumentasi Nmap menyebutkan tentang Nmap Scripting Engine (NSE) tapi Lua hanya disebut sekilas di tengah. Lua juga banyak dipakai di berbagai game (misalnya World Of Warcraft, Angry Birds) dan game engine. Interpreter Lua ukurannya sangat kecil sehingga bisa dipakai untuk embedded system/IOT (misalnya NodeMCU dan eLua).

Bahasa Lua

Bahasa Lua menurut saya relatif sederhana, tapi juga sangat fleksibel. Sintaksnya tidak aneh, sedikit mirip BASIC atau pascal. Contohnya seperti ini:

print "hello, berapa umur kamu?"
umur = io.read("*n")
if umur >= 17 then
   print("Halo kakak")
else
   print("Halo adik")
end

Bagi sebagian orang mungkin ini agak aneh mengingat bahasa sekarang sudah tidak lagi memakai kata-kata (“then”, “end”) tapi lebih memakai simbol ({} di C/C++/Java/.NET, indentasi di Python), tapi untuk yang dulu memakai Pascal dan BASIC ini terasa cukup wajar. Sebagian besar syntax Lua mirip dengan bahasa lain, tapi ada beberapa hal yang agak aneh. Di Lua, operator kurang dari, lebih besar, sama dengan, semuanya sama dengan bahasa lain (<, >, =) tapi untuk tidak sama dengan, syntax yang dipakai adalah ~=. Syntax untuk komentar juga agak kurang standar, memakai “–” untuk satu baris, dan memakai –[[ untuk komentar multi baris –]].

Secara umum bahasa Lua cukup mudah dibaca bahkan oleh pemula, tidak seperti bahasa Forth (yang saya bahas sebelumnya) atau bahasa fungsional seperti Scala.

Tipe primitif yang didukung Lua adalah: string, number, dan integer. Struktur data utama di Lua adalah table. Secara umum: tabel memetakan sesuatu ke sesuatu yang lain (seperti Map/Dictionary di bahasa lain). Hampir semua hal di Lua diimplementasikan dengan table. Sebuah array di Lua juga adalah tabel yang memetakan indeks dengan sebuah nilai, dengan nilai index awal default adalah 1. Masalah index ini agak mengganggu bagi saya, karena di bahasa lain biasanya array berbasis 0. Sebuah tabel bisa berisi tabel lain untuk merepresentasikan array multi dimensi.

Sebuah struktur (struct) juga bisa direpresentasikan dalam bentuk tabel (memetakan nama field sebagai string ke nilainya). Di Lua, sebuah fungsi juga bisa masuk ke dalam tabel. Contohnya kita bisa saja membuat tabel yang berisi berbagai operasi matriks yang berisi pemetaan nama operasi, misalnya “multiply” dengan implementasi fungsi perkalian matriks.

Lua juga memiliki konsep metatable, yang bisa digunakan untuk mendefinisikan operasi terhadap sebuah variabel. Dengan setmetatable(variabel, sebuahtabel), kita bisa mendefinisikan berbagai operator dan fungsi yang berlaku untuk variabel tersebut. Jadi jika kita ingin membuat sebuah variabel menjadi sebuah tipe matriks, kita bisa membuatnya dengan setmetatable(variabel1, tabel_operasi_matriks).

Lua tidak memiliki konsep OOP tapi dengan menggunakan berbagai trik table, metatable dan fleksibilitas function di Lua, kita bisa mengimplementasikan Object Oriented Programming. Saya sendiri kurang menyukai ini, kalau saya ingin memprogram dengan paradigma OO, saya akan memilih bahasa lain.

Fitur Lua lain yang menarik adalah coroutine, generalisasi dari subrutin, tapi fitur ini jarang saya pakai. Sebenarnya fitur ini powerful, tapi ketika melakukan embeding Lua ke bahasa lain (terutama C), fitur ini cukup merepotkan karena tidak mudah dipetakan ke C.

Embeding Lua ke bahasa C

Seperti sudah saya sebutkan di atas, fitur Lua adalah sangat cocok untuk diembed ke sebuah aplikasi. Sebenarnya bahasa lain seperti Python dan Ruby juga bisa diembed ke bahasa lain (misalnya C), tapi ada banyak kelebihan Lua:

  • Ukurannya sangat kecil, ratusan kilobyte. Python butuh minimal beberapa megabyte (disk space dan memori)
  • Mendukung multi thread (satu thread menjalankan instance Lua terpisah). Untuk embedding multi thread di Python sangat sulit, dan Python memiliki Global Interpreter Lock
  • Interfacing sangat mudah, baik coding manual atau dengan bantuan SWIG

Implementasi utama Lua adalah dalam bahasa C, walau ada juga Lua untuk Java. Kebanyakan bahasa memiliki build system yang kompleks dan butuh banyak library tambahan. Dalam kasus Lua: kita cuma perlu copy paste semua file .c (kecuali lua.c) ke project kita dan itu sudah cukup. Sebagai catatan: selain implementasi utama Lua, ada juga LuaJIT yang sangat cepat namun memiliki banyak batasan terutama di sistem 64 bit.

Setelah memasukkan semua file C dalam project kita, di bagian program yang butuh Lua, kita perlu menginclude beberapa file:

               
#include "lua.h"       
#include "lualib.h"       
#include "lauxlib.h"

Lalu kita bisa menginstansiasi interpreter Lua. Jika diperlukan, kita bisa membuat banyak instance (misalnya satu di setiap thread). Setelah itu biasanya kita ingin agar library standar Lua diaktifkan, jadi kita memakai luaL_openlibs.

lua_State *L = luaL_newstate(); 
luaL_openlibs(L);

Biasanya skrip Lua akan diletakkan di luar program, supaya bisa diubah dengan mudah tanpa rekompilasi. Untuk meload dari file, kita bisa memakai luaL_loadfile atau luaL_loadstring jika ingin memakai string. Di banyak Game, file lua dienkrip di file (agar tidak mudah dimodifikasi), lalu didekrip dimemor, dan diload menggunakan luaL_loadstring.

 
int err = luaL_loadfile(L, "myscript.lua");
if (err) {
       fprintf(stderr, "Failed loading file: %s\n", lua_tostring(L, -1));
       exit(1);
}

Dalam passing parameter ke Lua dan dari Lua, kita menggunakan konsep stack. Kita bisa memasukkan nilai ke stack dengan lua_pushstring/lua_pushnumber/lua_pushinteger, dan bisa mengambil nilai di stack dengan lua_tointeger, lua_tostring, lua_tonumber dan beberapa fungsi lain. Jadi ketika memanggil fungsi di Lua: kita melakukan push value ke stack, dan untuk mengambil nilai dari lua kita akses hasilnya di stack juga.

Kita bisa mencari fungsi yang diimplementasikan di Lua dengan lua_getglobal(L, "namafungsi"), ini akan mempush reference fungsi ke stack. Setelah itu kita bisa mempush nilai parameter untuk fungsi, memanggil fungsinya, dan mendapatkan hasilnya di stack.

 
lua_getglobal(L, "myfunction"); 
lua_pushinteger(L, 1); //param 1
lua_pushinteger(L, 2);  //param 2
int err = lua_pcall(L, 
    2 /*number of arguments*/, 
    1 /*number of result*/, 
    0 /*indeks ke error handler, jika 0 maka error akan dipush ke stack*/);

Membuat fungsi di C yang bisa dipanggil oleh Lua juga sangat mudah. Misalnya kita ingin membuat fungsi max yang menerima 2 double dari Lua dan mengembalikan angka terbesar, maka kita cukup membuat ini:

 
int max_for_lua(lua_State *L) 
{
  double num1 = lua_tointeger(L, 1); //param 1 dari Lua
  double num2 = lua_tointeger(L, 2); //param 2 dari Lua

  double max = num1>num2?num1:num2; //logika utama

  lua_pushnumber(L, max); //push ke stack Lua
  return 1; //hanya ada satu nilai kembalian
}

Tentunya keberadaan fungsi ini perlu diberitahukan ke Lua agar bisa diakses oleh skrip Lua. Nama fungsi di C dan Lua bisa berbeda, contohnya fungsi max_for_lua di atas bisa didaftarkan menjadi “my_max” seperti ini:

 
lua_pushcfunction(lua_state, max_for_lua);
lua_setglobal(lua_state, "my_max");

Penutup

Sering kali saya menemukan di mana saya butuh eksperimen cepat dan tidak ingin sering mengcompile ulang kode program saya. Contoh penggunaan yang baru-baru ini saya lakukan adalah ketika eksplorasi fitur tertentu di iOS. Jika saya mengandalkan objective C atau Swift saja, tiap kali saya mengubah kode (biasanya hanya logic tertentu), akan butuh beberapa detik sampai beberapa puluh detik sampai bisa menjalankan ulang aplikasinya. Walau hanya menekan tombol “run” di XCode yang terjadi di latar belakang adalah: kodenya harus dicompile, dilink, ditandangani secara digital, dicopy ke device, lalu dijalankan. Dengan memindahkan logic ke Lua, saya hanya perlu me-mount direktori dokumen aplikasi dengan ifuse, mengedit file Lua, lalu merestart aplikasinya.

Menambahkan sebuah scripting ke language ke aplikasi memang butuh waktu tapi di sisi lain juga sangat fleksibel karena skrip bisa dimodifikasi dengan cepat dan tidak seperti kode C, tidak perlu dikompilasi ulang. Jika Anda sudah beberapa kali menambahkan fitur scripting ke sebuah aplikasi, menambahkan ke aplikasi berikutnya tidak butuh waktu lama. Untuk aplikasi kompleks kita bisa memakai Simplified Wrapper and Interface Generator (SWIG) untuk mempercepat membuat interface dari C/C++ ke Lua.

Semoga tulisan singkat ini cukup untuk memperkenalkan bahasa Lua dan mungkin juga membuat Anda tertarik untuk menambahkan Lua ke aplikasi yang Anda buat.

Mengapa memakai bahasa pemrograman tertentu?

Mengapa seseorang memilih suatu bahasa tertentu untuk menyelesaikan suatu masalah atau membuat aplikasi tertentu? Ternyata jawabannya bisa banyak.  Kebanyakan pilihan intinya adalah keterpaksaan dan terakhir baru faktor kenyamanan.

Keterpaksaan pertama dari non teknis, misalnya dari permintaan atasan atau permintaan client. Hal ini sering kali tidak bisa ditawar lagi, terutama jika sudah melibatkan kontrak legal. Paksaan ini sering menghasilkan kode yang aneh atau tidak menggunakan fitur yang tepat dari sebuah bahasa, karena programmer dipaksa menggunakan bahasa lain, dan karena harus buru-buru mereka akan menggunakan gaya bahasa X di bahasa Y.

Sebuah teknologi tertentu kadang hanya bisa diprogram dengan satu bahasa. Misalnya dulu ponsel cuma bisa diprogram dengan Java (J2ME) jadi ya terpaksa harus memakai bahasa Java. Microcontroller tertentu perlu memakai assembly karena ukuran ROMnya sangat kecil dan tidak ada compiler C yang bisa menghasilkan kode sekecil itu.

Sekarang ini jumlah memori dan kecepatan berbagai device sudah sangat tinggi, sehingga memungkinkan interpreter berjalan di platform apa saja. Ini memberi kebebasan memilih bahasa. Contohnya: dulu microcontroller CPU dan RAMnya sangat kecil sehingga tidak cukup untuk menjalankan interpreter Python. Sekarang sudah banyak microcontroller yang bisa menjalankan MicroPython.

Bahasa tertentu dipilih karena masalah kinerja (performance). Meskipun Python bisa berjalan di microcontroller, tapi kecepatannya jauh lebih lambat dibandingkan C/C++ (karena interpreted). Faktor kecepatan ini sangat terasa untuk keperluan tertentu, misalnya untuk membuat animasi lampu yang jumlahnya banyak. Di dalam konteks lain juga sama: bahasa tertentu dipilih karena masalah performance.

Berbagai bahasa memiliki tingkat strictness yang berbeda, dan mulai masalah sintaks sampai tipe data. Sebagian bahasa memungkinkan kecerobohan yang sulit dilakukan di bahasa lain. Contoh sederhana, dalam JavaScript kita bisa membuat kode seperti ini dan akan berjalan, walau hasilnya mungkin tidak seperti yang diharapkan.

Tipe a adalah Number dan b adalah String, tapi keduanya bisa dioperasikan

Pemula Ruby yang beralih dari bahasa tertentu (misalnya C) mungkin akan bingung dengan sifat Ruby di mana 0 dianggap true. Alasannya: 0 adalah object yang valid, dan nilainya dianggap true.

Untuk bahasa yang sangat dinamik, dibutuhkan banyak test case untuk banyak hal sederhana, sedangkan di bahasa yang tipenya dicek oleh compiler, jumlah test bisa dikurangi. Ada bahasa yang lebih strict lagi, misalnya bahasa ADA yang sempat jadi bahasa wajib oleh Department of Defense Amerika. Secara umum: bahasa tertentu mengurangi kesalahan programmer (baik dari syntax, dari compiler), sehingga bahasa tersebut dipilih untuk tujuan tertentu.

Beberapa masalah mudah diselesaikan menggunakan library yang sudah ada. Ketersediaan library ini menjadi alasan kenapa memilih sebuah bahasa. Misalnya sudah ada library OpenCV (Open computer vision) untuk memproses video secara realtime (misalnya face recognition). OpenCV ditulis dalam C++ dan  ada banyak binding sehingga fungsi-fungsi C++ ini bisa diakses dari bahasa lain (misalnya Python, Java, C#, NodeJS) tapi tidak semua binding ini sempurna, dan sebagian ketinggalan versinya. Misalnya saat tulisan ini dibuat, binding OpenCV untuk JavaScript/NodeJS versi 3 belum ada.

Jika tidak ada batasan keterpaksaan, maka alasan berikutnya adalah kenyamanan. Beberapa aplikasi bisa ditulis dalam bahasa apapun karena tidak tidak keterpaksaan tertentu. Dalam kasus seperti ini, orang bisa memilih bahasa sesuai dengan familiaritas dengan bahasa tersebut. Sebagian programmer yang menguasai banyak bahasa akan memilih bahasa yang ternyaman (dan kalau bisa yang terbaik) untuk menyelesaikan suatu masalah.

Jangan heran kalau dalam sebagian masalah, C bisa lebih nyaman dari Java atau bahasa lain.  Contoh kecil: sering kali dalam memprogram hardware kita membutuhkan array of bytes (misalnya Protocol Data Unit pada NFC). Inisialisasi array of byte di Java jika nilainya kurang dari 127 bisa dilakukan seperit ini:

byte b [] = new byte[] {1, 127};

Tapi jika nilainya lebih dari 127 tiba-tiba kodenya tidak bisa dicompile:

byte b [] = new byte[] {1, 128};

Dengan error:

incompatible types: possible lossy conversion from int to byte

Cara yang benar adalah dengan menggunakan casting ke byte:

byte b [] = new byte[] {1, (byte)128};

Sedangkan di C, dengan tipe uint8_t (dari header stdint.h) kita tidak perlu memperdulikan hal kecil seperti itu. C juga mendukung bit fields yang bisa membuat manipulasi bit tidak diperlukan (karena dilakukan oleh compiler).

Di sisi lain: membuat aplikasi web lebih sulit dilakukan dengan C. Bereksperimen dengan bilangan integer yang besar (ratusan digit, misalnya untuk operasi RSA) jauh lebih mudah dilakukan di Python dibandingkan Javascript (big integer sudah built in di Python).

Perlu dicatat bahwa bahasa pemrograman yang populer juga masih terus berkembang. Dulu untuk melakukan iterasi list di Java butuh kode yang cukup panjang, tapi kemudian syntax for ditambahkan yang memudahkan ini. Ini hanyalah syntactic sugar alias pemanis saja, di dalamnya implementasinya masih sama. Sebagian bahasa memiliki sintaks yang sudah manis dari sejak dirancang sehingga kadang seseorang lebih memilih bahasa lain.

Kadang tool yang tersedia untuk sebuah bahasa juga menjadi faktor penting dalam memilih sebuah bahasa. Tools ini bisa berupa: compiler, debugger, IDE, dll. Compiler yang lambat bisa membuat frustrasi, jika debugger tidak tersedia akan membuat development lebih lama, IDE yang jelek bisa memperlama coding (apalagi jika sering error/hang) tapi IDE yang bagus bisa mempercepat coding.

Semoga sekarang cukup jelas kenapa ada banyak bahasa, kenapa seseorang perlu mempelajari banyak bahasa, dan kenapa bahasa tertentu dipilih untuk menyelesaikan masalah tertentu.

Programming dan Penetration Testing

Pentest adalah kegiatan menyerang sistem komputer untuk mencari kelemahan security, atau dari Wikipedia:

A penetration test, or the short form pentest, is an attack on a computer system with the intention of finding security weaknesses, potentially gaining access to it, its functionality and data.

Pentesting dilakukan atas permintaan client, jadi bukan hacking ke sembarang website. Contoh sederhana pentesting seperti ini: coba jebol website perusahaan kami, apakah ada bug securitinya? atau: coba pergi ke lobi atau tempat parkir perusahaan kami, apakah ada WIFI terbuka, apakah dari situ bisa masuk ke sistem internal perusahaan kami?. Kami punya app mobile, apakah bisa “dijebol” (misalnya apakah kita bisa membuat request ke server supaya memberikan data user lain).

Karena ini blog mengenai programming, saya tidak akan membahas banyak mengenai pentesting, hanya ingin menunjukkan betapa keahlian programming bisa sangat berguna untuk pentesting. Dari contoh yang saya sebutkan di atas, scope dari pentesting bisa sangat banyak, mulai dari yang on site: datang dan mengecek kabel, wireless network, dsb, sampai ke level network dan aplikasi (baik web, desktop, mobile). Saya hanya ingin membahas aspek programming, untuk mendorong peminat bidang security agar mau belajar programming.

Perkiraan saya mungkin sekitar 70% pekerjaan pentesting bisa dilakukan dengan tools yang sudah tersedia, baik yang open source maupun komersial, tanpa butuh keahlian scripting ataupun programming. Sisanya bakal butuh programming. Bahkan saya yakin 100% beberapa jenis bug tidak bisa ditemukan tanpa keahlian programming.

Screenshot_2014-11-27-18-19-36

Kemampuan programming minimal yang dibutuhkan adalah bisa mengcompile atau menjalankan program dalam berbagai bahasa. Kadang di sebuah server tidak tersedia tool tertentu, jadi kita perlu mengcompile dari source karena OS di server itu sudah terlalu lama (misalnya masih banyak yang memakai RHEL 4). Jika tidak tahu bagaimana menjalankan sesuatu, maka sudah game over di titik ini. Pernah saya baca di blog pak Budi Rahardjo mengenai kompetisi CDC (Cyber Defense Competition):

Salah satu soal yang disampaikan adalah men-decode data base64 yang kemudian menjadi skrip Python, yang tinggal dijalankan. Lucunya ada banyak yang gak tahu Python. ha ha ha. Mereka kebingungan menjalankannya. Padahal tinggal “python namaskrip.py”. Mereka tidak perlu tahu bahasanya. hi hi hi. Ada yang pura-pura tahu dan me-rename menjadi bahasa C, menambahkan “#include ” dibagian awalnya, kemudian mencoba compile. Ya gak bakalan jalanlah. he he he.

Kejadian semacam di atas itu yang mendorong saya menulis tulisan seperti ini.

Keahlian lain adalah mencari program atau menulis program kecil dalam bahasa tertentu yang kadang tidak terlalu umum atau sudah kuno. Contoh: jika sebuah web server bisa menerima upload file, dan web server itu hanya bisa menjalankan ASP versi klasik, ya kita harus bisa mencari shell dalam ASP Classic (bukan ASP.NET). Berbeda dengan software development biasa di mana kita bisa memilih bahasa pemrograman atau teknologi yang ingin kita pakai, di sini kita dipaksa mengikuti apa yang tersetup di server.

Jika beruntung, sudah ada orang yang menuliskan program standar (web shell) yang memiliki fungsi seperti “file manager/file upload” (untuk mengupload tool tambahan), “shell” (untuk mengeksekusi command apapun di sisi server), dan “database shell” (untuk mengirimkan query apapun ke database). Jika tidak beruntung, atau ada batasan tertentu di server, mungkin kita perlu membuat tool baru. Contoh sederhana: jika server membatasi upload hanya 100 KB saja, sementara binary nmap yang ingin diupload (plus librarnya) besarnya beberapa megabyte, maka kita perlu membuat program kecil untuk mengupload parsial lalu menggabungkan hasil uploadnya.

Keahlian membaca kode dalam berbagai bahasa juga diperlukan. Dalam beberapa kasus, kita bisa mendapatkan akses ke source code aplikasi web (karena bug, karena salah konfigurasi, karena lupa ikut mengupload direktori “.git”, dsb). Setelah punya source code, kita bisa menemukan bug tambahan dengan membaca kodenya. Sebagai pentester, tentunya Anda tidak akan bilang: wah ternyata dia pake Python, saya nggak tau cara ngehack aplikasi web dengan Python.

Dengan membaca source code, hal paling minimal adalah mengerti di mana file konfigurasi supaya bisa mengekstrasi user/password database, berikutnya adalah mencari bug-bug tambahan seperti SQL Injection, hidden functionality, dsb.

Dengan maraknya OS Mobile, sekarang pentesting juga masuk ke ranah mobile. Di sini ilmu reverse engineering sangat diperlukan. Untuk OS Android, reverse engineering bisa dilakukan dengan decompiler yang bisa mengembalikan kode Java ke bentuk semula (kecuali sudah diobfuscate), jadi kembali keahlian membaca kode diperlukan.

Setelah mengetahui kodenya, biasanya kita perlu melakukan fuzzing request, alias mencoba-coba berbagai kemungkinan request. Untuk aplikasi berbasis HTTP, kita masih bisa memakai proxy (misalnya ZAP) dan mengubah-ubah request untuk tahu reaksi server (bagaimana jika nilaiya negatif, bagaimana jika nama file mengandung “../”, dsb). Tapi untuk aplikasi yang memakai protokol khusus via SSL, kita perlu memahami, lalu mengimplementasikan ulang client sendiri yang bisa melakukan fuzzing.

Pengetahuan mengenai best practice dalam sebuah bahasa juga diperlukan dalam membuat aplikasi yang aman. Contoh: untuk menghasilkan bilangan random untuk kriptografi di Java gunakan SecureRandom (jangan Random). Di Android untuk melakukan koneksi SSL menggunakan socket, jangan gunakan SSLFactory tapi gunakan SSLCertificateSocketFactory, dsb. Pekerjaan pentester cukup mudah di sini, hanya menyarankan saja, dan programmer yang akan mengimplementasikan saran tersebut.

Saya sudah banyak menemukan bug yang memerlukan banyak coding. Ada bug yang terkait dengan penyimpanan password (terenkripsi) yang disimpan lokal di device yang seharusnya aman, tapi ternyata saya bisa membuat tool yang cukup optimal untuk bisa mengcrack dalam 1 jam karena banyak hal kecil yang lupa dilakukan oleh programmer (lupa menginisialisasi IV, memakai EBC). Saya pernah menemukan bug fatal yang berhubungan dengan menggunakan algoritma hashing yang tidak aman (tidak memakai Cryptographic Hash Function). Dengan tools yang tersedia saja, tanpa pengetahuan programming (dan kriptografi), kedua bug itu tidak akan bisa ditemukan.

Jadi saran saya: untuk mereka yang tertarik di bidang security, pelajari juga bagian programmingnya. Ini saya masih bicara level pentesting yang kebutuhkan programmingnya masih minimal. Di bagian lagin security, keahlian programming lebih dibutuhkan lagi, misalnya membuat exploit butuh pemahaman yang lebih mendalam tentang sistem dan low level programming.

Dalam full security audit, selain pentesting kadang client menginginkan juga review kode mereka, jadi kita perlu bisa membaca, menjalankan, dan memberi saran yang berkaitan dengan security.

Buat saya sendiri sebagai programmer, saya jadi rajin membaca mengenai best security practice untuk berbagai bahasa dan framework. Jika ada yang bertanya: buat apa sih belajar bahasa X atau teknologi Y, saya bisa bilang: mungkin suatu saat ada sistem yang memakai teknologi itu yang perlu saya test.

Jangan Fanatik Teknologi Tertentu

Setiap waktu, selalu ada orang yang fanatik menggunakan teknologi tertentu untuk menyelesaikan semua masalah. Karena ini blog “Cinta Programming”, pembahasannya tentunya adalah teknologi pemrograman. Lebih spesifiknya lagi: bahasa tertentu. Sekarang ini aplikasi web sedang sangat populer, dan orang-orang pun ingin memanfaatkan 100% teknologi HTML dan JavaScript untuk melakukan semua hal. Bahkan bukan cuma aplikasi web, tapi aplikasi desktop dan mobile.

Screenshot_2016-06-07-19-19-09

Saya hanya ingin menunjukkan bahwa kadang tidak semuanya bisa dilakukan oleh satu teknologi seratus persen. Seperti Anda lihat di posting-posting sebelumnya, saya sudah membuat beberapa aplikasi dengan HTML5 (dan bahkan sudah ada yang saya jual), tapi meski demikian saya masih sering “gemes” dengan orang yang terpaku pada HTML dan JS dan menganggap itu teknologi terbaik untuk semua hal (termasuk juga aplikasi mobile).

Teknologi sering harus dicampur-campur untuk membuat aplikasi terbaik. Twitter dua tahun yang lalu (2010) yakin bahwa rendering di sisi client dengan JavaScript adalah cara terbaik. Baru-baru ini disadari bahwa HTML5 masih terlalu lambat dan Twitter kembali melakukan rendering di sisi server. Twitter tentunya tidak membuang 100% Javascript, tapi sebagian besar pemrosesan dipindahkan ke server.

Di sisi mobile, Facebook membuat aplikasi untuk iOS (iPhone/iPad) menggunakan HTML5 sejak bertahun-tahun yang lalu. Semua orang komplain: aplikasinya terlalu lambat. Akhirnya Facebook menulis ulang aplikasinya dalam native code (dalam kasus ini native adalah Objective C) supaya dua kali lebih cepat. Tapi Facebook juga tidak membuang HTML5 seluruhnya, sebagian aplikasinya masih memakai HTML5.

Pelajaran yang bisa dipetik dari dua hal tersebut adalah: belajarlah banyak teknologi, supaya bisa tahu teknologi yang terbaik, dan jika memang ada yang lebih baik: jangan takut berpindah dari satu teknologi ke teknologi yang lain. Sekali lagi: jangan terpaku pada satu teknologi saja:

if all you have is a hammer, everything looks like a nail

Manipulasi bit bagian 3: bit shift

Di bagian ini saya akan menunjukkan apa artinya menggeser bit ke kiri dan ke kanan. Waktu saya belajar konversi biner, saya mendapati bahwa cara paling mudah bagi saya dalam konversi desimal dan biner adalah dengan membayangkan “tabel” seperti ini:

128 64 32 16 8 4 2 1

Misalnya saya ingin mengkonversi 1001 menjadi desimal, maka saya letakkan angka 1001 tersebut di bawah tabel:

128 64 32 16 8 4 2 1
        1 0 0 1

Kita lihat bit mana saja yang nilainya satu, lalu lihat angka di atas, dalam hal ini 8 dan 1, jika dijumlahkan, hasilnya adalah 9. Jadi 1001 biner = 9 desimal.

Dengan melihat tabel seperti itu, bisa kita bayangkan apa yang terjadi jika bit-bit biner semua digeser ke kiri satu posisi. Karena di geser satu posisi, maka kita menambahkan satu digit 0 di sebelah kanan, jadi 10010.

128 64 32 16 8 4 2 1
      1 0 0 1 0

Nilainya sekarang menjadi 16 + 2 = 18 atau dua kali 9. Cara lain untuk memahami ini adalah dalam desimal (basis 10), jika kita menambahkan 0 di kanan maka nilainya menjadi 10 kali lipat, misalnya 18 menjadi 180 menjadi 1800. Demikian juga dalam biner, jika kita menggeser bit ke kiri, artinya kita menambahkan digit 0 di kanan, dan nilainya menjadi dua kalinya.

Proses di atas bisa diulangi berkali-kali. Dan proses tersebut tentunya bisa dibalik, menggeser ke kanan, dalam desimal, jika kita potong satu digit terakhir, maka nilainya akan menjadi sepersepuluhnya, misalnya 180 menjadi 18. Atau tepatnya, sepersepuluh dengan pembulatan ke bawah, karena jika kita buang angka 5 dari 185 hasilnya adalah 18. Dalam notasi biner, jika kita geser ke kanan, maka nilainya akan menjadi setengahnya (juga dengan pembulatan ke bawah).

Jadi apa kegunaan bit shifting? Salah satu kegunaannya adalah untuk perkalian dan pembagian dengan dua. Kegunaan lainnya adalah untuk ekstraksi bit (mengambil bagian bit tertentu). Mengenai ekstraksi bit, akan saya bahas di bagian berikutnya.

Manipulasi bit bagian 1: representasi biner

Kemarin buku yang saya tunggu-tunggu akhirnya tiba juga: The Art of Computer Programming, Volumes 1-4A Boxed Set (Box Set). Dulu waktu kuliah, saya sudah membaca sebagian buku ini, tapi buku ini berat di matematika, jadi kebanyakan saya hanya membaca bagian algoritmanya saja, sambil mencoba-coba sedikit latihannya. Terinspirasi dari banyak programmer di buku Coders at work yang menyukai buku ini, dan minimal memakai buku ini sebagai referensi, maka saya sekarang berusaha membaca ulang koleksi buku Knuth, plus membaca volume yang baru: Volume 4a.

Sebagai catatan: dalam wawancara Knuth sendiri tidak menyarankan seseorang membaca buku ini dari sampul ke sampul sampai habis. Saya membaca buku ini pertama skimming dulu, mencari topik menarik, setelah itu baru saya baca teliti bagian-bagian yang saya perlukan. Jadi jika Anda merasa berat membaca buku ini: coba langsung skip ke bagian yang menarik.
Continue reading

Mengenal berbagai bahasa

Anda mungkin sering membaca di berbagai buku, kalimat-kalimat semacam ini: “Prolog itu bagus untuk Artificial Intelligence”, “LISP bagus untuk AI”, “Fortran bagus untuk aplikasi numerik”, “C bagus untuk pemrograman sistem”. Tapi biasanya penulisnya tidak mendeskripsikan lebih lanjut apa maksud dari kalimat-kalimat tersebut, dan apa contohnya. Bukankah semua bahasa yang turing complete itu sama saja?. Setelah lulus dari kuliah pun, banyak yang masih belum tahu: apakah pernyataan-pernyataan tersebut benar? kalau salah, yang benar seperti apa? apa contoh aplikasi di mana memprogram dengan Lisp/Prolog/Fortan akan lebih mudah atau lebih baik dari memprogram dengan bahasa lain?

Screenshot_2016-06-11-11-19-27

Ketika orang diperkenalkan pada suatu bahasa, kebanyakan caranya adalah melalui pengenalan sintaks, lalu kemudian membuat aplikasi kecil dalam bahasa tersebut. Jika Anda belajar bahasa yang paradigmanya sama atau mirip, cara ini memang akan berhasil, tapi tidak jika paradigmanya berbeda. Saya contohkan sedikit mengenai Lisp. Kebanyakan orang akan belajar mengenai apa itu atom, apa itu list, lalu operasi terhadap atom dan list (car, cdr, cons, list, dsb). Setelah itu kebanyakan akan membuat program manipulasi list sederhana. Di titik ini sebagian orang akan bertanya: di mana bagusnya Lisp, bagian mana yang membuat ini cocok untuk aplikasi AI?
Continue reading

Tools untuk debugging

Di posting ini saya sekedar ingin sharing metode dan tools yang biasa saya pakai untuk debugging program. Di lingkungan yang ideal, kita seharusnya punya debugger yang canggih, cepat dan reliable, tapi sayangnya sering kali ini tidak tersedia. Ini contoh beberapa lingkungan pengembangan yang “mengesalkan” yang pernah saya jumpai:

  • Symbian versi awal (versi 6): untuk bisa mendebug, perlu IDE khusus dengan license khusus yang mahal. Symbian versi terbaru sudah semakin baik (tapi sekarang ini Symbian sudah akan berakhir).
  • BlackBerry: startup time debugger sangat lama, dan debugger sering error. Setiap kali menginstall versi program baru di BlackBerry, perlu restart (butuh sekitar 5 menit tiap kali restart).
  • Mendebug aplikasi Android di emulator. Startup emulator sangat lambat (bahkan di processor AMD quad core 3 ghz memori 16 GB) , lebih cepat mencoba langsung di HP. Masalahnya adalah ketika ada laporan: ada bug kecil di Android versi 2.2, tapi HP saya memakai Android 2.3
  • Debugging PHP di web hosting. Kadang ada masalah yang hanya muncul di web hosting, tapi tidak muncul ketika dicoba di server sendiri. Beberapa kemungkinan adalah karena versi PHP yang tidak sama, versi library yang tidak sama. Di server sendiri bisa mudah menginstall Xdebug, tapi tidak tersedia di tempat hosting.
  • Nurit Point Of Sales. Device ini banyak bugnya, tidak tersedia debugger, dan jika crash, akan memprint isi memori di atas kertas thermal sekitar 1/2 meter.
  • Kernel FreeBSD. Ketika porting tahap awal, sangat sulit mendebug apa yang terjadi di level CPU. Masalahnya karena saya tidak menggunakan development board yang memiliki JTAG.

Sebenarnya tidak semua lingkungan itu benar-benar mengesalkan jika kita punya uang, contohnya: jika saya punya uang beberapa ribu dollar, saya akan bisa mendebug Symbian dengan mudah. Jika saya punya beberapa HP Android berbagai versi, debugging Android juga lebih mudah. Jika saya punya development board resmi dengan JTAG, porting kernel akan lebih mudah. Jika saya hosting VPS dengan versi PHP saya sendiri, maka tidak akan ada masalah dengan PHP, dan andaikan ada masalah, saya bisa menginstall XDebug. Pada kenyataannya seringkali tidak tersedia dana untuk menyelesaikan berbagai masalah tersebut.
Continue reading