2 poin oleh GN⁺ 2024-09-15 | 1 komentar | Bagikan ke WhatsApp
  • FlowTracker adalah Java agent yang melacak proses program Java membaca, memanipulasi, dan menulis data, serta menampilkan keterkaitan output dengan input, file, jaringan, atau konstanta kode asalnya
  • Dengan mengamati program yang sedang berjalan, FlowTracker menampilkan I/O file dan jaringan, dan khususnya melacak hubungan korespondensi antara input dan output sehingga membantu memahami apa arti output program Java dan mengapa output itu dibuat
  • Dalam demo Spring PetClinic, pengguna dapat menelusuri berbagai lapisan stack perangkat lunak, mulai dari header respons HTTP, template Thymeleaf, nilai database, hingga skrip penyisipan SQL
  • Secara internal, FlowTracker menggabungkan instrumentasi bytecode saat JVM memuat kelas, hook metode JDK, analisis aliran data, pelacakan panggilan berbasis ThreadLocal, dan ClassOriginTracker untuk mempertahankan pemetaan asal-usul yang berfokus pada string, karakter, dan byte
  • Statusnya saat ini lebih dekat ke proof-of-concept daripada siap produksi; meski bekerja baik pada beberapa program contoh, FlowTracker tidak cocok untuk semua program dan overhead yang besar membuat eksekusi jauh lebih lambat

Apa yang Dilacak FlowTracker

  • FlowTracker adalah Java agent yang melacak bagaimana data dibaca, diteruskan, diubah, dan ditulis di dalam program Java
  • Tidak berhenti pada menampilkan I/O file dan jaringan saja, FlowTracker juga menunjukkan output program berasal dari input apa
  • Tujuannya adalah membantu memahami apa makna output program Java, dan mengapa program menuliskan output tersebut
  • Saat ini proyek ini merupakan proof-of-concept untuk mengeksplorasi wawasan apa yang bisa diperoleh ketika perilaku program dilihat dari sudut pandang ini

Demo: Melacak Asal Respons HTTP di Spring PetClinic

  • Demo FlowTracker PetClinic memungkinkan Anda melihat di browser bagaimana Spring PetClinic memproses request HTTP dan menghasilkan halaman HTML berdasarkan template serta data database
  • Layar menampilkan respons HTTP yang dikirim PetClinic melalui jaringan; jika Anda mengklik sebagian isi body respons, tampilan di bagian bawah akan menunjukkan dari mana bagian tersebut berasal
  • Di tree sebelah kiri, atau tombol kiri bawah pada perangkat mobile, Anda dapat memilih input/asalan atau output/sink yang telah dilacak
  • Lapisan Pemrosesan HTTP

    • Jika mengklik "HTTP/1.1" atau header HTTP, Anda dapat melihat bahwa bagian respons ini dibuat oleh kelas Apache Coyote dalam paket org.apache.coyote
    • FlowTracker menampilkan kode mana yang menghasilkan output tertentu
  • Lapisan Template Thymeleaf

    • Jika mengklik nama tag HTML seperti "html" atau "head", Anda dapat melihat bahwa bagian HTML tersebut berasal dari file layout.html
    • Setelah mengklik layout.html, lalu menekan tombol warna + di bagian bawah, semua bagian yang berasal dari file tersebut akan ditampilkan dengan warna yang sama
    • Jika menggulir ke bawah, Anda dapat melihat bahwa sebagian respons berasal dari file lain, yaitu ownerDetails.html
    • Jika mengklik karakter < atau >, Anda dapat melihat bahwa karakter tersebut ditulis oleh library template Thymeleaf
  • Asal Nilai Database

    • Tabel pada halaman HTML berisi informasi yang berasal dari database
    • Jika mengklik George pada tabel, pelacakan tidak hanya menunjukkan bahwa nilai itu berasal dari database, tetapi juga sampai ke skrip SQL yang pertama kali menyisipkan nilai ke database
    • Alasan pelacakan dalam demo ini bisa sampai ke skrip SQL adalah karena database in-memory digunakan, sehingga isi database tidak keluar dari JVM

Demo MySQL dan Independensi Framework

  • Jika demo PetClinic yang sama dijalankan dengan database MySQL, nilai dilacak hingga titik koneksi database
  • Dalam kasus ini, Anda dapat melihat kueri SQL yang sebelumnya dikirim untuk membuat nilai tersebut serta detail bagaimana driver MySQL JDBC berkomunikasi dengan database
  • Demo FlowTracker PetClinic mysql juga menunjukkan bahwa FlowTracker mencegat konten yang telah didekripsi dan dikirim melalui koneksi SSL database
  • Spring PetClinic hanyalah contoh; FlowTracker tidak bergantung pada framework atau library tertentu
  • Demo javac menunjukkan bagaimana FlowTracker membantu memahami format file class yang dihasilkan dan bytecode di dalamnya dengan mengamati compiler Java

Cara Menggunakan dan Hal yang Perlu Diperhatikan

  • Saat ini FlowTracker lebih dekat ke proof-of-concept daripada siap produksi
  • FlowTracker bekerja baik pada beberapa program contoh, tetapi tidak dijamin bekerja baik di semua program
  • Karena menambahkan banyak overhead, eksekusi program menjadi jauh lebih lambat
  • Langkah penggunaan:
    • Unduh agent jar flowtracker-*.jar dari halaman rilis Github
    • Tambahkan -javaagent:path/to/flowtracker.jar ke command line Java
    • Tambahkan juga output java -jar flowtracker.jar jvmopts ke command line untuk mematikan beberapa optimasi JVM yang mengganggu FlowTracker
    • Secara default, FlowTracker memulai web server pada port 8011, jadi buka http://localhost:8011/ di browser
  • Opsi konfigurasi yang lebih detail tersedia di USAGE.md

Cara Kerja Internal: Instrumentasi Bytecode dan Model Tracker

  • FlowTracker adalah agent instrumentasi yang menyuntikkan kode ke file class, yaitu bytecode, saat JVM memuat kelas
  • Kode yang disuntikkan mempertahankan pemetaan antara data di memori dan asal-usulnya saat program membaca, meneruskan, dan menulis data
  • Fokus target pelacakan adalah data teks dan biner seperti String, char, byte[], bukan terutama angka, data terstruktur, atau data hasil perhitungan
  • Cara yang digunakan:
    • Mengganti beberapa pemanggilan metode JDK dengan pemanggilan metode versi FlowTracker
    • Menyuntikkan kode ke lokasi inti JDK untuk melacak input dan output
    • Melakukan analisis aliran data dan instrumentasi yang lebih dalam untuk melacak variabel lokal dan nilai stack di dalam metode
    • Menambahkan kode sebelum dan sesudah pemanggilan metode serta pada awal dan akhir metode yang dipanggil untuk melacak argumen dan nilai return dengan ThreadLocal
  • Model Data Tracker

    • Tracker: menyimpan isi objek yang dilacak dan informasi asal-usulnya
    • content: data seperti semua byte yang melewati InputStream atau OutputStream
    • source: menghubungkan rentang tertentu dari suatu konten dengan rentang tertentu pada tracker lain
    • TrackerRepository: menyimpan Map<Object, Tracker> global besar yang menghubungkan objek yang diminati dengan Tracker terkait
    • TrackerPoint: menunjuk satu posisi di dalam tracker dan merepresentasikan satu nilai primitive, seperti asal satu byte

Instrumentasi dasar: hook JDK dan ASM

  • FlowTracker menjaga Tracker tetap mutakhir dengan menyisipkan pemanggilan metode hook ketika metode JDK tertentu dipanggil
  • Contoh paling sederhana adalah System.arraycopy
    • Pemanggilan java.lang.System.arraycopy diganti dengan pemanggilan com.coekie.flowtracker.hook.SystemHook.arraycopy
    • SystemHook memanggil arraycopy yang sebenarnya, lalu mengambil tracker array sumber dan tujuan dari TrackerRepository dan memperbaruinya agar tracker tujuan menunjuk ke sumber
  • Untuk instrumentasi seperti ini, digunakan library manipulasi bytecode ASM
  • Sebagian besar hook ditambahkan di sisi yang dipanggil, di dalam metode JDK, bukan di sisi pemanggil
    • Misalnya, pemanggilan FileInputStreamHook.afterReadByteArray ditambahkan di akhir FileInputStream.read(byte[])
    • Instrumentasi ini diimplementasikan sebagai mikro-framework internal berbasis annotation yang menggunakan AdviceAdapter milik ASM
  • FlowTracker menambahkan hook ke kelas-kelas terkait I/O di JDK seperti java.io.FileInputStream, java.io.FileOutputStream, sun.nio.ch.FileChannelImpl, sun.nio.ch.IOUtil, dan sun.nio.ch.NioSocketImpl
  • Implementasi terkait:

Pelacakan nilai primitive dan analisis aliran data di dalam metode

  • Nilai primitive seperti byte tidak memiliki identity seperti objek, sehingga tidak dapat dilacak dengan aman sebagai key Map di TrackerRepository
  • FlowTracker menulis ulang kode agar asal-usul nilai primitive disimpan secara terpisah pada variabel lokal di dalam metode
  • Misalnya, setelah byte b = x[1], tracker untuk b diperoleh dengan ArrayHook.getElementTracker(x, 1), lalu saat y[2] = b, asal-usulnya dicatat pada array tujuan dengan ArrayHook.setElementTracker(y, 2, bTracker)
  • Untuk itu, FlowTracker melakukan interpretasi simbolik (symbolic interpretation) di atas fitur analisis ASM
  • Pada setiap titik dalam metode, ia memodelkan dari mana nilai pada variabel lokal dan stack berasal serta ke mana nilai tersebut mengalir
  • Implementasi terkait:
  • Tidak semua nilai primitive dilacak; fokusnya adalah byte dan char, sementara int dan long ditangani secara lebih terbatas

Aliran data yang melintasi pemanggilan metode

  • Analisis di dalam metode saja tidak dapat menangani situasi ketika nilai primitive mengalir sebagai argumen dan nilai kembalian metode lain
  • FlowTracker menyimpan PointTracker untuk argumen dan nilai kembalian di Invocation, lalu memasukkannya ke ThreadLocal tepat sebelum pemanggilan metode
  • Pada titik awal metode yang dipanggil, informasi dari ThreadLocal dapat diambil dengan Invocation.start(...) untuk menggunakan asal-usul argumen primitive
  • Dengan cara ini, bahkan ketika nilai primitive diteruskan ke metode seperti out.write(b), tracker untuk value dapat diwarisi di dalam write(byte value)
  • Implementasi terkait:

Memperlakukan kode itu sendiri sebagai sumber data

  • Sumber utama yang dilacak FlowTracker adalah nilai yang berasal dari I/O dan dari kode itu sendiri
  • Nilai yang berasal dari kode mencakup konstanta primitive dan String seperti 'a' dan "abc"
  • Untuk konstanta seperti ini, dibuat ClassOriginTracker per kelas, lalu disimpan representasi teks dari referensi kelas dan konstanta tersebut
  • Saat sebuah konstanta direferensikan, tracker untuk nilai tersebut akan menunjuk ke posisi di dalam representasi teks ini
  • Karena model ini memperlakukan konstanta seolah-olah dibaca dari representasi teks kode, ia menjadi mirip dengan model pelacakan I/O
  • Demi performa, digunakan ConstantDynamic (JEP 309) agar metode constantPoint tidak dipanggil setiap kali metode dijalankan
  • Implementasi terkait:

Penanganan string literal dan batasannya

  • String literal membuat salinan String baru, lalu menghubungkan byte[] di dalam String.value dengan ClassOriginTracker
  • Pernyataan seperti String s = "abc"; ditulis ulang menjadi bentuk seperti String s = StringHook.constantString("abc", 1234, 81);
  • Cara ini merusak jaminan String interning yang biasanya disediakan JVM
    • Semula, semua kemunculan konstanta String yang sama harus mereferensikan instance yang sama
    • Setelah instrumentation, kode yang bergantung pada jaminan ini bisa rusak
  • FlowTracker menyediakan beberapa mekanisme untuk mengurangi masalah ini
    • Menggunakan ConstantDynamic agar string literal yang sama pada baris yang sama tetap mengembalikan instance yang sama meski dieksekusi berkali-kali
    • Menulis ulang sebagian ekspresi stringA == stringB menjadi Objects.equals(stringA, stringB), sehingga dari sudut pandang tertentu terlihat seperti instance yang sama
    • Menonaktifkan pelacakan string literal di beberapa paket seperti java.lang.*
    • Perilaku ini dapat dikonfigurasi dengan breakStringInterning di USAGE.md
  • Implementasi terkait:

Fallback untuk nilai yang tidak dilacak

  • FlowTracker tidak melacak semua nilai dalam program
  • Alasannya adalah kekhawatiran performa, bagian yang belum diimplementasikan, nilai yang kurang relevan, serta kebutuhan model data yang lebih kompleks untuk merepresentasikan nilai yang muncul dari kombinasi beberapa sumber
  • Ketika nilai yang sebelumnya tidak dilacak mencapai lokasi yang harus mulai dilacak, nilai itu dihubungkan ke ClassOriginTracker seperti konstanta, dan posisinya direpresentasikan sebagai "<?>"
  • Misalnya, panjang array tidak dilacak, sehingga ketika write(array.length) dipanggil, Invocation menerima PointTracker yang menunjuk ke lokasi kode tempat pemanggilan write
  • Akibatnya, meskipun pada keluaran dalam format biner sumber aslinya tidak terlihat, dalam sebagian kasus makna nilai dapat ditafsirkan dengan cepat melalui string terlacak di sekitarnya dan lokasi kode

Topik implementasi yang bisa dibahas lebih lanjut

  • MergedValue menangani pelacakan nilai yang melewati percabangan dan loop, dan dianggap sebagai salah satu bagian tersulit dalam analisis aliran data
  • String concatenation ditangani melalui indification (JEP 280) dengan menambahkan hook pada MethodHandle yang dikembalikan oleh StringConcatFactory
  • Menemukan kode sumber, melakukan dekompilasi dengan Vineflower, serta menghubungkan bytecode dengan baris sumber juga termasuk dalam implementasi
  • Konfigurasi ClassLoader berfokus pada menghindari dependensi bootclasspath dan konflik aplikasi, sekaligus mempertahankan siklus pengembangan cepat tanpa shading maupun nested jar
  • Pelacakan nilai primitive yang disimpan di field juga termasuk dalam implementasi
  • Frontend terdiri dari server web berbasis Jetty dan JAX-RS, serta UI web berbasis Svelte

1 komentar

 
GN⁺ 2024-09-15
Komentar Hacker News
  • Keren. Saya membuat FlowStorm, alat untuk Clojure, dengan arah yang sama http://www.flow-storm.org/
    Untuk instrumentasi, alih-alih agen instrumentasi, saya memakai fork dari compiler resmi Clojure, dan memanfaatkan karakteristik Clojure yang memungkinkan compiler diganti dengan mudah saat pengembangan untuk menyisipkan bytecode tambahan
    Hal menarik dari rekaman eksekusi program Clojure adalah sebagian besar nilainya immutable, jadi snapshot bisa dibuat cukup dengan mempertahankan pointer
    Karena demo di tulisan asli menelusuri aplikasi web, saya juga meninggalkan demo debugging aplikasi web dengan FlowStorm bagi yang tertarik https://www.youtube.com/watch?v=h8AFpZkAwPo
    • Benar-benar keren. Penasaran kenapa memilih JavaFX. Setelah memilih JavaFX, apakah juga sempat melihat cljfx?
    • Bagus. Penasaran apakah Anda juga menyukai pendekatan memakai metadata struktur data untuk pelacakan nilai
  • Benar-benar hebat
    Senang melihat alat-alat di ekosistem Java/JVM yang bagus. Terakhir kali saya sekaget ini adalah saat dulu melihat jitwatch https://github.com/AdoptOpenJDK/jitwatch
    FlowTracker sedikit mengingatkan saya pada analisis taint, yang melacak bagaimana input pengguna yang belum tervalidasi atau nilai rahasia mengalir di dalam program agar tidak bocor atau dipakai tanpa validasi
    Kata kunci pencariannya adalah “dynamic taint tracking/analysis”
    https://github.com/gmu-swe/phosphor
    https://github.com/soot-oss/SootUp
    https://github.com/feliam/klee-taint
  • Demo yang menelusuri elemen HTML mundur sampai ke pernyataan SQL yang menambahkan nilainya ke database sangat mengesankan
    Ke depannya, mudah membayangkan alat seperti ini menjadi garis pertahanan pertama saat melacak bug
    • Terima kasih
      Saat mengembangkan FlowTracker, banyak pekerjaan muncul dari upaya membuat pelacakan berfungsi pada program contoh tertentu
      Saya tahu hasil yang ingin dicapai, tetapi sulit memprediksi mekanisme level rendah apa yang harus didukung agar contoh tertentu bisa berjalan, dan itu sering bergantung pada detail implementasi internal JDK atau library yang dilalui data
      Namun keterhubungan elemen HTML ke skrip SQL yang memasukkan data itu ke DB tidak terjadi dengan cara seperti itu
      Itu bukan sesuatu yang saya harapkan atau sengaja buat; memang terjadi begitu saja, jadi saya sendiri cukup terkejut dan jadi menantikan apa lagi yang bisa dilakukan dengan pendekatan ini
    • Kalau dipikir-pikir, andaikan ada cara standar untuk melacak asal-usul dan kebenaran data, rasanya sangat banyak masalah bisa dicegah, dan banyak aturan bisnis juga bisa diekspresikan dengan lebih mudah
      Akan bagus juga kalau ada cara melacak apakah data bersifat sementara atau harus ditulis kembali
      Semakin banyak constraint seperti ini bisa dideskripsikan di depan, semakin baik
  • Saya belum sepenuhnya memahami gambaran besarnya atau cara pemanfaatannya, tetapi ini mengingatkan saya pada lingkungan Smalltalk tempat semuanya bisa diinspeksi
    Di Smalltalk, semuanya adalah objek dan pesan, sehingga bisa ditelusuri balik dan diinteraksikan
  • Sangat keren. Video demonya juga bagus, dan jelas terlihat berguna saat menyelami codebase yang belum dikenal
  • Beberapa tahun lalu saya pernah bereksperimen dengan konsep serupa[1]. Saya ingin menerapkan sesuatu seperti source map JavaScript ke HTML
    Saya tidak sempat meluangkan waktu untuk mengembangkannya lebih jauh, tetapi alat pengembangan web sepertinya akan sangat diuntungkan oleh pelacakan atribusi full-stack semacam ini
    Namun mengintegrasikan solusi seperti ini ke framework yang ada terasa sebagai tantangan besar
    [1] HTML Source Maps - https://github.com/connorjclark/html-source-maps https://docs.google.com/document/d/19XYWiPL9h9vA6QcOrGV9Nfkr...
  • Dalam arti yang baik, ini mengingatkan saya pada demo Eve-lang yang saat debugging program cukup bertanya, “kenapa ini tidak ada di sini?”. Kerja yang luar biasa
    https://www.youtube.com/watch?v=TWAMr72VaaU&t=164s dan https://witheve.com/
  • Kalau ingatan saya benar, ada paper tentang alat serupa yang secara dinamis menemukan SQL injection di program Java. Apakah ini alat yang sama?
    • Bukan, kemungkinan itu alat lain
      Jika apa yang dilakukan FlowTracker diperluas, ia juga bisa menemukan SQL atau kerentanan injection lainnya. Jadi ada kemungkinan alat yang Anda ingat memakai pendekatan yang mirip
  • Dulu saya pernah membayangkan melacak data melintasi internet. Misalnya dari mana sebuah gambar berasal dan pernah berada di CDN mana
    Atau pertanyaan seperti “apa saja yang dilihat string ini sejak saat dibuat hingga tiba di layar saya”
    Ini terlihat seperti satu langkah ke arah itu
  • Saya sedang mencoba menjalankan alat ini di VSCode, bersama proyek yang ingin saya pahami
    Untuk saat ini saya harus berhenti dulu, tetapi saya menantikan bisa membuatnya berjalan dan mengutak-atiknya