[原]百度公交离线数据格式分析——2.从界面点击下载的流程

首先找到离线下载的界面(Activity),使用Apktool将APK包decode一下(Apktool的使用方法请参考官方文档)。这样decode之后生成的是源文件是.smali格式的,在这里也可以使用其他工具(如dex2jar+Java Decompiler或者Procyon)直接输出可读性更好的java文件,但是由于java的反编译或多或少存在一些问题,尤其对于inner class (delvik 中的 synthetic 方法)支持都不好,我就直接用smali了。可以看到,代码进行了简单的混淆。

1. 在 res/layout 下面找相应的 layout,可以看到,这些文件没有混淆,可以通过名字很容易的找到: offlinedata_manage_layout.xml.

2. 在 res/values/public.xml 中搜索“offlinedata_manage_layout”,找到对应的值 0x7f030024。

3. 在代码中搜索这个整数,0x7f030024 以及对应的 10 进制的值 2130903076,找到 com.baidu.bus.activity.OfflineDataManageActivity,在onCreate方法中(省略部分代码):

# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    const v0, 0x7f030024
    invoke-virtual {p0, v0}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->setContentView(I)V

4. 由 1 可知,在这个 layout 中只有一个 ExpandableListView,id 为 expandlistview_all_cities,对应的值为 0x7f06009f,在 OfflineDataManageActivity 中找到这个值使用的地方:

const v0, 0x7f06009f
invoke-virtual {p0, v0}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/ExpandableListView;
iput-object v0, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->z:Landroid/widget/ExpandableListView;

这段代码是查找 id 为 0x7f06009f 的 View,转换为 ExpandableListView 类型,并赋给 this.z 成员,类似:

this.z = (ExpandableListView) findViewById(0x7f06009f);

然后注意到 z.setAdapter() 的调用:

iget-object v0, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->z:Landroid/widget/ExpandableListView;
iget-object v1, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->y:Lcom/baidu/bus/activity/ck;
invoke-virtual {v0, v1}, Landroid/widget/ExpandableListView;->setAdapter(Landroid/widget/ExpandableListAdapter;)V

这段代码翻译一下就是:

this.z.setAdapter(this.y);

同时得知y的类型是com.baidu.bus.activity.ck,于是找到这个文件。

5. 在com/baidu/bus/activity/ck.smali 中开头两行可以看到:

.class final Lcom/baidu/bus/activity/ck;
.super Landroid/widget/BaseExpandableListAdapter;

这个类是从 BaseExpandableListAdapter 派生而来,于是可以断定这就是我们要找的类。在 getChildView() 中:

iget-object v1, p0, Lcom/baidu/bus/activity/ck;->a:Lcom/baidu/bus/activity/OfflineDataManageActivity;
invoke-static {v1}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->d(Lcom/baidu/bus/activity/OfflineDataManageActivity;)Landroid/view/LayoutInflater;
move-result-object v1
const v2, 0x7f030020
const/4 v3, 0x0
invoke-virtual {v1, v2, v3}, Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;)Landroid/view/View;

这段代码是调用 OfflineDataManageActivity 的一个静态方法 d() 获取一个 LayoutInflater 的实例,然后调用 inflate 找到 id 为 0x7f030020 的 layout,在 res/values/public.xml 中查找这个 id,发现是 offlinedata_manage_city_item。做了一系列动作(主要是创建了一个 com.baidu.bus.activity.cf,并且将 offlinedata_manage_city_item 上的各个控件赋给了 cf 的成员)之后,调用了下面一个方法:

invoke-virtual {v1, v2, v3}, Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;)Landroid/view/View;
move-result-object p4
...
iget-object v2, p0, Lcom/baidu/bus/activity/ck;->a:Lcom/baidu/bus/activity/OfflineDataManageActivity;
invoke-static {v2, v0, p4}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->a(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/b/a;Landroid/view/View;)Z

在 OfflineDataManagerActivity 中,对应的 a 方法是一个 synthetic 的:

.method static synthetic a(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/b/a;Landroid/view/View;)Z
    .locals 1
    const/4 v0, 0x1
    invoke-direct {p0, p1, p2, v0}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->a(Lcom/baidu/bus/b/a;Landroid/view/View;Z)Z
    move-result v0
    return v0
.end method

这段代码翻译后就是:

static boolean synthetic a(OfflineDataManageActivity activity, com.baidu.bus.b.a parama, View view)
{
    return activity.a(parama, view, true);
}

可以看到,调用的 a() 方法是 private 的:

.method private a(Lcom/baidu/bus/b/a;Landroid/view/View;Z)Z

在这个方法里,找到这段代码:

const v6, 0x7f060094
invoke-virtual {p2, v6}, Landroid/view/View;->findViewById(I)Landroid/view/View;
move-result-object v6
check-cast v6, Landroid/widget/FrameLayout;
......
new-instance v0, Lcom/baidu/bus/activity/ca;
invoke-direct {v0, p0}, Lcom/baidu/bus/activity/ca;-><init>(Lcom/baidu/bus/activity/OfflineDataManageActivity;)V
invoke-virtual {v6, v0}, Landroid/widget/FrameLayout;->setOnClickListener(Landroid/view/View$OnClickListener;)V

这段代码翻译后就是这样的:

FrameLayout frame = (FrameLayout) view.findViewById(0x7f060094);
frame.setOnClickListener(new ca(this));

在 res/values/public.xml 中可以看到,0x7f060094 对应的 id 是 fl_op,它在 res/layout/offlinedata_manage_city_item.xml 里面,这个 FrameLayout 有3个 ImageView,对应的图形分别是:

  start_download

  stop_download

  continue_download

6. 接下来分析 com.baidu.bus.activity.ca 类,在前几行可以看到:

.class final Lcom/baidu/bus/activity/ca;
.super Ljava/lang/Object;
.implements Landroid/view/View$OnClickListener;

这个类实现了 View.OnClickListener 的接口,接下来就看 onClick() 方法中干了什么。找到

invoke-virtual {p1}, Landroid/view/View;->getTag()Ljava/lang/Object;
move-result-object v0
...
iget v2, v0, Lcom/baidu/bus/b/a;->f:I
packed-switch v2, :pswitch_data_0

这里出现了一个 switch,查看 table - pswitch_data_0 :

:pswitch_data_0
.packed-switch 0x0
    :pswitch_0
    :pswitch_1
    :pswitch_2
    :pswitch_3
    :pswitch_0
.end packed-switch

只有4种可能,0-3. pswitch_0 的代码是这样的:

:pswitch_0
return-void

看来是个非法的值,所以default也会执行到这里。再观察1-3的分支,可以看到,2和3都调用了 com.baidu.bus.i.f->a(ApplicationContext, message, 0) 这个方法。2的调用:

const-string v3, "\u505c\u6b62\u4e0b\u8f7d"
const/4 v4, 0x0
invoke-static {v2, v3, v4}, Lcom/baidu/bus/i/f;->a(Landroid/content/Context;Ljava/lang/CharSequence;I)V

3的调用:

const-string v3, "\u7ee7\u7eed\u4e0b\u8f7d"
const/4 v4, 0x0
invoke-static {v2, v3, v4}, Lcom/baidu/bus/i/f;->a(Landroid/content/Context;Ljava/lang/CharSequence;I)V

看看这两个字符串分别是“停止下载”和“继续下载”,那么推测1应该就是“开始下载”。1-3的功能暂时这样猜测。再看1的分支调用的函数:

:pswitch_1
iget-object v2, p0, Lcom/baidu/bus/activity/ca;->a:Lcom/baidu/bus/activity/OfflineDataManageActivity;
invoke-static {v2, v0, v6, v7, v1}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->a(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/b/a;Lcom/baidu/bus/activity/cl;Lcom/baidu/bus/activity/cl;Lcom/baidu/bus/activity/cl;)V

翻译一下就是:

OfflineDataManageActivity.a(this.a, view.getTag(), cl_1, cl_2, cl_3);

注意到后面有3个 cl 类的对象作为参数,从 cl 类的定义:

final class cl {
  ProgressBar a = null;
  TextView b = null;
  ImageView c = null;
  ImageView d = null;
  ImageView e = null;
}

来看,这个类和下载的 FrameLayout 对应。接下来要回到 OfflineDataManageActivity 中查看调用的这个 a() 方法。

7. 在 OfflineDataManageActivity 中找到这个 a() 方法:

.method static synthetic a(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/b/a;Lcom/baidu/bus/activity/cl;Lcom/baidu/bus/activity/cl;Lcom/baidu/bus/activity/cl;)V

发现它也是一个 synthetic 的。在做了一系列动作之后,构造了一个com.baidu.bus.base.a 对象,com.baidu.bus.base.a 包含一个对话框,然后调用了这个对象的 a() 方法来构造并显示一个对话框:

new-instance v0, Lcom/baidu/bus/base/a;
invoke-direct/range {v0 .. v6}, Lcom/baidu/bus/base/a;-><init>(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
new-instance v1, Lcom/baidu/bus/activity/cb;
invoke-direct {v1, p0, v0}, Lcom/baidu/bus/activity/cb;-><init>(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/base/a;)V
invoke-virtual {v0, v1}, Lcom/baidu/bus/base/a;->a(Landroid/view/View$OnClickListener;)Lcom/baidu/bus/base/a;
invoke-virtual {v0}, Lcom/baidu/bus/base/a;->a()Landroid/app/Dialog;

这段代码翻译一下是(方法名的翻译不一定对):

dialog = new com.baidu.bus.base.a(context, titleText, messageText, bgResid, leftButtonText, rightButtonText);
listener = new com.baidu.bus.activity.cb(OfflineDataManageActivity.this, dialog);
dialog.setButtonOnClickListener(listener);
dialog.show();

所以要看点击“确定”后干了什么,查看 cb.onClick() 方法。

8. 在 com.baidu.bus.activity.cb 类中的 onClick() 方法中,有如下代码:

iget-object v0, p0, Lcom/baidu/bus/activity/cb;->a:Lcom/baidu/bus/activity/OfflineDataManageActivity;
iget-object v1, p0, Lcom/baidu/bus/activity/cb;->a:Lcom/baidu/bus/activity/OfflineDataManageActivity;
invoke-static {v1}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->u(Lcom/baidu/bus/activity/OfflineDataManageActivity;)Lcom/baidu/bus/b/a;
move-result-object v1
invoke-static {v0, v1}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->e(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/b/a;)V

翻译为Java代码:

OfflineDataManageActivity.e(this.a, OfflineDataManageActivity.u(this.a));

9. 查看OfflineDataManageActivity.e() 方法干了什么,e() 方法全部如下:

.method static synthetic e(Lcom/baidu/bus/activity/OfflineDataManageActivity;Lcom/baidu/bus/b/a;)V
    .locals 3
    new-instance v0, Lcom/baidu/bus/activity/cn;
    invoke-direct {v0, p0}, Lcom/baidu/bus/activity/cn;-><init>(Lcom/baidu/bus/activity/OfflineDataManageActivity;)V
    iput-object v0, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->G:Lcom/baidu/bus/activity/cn;
    iget-object v0, p0, Lcom/baidu/bus/activity/OfflineDataManageActivity;->G:Lcom/baidu/bus/activity/cn;
    const/4 v1, 0x1
    new-array v1, v1, [Lcom/baidu/bus/b/a;
    const/4 v2, 0x0
    aput-object p1, v1, v2
    invoke-virtual {v0, v1}, Lcom/baidu/bus/activity/cn;->execute([Ljava/lang/Object;)Landroid/os/AsyncTask;

    return-void
.end method

翻译为Java代码:

static void synthetic e(OfflineDataManageActivity activity, com.baidu.bus.b.a city)
{
    activity.G = new com.baidu.bus.activity.cn(activity);
    activity.G.execute(new com.baidu.bus.b.a[] {city});
}

com.baidu.bus.activity.cn 是从AsyncTask继承的类,它的 doInBackground() 方法调用了内部的 a() 方法:

invoke-direct {p0, p1}, Lcom/baidu/bus/activity/cn;->a([Lcom/baidu/bus/b/a;)Ljava/lang/Boolean;

在a() 方法中,首先创建一个Intent,然后调用 startService() 进行后台下载:

new-instance v0, Landroid/content/Intent;
invoke-direct {v0}, Landroid/content/Intent;-><init>()V
iget-object v1, p0, Lcom/baidu/bus/activity/cn;->a:Lcom/baidu/bus/activity/OfflineDataManageActivity;
invoke-virtual {v1}, Lcom/baidu/bus/activity/OfflineDataManageActivity;->getApplicationContext()Landroid/content/Context;
move-result-object v1
const-class v3, Lcom/baidu/bus/service/UpdateService;
invoke-virtual {v0, v1, v3}, Landroid/content/Intent;->setClass(Landroid/content/Context;Ljava/lang/Class;)Landroid/content/Intent;

这段代码翻译为 Java 代码就是:

intent = new Intent();
intent.setClass(this.a.getApplicationContext(), UpdateService.class);

这个 intent 调用了两次 putExtra() :

intent.putExtra("checkType", "checkSingleUpdateDownload");
intent.putExtra("downloadRecord", downloadRecord);

这里使用的 downloadRecord 类的定义为:

public class DownloadRecord implements Serializable {
    public String MD5;
    public String cityId;
    public String cityName;
    public String description;
    public String downLoadPath;
    public int engineVersion;
    public int id;
    public String localPath;
    public long size;
    public long version;
}

具体实例的内容是通过 com.baidu.bus.b.a 类中的 h 对象获取的:

public final class a {
    public int a;
    public String b;
    public String c;
    public int d = -1;
    public long e = -1L;
    public int f = 0;
    public int g = -1;
    public DataUpdateInfo h = null;
}

10. 在 startService() 调用之后,进入到 com.baidu.bus.service.UpdateService 中,这个类继承了 android 的 Service 类;观察它的代码还可以看到,在收到命令后,构造了一个 FutureTask 对象来执行下载任务。

至此,已经看到了从界面到下载的大体流程,但还没有看到下载的链接是如何生成的。接下来的任务就是回顾这个流程,以寻找下载链接。

时间: 2024-11-03 21:30:39

[原]百度公交离线数据格式分析——2.从界面点击下载的流程的相关文章

[原]百度公交离线数据格式分析——4.小结

还有两个问题没有解决. (1) prov_city_list.json 下载后,如果变为 com.baidu.bus.f.b 的对象的? 在 3.加载城市列表 的第 14 步中,hObject 的成员 c 被赋了一个值,类型就是 com.baidu.bus.f.a,向上找这个对象是如何生成的: invoke-static {v0, p1}, Lcom/baidu/bus/net/a/b;->a(Ljava/lang/String;Lcom/baidu/a/a/m;)Lcom/baidu/bus/

百度云离线下载含有违规内容检测方法分析

最近国家开始一轮净网行动,清除网上的淫秽***信息.各大互联网厂家纷纷开始行动,比如当年很好用的百度云离线下载就一度关闭.后来再次开启后,就出现了这句经典词,因含有违规内容被屏蔽无法下载. 其实被屏蔽的不一定都是不健康视频,有些仅仅是因为文件名含有一些字眼而已,比如一些美国大片的枪版就几乎都不能通过百度云的离线下载检测.据说这种方法还在迅雷等地方都有检测,因此我们来分析一下,这个检测到底是如何进行的. 首先上传了一个BT文件,BT文件里面的内容为大闹天宫的电影,但是我把文件名改成了含有敏感词汇的

百度地图离线化(API v=1.3)

毕设(北斗导航项目)进行了一段时间,近日在实验室给老师汇报进展时,由于网络不畅,加载百度在线地图及其各种操作时,时间过长,于是想将百度地图离线化.查阅网上很多资料,有的是广告(卖GIS应用的),有的版本太久......最后参考网上两位前辈的博客内容,加以实践,实现了地图完全离线且能进行基本操作.趁周末整理了实践过程并记录下来,希望能帮到有需要的朋友. 注:感谢两位前辈,其原文为:开源中国:Web版百度地图加载离线瓦片 :csdn:使用百度地图JS API构建离线地图应用(完整教程) 原文附带De

百度地图离线开发

:  百度地图离线开发,用在内网于局域网项目开发上,完全脱离互联网访问,可支持完整的全国瓦片图下载,使用目前最新的V2.0百度API,支持热力,聚合,海量标注,根据范围米数来圈范围,画图工具等完全支持.http:api.jjszd.com:8081apituiguanggisysw.html 以下是测试中的截图 地图标注,点击标注弹出层 地图标注,点击标注弹出层 支持多层弹出显示,也支持每次只显示一个层,可自由更改标注图标,大家发现没有,这个层不是带有箭头标识的层,箭头标识的是MBap中自带的,

解决百度云离线文件因含有违规内容被系统屏蔽无法下载问题

最近网上风声查的很严呀,好多视频都无法下载,一下载就提示  离线文件因含有违规内容被系统屏蔽无法下载,上次下载老外的视频教程都说是违规内容,忍无可忍,最近发现了一个神器,BTEditor ,应该算是种子文件编辑器吧,貌似能把那些被屏蔽的资源弄成可以离线下载的,网址   http://www.bteditor.com, 大家可以有空试试. 最近网上风声查的很严呀,好多视频都无法下载,一下载就提示  离线文件因含有违规内容被系统屏蔽无法下载,上次下载老外的视频教程都说是违规内容,忍无可忍,最近发现了

百度地图离线API 2.0(含示例,可完全断网访问)

由于公司需求,自己修改的离线地图API.该压缩包具有如下功能:1.支持使用google地图瓦片(不建议使用,效率不高,缩放级别较高时拖动有些卡顿,建议注释该代码块:overlayTileLayer.getTilesUrl,使用google转baidu的jar转换代码.2.包含拉框放大,测距,画线,打印等功能(已修改工具源码,支持右键取消放大,测距手势)3.自定缩放级别显示指定标签4.快速定位及隐藏指定类型注意事项:1.该示例不包含地图瓦片,所以访问时无背景(可以点击切片/影像查看在线地图效果).

[原]排错实战——通过对比分析sysinternals事件修复程序功能异常

原调试debug排错troubleshootprocess monitorsysinternals 缘起 最近,我们程序的某个功能在一台机器上不正常,但是在另外一台机器上却是正常的.代码是同一份,vs版本也一样(打的补丁也一样).编译出来的程序在两台电脑上运行的结果就是不一样.惊不惊喜,意不意外?如果是你遇到了这种情况,你会怎么调查呢?:upside_down_face: {% note info %} 说明: 为了跟大家分享这个问题,我事后在自己的机器上重新把整个过程梳理了一遍,并保存了pro

[原]用WebBrowser组件模拟人工运行搜索引擎自动点击搜索结果的实验

本代码只是业余时间无聊写着试试,用WebBrowser组件模拟人工运行搜索引擎自动点击搜索结果的实验 这是网络中盛传的提高搜索引擎点击率的一种方式,当然属于作弊,不推荐各位使用.另外这种方式的性能不佳,往往因为网络及本地机器的原因中途当机.当然如果只是写这游戏一下那是无妨.这里只是提供了其中的一种原理,方法还有很多.如果此方式继续优化详细写下去的话可能需要涉及到vpn定时更改ip地址到问题,当然现在即便是vpn也有办法逆向找到原始的ip地址. 代码示例如下: using System;using

从通话日志分析android 4.4.2 phone的拨号流程

网上关于拨号流程的文章有很多,大多讲逻辑,本文从logcat输出的日志入手.分析通话流程,还原系统应用真实的调试场景. adb logcat -b main -b radio -v time >> call.log 用如上adb命令将拨号至接通电话的日志输出. 1-01 08:02:07.458 V/OutgoingCallBroadcaster( 786): - Broadcasting intent: Intent { act=android.intent.action.NEW_OUTGO