Deni.dev Logo
20-07-2026
6 mnt baca

Menguak Keajaiban Frida Stalker: Menganalisis Eksekusi Instruksi Native via Dynamic Binary Translation

#Frida#Reverse Engineering#Dynamic Instrumentation#Software Architecture#Debugging

Menguak Keajaiban Frida Stalker: Menganalisis Eksekusi Instruksi Native via Dynamic Binary Translation

Dalam lanskap rekayasa perangkat lunak dan analisis dinamik, Frida telah berkembang jauh melampaui sekadar alat hooking fungsi sederhana. Mayoritas pengembang dan peneliti keamanan mengenal Frida melalui modul Interceptor, yang memungkinkan manipulasi parameter dan nilai balik fungsi pada tingkat API (misalnya fungsi Interceptor.attach). Namun, ketika kebutuhan berkembang ke titik di mana kita perlu memahami bagaimana sebuah fungsi dijalankan per instruksi CPU tanpa mengubah binary aslinya, modul Stalker menjadi komponen yang sangat vital.

Artikel ini membedah arsitektur Frida Stalker, mekanisme kerja Dynamic Binary Translation (DBT) di dalamnya, serta bagaimana Anda dapat memanfaatkannya untuk pemantauan performa, pelacakan alur eksekusi (code coverage), dan rekonstruksi perilaku program secara mendalam.


1. Perbedaan Mendasar: Interceptor vs. Stalker

Sebelum menyelami mekanisme Stalker, penting untuk memahami batasan dari modul Interceptor standar:

  • Interceptor: Bekerja dengan cara memodifikasi header fungsi target di memori (biasanya menimpa instruksi pertama dengan instruksi lompatan/trampoline). Ketika fungsi dipanggil, eksekusi dilompahkan ke skrip Frida, kemudian dikembalikan ke instruksi asli. Teknik ini sangat efisien untuk memantau panggilan fungsi (function calls), namun kurang ideal jika kita ingin melacak setiap instruksi individual atau cabang logika internal (conditional branches) di dalam fungsi tersebut.
  • Stalker: Mengadopsi pendekatan Dynamic Binary Translation (DBT). Stalker tidak menimpa instruksi pada fungsi asli secara langsung di alamat memori aslinya. Sebaliknya, Stalker menyalin blok instruksi native ke area memori baru, merekonstruksi instruksi tersebut di bawah kendali engine Frida, lalu mengeksekusi versi rekonstruksi tersebut.

2. Anatomi Kerja Frida Stalker

Mekanisme kerja Frida Stalker berpusat pada pemrosesan Basic Block (BB)—sekumpulan instruksi assembly linier yang dieksekusi dari awal hingga akhir tanpa adanya percabangan di tengah.

+--------------------------+
|   Fungsi Native Asli     |
+--------------------------+
             |
             v (Penyalinan & Transformasi)
+--------------------------+
|  Stalker Engine (DBT)    |
+--------------------------+
             |
             v (Eksekusi di Memory Buffer Baru)
+--------------------------+
| Code Cache / JIT Buffer  |
+--------------------------+

Proses eksekusi Stalker berlangsung melalui tahapan berikut:

  1. Disassembly & Analysis: Stalker membaca instruksi native dari alamat memori thread target hingga menemukan instruksi penutup blok, seperti JMP, CALL, RET, atau percabangan kondisional.
  2. Instruction Transformation: Sebelum dimasukkan ke dalam Code Cache, Stalker memberi kesempatan kepada pengguna untuk menginspeksi atau memodifikasi instruksi tersebut melalui panggilan balik (callback) transform.
  3. Re-compilation & Relocation: Jika ada instruksi yang bergantung pada lokasi memori relatif (seperti RIP-relative addressing pada x86_64), Stalker secara otomatis menyesuaikan nilai offset agar tetap valid saat dieksekusi di Code Cache.
  4. Execution & Branch Redirection: Ketika instruksi cabang dieksekusi dalam Code Cache, kontrol dikembalikan ke Stalker untuk menerjemahkan blok target berikutnya jika belum ada di dalam cache.

3. Implementasi Skrip Frida Stalker

Berikut adalah contoh skrip Frida JavaScript yang mengilustrasikan cara mengaktifkan Stalker pada thread tertentu untuk mencatat eksekusi basic block secara real-time.

// Melacak eksekusi instruksi pada thread saat ini
const currentThreadId = Process.getCurrentThreadId();

Stalker.follow(currentThreadId, {
  events: {
    call: false,      // Catat panggilan fungsi
    ret: false,       // Catat pengembalian fungsi
    exec: false,      // Catat setiap instruksi (high overhead)
    block: true,      // Catat setiap transmisi basic block
    compile: false    // Catat saat basic block baru dikompilasi
  },

  // Event listener untuk memproses data yang ditangkap
  onReceive: function (events) {
    // Unpack data event dari format biner
    const parsedEvents = Stalker.parse(events, {
      annotate: true
    });

    for (const entry of parsedEvents) {
      // Structure: [type, block_start, block_end]
      const [type, start, end] = entry;
      console.log(`[Stalker] Executed Basic Block: ${start} -> ${end}`);
    }
  },

  // Transformasi tingkat instruksi
  transform: function (iterator) {
    let instruction = iterator.next();

    do {
      // Contoh: Menyisipkan pemanggilan fungsi kustom sebelum instruksi spesifik
      if (instruction.mnemonic === 'xor') {
        iterator.putCallout((context) => {
          console.log(`[+] Instruksi XOR terdeteksi pada PC: ${context.pc}`);
        });
      }

      // Pertahankan instruksi asli
      iterator.keep();
    } while ((instruction = iterator.next()) !== null);
  }
});

4. Kasus Penggunaan: Observabilitas & Profiling Performa

Penggunaan Stalker memberikan tingkat transparansi yang tinggi terhadap internal aplikasi native tanpa perlu menyusun ulang (recompile) source code. Beberapa skenario penggunaan engineering meliputi:

A. Pengukuran Code Coverage Real-time

Dengan mencatat setiap basic block yang dieksekusi selama siklus pengujian integrasi, tim QA dan developer dapat memetakan area kode native mana saja yang telah tersentuh oleh skenario pengujian, membantu mengidentifikasi dead code atau area yang belum teruji.

B. Dynamic Call Graph Construction

Stalker dapat dikonfigurasikan untuk mencatat alur transaksi panggilan fungsi (call dan ret), memungkinkan pembuatan diagram alur panggilan secara presisi pada aplikasi yang menggunakan kompleksitas function pointer atau polymorphism di C++.

C. Profiling Beban Kerja Komputasi

Melalui fungsionalitas putCallout, pengembang dapat mengukur frekuensi eksekusi instruksi spesifik (misalnya instruksi SIMD atau loop komputasi berat) guna mengidentifikasi bottleneck performa di lapisan paling bawah dari runtime aplikasi.


5. Pertimbangan Performa & Pertumbuhan Memori

Meskipun sangat ampuh, penggunaan Dynamic Binary Translation memiliki dampak performa (overhead) yang perlu diperhitungkan:

  • Overhead Memori: Penyimpanan Code Cache membutuhkan alokasi RAM tambahan. Untuk proses dengan runtime panjang, direkomendasikan untuk membatasi rentang alamat memori yang ditelusuri menggunakan pustaka pencocokan alamat atau fungsi penyaring pada callback transform.
  • Overhead Eksekusi: Melacak setiap instruksi individual (exec: true) akan memperlambat eksekusi aplikasi secara signifikan. Praktik terbaik adalah membatasi pelacakan pada tingkat Basic Block (block: true) atau menggunakan pencatatan terfilter via transform.

Kesimpulan

Frida Stalker mengubah paradigma instrumentasi dinamik dari sekadar analisis tingkat API menjadi observabilitas mendalam hingga level instruksi CPU. Dengan memanfaatkan Dynamic Binary Translation, pengembang dan peneliti perangkat lunak memperoleh alat yang sangat fleksibel untuk membedah, memprofilkan, dan memahami perilaku runtime aplikasi native secara komprehensif.

Menikmati artikel ini? Berikan apresiasi Anda!