Showing posts with label odex. Show all posts
Showing posts with label odex. Show all posts

Monday, November 23, 2009

Droidcon+ODEX file disassembly

First, the advertisement. I will make a longer presentation at Droidcon London 2009 about Dalvik bytecode in general, using Dedexer examples. This will be a longer version of my previous, short presentation (also in podcast). If central London is convenient for you, please, come. Otherwise I will share the presentation after the event.

To celebrate the event, I finished the symbolic ODEX disassembly feature in Dedexer (look for version 1.8). This means that instead of ugly offsets, Dedexer now correctly decompiles the method and field names for execute-inline, iget/iput-quick and nvoke-virtual-quick instruction families if the dependency files are available. So instead of this:

.line 3041
invoke-virtual-quick {v5},vtable #0x2c
move-result-object v2
.line 3042
iget-object-quick v3,v5,[obj+0x28]
invoke-virtual-quick {v3},vtable #0xe
move-result-object v0
.line 3043
execute-inline {v2},inline #0x4
move-result v1

You will get this:

.line 3041
invoke-virtual-quick {v5},android/app/Activity/android/app/Activity/getPackageName ; getPackageName()Ljava/lang/String; , vtable #0x2c
move-result-object v2
.line 3042
iget-object-quick v3,v5,mComponent Landroid/content/ComponentName; ;[obj+0x28]
invoke-virtual-quick {v3},android/content/ComponentName/android/content/ComponentName/getClassName ; getClassName()Ljava/lang/String; , vtable #0xe
move-result-object v0
.line 3043
execute-inline {v2},Ljava/lang/String/length ; length()I , inline #0x4
move-result v1


Much better, isn't it? See you at Droidcon and I will explain how to interpret the code fragment above.

Sunday, October 18, 2009

Help needed

I decided to release a new version of dedexer but I am not satisfied. The Holy Grail I am chasing is the high-quality disassembly of ODEX files and I intended to use the hint received from Nenik. I extended the dedexer tool with data flow analysis so it now has knowledge about the types in Dalvik registers at any point of the execution of Android bytecode. If you ask nicely the new version of the tool (-r switch), it will even share this information with you. Now a decompiled method looks like this if this switch is used:

.method public (Ljava/lang/String;)V
.limit registers 4
; this: v2 (LLineReader;)
; parameter[0] : v3 (Ljava/lang/String;)
.catch java/io/IOException from lbba to lbda using lbdc
.line 18
invoke-direct {v2},java/lang/Object/ ; ()V
; v2 : LLineReader;
lbba:
.line 20
new-instance v0,java/io/FileInputStream
; v0 : Ljava/io/FileInputStream;
invoke-direct {v0,v3},java/io/FileInputStream/ ; (Ljava/lang/String;)V
; v0 : Ljava/io/FileInputStream; , v3 : Ljava/lang/String;
iput-object v0,v2,LineReader.fis Ljava/io/FileInputStream;
; v0 : Ljava/io/FileInputStream; , v2 : LLineReader;
.line 21
new-instance v0,java/io/BufferedInputStream
; v0 : Ljava/io/BufferedInputStream;
iget-object v1,v2,LineReader.fis Ljava/io/FileInputStream;
; v1 : Ljava/io/FileInputStream; , v2 : LLineReader;
invoke-direct {v0,v1},java/io/BufferedInputStream/ ; (Ljava/io/InputStream;)V
; v0 : Ljava/io/BufferedInputStream; , v1 : Ljava/io/FileInputStream;
iput-object v0,v2,LineReader.bis Ljava/io/BufferedInputStream;
; v0 : Ljava/io/BufferedInputStream; , v2 : LLineReader;
lbda:
.line 28
return-void
lbdc:
.line 23
move-exception v0
; v0 : Ljava/io/IOException;
goto lbda
.end method

Great then, but where is the invoke-quick disassembly? Well, erm, I ran into problems. First of all, I could not figure out the data structures that store the names of other ODEX files that this ODEX file depends on. They seem to be in some sort of data structure at the end of the ODEX file that stores the name of these files but its exact layout remains a mistery for me.

Second, in order to decode invoke-quick statements, iget-object-quick statements also need to be decoded because the type values they put into Dalvik registers are needed for the data flow analyser. The source of this instruction is known as an offset and the mapping of these offsets back to Java types.

I will try to progress with these problems, any help is appreciated.

And now some PR after the boring technical details.

I will make a short presentation about dedexer during the coming Android meetup in London. If you are interested about the tool and central London is accessible for you, let's see each other there.

Wednesday, May 13, 2009

About quick method invocation

A good 3 months ago I wrote about the ODEX format when support for that DEX variant was added to the dedexer tool. Then I asked if there is anybody out there who knows, how the index in the invoke-virtual-quick Dalvik instruction can be interpreted. For example:

invoke-virtual-quick {v1,v2},vtable #0x3b


That 3BH is an offset but into what? I did not know, therefore dedexer does not interpret the offset which makes its output on ODEX files less useful. Then Nenik (nickname according to his request) finally gave me the solution. Here comes his mail, verbatim.

The index computation is pretty simple, but for reverse analysis you need more data.

The vtable contains all the methods that can be invoked by invoke-virtual, that is
all nonprivate member methods (even those final and native).
The vtable is obviously constructed by copying superclass's vtable, then replacing
overridden methods and appending all additional virtual methods.

The methods in the vtable are ordered as they were in the dex file.
Let's look at android.view.KeyCharacterMap for example. It extends java.lang.Object,
so it starts with:
Object:
0: .method protected clone()Ljava/lang/Object;
1: .method public equals(Ljava/lang/Object;)Z
2: .method protected finalize()V
3: .method public final native getClass()Ljava/lang/Class;
4: .method public native hashCode()I
5: .method public final native notify()V
6: .method public final native notifyAll()V
7: .method public toString()Ljava/lang/String;
8: .method public final wait()V
9: .method public final wait(J)V
a: .method public final native wait(JI)V

Then it replaces:
2: .method protected finalize()V
with the implementation from KeyCharacterMap

and adds:
b: .method public get(II)I
c: .method public getDisplayLabel(I)C
d: .method public getEvents([C)[Landroid/view/KeyEvent;
e: .method public getKeyData(ILandroid/view/KeyCharacterMap$KeyData;)Z
f: .method public getKeyboardType()I
10: .method public getMatch(I[C)C
11: .method public getMatch(I[CI)C
12: .method public getNumber(I)C
13: .method public isPrintingKey(I)Z

Anyway,
invoke-virtual-quick {v1},vtable #0x2
on an instance of any kind is simply the quick variant of
invoke-virtual {v1}, Ljava/lang/Object;.finalize:()V


Obviously for decoding invoke-virtual-quick opcodes inside, say,
framework.odex, you need the coresponding core.odex and ext.odex
to reconstruct the vtables of all the base classes.

An .odex file contains a list of such dependencies appended
after the body of the encapsulated dex, together with their SHA-1s:


00498D10 44 6E 95 3A │ 13 BC 10 91 │ 0E 00 00 00 │ 02 00 00 00 Dn.:.¼..........
00498D20 1C 00 00 00 │ 2F 73 79 73 │ 74 65 6D 2F │ 66 72 61 6D ..../system/fram
00498D30 65 77 6F 72 │ 6B 2F 63 6F │ 72 65 2E 6F │ 64 65 78 00 ework/core.odex.
00498D40 93 97 93 82 │ 99 DA 46 4E │ E2 13 DD 35 │ 4C 48 B5 7B .....ÚFNâ.Ý5LHµ{
00498D50 F0 06 51 D7 │ 1B 00 00 00 │ 2F 73 79 73 │ 74 65 6D 2F ð.Q×..../system/
00498D60 66 72 61 6D │ 65 77 6F 72 │ 6B 2F 65 78 │ 74 2E 6F 64 framework/ext.od
00498D70 65 78 00 54 │ F1 D0 82 95 │ 97 53 15 60 │ E4 D6 2C 48 ex.TñÐ...S.`äÖ,H
00498D80 8E 36 51 C5 │ 75 BE 2C 00 │ 50 4B 4C 43 │ 08 80 01 00 .6QÅu¾,.PKLC....
00498D90 08 80 01 00 │ 00 20 00 00 │ 00 00 21 AA │ C2 5C 31 00 ..... ....!ªÂ\1.


With all this data, it should be possible to reconstruct .dex from current
.odex format, something I would like to be able to do.


Thanks, Nenik, this is the information I needed to improve the dedexer tool. I will do that soon. If anyone has something to add, please, comment.