Lompat ke konten
dokumentasi.id
Commit dd5271a
v2
Star6,4 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

2.1

Alur Eksekusi CLI

Satu request CLI masuk lewat dua pintu yang sama-sama berakhir di main(): entry point apkleaks = "apkleaks.cli:main" (pyproject.toml:16-17) dan berkas pembungkus di akar repository (apkleaks.py:1-5). main() sendiri hanya berisi lima langkah: mencetak banner, mem-parsing argumen, membangun objek APKLeaks, menjalankan tiga fase di dalam try, lalu memanggil cleanup() di blok finally (apkleaks/cli.py:32-41).

Urutan pemanggilan satu request

sequenceDiagram
    autonumber
    actor U as "shell"
    participant E as "console script apkleaks"
    participant C as "main() di cli.py"
    participant P as "argparse"
    participant A as "APKLeaks"
    participant J as "jadx"
    participant T as "thread extract()"

    U->>E: "apkleaks -f file.apk --json"
    E->>C: "main()"
    C->>C: "header(): VERSION + banner ke stderr"
    C->>P: "argument()"
    P-->>C: "Namespace(file, output, pattern, args, json)"
    C->>A: "APKLeaks(args)"
    A->>A: "mkdtemp(prefix=apkleaks-) + open(self.output)"
    Note over C,A: "blok try dimulai"
    C->>A: "integrity()"
    A->>A: "which(jadx), prompt unduh bila tidak ada"
    A->>A: "APK(self.file) menjadi self.apk"
    C->>A: "decompile()"
    A->>J: "os.system(jadx file -d tempdir)"
    J-->>A: "kode balik tidak diperiksa"
    C->>A: "scanning()"
    A->>T: "start() satu thread per pola"
    Note over C,A: "blok finally"
    C->>A: "cleanup()"
    A->>A: "rmtree(tempdir), close(fileout)"
%% fig: Urutan pemanggilan dari main() sampai cleanup()
%% desc: main() menyiapkan banner dan argumen, membangun APKLeaks, lalu menjalankan integrity, decompile, dan scanning sebelum cleanup di blok finally.
Fig. 1: Urutan pemanggilan dari main() sampai cleanup()main() menyiapkan banner dan argumen, membangun APKLeaks, lalu menjalankan integrity, decompile, dan scanning sebelum cleanup di blok finally.

header() mencetak banner ke sys.stderr, bukan ke stdout (apkleaks/cli.py:10-20). Pemisahan itu penting karena hasil pemindaian ditulis ke stdout lewat print(stdout) di extract() (apkleaks/apkleaks.py:100-115), sehingga keluaran bisa dialihkan ke berkas tanpa tercampur banner. Versi yang dicetak diambil bertingkat: importlib.metadata.version("apkleaks"), lalu pkg_resources.require("apkleaks")[0].version, dan terakhir isi berkas VERSION (apkleaks/cli.py:10-20).

argument() mengembalikan satu Namespace dari argparse: -f/--file wajib, -o/--output, -p/--pattern, dan -a/--args opsional, --json memakai action="store_true" (apkleaks/cli.py:22-30). Arti tiap flag dan bentuk --args dibahas di Opsi CLI dan Argumen Disassembler.

Keadaan yang disiapkan sebelum try

Objek APKLeaks tidak hanya menyimpan argumen. Konstruktornya sudah membuat direktori kerja sementara dan membuka berkas keluaran (apkleaks/apkleaks.py:25-39):

AtributNilai saat APKLeaks(args) dijalankanRujukan
self.fileos.path.realpath(args.file)apkleaks/apkleaks.py:27
self.tempdirtempfile.mkdtemp(prefix="apkleaks-")apkleaks/apkleaks.py:31
self.outputtempfile.mkstemp(...) bersuffix .json atau .txt, kecuali args.output diisiapkleaks/apkleaks.py:33
self.fileoutopen(self.output, "w") bila --json, selain itu "a"apkleaks/apkleaks.py:34
self.patternconfig/regexes.json milik paket, kecuali args.pattern diisiapkleaks/apkleaks.py:35
self.jadxwhich("jadx"), jika tidak ada memakai jadx/bin/jadx di dalam paketapkleaks/apkleaks.py:36
self.out_json, self.scanned{} dan Falseapkleaks/apkleaks.py:37-38

Pembuatan objek berada di luar blok try (apkleaks/cli.py:35-41). Artinya cleanup() hanya menjamin pembersihan untuk tiga fase sesudahnya, bukan untuk self.tempdir dan self.fileout yang dibuat di apkleaks/apkleaks.py:31-34.

⚠️ Bila mkstemp atau open(self.output) gagal, finally tidak pernah menyala. self.tempdir yang sudah dibuat di baris sebelumnya tertinggal di sistem berkas.

Fase dan jalur keluar

graph TD
    MAIN["main() cli.py:32"] --> HDR["header()"]
    HDR --> ARG["argument(): argparse"]
    ARG --> INIT["APKLeaks(args): mkdtemp + open(output)"]
    INIT --> INT["integrity()"]
    INT -->|"self.jadx tidak ada"| ASK{"prompt: Do you want to download jadx? (Y/n)"}
    ASK -->|"y"| DEP["dependencies()"]
    ASK -->|"n"| ABORT["sys.exit: Aborted"]
    DEP --> CHK
    INT -->|"self.jadx ada"| CHK{"os.path.isfile(self.file)"}
    CHK -->|"bukan berkas"| INVALID["sys.exit: not a valid file"]
    CHK -->|"berkas valid"| APKI["APK(self.file) menjadi self.apk"]
    APKI --> DEC["decompile(): os.system jadx"]
    DEC --> SCN["scanning(): thread per pola"]
    SCN --> CLN["finally: cleanup()"]
    ABORT --> CLN
    INVALID --> CLN
    CLN --> SCANNED{"self.scanned"}
    SCANNED -->|"True"| SAVED["tulis out_json, Results saved into ..."]
    SCANNED -->|"False"| EMPTY["os.remove(output), Done with nothing"]
    style INIT fill:#336791,color:#fff
    style CLN fill:#f59e0b,color:#000
    style ASK fill:#f59e0b,color:#000
    style CHK fill:#f59e0b,color:#000
    style SCANNED fill:#f59e0b,color:#000
    style DEP fill:#2d7d46,color:#fff
    style SAVED fill:#2d7d46,color:#fff
    style ABORT fill:#dc2626,color:#fff
    style INVALID fill:#dc2626,color:#fff
    style EMPTY fill:#dc2626,color:#fff
    style DEC fill:#8b5cf6,color:#fff
    style SCN fill:#8b5cf6,color:#fff
%% fig: Percabangan integrity(), decompile(), dan cleanup()
%% desc: integrity() dapat berhenti lewat sys.exit, sementara cleanup() selalu jalan dan memilih antara menyimpan hasil atau menghapus berkas keluaran berdasarkan self.scanned.
Fig. 2: Percabangan integrity(), decompile(), dan cleanup()integrity() dapat berhenti lewat sys.exit, sementara cleanup() selalu jalan dan memilih antara menyimpan hasil atau menghapus berkas keluaran berdasarkan self.scanned.
FaseMasukanEfek sampingRujukan
integrity()self.jadx, self.filememinta konfirmasi unduh, memanggil dependencies(), menetapkan self.apkapkleaks/apkleaks.py:55-87
dependencies()URL jadx-1.2.0.zipekstraksi ke direktori jadx/, lalu os.chmod(self.jadx, 33268)apkleaks/apkleaks.py:44-53
decompile()self.jadx, self.file, self.tempdir, self.disargos.system menulis hasil jadx ke self.tempdirapkleaks/apkleaks.py:89-98
scanning()self.pattern, self.tempdirmengisi self.out_json, self.scanned, dan self.fileoutapkleaks/apkleaks.py:117-138
cleanup()self.tempdir, self.scanned, self.fileoutshutil.rmtree, menutup fileout, os.remove(self.output) bila tidak ada temuanapkleaks/apkleaks.py:140-148

decompile() menyusun args menjadi [self.jadx, self.file, "-d", self.tempdir], menambahkan re.split(r"\s|=", self.disarg) di dalam try/except yang menelan semua error, lalu menjalankannya lewat os.system (apkleaks/apkleaks.py:89-98). scanning() menolak berjalan bila self.apk is None (apkleaks/apkleaks.py:117-119).

Pesan keluar dan pemicunya

Semua pesan ditulis lewat util.writeln dengan warna dari apkleaks.colors.color seperti col.OKBLUE, col.WARNING, dan col.FAIL (apkleaks/colors.py:4-9).

PesanPemicuRujukan
Can't find jadx binary.self.jadx tidak ada, sebelum prompt unduhapkleaks/apkleaks.py:57
Please respond with 'yes' or 'no' (or 'y' or 'n').jawaban prompt tidak ada di validapkleaks/apkleaks.py:70
** Interrupted. Aborting.KeyboardInterrupt saat promptapkleaks/apkleaks.py:72
** Aborted.prompt dijawab napkleaks/apkleaks.py:77
It's not a valid file!os.path.isfile(self.file) bernilai salahapkleaks/apkleaks.py:87
** Undefined package. Exit!self.apk is None saat scanning()apkleaks/apkleaks.py:119
** Results saved into ...self.scanned bernilai benarapkleaks/apkleaks.py:145
** Done with nothing. ¯\_(ツ)_/¯self.scanned bernilai salah, berkas keluaran dihapusapkleaks/apkleaks.py:149

⚠️ sys.exit() melempar SystemExit, bukan Exception. Blok finally di apkleaks/cli.py:40-41 tetap dieksekusi, sehingga sesi yang dibatalkan di tengah integrity() pun menghapus self.tempdir dan menghapus self.output lewat jalur ** Done with nothing. di apkleaks/apkleaks.py:146-149.

Dua jebakan di ujung alur

os.system di decompile() tidak memeriksa kode balik (apkleaks/apkleaks.py:96-98). jadx yang gagal tetap dilanjutkan ke scanning(), yang menyusuri self.tempdir kosong, tidak menemukan apa pun, dan berakhir di ** Done with nothing. sehingga kegagalan disassembler tampak seperti APK bersih.

scanning() tidak memanggil join() pada thread yang dijalankannya (apkleaks/apkleaks.py:129-136). Nilai util.finder(p, self.tempdir) dievaluasi lebih dulu di thread pemanggil (apkleaks/utils.py:17-31), jadi thread hanya membawa extract() yang menulis ke self.fileout. Karena cleanup() langsung menutup self.fileout setelah scanning() kembali (apkleaks/apkleaks.py:140-144), penulisan hasil bisa terpotong.