# Mobile `Internals` Challenge TCP1P Capture The Flag Writeup by `azka` ![](https://hackmd.io/_uploads/SJeb33cZT.png) Author: [aimardcr](https://github.com/aimardcr) Seperti yang sudah ada dideskripsi challenge nya, untuk menyelesaikan challenge ini kita perlu untuk membuat malicious/exploit aplikasi sendiri yang dimana nanti akan diinstall di Virtual Android Device dari TCP1P. Sebelum kita membuat malicious code atau exploitnya, penting bagi kita untuk memahami aplikasi internals ini secara mendalam. Langkah pertama yang krusial adalah menganalisis aplikasi tersebut dengan menggunakan tools decompiler. [Decompiler](https://en.wikipedia.org/wiki/Decompiler) ini memungkinkan kita untuk membuka aplikasi "challenge" menjadi source code yang dapat kita baca dan pahami. Saya pribadi menggunakan tools decompiler dari [jadx](https://github.com/skylot/jadx) untuk melakukan ini. Cara menggunakan jadx: 1. Buka exe dari jadx yang sudah didownload, dan akan menampilkan seperti gambar berikut ini. ![](https://hackmd.io/_uploads/By8BJpc-T.png) 2. Kita klik tombol `Open file` dan kemudian cari .apk challenge yang sudah didownload oleh teman teman. 3. Setelah itu jadx akan menalisis nya dan mendecompile apk tersebut agar dapat kita baca, beriku ini hasil decompile dari jadx. ![](https://hackmd.io/_uploads/rJtoyp5-p.png) 4. Cari nama package dari aplikasi challenge terlebih dahulu, untuk mengetahui nama package dari aplikasi ini adalah dengan melihat code dari file `AndroidManifest.xml` terlebih dahulu, yang didapatkan dari isi dari `Resources -> AndroidManifest.xml`. ![](https://hackmd.io/_uploads/S1lLxT5Z6.png) 5. Buka file `AndroidManifest.xml` nya dan cari kata `MainActivity`, karena biasanya developer android menggunakan `MainActivity` sebagai class utama dari aplikasi mereka. ![](https://hackmd.io/_uploads/SkvkW65-6.png) 6. Dalam contoh kasus ini nama package dari aplikasi ini adalah `com.kuro.internals`. 7. Setelah mendapatkan nama package dari aplikasi nya, kita bisa melihat source code dari aplikasinya lewat jadx yang ada pada `Source code -> [nama package dari aplikasi]` contohnya `Source Code -> com.kuro.internals`. ![](https://hackmd.io/_uploads/Hkz87acba.png) ## Internals ![](https://hackmd.io/_uploads/SyX66yjba.png) Link aplikasi: [challenge.apk](https://github.com/TCP1P/TCP1P-CTF-2023-Challenges/raw/main/Mobile/Internals/src/challenge.apk) Diberikan challenge dengan nama `Internals` dengan deskripsi sebagai berikut: :::spoiler Let's see how well your knowledge about android internals. Using any type of external library will deduct your points by half. Hint : 1. You do know android is open source right? Then it's time to read the source code! Especially on getPackageName. 2. Do some OSINT on the author's repositories, maybe you'll find an interesting project. ::: Dari deskripsi yang diberikan terlihat kita dichallenge oleh pembuat soal terkait knowledge kita tentang andoir internals, dan pembuat soal juga melarang kita untuk menggunakan library external untuk melakukan exploit nya, kalaupun kita menggunakan library external kita akan mendapatkan pengurangan point. Sekarang coba kita install dan buka terlebih dahulu aplikasi challenge ini dan melihat bagaimana tampilan nya. ![](https://hackmd.io/_uploads/B1CAva5b6.png) Terlihat aplikasi ini meminta url yang akan mendownload `payload.dex` dan akan meload dex nya. Oke setelah membaca deskripsi dan isi aplikasi dari soal kita perlu untuk melihat terlebih dahulu source code dari aplikasi `internals` ini, dan didpatkan satu activity yaitu `MainActivity` saja: ![](https://hackmd.io/_uploads/HJ-dQTcZ6.png) Dengan isi dari activitynya sebagai berikut: :::spoiler ```java package com.kuro.internals; import android.app.ProgressDialog; import android.content.DialogInterface; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.View; import android.widget.Button; import android.widget.EditText; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import dalvik.system.DexClassLoader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /* loaded from: classes3.dex */ public class MainActivity extends AppCompatActivity { Button btn_load; EditText input_url; /* JADX INFO: Access modifiers changed from: protected */ @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.input_url = (EditText) findViewById(R.id.input_url); Button button = (Button) findViewById(R.id.btn_load); this.btn_load = button; button.setOnClickListener(new View.OnClickListener() { // from class: com.kuro.internals.MainActivity.1 @Override // android.view.View.OnClickListener public void onClick(View v) { String url = MainActivity.this.input_url.getText().toString(); if (url.isEmpty()) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage("URL cannot be empty!"); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); return; } MainActivity.this.downloadDex(url); } }); } void downloadDex(String url) { ProgressDialog pDialog = new ProgressDialog(this); pDialog.setTitle("Downloading..."); pDialog.setMessage("Please wait..."); pDialog.setCancelable(false); pDialog.setProgressStyle(0); pDialog.show(); ExecutorService executor = Executors.newSingleThreadExecutor(); Handler handler = new Handler(Looper.getMainLooper()); executor.execute(new AnonymousClass2(url, handler, pDialog)); } /* JADX INFO: Access modifiers changed from: package-private */ /* renamed from: com.kuro.internals.MainActivity$2 reason: invalid class name */ /* loaded from: classes3.dex */ public class AnonymousClass2 implements Runnable { final /* synthetic */ Handler val$handler; final /* synthetic */ ProgressDialog val$pDialog; final /* synthetic */ String val$url; AnonymousClass2(String str, Handler handler, ProgressDialog progressDialog) { this.val$url = str; this.val$handler = handler; this.val$pDialog = progressDialog; } @Override // java.lang.Runnable public void run() { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(this.val$url).build(); try { client.newCall(request).enqueue(new Callback() { // from class: com.kuro.internals.MainActivity.2.1 @Override // okhttp3.Callback public void onFailure(Call call, IOException e) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage(e.getMessage()); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); } @Override // okhttp3.Callback public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage(response.message()); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); return; } InputStream inputStream = response.body().byteStream(); OutputStream outputStream = MainActivity.this.openFileOutput("payload.dex", 0); try { byte[] buffer = new byte[1024]; while (true) { int len = inputStream.read(buffer); if (len != -1) { outputStream.write(buffer, 0, len); } else { outputStream.close(); inputStream.close(); AnonymousClass2.this.val$handler.post(new Runnable() { // from class: com.kuro.internals.MainActivity.2.1.1 @Override // java.lang.Runnable public void run() { AnonymousClass2.this.val$pDialog.dismiss(); MainActivity.this.loadDex(); } }); return; } } } catch (Exception e) { e.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); } } } void loadDex() { File dexPath = getFileStreamPath("payload.dex"); if (!dexPath.exists()) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Error"); builder.setMessage("payload.dex not found"); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); return; } try { DexClassLoader dexClassLoader = new DexClassLoader(dexPath.getAbsolutePath(), getFilesDir().getAbsolutePath(), null, getClassLoader()); Class<?> clazz = dexClassLoader.loadClass("com.kuro.payload.Main"); clazz.getMethod("execute", new Class[0]).invoke(null, null); if (getPackageName().equals("l33t_h4x0r")) { AlertDialog.Builder builder2 = new AlertDialog.Builder(this); builder2.setTitle("Gr4tz"); builder2.setMessage("Flag: flag{fake_flag_dont_submit}"); builder2.setCancelable(false); builder2.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder2.show(); } } catch (Exception e) { e.printStackTrace(); } } } ``` ::: Bisa dilihat bahwa aplikasi menggunakan layout `activity_main` yang bisa didapatkan di jadx didalam folder `Resources/res/layout/activity_main.xml`, aplikasi ini juga mempunyai 1 button dan 1 EditText. ![](https://hackmd.io/_uploads/rJneta5-6.png) Jadi simplenya ketika user sudah memasukan url kedalam box dan menekan button nya maka aplikasi akan mengambil url tersebut dan melemparkannya ke fungsi `downloadDex`, jika user tidak memasukan url kedalam box maka akan memunculkan popup `URL cannot be empty!`. ![](https://hackmd.io/_uploads/BJnsta5b6.png) Pada fungsi `downloadDex` bisa dilihat bahwa aplikasi akan memunculkan dialog `Downloading...` dan akan melemparkannya ke fungsi `AnonymousClass2`. ![](https://hackmd.io/_uploads/Syz0tacZ6.png) berikut ini isi dari fungsi `AnonymousClass2` :::spoiler ```java public class AnonymousClass2 implements Runnable { final /* synthetic */ Handler val$handler; final /* synthetic */ ProgressDialog val$pDialog; final /* synthetic */ String val$url; AnonymousClass2(String str, Handler handler, ProgressDialog progressDialog) { this.val$url = str; this.val$handler = handler; this.val$pDialog = progressDialog; } @Override // java.lang.Runnable public void run() { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(this.val$url).build(); try { client.newCall(request).enqueue(new Callback() { // from class: com.kuro.internals.MainActivity.2.1 @Override // okhttp3.Callback public void onFailure(Call call, IOException e) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage(e.getMessage()); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); } @Override // okhttp3.Callback public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage(response.message()); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); return; } InputStream inputStream = response.body().byteStream(); OutputStream outputStream = MainActivity.this.openFileOutput("payload.dex", 0); try { byte[] buffer = new byte[1024]; while (true) { int len = inputStream.read(buffer); if (len != -1) { outputStream.write(buffer, 0, len); } else { outputStream.close(); inputStream.close(); AnonymousClass2.this.val$handler.post(new Runnable() { // from class: com.kuro.internals.MainActivity.2.1.1 @Override // java.lang.Runnable public void run() { AnonymousClass2.this.val$pDialog.dismiss(); MainActivity.this.loadDex(); } }); return; } } } catch (Exception e) { e.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); } } } ``` ::: ![](https://hackmd.io/_uploads/rkL0cT9WT.png) Setelah membaca source code dari fungsi `AnonymousClass2` bisa dilihat bahwa dia mencoba untuk mendownload file dex dari url yang kita berikan dan menyimpannya dengan nama file `payload.dex` yang kemudian akan dialihkan ke fungsi `loadDex()`. Dan disinilah bagian menariknya muncul dan bagaimana kita akan membuat malicious dex nya. ![](https://hackmd.io/_uploads/B12WV6q-6.png) Yang dimana dalam fungsi ini dia mengecheck terlebih dahulu apakah ada file `payload.dex` didalam folder files aplikasi kita, perlu dinote setiap file yang diload oleh android studio itu ada pada folder `/data/data/[nama package apk]/files`, jadi dalam kode ini aplikasi challenge akan mengecheck terlebih dahulu apakah file `payload.dex` itu ada atau tidak didalam folder files kita. Selanjutnya ketika file `payload.dex` itu ada didalam folder files, `payload.dex` akan diload dengan [DexClassLoader](https://developer.android.com/reference/dalvik/system/DexClassLoader) yang nantinya akan dicheck dengan menggunakan [Reflection](https://www.haptik.ai/tech/using-reflection-in-android/) untuk meload class didalam file dex nya, yang dimana dikode ini aplikasi challenge berusaha untuk meload class dari package `com.kuro.payload` dengan nama class `Main` dan juga dia mengambil method dari `execute` untuk dijalankan diaplikasi challenge, setelah berhasil loadClass dari `payload.dex` dia akan mengecheck nama package dari aplikasi challenge yang dimana saat ini masihlah `com.kuro.internals` sudah berubah ke nama package baru atau tidak yaitu `l33t_h4x0r`, ketika kondisi ini terpenuhi aplikasi akan menampilkan flag nya. Setelah berhasil memahami bagaimana alur dari aplikasi ini berjalan, penulis sudah mempunya gambaran tentang bagaimana membuat malicious dex nya agar ketika dex yang penulis buat nanti bisa merubah packageName dari aplikasi challenge dari `com.kuro.internals` ke `l33t_h4x0r`. Dari hint yang diberikan oleh pembuat soal, pembuat soal meminta kita untuk melakukan osint digithub akunnya dia. ![](https://hackmd.io/_uploads/H16yhacZa.png) Akhirnya penulis menemukan akun githubnya dan menemukan repository yang menarik didalamnya yaitu [APKKiller](https://github.com/aimardcr/APKKiller), didalam repository ini pembuat soal memberitahukan bahwa dengan menggunakan `Reflection` kita bisa membaca dan memodifikasi internal classes dan fields nya. ![](https://hackmd.io/_uploads/H1iS26cZp.png) Setelah membaca dan melakukan trial and error tentang `Reflection` penulis menemukan cara bahwa kita bisa menggunakan class dari [ActivityThread](https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/android/app/ActivityThread.java) untuk mengubah packageName ke packageName yang kita mau. Cobalah untuk membuat projek baru di `Android Studio` dengan setup sebagai berikut: 1. Pilih `Empty Views Activity` lalu Next. ![](https://hackmd.io/_uploads/HJPOAaq-a.png) 2. Buatlah nama terserah kalian, asalkan nama package nya itu `com.kuro.payload` karena pada aplikasi challenge package ini yang akan di load, lalu pilihlah `Java` sebagai bahasa pemrogramannya, karena `Reflection` bisa kita pakai di java. ![](https://hackmd.io/_uploads/SJ5NyR9Wp.png) 3. Setelah itu klik `Finish` dan tunggu sampai android studio menyiapkan setupnya. 4. Setelah semuanya sudah selesai, kita buat class baru dengan nama `Main` karena aplikasi challenge akan meload class dari package kita dengan nama `Main` dengan klik kanan pada package `com.kuro.payload` lalu klik `New -> Java Class`. ![](https://hackmd.io/_uploads/BJlf6kRqb6.png) 5. Masukan nama `Main` dan enter. ![](https://hackmd.io/_uploads/H1Z-xA5bT.png) 6. Kita akan memakai activity `MainActivity` terlebih dahulu untuk debugging. Jadi step pertama adalah cari terlebih dahulu field dari packageName dan kemudian baru kita set ke value yang baru ```java package com.kuro.payload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.lang.reflect.Field; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { Class<?> clazz = Class.forName("android.app.ActivityThread"); Field[] fs = clazz.getDeclaredFields(); for(int i = 0; i < fs.length; i++) { Log.e("Field" + String.valueOf(i), fs[i].getName()); } } catch (Exception e) { e.printStackTrace(); } } } ``` Coba gunakan kodingan diatas dan jalankan, lalu coba untuk melihat logcat nya karena saya memasukan `Log.e` disitu untuk debugging dan melihat isi field dari `ActivityThread`. ![](https://hackmd.io/_uploads/B1PSbRqba.png) Dan didapatlah isi field dari class `ActivityThread`. ![](https://hackmd.io/_uploads/SJlKbR9bp.png) Seperti yang diberikan hint oleh pembuat soal bahwa getPackageName itu terdapat pada mPackageInfo. ![](https://hackmd.io/_uploads/HkTkDRcbp.png) Setelah menghabiskan banyak waktu disini, akhirnya saya menemukan bahwa didalam fields `mBoundApplication` terdapat field `info` ![](https://hackmd.io/_uploads/ryJQPC9ZT.png) ```java package com.kuro.payload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { Class<?> clazz = Class.forName("android.app.ActivityThread"); Method currentActivityThread = clazz.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null); Field[] fields = clazz.getDeclaredFields(); for(int i = 0; i < fields.length; i++) { Log.e("Field" + String.valueOf(i), fields[i].getName()); } Field mBoundApplicationField = clazz.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); Object mBoundApplication = mBoundApplicationField.get(activityThread); Field[] mBoandFields = mBoundApplication.getClass().getDeclaredFields(); for(int i = 0; i < fields.length; i++) { Log.e("mBoandFields" + String.valueOf(i), mBoandFields[i].getName()); } } catch (Exception e) { e.printStackTrace(); } } } ``` Dan akhirnya didapatilah field `mPackageName` ![](https://hackmd.io/_uploads/r1YNF0qWa.png) ```java package com.kuro.payload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { Class<?> clazz = Class.forName("android.app.ActivityThread"); Method currentActivityThread = clazz.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null); Field[] fields = clazz.getDeclaredFields(); for(int i = 0; i < fields.length; i++) { Log.e("Field" + String.valueOf(i), fields[i].getName()); } Field mBoundApplicationField = clazz.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); Object mBoundApplication = mBoundApplicationField.get(activityThread); // Field[] mBoundFields = mBoundApplication.getClass().getDeclaredFields(); // for(int i = 0; i < fields.length; i++) { // Log.e("mBoundFields" + String.valueOf(i), mBoundFields[i].getName()); // } Field loadedApkInfoField = mBoundApplication.getClass().getDeclaredField("info"); loadedApkInfoField.setAccessible(true); Object loadedApkInfo = loadedApkInfoField.get(mBoundApplication); Field[] apkInfoFields = loadedApkInfo.getClass().getDeclaredFields(); for(int i = 0; i < fields.length; i++) { Log.e("apkInfoFields" + String.valueOf(i), apkInfoFields[i].getName()); } } catch (Exception e) { e.printStackTrace(); } } } ``` Setelah mendapatkan field yang benar, sekarang tinggal merubah value dari mPackageName ini ke `l33t_h4x0r` dengan kode berikut ini: ```java package com.kuro.payload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { // Get the current ActivityThread instance Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null); // Get the loaded package info Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); Object mBoundApplication = mBoundApplicationField.get(activityThread); Field loadedApkInfoField = mBoundApplication.getClass().getDeclaredField("info"); loadedApkInfoField.setAccessible(true); Object loadedApkInfo = loadedApkInfoField.get(mBoundApplication); // Set the new package name Field packageNameField = loadedApkInfo.getClass().getDeclaredField("mPackageName"); packageNameField.setAccessible(true); packageNameField.set(loadedApkInfo, "l33t_h4x0r"); Log.e("PackageName", getPackageName()); } catch (Exception e) { e.printStackTrace(); } } } ``` Setelah dijalankan dan dilihat dilogcat package name sudah berhasil dirubah ke `l33t_h4x0r`. ![](https://hackmd.io/_uploads/rJHpY0qZp.png) Setelah itu tinggal copy code nya dan masukan kedalam class `Main` dan didalam fungsi `execute`. ```java package com.kuro.payload; import android.content.pm.ApplicationInfo; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Main { public static void execute() { try { // Get the current ActivityThread instance Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null); // Get the loaded package info Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); Object mBoundApplication = mBoundApplicationField.get(activityThread); Field loadedApkInfoField = mBoundApplication.getClass().getDeclaredField("info"); loadedApkInfoField.setAccessible(true); Object loadedApkInfo = loadedApkInfoField.get(mBoundApplication); // Set the new package name Field packageNameField = loadedApkInfo.getClass().getDeclaredField("mPackageName"); packageNameField.setAccessible(true); packageNameField.set(loadedApkInfo, "l33t_h4x0r"); } catch (Exception e) { e.printStackTrace(); } } } ``` Setelah itu set gradle nya agar ketika dibuild classes.dex nya itu hanya satu dan tidak multiple dengan menambahkan config sebagai berikut: ![](https://hackmd.io/_uploads/SkBPcA5W6.png) Setelah itu kita build projek ini. ![](https://hackmd.io/_uploads/H1mFcA5-a.png) Akan muncul popup di pojok kanan bawah seperti berikut ini. ![](https://hackmd.io/_uploads/HJBiqCcba.png) Klik `locate` dan akan diarahkan ke folder apk yang sudah dibuild: ![](https://hackmd.io/_uploads/H1UA909-T.png) Masuk kedalam folder debug. ![](https://hackmd.io/_uploads/H1F1iR9W6.png) Dan `app-debug.apk` ini adalah hasil compile kita tadi, setelahnya kita buka apk ini di `jadx` untuk kita ambil classes.dex nya dan kita pakai di aplikasi challenge. folder apk hasil compile ada di `<Nama Folder Projek>\app\build\outputs\apk\debug`. Setelah dibuka dengan jadx, kita save all hasil decompile ini dan masukan kedalam sebuah folder, saya sendiri memasukannya ke folder `kelasss`. ![](https://hackmd.io/_uploads/BJIcoAqWT.png) Setelah itu kita copy `classes.dex` nya yang terdapat di folder `kelasss/resources/classes.dex` ke folder yang lain untuk bisa kita transfer ke aplikasi challenge. ![](https://hackmd.io/_uploads/ByiAoAcWT.png) Saya memasukannya di folder internals saja dan saya rubah namanya ke `payload.dex` dan menjalankan http.server dan ngrok, setelah itu tinggal masukan url nya ke box dan exploit kita berhasil untuk mendapatkan flagnya. ![](https://hackmd.io/_uploads/HkEfp0qbp.png) Tinggal dijalankan di Virtual Android Device dan didapatkan flagnya: ![](https://hackmd.io/_uploads/SydWl1jba.png) Reference: - https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/android/app/ActivityThread.java - https://www.digitalocean.com/community/tutorials/java-reflection-example-tutorial - https://www.haptik.ai/tech/using-reflection-in-android/ - https://stackoverflow.com/questions/1754714/android-and-reflection - https://stackoverflow.com/questions/1438420/how-to-get-a-class-object-from-the-class-name-in-java - https://www.geeksforgeeks.org/reflection-in-java/ - https://stackoverflow.com/questions/61757838/is-it-possible-to-get-class-object-of-an-app-from-injected-dex-by-using-java-ref - https://github.com/aimardcr/APKKiller