4
Dekomplikasi dan jadx
APKLeaks tidak membaca classes.dex secara langsung. Seluruh isi APK dibongkar lebih dulu oleh jadx ke sebuah direktori sementara, lalu mesin pemindaian pola membaca berkas hasil disassembly itu. README.md menyebut ketergantungan ini secara eksplisit: alat ini memakai disassembler jadx dan menawarkan unduhan bila jadx tidak ada di sistem. README.md:531
Siklus dekompilasi
cli.main memanggil tiga metode berurutan di dalam satu blok try, dan cleanup() selalu dijalankan lewat finally. Urutannya integrity(), decompile(), lalu scanning(). apkleaks/cli.py:36-412
sequenceDiagram
autonumber
participant CLI as cli.main
participant A as APKLeaks
participant FS as filesystem
participant GH as GitHub release zip
participant SH as shell
CLI->>A: init.integrity()
A->>FS: os.path.exists(self.jadx)
alt jadx tidak ditemukan
A->>CLI: tulis prompt Do you want to download jadx? (Y/n)
CLI-->>A: input()
A->>A: dependencies()
A->>GH: urlopen(exter)
GH-->>A: read()
A->>FS: ZipFile.extractall ke folder jadx di akar repository
A->>FS: os.chmod(self.jadx, 33268)
end
CLI->>A: init.decompile()
A->>SH: os.system(comm)
SH->>FS: tulis hasil disassembly ke self.tempdir
CLI->>A: init.scanning()
A->>FS: util.finder(pattern, self.tempdir)
CLI->>A: init.cleanup()
A->>FS: shutil.rmtree(self.tempdir)
%% fig: Siklus dekompilasi APK dengan jadx
%% desc: integrity memastikan biner jadx tersedia, decompile menjalankannya lewat shell ke self.tempdir, lalu scanning membaca direktori itu sebelum cleanup menghapusnya.Direktori tujuan dekompilasi adalah self.tempdir, hasil tempfile.mkdtemp(prefix="apkleaks-") dengan prefiks apkleaks-. Direktori itu dibuat di __init__ bersama self.jadx, sehingga satu objek APKLeaks memiliki tepat satu direktori kerja untuk satu berkas APK. apkleaks/apkleaks.py:31-363 Setelah pemindaian selesai, cleanup() menghapus direktori tersebut dengan shutil.rmtree(self.tempdir), jadi salinan hasil dekompilasi tidak tertinggal di disk. apkleaks/apkleaks.py:140-1414 Rincian pemakaian direktori itu saat pencocokan pola ada di Mesin Pemindaian Pola.
Resolusi biner jadx
self.jadx diisi sekali saat objek dibuat, tanpa memanggil jaringan. Nilainya ditentukan oleh urutan berikut. apkleaks/apkleaks.py:31-363
| Urutan | Ekspresi | Perilaku |
|---|---|---|
| 1 | which("jadx") | Memakai biner yang ditemukan di PATH, termasuk symlink yang dibuat image Docker |
| 2 | <akar repository>/jadx/bin/jadx | Fallback bila which mengembalikan None |
| 3 | sufiks .bat saat os.name == "nt" | Menyesuaikan nama berkas di Windows |
| 4 | .replace("\\", "/") | Menormalkan backslash menjadi garis miring |
Folder jadx pada urutan kedua berada di akar repository, yaitu parent dari direktori paket apkleaks. Folder yang sama menjadi target ekstraksi unduhan otomatis, sehingga biner hasil unduhan langsung terbaca oleh fallback tersebut. apkleaks/apkleaks.py:44-535
Unduhan otomatis jadx 1.2.0
Pemeriksaan keberadaan biner dilakukan integrity() dengan os.path.exists(self.jadx). Bila berkas tidak ada, APKLeaks mencetak "Can't find jadx binary." lalu meminta konfirmasi interaktif. apkleaks/apkleaks.py:55-776
| Tahap | Nilai di kode |
|---|---|
| Prompt | "Do you want to download jadx? (Y/n) " |
| Jawaban yang diterima | yes, y, ye, no, n, atau kosong |
| Jawaban kosong | Dianggap y sehingga unduhan dilanjutkan |
| Jawaban tidak dikenal | "\nPlease respond with 'yes' or 'no' (or 'y' or 'n')." lalu prompt diulang |
Ctrl-C saat prompt | "\n** Interrupted. Aborting." lalu sys.exit |
Jawaban no / n | "\n** Aborted." lalu sys.exit |
Ketika jawabannya ya, integrity() memanggil dependencies(). Metode itu mengunduh https://github.com/skylot/jadx/releases/download/v1.2.0/jadx-1.2.0.zip dengan urlopen, membaca isinya sebagai ZipFile, mengekstrak seluruh arsip ke folder jadx di akar repository, lalu menjalankan os.chmod(self.jadx, 33268) agar biner dapat dieksekusi. apkleaks/apkleaks.py:44-535
⚠️ Kegagalan unduh tidak diberi
retry.dependencies()menangkapException, mencetak pesan galat apa adanya lewatutil.writeln(str(error), col.WARNING), lalu langsung memanggilsys.exit(). Setelah itu tidak ada pemindaian yang dijalankan. apkleaks/apkleaks.py:51-537
Penyusunan argumen -a
Opsi -a/--args dideklarasikan sebagai --args dengan type=str dan tanpa required, sehingga nilainya bisa None saat pengguna tidak memberi argumen tambahan. apkleaks/cli.py:278 decompile() menyusun satu string perintah dari nilai itu dengan lima langkah berurutan. apkleaks/apkleaks.py:89-989
graph LR
A["self.disarg dari -a"] --> B["re.split dengan pola spasi atau tanda sama dengan"]
D["args awal: jadx, file, -d, tempdir"] --> C["args.extend"]
B --> C
C --> E["quote per argumen"]
E --> F["join dengan spasi"]
F --> G["replace kutip tunggal menjadi kutip ganda"]
G --> H["os.system"]
H --> I["biner jadx menulis ke self.tempdir"]
style A fill:#f59e0b,color:#000
style D fill:#336791,color:#fff
style B fill:#8b5cf6,color:#fff
style C fill:#8b5cf6,color:#fff
style E fill:#8b5cf6,color:#fff
style H fill:#dc2626,color:#fff
style I fill:#2d7d46,color:#fff
%% fig: Penyusunan perintah jadx dari opsi -a
%% desc: Argumen default jadx, file apk, dan tempdir digabung dengan token hasil pemecahan self.disarg, di-quote, lalu dijalankan lewat os.system sehingga hasilnya mendarat di self.tempdir.| Langkah | Ekspresi di decompile() | Keterangan |
|---|---|---|
| 1 | args = [self.jadx, self.file, "-d", self.tempdir] | Tiga bagian tetap: biner, berkas APK, dan direktori tujuan |
| 2 | `args.extend(re.split(r"\s | =", self.disarg))` |
| 3 | " ".join(quote(arg) for arg in args) | Setiap token di-quote lewat shlex.quote sebelum digabung |
| 4 | comm.replace("'", "\"") | Kutip tunggal hasil quote diganti kutip ganda |
| 5 | os.system(comm) | Perintah dijalankan oleh shell, bukan subprocess |
Langkah 2 dibungkus try dengan except Exception: pass. Karena self.disarg bernilai None ketika -a tidak dipakai, re.split melempar TypeError yang ditelan blok itu, dan perintah berjalan hanya dengan argumen default. apkleaks/apkleaks.py:92-9510
⚠️ Argumen pengguna ditambahkan setelah argumen default, sehingga
-d self.tempdirbisa bertabrakan bila pengguna mengirim-dsendiri.README.mdmemperingatkan hal ini: "Please pay attention to the default disassembler arguments we use to prevent collisions." README.md:106-11511 Perintah juga dijalankan lewatos.system, jadi isi-amelewati shell mesin pengguna. apkleaks/apkleaks.py:96-9812
jadx di dalam image Docker
Image Docker tidak mengandalkan unduhan saat runtime. Dockerfile memasang openjdk-17-jre-headless sebagai runtime Java, mengunduh jadx-1.2.0.zip ke /tmp/jadx.zip, mengekstraknya ke /opt/jadx, lalu membuat symlink /usr/local/bin/jadx menuju /opt/jadx/bin/jadx. Dockerfile:7-16 Berkat symlink itu, which("jadx") berhasil pada urutan pertama resolusi dan prompt unduhan tidak pernah muncul di dalam container. Proses build image dijelaskan di Build dan Rilis, sedangkan pemasangan di luar Docker ada di Instalasi dan Ketergantungan.