换了手机后,处理能力加强了些,甚至还能插卡了,自此便多了许多可以做的事情,比如拿着她来看看电子书。躺在床上,摘掉眼镜,举着右手掣着手机,慢慢品味一些书籍实在是惬意之至,至少这可以打磨去心中那挥之不去的惆怅,赢得一宿良眠。
最近在看魏晋之时的志人笔记《世说新语》,看着这些记载着千年前人们言行的小小短文,回想起魏晋时期内忧外困的环境,不禁心生嗟叹。想起前些日子看过了那个肤浅的《百家讲坛》里易中天评三国,内中对曹操评价颇高,而我颇不以之为然。无论易中天怎么去狡辩,曹操都不过是一个奸雄而已。儒学向来以忠为本,而曹操名为”汉相,实为汉贼“,为了摆脱道德的困境曹操便片面夸大了儒学中孝的一部分,试图使之凌驾于忠国之上。那个让梨的孔融看破了曹某人的企图,于是声称父母不过是为孩子提供了一个出生用的“瓶子”,孩子实在是不需去太感激瓶子的主人,所以不孝之罪又岂能超过不忠之罪呢。等到曹丕纳袁熙妻为妾,孔融又写信给曹操,告诉他”武王伐纣,以妲己赐周公”。傻乎乎的曹操问起这事情的出处时,孔融答道:“以今度之,想当然耳。”,曹操哭笑不得。曹操禁酒,孔先生自然不会情愿,他问道:如果酒能亡国,那么美色也能亡国,曹某人你怎么不禁婚姻呢?而曹某人你15个老婆,是不是该检讨一下了呢?曹操打破了牙吞肚子,只等到平定了北方诸雄,便以不孝之罪将孔融弃市。
自然,现在的人们却喜欢去讨论孔融是怎么样的一个废人,下不能齐家,上不能安天下,只不过是一根破笔头,和曹操斗是自寻死路还搭上自己的亲人和朋友。这些犬儒们,他们怕是不知道,如果没有孔融们的硬骨头,那曹操便会真以为天下人好欺!
无忠的曹家自然是无法获得司马的忠诚,而司马的皇帝们甚至不能获得自己藩王的忠诚,更别说那些内附的匈奴人了。一个国家的根基被损坏了,一个国家的命运便也只能走向深渊,五胡乱华只是对我们的小小惩戒,而后来奴才们对阴谋胜利者的恬不知耻的不断讴歌才是烙在我们头上永恒的印记。
遗憾的是,之后的名士们便再也没敢重拾建安士子的豪气,只是对他们的表象进行着模糊地模仿,或是沉迷于互相吹捧,或是忘形于酒石,寓情于山水。他们再也无法像孔融一般,为了维护忠国,甚至不惜将孝道贬低,他们反被窃国者们的孝道俘获,勉强维持着自己些许的自由。偶尔几个有才的如谢安,却做着矫揉造作地掩饰着自己对生死攸关战争结果的关注,等到只有自己一人时却又兴奋地把木屐的齿碰掉这样的可爱之事,做着虚假的自我。
不过国人的自由在高压之下虽然畸形,却毕竟延续了下来,直到唐时儒学复兴,自由的传统终于复回,虽然已经少却了许多点点滴滴。这本小小的册子便是刘寄奴之宋时刘义庆编撰,记载者魏晋之时那些热爱自由,却不得不压抑着自己的人们,他们的一言一行中所闪耀的自由之光,有时候可笑,有时候迂腐,有时候甚至有些孩子气,在黑暗的天幕上他们微弱地发射着不同的光芒,告诉这群古老的人民,天终究会再亮的。
代码已经发布到:
http://code.google.com/p/jythonroid/
今天开始着手写界面了。Anroid自带了一个EditText控件,但这个狗屁控件居然无法调整光标位置。而且对于文本的颜色管理很粗糙,只能为所有文本指定颜色,而不能针对性的设置。这只能拿来做一个简单的文本编辑器了,做Shell的话易用性会很差的。
准备是Shell为一个Android的Activity,然后Jython是一个运行于后台的进程,二者通过socket通信。写好代码调试却发现ServerSocket没法用,Google了半天找到了解决:
you need to create a TCP or UDP redirection to let your client on the PC
access the server on the emulated device.
for example, if you connect to the emula×’s control console, and type
something like:
redir add tcp:5000:6000
this will redirect all connections to your PC’s localhost:5000 towards the
emulated localhost:6000
have a look at the emula× documentation for more details (or you could
type “redir help” in the control console)
好了,继续。。。
废话不多说了,基本是搞定了。过两天整理一下,可以发布了
昏倒,这个破公司居然限制上传文件,看来只有等回去才能上传了。。。还有四天
载入动态生成的字节码已经解决:
Anroid下可以使用DexFile dx=new DexFile(new File(”filename”));来载入一个编译好了的程序。但限制比较紧:需要文件以.apk结尾,文件需要是一个zip压缩包,压缩包里需要有个文件classes.dex…
获取DexFile对象后就可以根据它获取相应的类的Class对象了,但是Android傻得可以,凡是有父类或者实现了接口的类就无法通过这个方法获取,不过如果实现将父类和接口获取了就可以了。动态生成的代码比较一致,都是继承org/python/core/PyFunctionTable
实现org/python/core/PyRunnable,所以事先把他们声明就是了。具体代码如下:
修补好这点后,又出现了新问题。现在是可以获取Class实例了,但是当使用这个Class势力来生成对象的时候却报错,调试后发现是PathClassLoader无法载入org.python.core.Py,这个ClassLoader够弱智的,看来可能需要对它动手术了。。。
发现一篇博文关于.dex文件反编译的,还不错,可惜要爬墙才能看到,伟大的墙!
不管了,转载自:http://zeaster.blogspot.com/2007/11/how-to-decompile-dex-file-on-android_28.html
How to decompile .dex file on Android
Lucky to see the Google Android build-in Contacts is also developed by Android SDK.
This article will take it for example and discuss these How-To’s:
1 How to find the Contacts App file on Google Android
2 How to find the Contacts App’s classes.dex on Google Android
3 How to dump the Contacts App’s classes
4 How to decompile the dumped file
{
Actually, I think the Contacts Content Provider that Google provides has some inconvenience to use.
And I will post another blog to list what’s inconvenience and request some improvement from Google.
The blog link is here:
http://zeaster.blogspot.com/2007/11/inconvenienced-when-using-contact.html
}
1 How to find the Contacts App on Google Android
Using adb tool
$ adb shell
# cd /system/app
cd /system/app
# ls
ls
-rw-r–r– root root 25519 2007-11-14 20:40 ContactsProvider.apk
-rw-r–r– root root 7544 2007-11-14 20:40 GoogleAppsProvider.apk
-rw-r–r– root root 16198 2007-11-14 20:40 ImProvider.apk
-rw-r–r– root root 20308 2007-11-14 20:40 MediaProvider.apk
-rw-r–r– root root 21272 2007-11-14 20:40 TelephonyProvider.apk
-rw-r–r– root root 11809 2007-11-14 20:40 SettingsProvider.apk
-rw-r–r– root root 418688 2007-11-14 20:41 Browser.apk
-rw-r–r– root root 68077 2007-11-14 20:41 Contacts.apk
-rw-r–r– root root 96287 2007-11-14 20:41 Development.apk
-rw-r–r– root root 44790 2007-11-14 20:41 GoogleApps.apk
-rw-r–r– root root 8637 2007-11-14 20:41 Fallback.apk
-rw-r–r– root root 99431 2007-11-14 20:41 Home.apk
-rw-r–r– root root 171614 2007-11-14 20:41 Maps.apk
-rw-r–r– root root 424601 2007-11-14 20:41 Phone.apk
-rw-r–r– root root 192119 2007-11-14 20:41 XmppService.apk
-rw-r–r– root root 6614 2007-11-14 20:41 XmppSettings.apk
#
The Contacts.apk is the Contacts App and ContactsProvider.apk is the app that provides the Contact Content Provider for reading and writing “content://contacts/” resources.
we can see many system apps developed by Android Java SDK are put here.
However what is inside *.apk file. Actually it is a .zip file. It is easy to unzip and get the answer.
take Contacts.apk for example, it contains:
META-INF\
res\
AndroidManifest.xml
classes.dex
resources.arsc
The classes.dex file contains all compiled Java code.
2 How to find the Contacts App’s classes.dex on Google Android
No need to unzip Contacts App’s classes.dex file from Contacts.apk file.
The Dalvik VM has a cached file for us. Go head to find it.
$ adb shell
# cd /data/dalvik-cache
cd /data/dalvik-cache
# ls
ls
-rw-rw-rw- root root 73852 2007-11-27 05:16 system@app@Contacts.apk@classes.dex
-rw-rw-rw- app_0 app_0 64172 2007-11-27 05:17 system@app@ContactsProvider.apk@classes.dex
-rw-rw-rw- root root 15204 2007-11-27 05:17 system@framework@am.jar@classes.dex
-rw-rw-rw- app_3 app_3 3012 2007-11-27 07:33 system@app@Fallback.apk@classes.dex
-rw-rw-rw- root root 7252804 2007-11-27 05:16 system@framework@core.jar@classes.dex
….and many other cached files.
3 How to dump the Contacts App’s classes
Unfortunately the classes.dex is not a .jar file. It’s Dalvik executable format that is optimized for efficient.
so we can not pull the .class files as .jar format. However Google Android provides a tool named dexdump included in the emula× to dump .dex file. Here is how to use it.
# dexdump
dexdump: no file specified
dexdump: [-f] [-h] dexfile…
-d : disassemble code sections
-f : display summary information from file header
-h : display file header details
-C : decode (demangle) low-level symbol names
-S : compute sizes only
Now we dump the system@app@Contacts.apk@classes.dex and system@app@ContactsProvider.apk@classes.dex.
# dexdump -d -f -h -C system@app@Contacts.apk@classes.dex >> Contacts.apk.dump
# dexdump -d -f -h -C system@app@ContactsProvider.apk@classes.dex >> ContactsProvider.apk.dump
and then pull it out.
# exit
$ adb pull /data/dalvik-cache/Contacts.apk.dump ~/android
$ adb pull /data/dalvik-cache/ContactsProvider.apk.dump ~/android
Now we get the dumped files on ~/android folder.
4 How to decompile the dumped file
.dex file is optimized, so we can not decompile it as normal .class file.
Fortunately the dumped file is a bit readable for me.
As it is too big to analyze all the code, so I just take “how to create a contact” for example.
It’s easy to get this info from the dumped file.
It has these classes:
…
Class name : ‘com/google/android/contacts/AttachImage’
Class name : ‘com/google/android/contacts/ContactEntryAdapter’
Class name : ‘com/google/android/contacts/EditContactActivity’
Class name : ‘com/google/android/contacts/EditContactActivity$EditEntry’
…
The EditContactActivity class has a private void create() method.
#3 : (in com/google/android/contacts/EditContactActivity)
name : ‘create’
type : ‘()V’
access : 0×0002 (PRIVATE)
So all the code that create a contact is here from the dumped file, my comments inlined:
// private void create()
009b24: |[009b24] com/google/android/contacts/EditContactActivity.create:()V
// ContentValues v9 = new ContentValues();
009b28: 2109 3100 |0000: new-instance v9, android/content/ContentValues // class@0031
009b2c: 6f01 0f00 0900 |0002: invoke-direct {v9}, android/content/ContentValues.:()V // method@000f
// Entry v12 = this.getCurrentEntry();
009b32: f4fc a800 |0005: +iget-object-quick v12, v15, [obj+00a8]
// int v11 = v12.lines
009b36: f801 1500 0c00 |0007: +invoke-virtual-quick {v12}, [0015] // vtable #0015
009b3c: 0a0b |000a: move-result v11
// int v5 = 0
009b3e: 1205 |000b: const/4 v5, #int 0 // #0
// if v5 >= v11 goto 0046
009b40: 3ab5 3a00 |000c: if-ge v5, v11, 0046 // +003a
// EditEntry v8 = (EditEntry)this.getCurrentEntry();
009b44: f4fc a800 |000e: +iget-object-quick v12, v15, [obj+00a8]
009b48: f802 1a00 5c00 |0010: +invoke-virtual-quick {v12, v5}, [001a] // vtable #001a
009b4e: 0c0c |0013: move-result-object v12
009b50: 07c0 |0014: move-object v0, v12
009b52: 1e00 3d00 |0015: check-cast v0, com/google/android/contacts/EditContactActivity$EditEntry // class@003d
009b56: 0708 |0017: move-object v8, v0
// int v12 = v8.kind // The Instance field kind is defined in its superclass Entry. see details in dumped Entry and EditEntry sections.
009b58: f28c 1400 |0018: +iget-quick v12, v8, [obj+0014]
// int v13 = -3 // PHOTO_KIND = -3 defined in v8’s superclass Entry
009b5c: 12dd |001a: const/4 v13, #int -3 // #fd
// if v12 != v13 goto 0020 // a photo entry, goto 0020
009b5e: 38dc 0500 |001b: if-ne v12, v13, 0020 // +0005
// int v5++;
009b62: d805 0501 |001d: add-int/lit8 v5, v5, #int 1 // #01
// goto 000c
009b66: 33ed |001f: goto 000c // -0013
// String v7 = v8.getData();
009b68: f801 0c00 0800 |0020: +invoke-virtual-quick {v8}, [000c] // vtable #000c
009b6e: 0c07 |0023: move-result-object v7
// boolean v12 = TextUtils.isEmpty(v7)
009b70: 7001 4200 0700 |0024: invoke-static {v7}, android/text/TextUtils.isEmpty:(Ljava/lang/CharSequence;)Z // method@0042
009b76: 0a0c |0027: move-result v12
if v12!=false/0 goto 0030
009b78: 3e0c 0800 |0028: if-nez v12, 0030 // +0008
// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009b7c: f48c 2800 |002a: +iget-object-quick v12, v8, [obj+0028]
// v9.put(v12,v7) // v9 is a ContentValues, v12 is the column name, v7 is the value.
009b80: f803 0b00 c907 |002c: +invoke-virtual-quick {v9, v12, v7}, [000b] // vtable #000b
// goto 001d
009b86: 33ee |002f: goto 001d // -0012
// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009b88: f48c 2800 |0030: +iget-object-quick v12, v8, [obj+0028]
// String v13 = “name”;
009b8c: 180d 1c00 |0032: const-string v13, “name” // string@001c
// boolean v12 = v12.equals(v13);
009b90: ee02 0300 dc00 |0034: +execute-inline {v12, v13}, java/lang/String.equals:(Ljava/lang/Object;)Z // inline #0003
009b96: 0a0c |0037: move-result v12
// if v12 != false/0, goto 001d
009b98: 3d0c e5ff |0038: if-eqz v12, 001d // -001b
// String v12 = “EditContactActivity”
009b9c: 180c 0100 |003a: const-string v12, “EditContactActivity” // string@0001
// String v13 = “Name is required”
009ba0: 180d 0200 |003c: const-string v13, “Name is required” // string@0002
// Log.e(v12, v13); //log info using android/util/Log
009ba4: 7002 4700 dc00 |003e: invoke-static {v12, v13}, android/util/Log.e:(Ljava/lang/String;Ljava/lang/String;)I // method@0047
009baa: 0a0c |0041: move-result v12
// call this.XXX()
009bac: f801 8400 0f00 |0042: +invoke-virtual-quick {v15}, [0084] // vtable #0084
// return void
009bb2: 0e00 |0045: return-void
// ContentResolver v12 = this.getContentResolver();
009bb4: f4fc a400 |0046: +iget-object-quick v12, v15, [obj+00a4]
// CONTENT_URI v13 = People.CONTENT_URI;
009bb8: 610d 0300 |0048: sget-object v13, android/provider/Contacts$People.CONTENT_URI:Landroid/net/ContentURI; // field@0003
// CONTENT_URI v10 = v12.insert(v13, v9) // CONTENT_URI v10 = getContentResolver().insert(uri, values);
// insert people into contacts
009bbc: f803 1100 dc09 |004a: +invoke-virtual-quick {v12, v13, v9}, [0011] // vtable #0011
009bc2: 0c0a |004d: move-result-object v10
// List v12 = this.mContactEntries
009bc4: f4fc c000 |004e: +iget-object-quick v12, v15, [obj+00c0]
009bc8: 7001 8900 0c00 |0050: invoke-static {v12}, com/google/android/contacts/ContactEntryAdapter.countEntries:(Ljava/util/ArrayList;)I // method@0089
009bce: 0a06 |0053: move-result v6
// int v5 = v11 //contact Entry Count
009bd0: 01b5 |0054: move v5, v11
// if v5 >= v6, goto 009e
009bd2: 3a65 4900 |0055: if-ge v5, v6, 009e // +0049
// List v12 = this.mContactEntries
009bd6: f4fc c000 |0057: +iget-object-quick v12, v15, [obj+00c0]
// EditEntry v8 = (EditEntry) getEntry(v12, v5);
009bda: 7002 8b00 5c00 |0059: invoke-static {v12, v5}, com/google/android/contacts/ContactEntryAdapter.getEntry:(Ljava/util/ArrayList;I)Lcom/google/android/contacts/ContactEntryAdapter$Entry; // method@008b
009be0: 0c0c |005c: move-result-object v12
009be2: 07c0 |005d: move-object v0, v12
009be4: 1e00 3d00 |005e: check-cast v0, com/google/android/contacts/EditContactActivity$EditEntry // class@003d
009be8: 0708 |0060: move-object v8, v0
// int v12 = v8.kind // The Instance field kind is defined in its superclass Entry. see details in dumped Entry and EditEntry sections.
009bea: f28c 1400 |0061: +iget-quick v12, v8, [obj+0014]
// int v13 = -1 // CONTACT_KIND = -1 defined in v8’s superclass Entry
009bee: 12fd |0063: const/4 v13, #int -1 // #ff
// if v12 == v13 goto 007e
009bf0: 37dc 1a00 |0064: if-eq v12, v13, 007e // +001a
// v9.clear() // ContentValues.clear()
009bf4: f801 1500 0900 |0066: +invoke-virtual-quick {v9}, [0015] // vtable #0015
// boolean v12 = v8.toValues(v9) // boolean EditEnty.toValues(ContentValues cv)
009bfa: f802 0d00 9800 |0069: +invoke-virtual-quick {v8, v9}, [000d] // vtable #000d
009c00: 0a0c |006c: move-result v12
//if v12 !=false/0, goto 007b
009c02: 3d0c 0e00 |006d: if-eqz v12, 007b // +000e
// ContentResolver v12 = this.getContentResolver();
009c06: f4fc a400 |006f: +iget-object-quick v12, v15, [obj+00a4]
// String v13 = v8.contentDirec×y
009c0a: f48d 2c00 |0071: +iget-object-quick v13, v8, [obj+002c]
// v10.addPath(v13);
009c0e: f802 1800 da00 |0073: +invoke-virtual-quick {v10, v13}, [0018] // vtable #0018
009c14: 0c0d |0076: move-result-object v13
// CONTENT_URI v12 = v12.insert(v13, v9) // CONTENT_URI v12 = getContentResolver().insert(uri, values);
009c16: f803 1100 dc09 |0077: +invoke-virtual-quick {v12, v13, v9}, [0011] // vtable #0011
009c1c: 0c0c |007a: move-result-object v12
// int v5++;
009c1e: d805 0501 |007b: add-int/lit8 v5, v5, #int 1 // #01
// goto 0055
009c22: 33d8 |007d: goto 0055 // -0028
// String v7 = v8.getData();
009c24: f801 0c00 0800 |007e: +invoke-virtual-quick {v8}, [000c] // vtable #000c
009c2a: 0c07 |0081: move-result-object v7
// v9.lines
009c2c: f801 1500 0900 |0082: +invoke-virtual-quick {v9}, [0015] // vtable #0015
// boolean v12 = TextUtils.isEmpty(v7)
009c32: 7001 4200 0700 |0085: invoke-static {v7}, android/text/TextUtils.isEmpty:(Ljava/lang/CharSequence;)Z // method@0042
009c38: 0a0c |0088: move-result v12
if v12!=false/0 goto 007b
009c3a: 3e0c f2ff |0089: if-nez v12, 007b // -000e
// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009c3e: f48c 2800 |008b: +iget-object-quick v12, v8, [obj+0028]
// v9.put(v12,v7) // v9 is a ContentValues, v12 is the column name, v7 is the value.
009c42: f803 0b00 c907 |008d: +invoke-virtual-quick {v9, v12, v7}, [000b] // vtable #000b
// ContentResolver v12 = this.getContentResolver();
009c48: f4fc a400 |0090: +iget-object-quick v12, v15, [obj+00a4]
// int v13 = 0
009c4c: 120d |0092: const/4 v13, #int 0 // #0
// int v14 = 0
009c4e: 120e |0093: const/4 v14, #int 0 // #0
009c50: 07c0 |0094: move-object v0, v12
009c52: 07a1 |0095: move-object v1, v10
009c54: 0792 |0096: move-object v2, v9
009c56: 07d3 |0097: move-object v3, v13
009c58: 07e4 |0098: move-object v4, v14
// int v12 = v12.update(v1,v2,v3,v4) // getContentResolver(),update(uri, values, null, null)
// refer to this method, public final int update(ContentURI uri, ContentValues values, String where, String[] selectionArgs)
009c5a: f905 1400 0000 |0099: +invoke-virtual-quick/range {v0, v1, v2, v3, v4}, [0014] // vtable #0014
009c60: 0a0c |009c: move-result v12
// goto 007b
009c62: 33de |009d: goto 007b // -0022
// int v12 = 4
009c64: 124c |009e: const/4 v12, #int 4 // #4
009c66: f5fc c800 |009f: +iput-quick v12, v15, [obj+00c8]
009c6a: f7fa 9000 |00a1: +iput-object-quick v10, v15, [obj+0090]
// int v12 = -1
009c6e: 12fc |00a3: const/4 v12, #int -1 // #ff
009c70: f801 0700 0a00 |00a4: +invoke-virtual-quick {v10}, [0007] // vtable #0007
009c76: 0c0d |00a7: move-result-object v13
009c78: f803 7f00 cf0d |00a8: +invoke-virtual-quick {v15, v12, v13}, [007f] // vtable #007f
// goto 0045
009c7e: 339a |00ab: goto 0045 // -0066
exceptions : (none)
positions : 30
0×0000 line=471
…..
0×00a3 line=515
locals : 10
//the 10 local variables defined here
0×0000 - 0×00ac reg=15 this Lcom/google/android/contacts/EditContactActivity;
0×0002 - 0×00ac reg=9 values Landroid/content/ContentValues;
0×000b - 0×00ac reg=11 contactEntryCount I
0×000c - 0×00ac reg=5 i I
0×0018 - 0×0045 reg=8 entry Lcom/google/android/contacts/EditContactActivity$EditEntry;
0×0024 - 0×0045 reg=7 data Ljava/lang/String;
0×004e - 0×00ac reg=10 contactUri Landroid/net/ContentURI;
0×0054 - 0×00ac reg=6 entryCount I
0×0061 - 0×009e reg=8 entry Lcom/google/android/contacts/EditContactActivity$EditEntry;
0×0082 - 0×009e reg=7 data Ljava/lang/String;
又遇到一个unsupported的方法了,每当遇到这玩意就想问候谷歌的祖宗,可惜这贱人没祖宗。。。
翻译好dalvik字节码byte[]数组后,想使用ClassLoader.defineClass()来载入,但可惜这个方法还没实现。。。
如果它的所有的类都要使用ClassLoader来载入的话,那么说明这个方法并不是类载入的唯一途径了。也许它还有直接从文件中载入类的方法吧。。。
所以,只好想办法把得到的字节数组写入文件了。DexFile构造是不可能的,因为它貌似只认.apk文件,还挺纯情的。研究了一下apk文件,貌似里面就一个classes.dex文件,运行的时候会把这个文件拷贝到/data/dalvik-cache中去,并赋予一个诡异的名字,例如:data@app@Jythonroid.apk@classes.dex,我应该尝试直接载入这个文件夹里的文件,看看是不是可以直接用来生成对象:
ps:从http://www.osgi.org/blog/2007/11/android-and-osgi.html看到这么一段话,贱人谷歌!
Unfortunately, Dalvik does not support the normal Class Loader model that is so powerful in standard Java. Instead, the android.dalvik.DexFile class can be used to do class loading, but it is not clear if this is a standard class bound to be available in the future or if this is is an implementation class (android clearly lacks modularization meta data). Even so, the design is awkward; a DexFile models a JAR file with dex files. There is no way to load just bytes as a standard class loader does. The bytes must be in a JAR file, a perfect example of an unnecessary restrictive design. Useful techniques like the Bundle-Classpath where the bundle can contain embedded JARs must be emulated by extracting them to the file system which is of course an unnecessary waste of persistent memory. The bundles must be converted to dex files anyway, so that a tool like bnd could convert the bytecodes and flatten the classpath when the bundle is created.
发现一篇博文关于.dex文件反编译的,还不错,可惜要爬墙才能看到,伟大的墙!
不管了,转载自:http://zeaster.blogspot.com/2007/11/how-to-decompile-dex-file-on-android_28.html
How to decompile .dex file on Android
Lucky to see the Google Android build-in Contacts is also developed by Android SDK.
This article will take it for example and discuss these How-To’s:
1 How to find the Contacts App file on Google Android
2 How to find the Contacts App’s classes.dex on Google Android
3 How to dump the Contacts App’s classes
4 How to decompile the dumped file
{
Actually, I think the Contacts Content Provider that Google provides has some inconvenience to use.
And I will post another blog to list what’s inconvenience and request some improvement from Google.
The blog link is here:
http://zeaster.blogspot.com/2007/11/inconvenienced-when-using-contact.html
}
1 How to find the Contacts App on Google Android
Using adb tool
$ adb shell
# cd /system/app
cd /system/app
# ls
ls
-rw-r–r– root root 25519 2007-11-14 20:40 ContactsProvider.apk
-rw-r–r– root root 7544 2007-11-14 20:40 GoogleAppsProvider.apk
-rw-r–r– root root 16198 2007-11-14 20:40 ImProvider.apk
-rw-r–r– root root 20308 2007-11-14 20:40 MediaProvider.apk
-rw-r–r– root root 21272 2007-11-14 20:40 TelephonyProvider.apk
-rw-r–r– root root 11809 2007-11-14 20:40 SettingsProvider.apk
-rw-r–r– root root 418688 2007-11-14 20:41 Browser.apk
-rw-r–r– root root 68077 2007-11-14 20:41 Contacts.apk
-rw-r–r– root root 96287 2007-11-14 20:41 Development.apk
-rw-r–r– root root 44790 2007-11-14 20:41 GoogleApps.apk
-rw-r–r– root root 8637 2007-11-14 20:41 Fallback.apk
-rw-r–r– root root 99431 2007-11-14 20:41 Home.apk
-rw-r–r– root root 171614 2007-11-14 20:41 Maps.apk
-rw-r–r– root root 424601 2007-11-14 20:41 Phone.apk
-rw-r–r– root root 192119 2007-11-14 20:41 XmppService.apk
-rw-r–r– root root 6614 2007-11-14 20:41 XmppSettings.apk
#
The Contacts.apk is the Contacts App and ContactsProvider.apk is the app that provides the Contact Content Provider for reading and writing “content://contacts/” resources.
we can see many system apps developed by Android Java SDK are put here.
However what is inside *.apk file. Actually it is a .zip file. It is easy to unzip and get the answer.
take Contacts.apk for example, it contains:
META-INF\
res\
AndroidManifest.xml
classes.dex
resources.arsc
The classes.dex file contains all compiled Java code.
2 How to find the Contacts App’s classes.dex on Google Android
No need to unzip Contacts App’s classes.dex file from Contacts.apk file.
The Dalvik VM has a cached file for us. Go head to find it.
$ adb shell
# cd /data/dalvik-cache
cd /data/dalvik-cache
# ls
ls
-rw-rw-rw- root root 73852 2007-11-27 05:16 system@app@Contacts.apk@classes.dex
-rw-rw-rw- app_0 app_0 64172 2007-11-27 05:17 system@app@ContactsProvider.apk@classes.dex
-rw-rw-rw- root root 15204 2007-11-27 05:17 system@framework@am.jar@classes.dex
-rw-rw-rw- app_3 app_3 3012 2007-11-27 07:33 system@app@Fallback.apk@classes.dex
-rw-rw-rw- root root 7252804 2007-11-27 05:16 system@framework@core.jar@classes.dex
….and many other cached files.
3 How to dump the Contacts App’s classes
Unfortunately the classes.dex is not a .jar file. It’s Dalvik executable format that is optimized for efficient.
so we can not pull the .class files as .jar format. However Google Android provides a tool named dexdump included in the emula× to dump .dex file. Here is how to use it.
# dexdump
dexdump: no file specified
dexdump: [-f] [-h] dexfile…
-d : disassemble code sections
-f : display summary information from file header
-h : display file header details
-C : decode (demangle) low-level symbol names
-S : compute sizes only
Now we dump the system@app@Contacts.apk@classes.dex and system@app@ContactsProvider.apk@classes.dex.
# dexdump -d -f -h -C system@app@Contacts.apk@classes.dex >> Contacts.apk.dump
# dexdump -d -f -h -C system@app@ContactsProvider.apk@classes.dex >> ContactsProvider.apk.dump
and then pull it out.
# exit
$ adb pull /data/dalvik-cache/Contacts.apk.dump ~/android
$ adb pull /data/dalvik-cache/ContactsProvider.apk.dump ~/android
Now we get the dumped files on ~/android folder.
4 How to decompile the dumped file
.dex file is optimized, so we can not decompile it as normal .class file.
Fortunately the dumped file is a bit readable for me.
As it is too big to analyze all the code, so I just take “how to create a contact” for example.
It’s easy to get this info from the dumped file.
It has these classes:
…
Class name : ‘com/google/android/contacts/AttachImage’
Class name : ‘com/google/android/contacts/ContactEntryAdapter’
Class name : ‘com/google/android/contacts/EditContactActivity’
Class name : ‘com/google/android/contacts/EditContactActivity$EditEntry’
…
The EditContactActivity class has a private void create() method.
#3 : (in com/google/android/contacts/EditContactActivity)
name : ‘create’
type : ‘()V’
access : 0×0002 (PRIVATE)
So all the code that create a contact is here from the dumped file, my comments inlined:
// private void create()
009b24: |[009b24] com/google/android/contacts/EditContactActivity.create:()V
// ContentValues v9 = new ContentValues();
009b28: 2109 3100 |0000: new-instance v9, android/content/ContentValues // class@0031
009b2c: 6f01 0f00 0900 |0002: invoke-direct {v9}, android/content/ContentValues.:()V // method@000f
// Entry v12 = this.getCurrentEntry();
009b32: f4fc a800 |0005: +iget-object-quick v12, v15, [obj+00a8]
// int v11 = v12.lines
009b36: f801 1500 0c00 |0007: +invoke-virtual-quick {v12}, [0015] // vtable #0015
009b3c: 0a0b |000a: move-result v11
// int v5 = 0
009b3e: 1205 |000b: const/4 v5, #int 0 // #0
// if v5 >= v11 goto 0046
009b40: 3ab5 3a00 |000c: if-ge v5, v11, 0046 // +003a
// EditEntry v8 = (EditEntry)this.getCurrentEntry();
009b44: f4fc a800 |000e: +iget-object-quick v12, v15, [obj+00a8]
009b48: f802 1a00 5c00 |0010: +invoke-virtual-quick {v12, v5}, [001a] // vtable #001a
009b4e: 0c0c |0013: move-result-object v12
009b50: 07c0 |0014: move-object v0, v12
009b52: 1e00 3d00 |0015: check-cast v0, com/google/android/contacts/EditContactActivity$EditEntry // class@003d
009b56: 0708 |0017: move-object v8, v0
// int v12 = v8.kind // The Instance field kind is defined in its superclass Entry. see details in dumped Entry and EditEntry sections.
009b58: f28c 1400 |0018: +iget-quick v12, v8, [obj+0014]
// int v13 = -3 // PHOTO_KIND = -3 defined in v8’s superclass Entry
009b5c: 12dd |001a: const/4 v13, #int -3 // #fd
// if v12 != v13 goto 0020 // a photo entry, goto 0020
009b5e: 38dc 0500 |001b: if-ne v12, v13, 0020 // +0005
// int v5++;
009b62: d805 0501 |001d: add-int/lit8 v5, v5, #int 1 // #01
// goto 000c
009b66: 33ed |001f: goto 000c // -0013
// String v7 = v8.getData();
009b68: f801 0c00 0800 |0020: +invoke-virtual-quick {v8}, [000c] // vtable #000c
009b6e: 0c07 |0023: move-result-object v7
// boolean v12 = TextUtils.isEmpty(v7)
009b70: 7001 4200 0700 |0024: invoke-static {v7}, android/text/TextUtils.isEmpty:(Ljava/lang/CharSequence;)Z // method@0042
009b76: 0a0c |0027: move-result v12
if v12!=false/0 goto 0030
009b78: 3e0c 0800 |0028: if-nez v12, 0030 // +0008
// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009b7c: f48c 2800 |002a: +iget-object-quick v12, v8, [obj+0028]
// v9.put(v12,v7) // v9 is a ContentValues, v12 is the column name, v7 is the value.
009b80: f803 0b00 c907 |002c: +invoke-virtual-quick {v9, v12, v7}, [000b] // vtable #000b
// goto 001d
009b86: 33ee |002f: goto 001d // -0012
// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009b88: f48c 2800 |0030: +iget-object-quick v12, v8, [obj+0028]
// String v13 = “name”;
009b8c: 180d 1c00 |0032: const-string v13, “name” // string@001c
// boolean v12 = v12.equals(v13);
009b90: ee02 0300 dc00 |0034: +execute-inline {v12, v13}, java/lang/String.equals:(Ljava/lang/Object;)Z // inline #0003
009b96: 0a0c |0037: move-result v12
// if v12 != false/0, goto 001d
009b98: 3d0c e5ff |0038: if-eqz v12, 001d // -001b
// String v12 = “EditContactActivity”
009b9c: 180c 0100 |003a: const-string v12, “EditContactActivity” // string@0001
// String v13 = “Name is required”
009ba0: 180d 0200 |003c: const-string v13, “Name is required” // string@0002
// Log.e(v12, v13); //log info using android/util/Log
009ba4: 7002 4700 dc00 |003e: invoke-static {v12, v13}, android/util/Log.e:(Ljava/lang/String;Ljava/lang/String;)I // method@0047
009baa: 0a0c |0041: move-result v12
// call this.XXX()
009bac: f801 8400 0f00 |0042: +invoke-virtual-quick {v15}, [0084] // vtable #0084
// return void
009bb2: 0e00 |0045: return-void
// ContentResolver v12 = this.getContentResolver();
009bb4: f4fc a400 |0046: +iget-object-quick v12, v15, [obj+00a4]
// CONTENT_URI v13 = People.CONTENT_URI;
009bb8: 610d 0300 |0048: sget-object v13, android/provider/Contacts$People.CONTENT_URI:Landroid/net/ContentURI; // field@0003
// CONTENT_URI v10 = v12.insert(v13, v9) // CONTENT_URI v10 = getContentResolver().insert(uri, values);
// insert people into contacts
009bbc: f803 1100 dc09 |004a: +invoke-virtual-quick {v12, v13, v9}, [0011] // vtable #0011
009bc2: 0c0a |004d: move-result-object v10
// List v12 = this.mContactEntries
009bc4: f4fc c000 |004e: +iget-object-quick v12, v15, [obj+00c0]
009bc8: 7001 8900 0c00 |0050: invoke-static {v12}, com/google/android/contacts/ContactEntryAdapter.countEntries:(Ljava/util/ArrayList;)I // method@0089
009bce: 0a06 |0053: move-result v6
// int v5 = v11 //contact Entry Count
009bd0: 01b5 |0054: move v5, v11
// if v5 >= v6, goto 009e
009bd2: 3a65 4900 |0055: if-ge v5, v6, 009e // +0049
// List v12 = this.mContactEntries
009bd6: f4fc c000 |0057: +iget-object-quick v12, v15, [obj+00c0]
// EditEntry v8 = (EditEntry) getEntry(v12, v5);
009bda: 7002 8b00 5c00 |0059: invoke-static {v12, v5}, com/google/android/contacts/ContactEntryAdapter.getEntry:(Ljava/util/ArrayList;I)Lcom/google/android/contacts/ContactEntryAdapter$Entry; // method@008b
009be0: 0c0c |005c: move-result-object v12
009be2: 07c0 |005d: move-object v0, v12
009be4: 1e00 3d00 |005e: check-cast v0, com/google/android/contacts/EditContactActivity$EditEntry // class@003d
009be8: 0708 |0060: move-object v8, v0
// int v12 = v8.kind // The Instance field kind is defined in its superclass Entry. see details in dumped Entry and EditEntry sections.
009bea: f28c 1400 |0061: +iget-quick v12, v8, [obj+0014]
// int v13 = -1 // CONTACT_KIND = -1 defined in v8’s superclass Entry
009bee: 12fd |0063: const/4 v13, #int -1 // #ff
// if v12 == v13 goto 007e
009bf0: 37dc 1a00 |0064: if-eq v12, v13, 007e // +001a
// v9.clear() // ContentValues.clear()
009bf4: f801 1500 0900 |0066: +invoke-virtual-quick {v9}, [0015] // vtable #0015
// boolean v12 = v8.toValues(v9) // boolean EditEnty.toValues(ContentValues cv)
009bfa: f802 0d00 9800 |0069: +invoke-virtual-quick {v8, v9}, [000d] // vtable #000d
009c00: 0a0c |006c: move-result v12
//if v12 !=false/0, goto 007b
009c02: 3d0c 0e00 |006d: if-eqz v12, 007b // +000e
// ContentResolver v12 = this.getContentResolver();
009c06: f4fc a400 |006f: +iget-object-quick v12, v15, [obj+00a4]
// String v13 = v8.contentDirec×y
009c0a: f48d 2c00 |0071: +iget-object-quick v13, v8, [obj+002c]
// v10.addPath(v13);
009c0e: f802 1800 da00 |0073: +invoke-virtual-quick {v10, v13}, [0018] // vtable #0018
009c14: 0c0d |0076: move-result-object v13
// CONTENT_URI v12 = v12.insert(v13, v9) // CONTENT_URI v12 = getContentResolver().insert(uri, values);
009c16: f803 1100 dc09 |0077: +invoke-virtual-quick {v12, v13, v9}, [0011] // vtable #0011
009c1c: 0c0c |007a: move-result-object v12
// int v5++;
009c1e: d805 0501 |007b: add-int/lit8 v5, v5, #int 1 // #01
// goto 0055
009c22: 33d8 |007d: goto 0055 // -0028
// String v7 = v8.getData();
009c24: f801 0c00 0800 |007e: +invoke-virtual-quick {v8}, [000c] // vtable #000c
009c2a: 0c07 |0081: move-result-object v7
// v9.lines
009c2c: f801 1500 0900 |0082: +invoke-virtual-quick {v9}, [0015] // vtable #0015
// boolean v12 = TextUtils.isEmpty(v7)
009c32: 7001 4200 0700 |0085: invoke-static {v7}, android/text/TextUtils.isEmpty:(Ljava/lang/CharSequence;)Z // method@0042
009c38: 0a0c |0088: move-result v12
if v12!=false/0 goto 007b
009c3a: 3e0c f2ff |0089: if-nez v12, 007b // -000e
// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009c3e: f48c 2800 |008b: +iget-object-quick v12, v8, [obj+0028]
// v9.put(v12,v7) // v9 is a ContentValues, v12 is the column name, v7 is the value.
009c42: f803 0b00 c907 |008d: +invoke-virtual-quick {v9, v12, v7}, [000b] // vtable #000b
// ContentResolver v12 = this.getContentResolver();
009c48: f4fc a400 |0090: +iget-object-quick v12, v15, [obj+00a4]
// int v13 = 0
009c4c: 120d |0092: const/4 v13, #int 0 // #0
// int v14 = 0
009c4e: 120e |0093: const/4 v14, #int 0 // #0
009c50: 07c0 |0094: move-object v0, v12
009c52: 07a1 |0095: move-object v1, v10
009c54: 0792 |0096: move-object v2, v9
009c56: 07d3 |0097: move-object v3, v13
009c58: 07e4 |0098: move-object v4, v14
// int v12 = v12.update(v1,v2,v3,v4) // getContentResolver(),update(uri, values, null, null)
// refer to this method, public final int update(ContentURI uri, ContentValues values, String where, String[] selectionArgs)
009c5a: f905 1400 0000 |0099: +invoke-virtual-quick/range {v0, v1, v2, v3, v4}, [0014] // vtable #0014
009c60: 0a0c |009c: move-result v12
// goto 007b
009c62: 33de |009d: goto 007b // -0022
// int v12 = 4
009c64: 124c |009e: const/4 v12, #int 4 // #4
009c66: f5fc c800 |009f: +iput-quick v12, v15, [obj+00c8]
009c6a: f7fa 9000 |00a1: +iput-object-quick v10, v15, [obj+0090]
// int v12 = -1
009c6e: 12fc |00a3: const/4 v12, #int -1 // #ff
009c70: f801 0700 0a00 |00a4: +invoke-virtual-quick {v10}, [0007] // vtable #0007
009c76: 0c0d |00a7: move-result-object v13
009c78: f803 7f00 cf0d |00a8: +invoke-virtual-quick {v15, v12, v13}, [007f] // vtable #007f
// goto 0045
009c7e: 339a |00ab: goto 0045 // -0066
exceptions : (none)
positions : 30
0×0000 line=471
…..
0×00a3 line=515
locals : 10
//the 10 local variables defined here
0×0000 - 0×00ac reg=15 this Lcom/google/android/contacts/EditContactActivity;
0×0002 - 0×00ac reg=9 values Landroid/content/ContentValues;
0×000b - 0×00ac reg=11 contactEntryCount I
0×000c - 0×00ac reg=5 i I
0×0018 - 0×0045 reg=8 entry Lcom/google/android/contacts/EditContactActivity$EditEntry;
0×0024 - 0×0045 reg=7 data Ljava/lang/String;
0×004e - 0×00ac reg=10 contactUri Landroid/net/ContentURI;
0×0054 - 0×00ac reg=6 entryCount I
0×0061 - 0×009e reg=8 entry Lcom/google/android/contacts/EditContactActivity$EditEntry;
0×0082 - 0×009e reg=7 data Ljava/lang/String;