zoukankan      html  css  js  c++  java
  • [原]百度公交离线数据格式分析——5.读取离线文件

    在 com.baidu.bus.offline 下面有一个 CAPI.smali 文件,里面定义了几个JNI的接口:

    public class CAPI {
      static {
        System.loadLibrary("busoffline");
      }
      public native int closeDB();
      public native int getEngineVersion();
      public native int openDB(String filePath);
      public native List queryFullMatchStation(String[] stations);
      public native RLineInfo queryLineInfo(int lineId);
      public native List queryLinesByName(String[] lineName);
      public native List queryLinesBySid(int sid);
      public native List queryRoundStation(int p1, int p2, int p3);
      public native List queryStation(String[] stations);
      public native List queryTransferInfos(int p1, int p2, int p3);
    }

    首先查看 getEngineVersion() 的处理。用 IDA 打开 libbusoffline.so 之后找到函数:

    Java_com_baidu_bus_offline_CAPI_getEngineVersion()

    代码很简单:

    LDR     R3, =(ENGINE_VERSION_ptr - 0x5DC0862A)
    ADD     R3, PC ; ENGINE_VERSION_ptr
    LDR     R3, [R3] ; ENGINE_VERSION
    LDR     R0, [R3]
    BX      LR

    另外可以看到 ENGINE_VERSION 是 .rodata 段的一个常量,这段代码就是返回这个常量,查看 ENGINE_VERSION 的定义:

    .rodata:5DC0D3A8 ENGINE_VERSION  DCB    3 
    .rodata:5DC0D3A9                 DCB    0
    .rodata:5DC0D3A9                 DCB    0
    .rodata:5DC0D3A9                 DCB    0

    百度公交离线文件下载后,保存在 $SDCARD/Android/data/com.baidu.bus/files/ 下面,以城市ID(数字)建立文件夹,然后在下面以 versionCode 为名建立文件,后缀为 .bdp,如下图:

    跟踪 openDB() 函数,JNI中的 Java_com_baidu_bus_offline_CAPI_openDB() 函数,发现它简单调用了 OpenDB() 函数。
    跟踪 OpenDB() 函数,读取的过程是这样的:
    (1) 使用 fopen() 打开文件;
    (2) 读取30个字节(诡异的是,这个30是以字符串形式保存在so中,在运行时使用 atoi() 函数转换为整数);
    (3) 确保读出的内容以“-boundary-3-”开头(比较字符串的前12个字符),如果不是,则判定为文件格式错误,退出;
    (4) 在“-boundary-3-”后面查找字符“.”(即英文的句点,ASCII值为0x2E);
    (5) 在句点后面查找换行符“ ”;
    (6) 将句点和换行符之间的字符,用atoi() 转换为数字,这个数字表示的是该条记录(一行)的长度;
    (7) 读取换行后面的内容,长度为刚才得到的数字(在实际操作中,由于前面读取了30个字符,因此这里继续从文件的当前位置读取“行记录长度”减去30,加上截至到换行符前的长度);
    (8) 调用 GetCodecString() 函数解码;
    (9) 调用 LoadData() 函数将解码后的内容以特定的格式保存到内存中
    (10) 调用 ReleaseCodecString() 函数释放 (8) 中申请的空间;
    (11) 反复执行步骤 (2)~(10) 直到文件结束。

    第 (8) 步的 GetCodecString() 是解密文件的关键步骤,通过查看这个函数的代码,发现是将文件的内容(乱码)与一个字符串中的字符逐一进行异或,这个字符串是:

    x84xf4xedKM~x02xa3x0fxafxf5Ax12s9M[

    注:在so中,这个字符串并不是这样存储的,而是逆序保存的,并且每个字符的值再减去1,原始的字符串是:ZL8rx11@xf4xaex0exa2x01}LJxecxf3x83

    如此处理之后,文件就可以阅读了,以北京为例,前几行的内容(字符编码为UTF-8)为:

    11911~2872~78062~19261~3565~9112~131~1.0
    1~南站村~12923480~4879551~69303;69306~2557;2558~~0
    2~安乐庄~13001600~4860283~126;161~5;6~~0
    3~小胡营~12993797~4871232~35489;35576;67871;67808~2514;1393;1392;2513~~0
    4~老才臣北厂~13039643~4862075~64681;64640~2428;2427~8595~0

    继续查看 LoadData() 函数,它将每行的数据按“~”分隔成为一个字符串数组,然后交给 AnalyseRow() 来处理。
    第一行有几个数字,这是索引该文件的关键,百度公交离线文件分为6个区域,这行的前6个数字分别表示每个区有几行,最好一个1.0含义未明。这6个区域的数据分别表示:
    (1) 公交站点(Station Data);
    (2) 公交线路(Line Data);
    (3) 站点信息(Stop Data);
    (4) 站点索引(Station Index Data);
    (5) 线路索引(Line Index Data);
    (6) 地图坐标(X-Y Map Data)。
    仍以北京为例,可以知道,北京共有11911个站点,2872条线路(包括地铁,普通公交车往返线路算作两条不同的公交线路)。


    每个区域都有固定的格式,下面做简单的说明:

    1. 公交站点(Station Data):

    序号 1
    站点名称 南站村
    未知1 12923480
    未知2 4879551
    在StopIndex区中的序号 69303;69306
    经过该站点的公交线路 2557;2558
    未知3 0(好像都是0)

    2. 公交线路(Line Data):

    序号 1
    公交线路名称 571路(城铁望京西站-朝新嘉园)
    它的返程路线在本区的索引 2
    依次经过的公交站点 1;2;3;…;25
    未知1  
    未知2  
    线路类型(0:公交车, 1:地铁, 6:公交环线) 0
    总票价(单位:分) 100
    首班时间 06:20
    末班时间 22:00
    总长度(单位:米) 15960
    未知3 0(好像都是0)

    3. 站点信息(Stop Data):

    序号 1
    未知1 12963798
    未知2 4838276
    在Station Data区的索引 2214
    经过它的公交线路在Line Data中的索引 1
    在经过它的攻击线路上它是第几站 1
    未知3 0
    未知4 524(好像都是524)

    这个站点在Station Data区的索引是2214,搜索结果为:

    2214~城铁望京西站~12963779~4838233~...

    所以该站表示“城铁望京西站”。经过该站点的公交线路为1,搜索结果为:

    1~571路(城铁望京西站-朝新嘉园)~2~...

    4. 站点索引(Station Index Data):

    站点名称拼音首字母索引 GDC
    与该索引匹配的站点在 Station Data 区的索引 2740;2978;4034;4941;6995;11521
    从第几个字开始匹配 0;0;0;0;0;0

    例如GDC,与之匹配的站点有6个,分别为:

    2740~沟东村~
    2978~官道村东~
    4034~高佃村北~
    4941~郭翟村~
    6995~官道村~
    11521~高佃村~

    5. 线路索引(Line Index Data):

    线路索引 Z21L
    该线路在LineData区的索引 808;809
    从第几个字开始匹配 0;0

    如:Z21L在Line Data 区的索引为808和809:

    808~专21路(城铁霍营站-龙锦苑公交场站)~
    809~专21路(龙锦苑公交场站-城铁霍营站)~

    6. 地图坐标(X-Y Map Data):

    未知1 25872_9650
    未知2 880

    至此,百度公交离线文件格式分析完成,虽然还有一些未知的数据意义,但是已经足以让我们了解百度公交的原理。

  • 相关阅读:
    Nginx 学习笔记(七)如何解决nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
    jQuery基础 (四)——使用jquery-cookie 实现点赞功能
    Travis CI实现持续部署
    三大云安全工具(CASB、CSPM、CWPP)的使用场景
    数据访问安全代理 CASB
    SDP(软件定义边界)让SDN更安全,你的对面可不能是一条狗!
    从BeyondCorp说起
    [Docker] Docker整体架构图
    当博弈论遇上机器学习:一文读懂相关理论
    用Rust重写Linux内核模块体验
  • 原文地址:https://www.cnblogs.com/zhangbaoqiang/p/5141873.html
Copyright © 2011-2022 走看看