1. 在进入OfflineDataManageActivity时,找到 onCreate() 方法,在最后几行:
new-instance v0, Lcom/baidu/bus/d/i; iget-object v1, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->U:Landroid/os/Handler; invoke-direct {v0, v1}, Lcom/baidu/bus/d/i;-><init>(Landroid/os/Handler;)V iput-object v0, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->A:Lcom/baidu/bus/d/i; iget-object v0, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->A:Lcom/baidu/bus/d/i; invoke-virtual {v0}, Lcom/baidu/bus/d/i;->a()V
翻译为Java代码就是:
this.A = new com.baidu.bus.d.i(this.U); this.A.a();
上面的 U 是一个 com.baidu.bus.activity.bv 的对象,bv 是从 Handler 继承的类。首先看 A.a() 干了什么。
2. 在com.baidu.bus.d.i 类中找到 a() 方法,关键的代码是下面几行:
new-instance v0, Lcom/baidu/bus/net/b/a; const-string v1, "city_list" invoke-direct {v0, v1}, Lcom/baidu/bus/net/b/a;-><init>(Ljava/lang/String;)V iput-object v0, p0, Lcom/baidu/bus/d/i;->c:Lcom/baidu/bus/net/b/a; iget-object v0, p0, Lcom/baidu/bus/d/i;->c:Lcom/baidu/bus/net/b/a; iget-object v1, p0, Lcom/baidu/bus/d/i;->e:Lcom/baidu/bus/net/a/c; invoke-virtual {v0, v1}, Lcom/baidu/bus/net/b/a;->a(Lcom/baidu/a/a/k;)Z sget-object v0, Lcom/baidu/bus/activity/App;->g:Lcom/baidu/a/a/l; invoke-virtual {v0}, Lcom/baidu/a/a/l;->a()Lcom/baidu/a/a/h; move-result-object v0 iget-object v1, p0, Lcom/baidu/bus/d/i;->c:Lcom/baidu/bus/net/b/a; invoke-virtual {v0, v1}, Lcom/baidu/a/a/h;->c(Lcom/baidu/a/a/m;)Z
这几行的代码翻译为Java代码就是:
this.c = new com.baidu.bus.net.b.a("city_list"); this.c.a(this.e); App.g.a().c(this.c);
在这里,e 成员是com.baidu.bus.net.a.c 的对象,查看这个类:
.class public abstract Lcom/baidu/bus/net/a/c; .super Ljava/lang/Object; .implements Lcom/baidu/a/a/k; .implements Lcom/baidu/bus/net/a/a;
实现了两个接口类,分别是 com.baidu.a.a.k 和 com.baidu.bus.net.a.a,从构造函数中:
const-string v0, "<<ModelCallBack" iput-object v0, p0, Lcom/baidu/bus/net/a/c;->b:Ljava/lang/String;
这两行代码是将类中的成员b赋了一个“<<ModelCallBack”的字符串,以此可以推测,这个类是HTTP收到 Response 后的回调。
3. 继续看 com.baidu.bus.net.b.a 类:
.class public final Lcom/baidu/bus/net/b/a; .super Lcom/baidu/bus/base/f;
从 com.baidu.bus.base.f 类继承而来,没有 a() 方法,应该是父类的方法。但在这个类的构造函数里面可以看到:
const-string v0, "http://bs.baidu.com/offlinebusdata/prov_city_list.json" iput-object v0, p0, Lcom/baidu/bus/net/b/a;->c:Ljava/lang/String; iput-object p1, p0, Lcom/baidu/bus/net/b/a;->b:Ljava/lang/String;
翻译为Java代码是:
this.c = "http://bs.baidu.com/offlinebusdata/prov_city_list.json"; this.b = paramString;
这里的 paramString 就是传入的 city_list 字符串。在浏览器中查看this.c 的地址,下载了一个 prov_city_list.json 的文件,打开可以看到下面内容:
{ "errorNo": 0, "name": "中国", "allList": [...], "hotCityList": [...] }
在这里将城市列表隐去,在里面可以查看城市的 id 和 name。
4. 继续看com.baidu.bus.base.f 类:
.class public abstract Lcom/baidu/bus/base/f; .super Lcom/baidu/a/a/m;
可以看到,父类是 com.baidu.a.a.m,在这个类里面有 a() 方法:
.method public final a()Ljava/lang/Object; .method public final a(Ljava/lang/Object;)V .method public final a(Ljava/lang/String;Ljava/lang/String;)V .method public final declared-synchronized a(Lcom/baidu/a/a/k;)Z
有多个 a() 方法。这就是代码混淆后的迷惑作用。由于在 2 中传入的参数实现了 k 接口,因此要看最后一个方法,这个方法里调用了父类的 b() 方法:
invoke-super {p0, p1}, Lcom/baidu/a/a/m;->b(Lcom/baidu/a/a/k;)Z
5. 查看 com.baidu.a.a.m 类的 b() 方法,这个方法的关键代码就是下面这行:
iget-object v0, p0, Lcom/baidu/a/a/m;->g:Ljava/util/List; invoke-interface {v0, p1}, Ljava/util/List;->add(Ljava/lang/Object;)Z
把参数传过来的 com.baidu.a.a.k 对象添加到 g 成员里,g 就是一个 List. 因此 2 中的:
this.c.a(this.e);
就是向类成员 c (com.baidu.bus.net.b.a) 包含的一个成员中的 List 中添加了一个用于回调的 e (com.baidu.bus.net.a.c) 对象。接下来是下一行:
App.g.a().c(this.c);
无脑猜测,这行代码就是要执行c对象,也就是下载 http://bs.baidu.com/offlinebusdata/prov_city_list.json 这个链接了。
6. App.g 是一个 com.baidu.a.a.l 的对象,它的 a() 方法返回一个 com.baidu.a.a.h 的对象,查看 h 里面的 c() 方法。在这个方法中,可以看到下面代码:
new-instance v3, Lcom/baidu/a/a/c; iget-object v4, p0, Lcom/baidu/a/a/h;->a:Lcom/baidu/a/a/l; invoke-direct {v3, v4, p1}, Lcom/baidu/a/a/c;-><init>(Lcom/baidu/a/a/l;Lcom/baidu/a/a/m;)V
这是根据 this.a (com.baidu.a.a.h) 和传入的 com.baidu.a.a.m 对象构造了一个 com.baidu.a.a.c 的对象,然后调用了 c.c() 方法,返回 Boolean 类型:
invoke-virtual {v3}, Lcom/baidu/a/a/c;->c()Z
7. 查看 com.baidu.a.a.c 中的 c() 方法,这个方法包含下面代码:
new-instance v0, Lcom/baidu/a/a/d; invoke-direct {v0, p0}, Lcom/baidu/a/a/d;-><init>(Lcom/baidu/a/a/c;)V iput-object v0, p0, Lcom/baidu/a/a/c;->d:Lcom/baidu/a/a/d; iget-object v0, p0, Lcom/baidu/a/a/c;->d:Lcom/baidu/a/a/d; invoke-virtual {v0}, Lcom/baidu/a/a/d;->start()V
8. 查看 com.baidu.a.a.d 类:
.class final Lcom/baidu/a/a/d; .super Ljava/lang/Thread;
从 Thread 派生出的类,那就查看 run() 方法,有下面两行代码:
iget-object v0, p0, Lcom/baidu/a/a/d;->a:Lcom/baidu/a/a/c; invoke-virtual {v0}, Lcom/baidu/a/a/c;->d()Lorg/apache/http/HttpResponse;
在构造函数中可以看到,this.a 就是传入的 com.baidu.a.a.c 对象,在这里调用了它的 d() 方法,结果返回的是一个 HttpResponse 对象。
9. 查看 com.baidu.a.a.c 里面的 d() 方法,现在可以断定这个方法是发送HTTP请求,因此直接查找相关的代码:
new-instance v0, Lorg/apache/http/impl/client/DefaultHttpClient; invoke-direct {v0, v1}, Lorg/apache/http/impl/client/DefaultHttpClient;-><init>(Lorg/apache/http/params/HttpParams;)V
构造了一个 DefaultHttpClient 对象,传入的参数暂时忽略。查看调用 execute 的地方:
iget-object v1, p0, Lcom/baidu/a/a/c;->a:Lcom/baidu/a/a/m; invoke-virtual {v1}, Lcom/baidu/a/a/m;->b()Lorg/apache/http/client/methods/HttpUriRequest; move-result-object v1 invoke-virtual {v0, v1}, Lorg/apache/http/impl/client/AbstractHttpClient;->execute(Lorg/apache/http/client/methods/HttpUriRequest;)Lorg/apache/http/HttpResponse;
在构造函数中可以看到,this.a 就是传入的第二个参数,一个 com.baidu.a.a.m 对象,回到第(6)中可以知道,这个对象是在(2)中构造的c对象:
this.c = new com.baidu.bus.net.b.a("city_list");
10. 查看 com.baidu.bus.net.b.a 的 b() 方法:
.method public final b()Lorg/apache/http/client/methods/HttpUriRequest; .locals 3 new-instance v0, Lorg/apache/http/client/methods/HttpGet; iget-object v1, p0, Lcom/baidu/bus/net/b/a;->c:Ljava/lang/String; invoke-direct {v0, v1}, Lorg/apache/http/client/methods/HttpGet;-><init>(Ljava/lang/String;)V const-string v1, "Accept-Encoding" const-string v2, "gzip,deflate" invoke-interface {v0, v1, v2}, Lorg/apache/http/client/methods/HttpUriRequest;->addHeader(Ljava/lang/String;Ljava/lang/String;)V return-object v0 .end method
根据 this.c 成员(字符串)构造了一个HttpGet对象,添加了一个Header (Accept-Encoding: gzip,deflate),然后返回这个 HttpGet 对象。从(3)里可以知道,this.c 就是这个URL:
http://bs.baidu.com/offlinebusdata/prov_city_list.json
11. 这且还没有完,还没有解决下载后的数据如何解析,如何在 OfflineDataManageActivity 中展示。
12. 继续查看 com.baidu.a.a.d (从Thread类派生)类的 run() 方法,在取得 HttpResponse 之后,调用了两个静态方法:
iget-object v2, p0, Lcom/baidu/a/a/d;->a:Lcom/baidu/a/a/c; iget-object v3, p0, Lcom/baidu/a/a/d;->a:Lcom/baidu/a/a/c; invoke-static {v3}, Lcom/baidu/a/a/c;->c(Lcom/baidu/a/a/c;)Lcom/baidu/a/a/m; move-result-object v3 invoke-virtual {v3}, Lcom/baidu/a/a/m;->e()I invoke-static {v2, p0, v0}, Lcom/baidu/a/a/c;->a(Lcom/baidu/a/a/c;Lcom/baidu/a/a/d;Lorg/apache/http/HttpResponse;)V
查看 com.baidu.bus.a.a.c 类中的 a() 方法,调用了 HttpResponse 的 getEntity() ,获取 HttpEntity 之后调用了 getContent 获取了一个 InputStream,并调用 read() 读取了里面的内容,但是没有对 bytes 做任何处理。忽略。
iget-object v1, p0, Lcom/baidu/a/a/d;->a:Lcom/baidu/a/a/c; invoke-static {v1}, Lcom/baidu/a/a/c;->a(Lcom/baidu/a/a/c;)Lcom/baidu/a/a/e; move-result-object v1 invoke-virtual {v1, v0}, Lcom/baidu/a/a/e;->a(Lorg/apache/http/HttpResponse;)V
调用 com.baidu.a.a.c 里的一个静态方法 a() 获取一个 com.baidu.a.a.e 对象,然后调用 e 对象的 a() 方法,传入 HttpResponse。
13. 查看 com.baidu.a.a.e 中的 a() 方法,这个方法是分发 HttpResponse,对成员 b (com.baidu.a.a.m) 中的一个 List 中包含的所有对象分发 HttpResponse。主要代码是下面两行:
iget-object v2, p0, Lcom/baidu/a/a/e;->b:Lcom/baidu/a/a/m; invoke-interface {v0, v2, p1}, Lcom/baidu/a/a/k;->a(Lcom/baidu/a/a/m;Lorg/apache/http/HttpResponse;)V
上面的 v0 的类型是 com.baidu.a.a.k,是这样取得的:
iget-object v0, p0, Lcom/baidu/a/a/e;->b:Lcom/baidu/a/a/m; invoke-virtual {v0}, Lcom/baidu/a/a/m;->c()Ljava/util/List; move-result-object v0 invoke-interface {v0}, Ljava/util/List;->iterator()Ljava/util/Iterator; move-result-object v1 ... invoke-interface {v1}, Ljava/util/Iterator;->next()Ljava/lang/Object; move-result-object v0 check-cast v0, Lcom/baidu/a/a/k;
即
List list = this.b.c(); iter = list.iterator(); v0 = iter.next();
成员 b 是一个 com.baidu.a.a.m 的对象,它的 c() 方法返回对象内的 g 成员,这是一个 List 的对象(ArrayList)。在 (5) 中我们知道,com.baidu.bus.d.i 向这个 List 里添加了一个元素,I 类中的 e (com.baidu.bus.net.a.c). 现在就调用它的 a() 方法:
v0.a(this.b, httpResponse);
同时我们知道,com.baidu.bus.net.a.c 实现了接口类 com.baidu.a.a.k,所以调用 k.a() 是完全没有问题的。
14. 接下来看 com.baidu.bus.net.a.c 类的 a() 方法,这个方法主要是读取 HttpResponse 中的数据,稍微特殊一点的地方就是,如果内容是压缩的(Content-Encoding: gzip),那么回调用com.baidu.bus.i.f 类的 a() 方法,看起来这个类是解压 gzip 的。获取到内容后,用UTF-8 进行解码,然后看下面代码:
new-instance v1, Landroid/os/Message; invoke-direct {v1}, Landroid/os/Message;-><init>()V const/4 v2, 0x4 iput v2, v1, Landroid/os/Message;->what:I ... iput-object v2, v1, Landroid/os/Message;->obj:Ljava/lang/Object; iget-object v0, p0, Lcom/baidu/bus/net/a/c;->a:Landroid/os/Handler; invoke-virtual {v0, v1}, Landroid/os/Handler;->sendMessage(Landroid/os/Message;)Z
这里的 hObject 是一个 com.baidu.bus.net.a.h 的对象,h 类是一个简单的数据类,定义如下:
public final class h { public com.baidu.a.a.m a; public HttpResponse b; public com.baidu.bus.f.a c; }
在 com.baidu.bus.net.a.c 类的 a() 方法中,只为h类中的 a 和 c 进行了赋值:
invoke-static {v0, p1}, Lcom/baidu/bus/net/a/b;->a(Ljava/lang/String;Lcom/baidu/a/a/m;)Lcom/baidu/bus/f/a; move-result-object v0 ... iput-object v0, v2, Lcom/baidu/bus/net/a/h;->c:Lcom/baidu/bus/f/a; iput-object p1, v2, Lcom/baidu/bus/net/a/h;->a:Lcom/baidu/a/a/m;
上面的 m 就是向 a() 方法传递的第1个参数,类型是 com.baidu.a.a.m,即 com.baidu.a.a.e 类中的 b 成员。处理这个消息的 a 是 com.baidu.bus.net.a.d 类的对象。
15. 查看 com.baidu.bus.net.a.d 的 handleMessage() 方法,取出 Message 的 what 值,进入一个 switch 语句,上面传入的值是4,因此进入了 pswitch_3。
iget-object v1, p0, Lcom/baidu/bus/net/a/d;->a:Lcom/baidu/bus/net/a/c; iget-object v2, v0, Lcom/baidu/bus/net/a/h;->c:Lcom/baidu/bus/f/a; … iget-object v0, v0, Lcom/baidu/bus/net/a/h;->a:Lcom/baidu/a/a/m; invoke-virtual {v1, v2, v0}, Lcom/baidu/bus/net/a/c;->a(Lcom/baidu/bus/f/a;Lcom/baidu/a/a/m;)V
类里的 a 成员是一个 com.baidu.bus.net.a.c 的对象,它是在 com.baidu.bus.net.a.d 初始化时传入的参数;com.baidu.bus.net.a.d 初始化是在 com.baidu.bus.net.a.c 中的,传入的是 this 。因此接下来查看 com.baidu.bus.net.a.c 类的 a() 方法。
16. 查看 com.baidu.bus.net.a.c 类的 a() 方法,很可惜,没有找到合适的方法。观察到 c 类是一个抽象类,并且从 (2) 中得知,它的实例是 com.baidu.bus.d.i 类中的成员 e,查看 com.baidu.bus.d.i 类中 e 的初始化:
new-instance v0, Lcom/baidu/bus/d/j; invoke-direct {v0, p0}, Lcom/baidu/bus/d/j;-><init>(Lcom/baidu/bus/d/i;)V iput-object v0, p0, Lcom/baidu/bus/d/i;->e:Lcom/baidu/bus/net/a/c;
因此要从 com.baidu.bus.d.j 类中找相应的 a() 方法,查看com.baidu.bus.d.j 类:
.class final Lcom/baidu/bus/d/j; .super Lcom/baidu/bus/net/a/c;
果然从 com.baidu.bus.net.a.c 继承,并且只有一个方法,正是我们要找的方法。
17. 查看 com.baidu.bus.d.j 中的 a() 方法,看到下面的代码:
iget-object v0, p0, Lcom/baidu/bus/d/j;->a:Lcom/baidu/bus/d/i; check-cast p1, Lcom/baidu/bus/f/b; invoke-static {v0, p1}, Lcom/baidu/bus/d/i;->a(Lcom/baidu/bus/d/i;Lcom/baidu/bus/f/b;)V
可以看到,调用了 com.baidu.bus.d.i 的静态方法 a(),第一个参数 this.a 是一个 com.baidu.bus.d.i 的对象,第二个参数是传入的第一个参数,即上面的 hObject.a,并转换为 com.baidu.bus.f.b 类型。查看 com.baidu.bus.d.i 中的 a() 方法,只是将传入的 paramA 赋给了类里面的成员d。回到 com.baidu.bus.d.j 中继续看下面的代码。然后,发送了一个Message:
new-instance v0, Landroid/os/Message; invoke-direct {v0}, Landroid/os/Message;-><init>()V const/16 v1, 0x3e9 iput v1, v0, Landroid/os/Message;->what:I iget-object v1, p0, Lcom/baidu/bus/d/j;->a:Lcom/baidu/bus/d/i; invoke-static {v1}, Lcom/baidu/bus/d/i;->a(Lcom/baidu/bus/d/i;)Lcom/baidu/bus/f/b; move-result-object v1 iput-object v1, v0, Landroid/os/Message;->obj:Ljava/lang/Object; iget-object v1, p0, Lcom/baidu/bus/d/j;->a:Lcom/baidu/bus/d/i; invoke-static {v1}, Lcom/baidu/bus/d/i;->b(Lcom/baidu/bus/d/i;)Landroid/os/Handler; move-result-object v1 invoke-virtual {v1, v0}, Landroid/os/Handler;->sendMessage(Landroid/os/Message;)Z
翻译为Java代码:
message = new Message(); message.what = 0x3e9; // 1001 message.obj = com.baidu.bus.d.i.a(this.a); com.baidu.bus.d.i.b(this.a).sendMessage(message);
创建 Message,what 赋值为1001;查看 com.baidu.bus.d.i 中的 a() 方法(需要注意参数的类型和个数,混淆后的代码,有很多函数名是一样的,只能根据参数类型和个数区分),只是获取类里面的成员d(还记得刚才赋过值吗),其实就是刚才传入的 paramA,即这个函数内的第一个参数;然后调用 b() 方法,取得了类里面的 b 成员,它是个 Handler 对象,发送了创建的 Message。通过第1步我们知道,com.baidu.bus.d.i 里面的b成员,是 OfflineDataManageActivity 类里面的U成员,它是一个 com.baidu.bus.activity.bv 对象。
18. 查看 com.baidu.bus.activity.bv 类里面的 handleMessage() 方法,在 packed-switch 中正好有我们要找的 0x3e9 (1001),并且只有这一个分支:
.packed-switch 0x3e9 :pswitch_0 .end packed-switch
在分支的代码中,找到这样的代码:
iget-object v0, p1, Landroid/os/Message;->obj:Ljava/lang/Object; check-cast v0, Lcom/baidu/bus/f/b; invoke-static {v1, v0}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->a(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/f/b;)V
19. 回到 OfflineDataManageActivity 中查看 a() 方法,直接把传入的对象赋给了 n 成员。再回到 com.baidu.bus.activity.bv 的 handleMessage() 方法中,接下来的代码:
invoke-static {v1}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->a(Lcom/baidu/bus/activity/OfflineDataManageActivity;)Lcom/baidu/bus/f/b; move-result-object v1 invoke-static {v0, v1}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->b(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/f/b;)V
不用想,第一行的 a() 方法肯定是获取刚才赋过值的 n 成员,接下来看它的 b() 方法。
20. 查看 OfflineDataManageActivity 的 b() 方法,完整的代码如下:
new-instance v0, Lcom/baidu/bus/activity/ch; invoke-direct {v0, p0}, Lcom/baidu/bus/activity/ch;-><init>(Lcom/baidu/bus/activity/OfflineDataManageActivity;)V iput-object v0, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->B:Lcom/baidu/bus/activity/ch; iget-object v0, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->B:Lcom/baidu/bus/activity/ch; const/4 v1, 0x1 new-array v1, v1, [Lcom/baidu/bus/f/b; const/4 v2, 0x0 aput-object p1, v1, v2 invoke-virtual {v0, v1}, Lcom/baidu/bus/activity/ch;->execute([Ljava/lang/Object;)Landroid/os/AsyncTask; return-void
翻译为Java代码:
v0 = new com.baidu.bus.activity.ch(offlineDataManageActivity); v0.B = offlineDataManageActivity; v1 = new com.baidu.bus.f.b [] {p1}; v0.execute(v1);
上面的p1就是传入的 com.baidu.bus.f.b 对象。
21. 查看 com.baidu.bus.activity.ch 类,可以看到,它是从 AsyncTask 派生出来的:
.class final Lcom/baidu/bus/activity/ch; .super Landroid/os/AsyncTask;
那么需要查看doInBackground() 方法,发现它直接调用了它的 a() 方法,查看 a() 方法。
22. 查看 com.baidu.bus.activity.ch 类里的 a() 方法,这个方法很长而且很无聊,大体就是将传入的 com.baidu.bus.f.b 对象中的 b 成员,附加到 ExpandableListAdapter 上,我们只要略微了解一下ExpandableListView 和 ExpandableLsitAdapter 的用法即可。此处,附加的过程略去,只要知道以下结果:
(1) OfflineDataManageActivity.L 保存省份,每个元素是 com.baidu.bus.b.f 的对象;
(2) com.baidu.bus.b.f 类里面有一个 List 类型的成员d,它保存着该省份下面的每个城市,每个元素都是 com.baidu.bus.b.a 的对象。
前面已经介绍了ExpandableView 如何响应点击事件,后面的过程就不赘述了。