Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reverse-Engineering apps on the device - how far can we go?

Reverse-Engineering apps on the device - how far can we go?

As Android developers, we know that our app is insulated from other apps in the device by the Android Application Sandboxing model. But the reality is that this sandbox only protects your data. Your APK is completely exposed to other apps, including resources, assets, and code. I wanted to know how much reverse-engineering I could do from within an app, so I built an app that can run on your phone and inspect any other app’s resources, looking at image assets and reading string values. It can even load another app’s code, and execute it. All this happens without leaving the device, without classic reverse-engineering tools on a developer machine.

I’ll walk through the APIs that expose your resources, and show how to put them into action. I will demonstrate how we can inspect, and even execute, another app's code. You will come away with a better understanding of what is and is not protected by the Android Application Sandbox, and which parts of your app are freely available for reverse-engineering.

0aa5e9706ca27fc5c8a100a627c20a16?s=128

jebstuart

August 26, 2019
Tweet

Transcript

  1. Reverse-Engineering on the Device:
 How Far Can We Go? Jeb

    Ware American Express
  2. Reverse-Engineering on the Device:
 How Far Can We Go? Jeb

    Ware American Express
  3. Views expressed here are my own 
 and do not

    necessarily reflect 
 the views of my employer.
  4. App Sandboxing APK Sandbox res dex so asset files db

    shared pref keys APK Sandbox res dex so asset files db shared pref keys APK Sandbox res dex so asset files db shared pref keys
  5. /data/app/com.example.yourapp-1/base.apk

  6. /data/app/com.example.yourapp-1/base.apk context.packageManager .getInstalledApplications(...) .map { it.publicSourceDir }

  7. /data/app/com.example.yourapp-1/base.apk context.packageManager .getInstalledApplications(...) .map { it.publicSourceDir }

  8. APK • AndroidManifest.xml • assets/ • … • classes.dex •

    classes2.dex • res/ • anim/ • color/ • drawable/ • layout/ • raw/ • …
  9. val zip = ZipFile(appInfo.publicSourceDir)
 zip.entries().toList() .map { zip.getInputStream(it) } .onEach

    { ... }
  10. Compiled XML ? @.;FP}?
 interpolatordurationshareInterpolator fromYDeltatoYDeltaandroid**http:// schemas.android.com/apk/res/androidset translate?A????????8????????????t???????????? ?????????????????????????????????

  11. val appId = “com.example.yourapp" val res: Resources = packageManager.getResourcesForApplication(appId)

  12. val res: Resources = packageManager.getResourcesForApplication(appId) res.getString(0x7f0e0003)

  13. val res: Resources = packageManager.getResourcesForApplication(appId) res.getString(0x7f0e0003) “Hello, World!”

  14. val res: Resources = packageManager.getResourcesForApplication(appId) res.getString(0x7f0e0003) “Hello, World!” ???

  15. Resource IDs 0x7f0e0003

  16. Resource IDs 0x7f0e0003 ??? ??? ???

  17. None
  18. None
  19. Resource IDs 0x7f0e0003

  20. 0e Resource IDs { const 0x7f 0003

  21. 0e Resource IDs { { const type 0x7f 0003

  22. 0e Resource IDs { { { const type identifier 0x7f

    0003
  23. Enumerating Resources 0x7f 0000 01

  24. Enumerating Resources 0x7f 0001 01

  25. Enumerating Resources 0x7f 0002 01

  26. Enumerating Resources 0x7f 0003 01

  27. Enumerating Resources 0x7f 0003 01 ResourceNotFoundException

  28. Enumerating Resources 0x7f 0000 02

  29. Enumerating Resources val res: Resources = packageManager.getResourcesForApplication(appId) val name =

    res.getResourceName(0x7f0e0003) val type = res.getResourceTypeName(0x7f0e0003) val entry = res.getResourceEntryName(0x7f0e0003)
  30. Enumerating Resources val res: Resources = packageManager.getResourcesForApplication(appId) val name =

    res.getResourceName(0x7f0e0003) val type = res.getResourceTypeName(0x7f0e0003) val entry = res.getResourceEntryName(0x7f0e0003) “anim” “abc_slide_out_top” “com.example:anim/abc_slide_out_top”
  31. Loading Resource Values val res: Resources = packageManager.getResourcesForApplication(appId) res.getBoolean(0x7f0e0123) res.getColor(0x7f0e0123,

    null) res.getDimension(0x7f0e0123) res.getDrawable(0x7f0e0123, null) res.getInteger(0x7f0e0123) res.getString(0x7f0e0123)
  32. Live demo!

  33. Enumerating Assets val res: Resources = packageManager.getResourcesForApplication(appId) val fileNames =

    res.assets.list("")
  34. Enumerating Assets val res: Resources = packageManager.getResourcesForApplication(appId) val fileNames =

    res.assets.list("") AMobileConfig.json bar.properties fonts html settings.json
  35. Enumerating Assets val inputStream = res.assets.open(path) // for image val

    bitmap = BitmapFactory.decodeStream(inputStream) // for text file val text = inputStream.reader().readLines()
  36. Live demo?

  37. DEX files

  38. DEX files

  39. DEX files val apk = ZipFile(appInfo.publicSourceDir) val dexEntries = apk.entries().toList()

    .filter { it.name.startsWith("classes") && it.name.endsWith(".dex") }
  40. DexClassLoader classLoader = DexClassLoader( targetFile.absolutePath, null, null, classLoader)

  41. val clazz = classLoader.loadClass("com.example.MyClass")

  42. implementation 'com.jakewharton.dex:dex-member-list:3.3.0'

  43. implementation 'com.jakewharton.dex:dex-member-list:3.3.0' implementation('androidx.appcompat:appcompat:1.0.2') { exclude group: 'com.google.guava', module: 'listenablefuture' }

  44. Enumerating Classes val members: List<DexMember> = DexParser.fromFile(dexFile).list() member.declaringType

  45. val clazz = classLoader.loadClass(fqcn)
 val modifiers = clazz.getDeclaredField(fieldName).modifiers val public:

    Boolean = Modifier.isPublic(modifiers)
  46. val field = clazz.getDeclaredField(fieldName) val instance = clazz.newInstance() 
 val

    value = field.get(instance)
  47. val clazz = targetClassLoader.loadClass(declaringType) val instance = clazz.newInstance() val method

    = clazz.getDeclaredMethod(methodName, String::class.java, Boolean::class.java) val result = method.invoke(instance, "arg1", true)
  48. Live demo?!

  49. Loading Layouts

  50. java.lang.ClassNotFoundException: Didn't find class "android.support.v7.widget.ActivityChooserView$InnerLayout" on path: DexPathList[ [zip file

    “/data/app/com.jebware.appspy-A_W1…2Q==/base.apk”], nativeLibraryDirectories=[ /data/app/com.jebware.appspy-A_W1…2Q==/lib/arm64, /system/lib64
 ] ]
  51. val flags = CONTEXT_INCLUDE_CODE val targetContext = createPackageContext("com.example.app", flags)

  52. val flags = CONTEXT_INCLUDE_CODE val targetContext = createPackageContext("com.example.app", flags) SecurityException

  53. val flags = CONTEXT_INCLUDE_CODE or CONTEXT_IGNORE_SECURITY val targetContext = createPackageContext("com.example.app",

    flags)
  54. val flags = CONTEXT_INCLUDE_CODE or CONTEXT_IGNORE_SECURITY val targetContext = createPackageContext(targetPackageName,

    flags)
  55. val targetLayoutInflater = layoutInflater .cloneInContext(targetContext)

  56. val targetLayout = targetLayoutInflater .inflate(targetLayoutId, null) setContentView(targetLayout)

  57. Live Demo!!

  58. Takeaways • Sandbox protects private data, keys, etc. • Not

    code, resources, assets.
  59. Questions? Jeb Ware
 American Express
 
 @jebstuart