Friday, February 8, 2013

Android hacking: replacing system classes methods with your own

If you read my previous post you already know it is really easy to intercept libc functions used by Dalvik. However we can go a bit further and hack DVM from inside out. This time we will be replacing Android framework system classes in runtime, without rooting the device or recompilation of system class libs.

Sounds impossible? Yet, it can be done.
We will dive deeply into Dalvik internals, a bit in DEX bytecode and plain old hacking.

The challenge

It is pretty much impossible to replace a loaded system class on Android (unless you've got root).
  • When your app is started, most classes are already loaded/linked thanks to Zygote
  • Because of that, you can not override system class path
  • You may not load your custom classes with system class loader
  • In other words, you can not replace system java class from inside of java

DVM internals


Fortunately, many DVM methods and global variables are exported libdvm.so. In fact several libs like androidruntime.so and libbinder.so relay on that.


So even though we may not just link with -ldvm (at least it didn't work for me, so I gave up) it is still possible to dlopen() and dlsym() these functions/globals and hack our way into Dalvik.

On Dalvik all Java class/object mapping to native C structs is happening in vm/oo/* files.
Object instances are mirrored with ClassObject structs, and methods with Methods.
So each ClassObject comes with 2d vtable array, which simply contains pointers to Methods.

Now then, from JNI code we can issue following calls to get the ClassObjects we want:
We can easily invoke these to get class we need. Note however the class descriptor is different from traditional class name and dalvik.system.class would look like  "Ldalvik/system/class;"

So, what we could do is to dvmFindSystemClass() the target class and dvmFindClass() and swap Method's class bytecode pointers, right? Wrong.

Dalvik bytecode


On Android, compiled system classes bytecode sit in .dex file. They are "optimized". Normally it is a good thing, but makes injecting code harder. Why?
Because unlike traditional Java's invokevirtual, Android's invoke-virtual opcode takes method reference index as an argument. Lets disassemble some .dex and see how methods are invoked:


So this invoke-virtual opcode address println method by @001f index, rather than string. This index is specific to .dex file and is populated during its creation. So if we just copy new bytecode over original bytecode, DVM will end up calling wrong methods. Unless we want to manually patch bytecode or hack into Dalvik's method index tables, we better leave this approach. Another approach would be to write bytecode payload by hand, but it it is a tedious and the code will be extremely version-specific and probably break on different device/version.

What we could also do is to swap the  Method structure pointer in vtable[]. Since every Method contains reference to parent object pointer, the interpreter will be able to invoke referenced methods properly. However, access to local variables will not work, because DVM does not use any kind of indexing in such case. If we need to access some local variables, we will have to use a helper singleton class. On native side, replacing Method pointers in vtable[] is not enough, we would also need to update methodIndex. If we need to call original method, we will need to prefix it and rename it, and then we will be able to use Reflection API to call it.

Putting it all together

To summarize all we need to do is:

  • In your class, add a method with exactly the same signature as your target method
  • Write the method as you normally would, but
  • Avoid accessing class members
  • Local variables are fine
  • Use helper singleton if you need to speak to outside world
  • Use Reflection API to call original method, but add "_orig_" to your method name
Lets override ClipboardManager.setText() method so that we whatever something is send to clipboard, we add a custom string.


So the actual code to do swapping is trivially simple:

Now, once you do the following from your .so:
every time something is pasted in clipboard, the "pwned:" will be added to the clip :)

Congratulations! you've successfully overridden system class on Android.
compilable source