跳转至

房天下 app wirelesscode 参数获取方法说明

本文介绍房天下 app9.0.0 版本发包请求中的 wirelesscode 参数的生成方式,并用 py 实现其逻辑。

1. 前提条件

文中提到的所有工具都可以换成你所熟悉的,或者更为好用的。看个人喜好和应用场景,不作限定。比如不用真机而使用安卓模拟器,抓包换成 packet capture 等等一般也无伤大雅,选择适合自己的工具即可。

自测在 Nexus 5xroot 安卓 8.1.0 同样成功

房天下 app wirelesscode 参数逆向前准备如下:

  • root 后的安卓手机一台,本文使用的为一加 7 pro
  • jeb 或者 ida 等反编译调试工具都可以
  • jadx 工具,版本无限制
  • 房天下 v9.0.0(选择未加壳的可用的版本即可。当然选最新的加壳版也行,只是会多一步脱壳步骤而已。)
  • HttpCanaryFiddler

此实现场景为一加7 pro 12G + 256G 国行 root 的安卓 9.5.9,自测在 Nexus 5xroot 安卓 8.1.0 同样成功;未测试模拟器环境下的情况,应该也没啥问题。以下步骤为一加7 pro 的对应场景,其它环境下也可参考:

本文中房天下 app 包信息如下:

image-20220124173838615

2. 操作步骤

以下步骤非保姆级的流程,简单及非必要步骤略过。

2.1. 抓目标请求包

使用抓包工具抓 app 目标 api,确定需要获取的参数目标。

这里放上抓包示例:

image-20220125091359118

2.2. jadx 参数生成位置定位

使用 jadx 工具打开 apk 文件,搜索目标参数 "&wirelesscode=" 定位到参数生成位置。

image-20220617110607445

至于为什么直接搜索 "&wirelesscode=" 字符就能定位呢?
  • 因为一般最简单直接的方法就是搜 wirelesscode.put("wirelesscode""wirelesscode" 等字符,也可搜索接口请求包中其它比较好定位的参数;
  • 等后面也会添加 trace 等功能说明。

然后查看其用例就可以定位到 java 调用 so 方法的地方:getSec 方法。

/* loaded from: classes4.dex */
public class SouFunSec {
  public static native String getAESSec();

  public static native String getDBSec();

  public static native String getSec(String str);

  public static native String getZFDESSec();

  public static native void setMessage(Context context, String str);

  static {
     System.loadLibrary("SouFunSec");
  }
}

2.3. frida hook 参数值

这里 frida 等工具的配置暂略,可以搜下 frida 入门教程文章。

另外,我这里取 hook 参数值是已经知道 getSecmd5 的处理了,因为 wirelesscode32 位数据,且抓包修改 wirelesscode 为错误时提示 md5 校验失败。

image-20220125105436767

所以想试试可不可以直接 hook 此方法的参数,直接 md5 后的结果是否和抓包结果一致(其实一般都是不一致的,会对参数加盐等处理。但万一呢。)

编写 frida hook 脚本:

# 编写脚本如下可以修改 arg0 参数为固定值会发现调用 so 文件中的方法的返回值也固定
setImmediate(function() {
    Java.perform(function() {
        var gclass = Java.use('com.soufun.app.sec.SouFunSec');
        var methodName = "getSec";
        gclass.getSec.overload('java.lang.String').implementation = function(arg0) {
            console.log('\n[Hook getSec]'+'\n\targ0 = '+arg0);
            var i = this[methodName](arg0);
            console.log('\treturn '+i);
            return i;
        }
    })
})

hook 得到的结果为:

image-20220125120410260

image-20220125120429683

但是通过手动 hook 得到的参数 md5 测试后与抓包的值不一致,一开始以为 url 编码的问题,修改 hook 得到的参数为固定值 a 后,其结果也非其 md5 后的值。

至此,我们可知其应该对参数进行了处理,或者它是魔改的 md5,或者其它返回 32 位的加密。

2.4. 分析及调试 so

所以这里我们就逃不过 so 文件了,可选择以下方法解决:

  1. 使用 frida-rpc 调用引用 so 的函数
  2. 使用 unidbg 调用 so 中的函数
  3. 调试 so 文件,查看反编译代码后,通过 py 实现

这里选择反编译 so 文件的方法。

2.4.1. 静态分析 so

  • jadx 可知 java 调用的是 libSouFunSec.so 文件,我们直接查看此文件即可。

  • libSouFunSecso 文件拖入 ida 进行分析,逻辑还是比较清晰的。

  • 搜索定位到 getSec 方法,这里的伪 c 代码就是 wirelesscode 生成的逻辑的。如果懂 c 就可以直接实现即可。

image-20220331085923782

据上图静态分析可知大致流程:

  • 我们 java 层传给 sonative 层参数 city=%E5*** *** 经过 JString 转为 CString 后得到 v10;
  • v10 字符拼接上 p;
  • p 又是由 phonemessagev23 拼接而得;
  • v23 是通过 strcpy 赋值后再经过 j_encrypt 处理而得。

2.4.2. 动态调试 so

调试 so 文件:

  • 若不懂 c 或逻辑嵌套比较复杂,就需要动态调试了。打开 jebida 的调试 so 功能,即可定位到参数处理的地方。

这里不讲如何配置动态调试,请自行搜索,网上教程很多!

进入调试后,先定位到具体的 so 文件:

image-20220329222517142

再定位到 so 中的具体方法:

image-20220329222550458

根据静态分析中的结果,可定位到我们关心的几个方法,可知其偏移地址。记住这些地址,在动态调试中分别断点并调试,这里放上关键的最后一次 j_join 方法的地址信息:

image-20220331092827179

然后在动态调试中断点调试,查看其参数及返回值信息:

注意:要在自己需要断点的请求中下断点,因为好多请求都带有 wirelesscode 这个参数,一般要结合之前抓包的结果来确定其请求 url 及其参数规则。

c157405e4c617eb3fa6319aa8d9fcf4

走到这里我们就可以得到参数的整个处理结果了,根据静态分析和调试的结果就能实现 wirelesscode 的生成方法!

3. 验证结果

直接测试以上获取到的参数处理方法是否与抓包一致。

f273a24eefcf61f31efefc83ffd2574

3.1. 逆向简析

wirelesscode 生成逻辑

  1. 请求参数排序:getparams 参数或者 postdata 参数通过字母排序(大写字母优先级大于小写字母);
  2. 排序后拼接:排序后的各个参数再通过 &key=value 的方式连接;
  3. 再拼接:参数后面拼接参数 86358*****54433*****n#4Ov*****mUI*9*****gKs*D*****graVG*****ZMHJO*****&xksM****,这个值自己去调试下,不提供;
  4. md5 处理:将处理后的参数 md5 即可。

其实可以根据上面的简析再来分析 so 文件,或者再多设备动态调试下,你会得到更深的了解(知其然知其所以然),这里添加下请求参数排序和拼接示例:

# 如果请求参数为:
params = (
    ('city', '\u5E7F\u5DDE'),
    ('correctcategory', '1'),
    ('esfposition', '1'),
    ('feedStream', '1'),
    ('gettype', 'android'),
    ('iskuaishai', '1'),
    ('isshowtuijian', '1'),
    ('maptype', 'baidu'),
    ('messagename', 'newhouselist'),
    ('page', '1'),
    ('pageSource', 'newhouselist'),
    ('pagesize', '20'),
    ('searchLocationInApp', 'quick_list'),
    ('showds', '1'),
    ('shujuliu_ad_platform', 'android'),
    ('strSort', 'zhiding'),
    # ('wirelesscode', 'D8C8B49EB4CF31D40848D6051A31FF85'),
)

# 则排序和拼接后的值为:
city=%E5%B9%BF%E5%B7%9E&correctcategory=1&esfposition=1&feedStream=1&gettype=android&iskuaishai=1&isshowtuijian=1&maptype=baidu&messagename=newhouselist&page=1&pageSource=newhouselist&pagesize=20&searchLocationInApp=quick_list&showds=1&shujuliu_ad_platform=android&strSort=zhiding86358...

注意:参数中部分值需要 url 编码,比如 city(搜索城市关键字)的值和 {} 的值等,具体也是自己调试试试看。

4. 补充说明

以下为本文中的补充说明,由于此说明文档过于简约,没办法,用到的工具太多,需要自己搜索其使用方法(安装及配置,反编译,调试,快速定位,快捷键等)之类的文章。

一个说明文档无法做到发散这么多内容介绍这些,请自行查看相关文章教程,我会在参考文章中添加部分教程。

5. 参考文章

参考文章也只放些工具介绍,或类似需求的文章。最好自行扩充和搜索。

评论

回到页面顶部