记一次微信朋友圈逆向

来源:互联网 发布:淘宝店铺导航全屏代码 编辑:程序博客网 时间:2024/05/21 22:55

转:http://www.easydone.cn/2016/02/27/


最近入了一个 apk 逆向的坑,坑深似海,不过初步算是有了一些成果,记录一下先。

###确定方案
刚开始想到两个方案:

  1. 借助 Xposed ,有现成的插件可以获取数据,但是由于并无法获取朋友圈条目实体的字段,而是根据获取的部分自己去拼的实体,同时还要求 root ,armv7 的手机还不支持 Xposed 等,局限性有点大,就舍弃了该方案。在此感谢Chion Tang 同学,技术非常棒的一位大二学生,帮我提供了不少有用的信息。网上看到他的文章,然后还加了微信,又认识一位小伙伴,感觉很棒。
  2. 通过查看微信的数据库缓存发现,微信的 Sqlite 里朋友圈这部分数据是没有加密的,于是就设想模拟滑动,然后不断从微信服务器拉取数据,然后缓存起来,再通过缓存取得数据。这种方案的局限显而易见,就是对于微信的存储方案并不了解,获取的数据完整性和可靠性不能保证,也就失去了其意义。
  3. 在上面两种方式都遇到瓶颈的时候,我师傅小虎提供了一种思路: Android 里 Java 代码会被先编译为 smali 代码,于是就设想通过在获取服务端数据之后,显示在界面上之前,即 ListView 的 AdapternotifyDataSetChanged 之前,插入我们想要执行的 smali 代码(比如获取 adapter 里 list 集合的变量,然后遍历集合,将数据存储在一个文件里,保存在 SD 卡),这样就可以获取朋友圈的数据。

最终选择了第三种方案。假如微信没有做服务端的一些验证的话,这种方式是有可能会获取完整可靠的数据的。当然,也有不少技术难点。

  1. 找到 notifyDataSetChanged 的代码位置;
  2. APK 解包,二次打包能否正常进行;
  3. smali 语法。

###确定插入代码位置
要确定插入代码的位置,我使用了 jadx 这个工具,对 apk 进行逆向,使用 jadx-gui 查看被混淆过的 java 代码,由于 Activity 及其里面的方法名是无法被混淆的,很容易就找到了具体用户的朋友圈入口,com.tencent.mm.plugin.sns.ui.SnsUserUI ,然后通过关键字搜索找到了notifyDataSetChanged 所在的位置。调用这个方法的是 SnsUserUI 这个类里的 aBp()方法,继承自父类 SnsActivity。最终确定了混淆后的 Adapter 类名,实体类名,以及相关的一些变量名。

###验证能否正常解包和二次打包

  1. 正常反编译的命令是 apktool d -o weixin wx.apk -o 就是 output ,将 wx.apk 反编译后输出到当前目录下的weixin 目录里。但是使用这个参数在二次打包的时候,可能会因为资源文件无法加入构建而造成打包失败。因此在解包时需要忽略资源文件,apktool d -f -r apkfilename.apk-f 指替换之前反编译的 apk 里代码,-r 表示在反编译的时候忽略资源文件,因此二次打包就不会再遇到上述问题。(参考:apktool build apk fails)
  2. 二次打包的命令为 apktool b wx new.apk ,反编译后的输出目录为 wx ,二次打包就是将 wx 目录二次打包为 new.apk ,new.apk 的输出目录为wx 目录下的 dist 目录。可以添加 -o 指令,设置另外的 apk 输出目录。
  3. 使用 debugkey 对二次打包的 apk 进行签名 jarsigner -keystore debug.keystore -storepass android -keypass android new.apk androiddebugkey ,然后安装。

至此,进入了整个流程里最困难的一个环节。

###插入 smali 代码
由于对 smali 语法的一无所知,同时 smali 代码的调试也是一个大问题。(在此,感谢 Trinea 4群的小伙伴们的帮助,群里当真是卧虎藏龙,帮我指出了不少 smali 语法上的错误。)我先学习了一下语法,大致能看明白一部分的 smali 代码,然后在 smali 里notifyDataSetChanged 之前,插入了打印日志的代码,再打包执行,没有问题!但是因为真正要插入的保存数据的代码比较复杂,自己写暂时还不太现实,最后决定先写 demo ,将要插入的代码先在 demo 里写,然后再用 apktool 解包 apk,获取插入的那一段 smali 代码,再对其修改,然后插入到 SnsUserUI.smali 里。经过一番尝试,修改了已知的语法错误后,进入具体用户的朋友圈,微信仍然是闪退…

接着尝试对 smali 代码进行 debug 调试,以查看具体哪部分报错,最终找到了 smalidea 这个 IDEA 插件,官方给的教程并不详细,经过一番摸索,总结了一下,教程大致如下:

  1. 下载 zip 插件包,然后安装;
  2. 下载 smali.jar 和 baksmali.jar,smali 和 baksmali 脚本,然后 chmod 添加执行权限,再然后配置环境变量;
  3. 使用 smali 脚本解包 apk ;
  4. 将解包后的目录导入到 Android Studio,然后配置 SourceRoot 目录,再配置 SDK 和 JDK,增加远程调试选项并设置调试端口;
  5. adb install 安装apk,然后打开 ddms ,开始调试。

最终在打开 USB 调试,选择调试应用的时候,然后发现 release 的应用都是不行的。因此只能启用 logcat 大法了。

接下来只好一步步去排除可能会导致这样的结果的原因:

  1. 然后尝试抓包,看是否有请求发送和响应,发现请求了,同时返回了200,再在无网环境下测试,依然会闪退。说明不是服务端校验的问题。至此就基本确定,是那段 smali 代码的问题。
  2. 接着,我确定了另一个方向,先写一个往文件里写数据,再把文件保存在 SD 卡的方法(writeDataToSdCard(String message)),然后去调这个方法,经过尝试发现writeDataToSdCard("TESTTESTTESTTEST") ,直接写字符串也是不行的(后来发现还是因为寄存器用的不对,最简单的一种解决方式就是方法名用 static 修饰)。
  3. 此路不通,只好再用打印日志的方式,由于之前已经测试单纯地打印字符串日志是没问题的,这时候只要 for 循环 adapter 里的 list ,然后打印就可以了。当然,要先打印一条数据试试看这个 list 的获取是否正确,Log.i(TAG, list.get(0).toString());
  4. 结果依然不行,logcat 里报了 Verifier rejected SnsUserUI is not the default one 的错误,目测是 Android 的一些验证失败了。所以这也并不能证明是获取的 list 的问题。
  5. 接下来将 forEach 循环换成了 for(int i = 0; i < adapter.getCount(); i++) 的形式,然后打印输出adapter.getItem(i).toString() ,如此就避免了直接操作 list 的问题,因此就排除了4里提到的获取 list 错误的问题,但是结果在 logcat 里仍然出现了4中的错误。
  6. 再次无路可走,这时候我师傅小虎又出马了,提醒我可以把5里面的 for 循环抽出来成静态方法,其实抽取方法以保证寄存器等不受影响,我在上面已经用过了,但是这时候思维已经钻进死胡同了,没有想到这一步。于是,按照师傅的指点,果然,大功告成了。还是插入的 smali 代码有问题,而且还发现之前找的notifyDataSetChanged 的入口是有问题的。
  7. 最后就是从 Java 代码里找入口,确定字段的体力活了。不过,一眼看去,密密麻麻朋友圈实体里有几十上百个字段,还各种父类继承,Map,linkedList,一堆一堆的,这体力活真的好大…

至此,大致过程就 OK 了。顺带提一句,掌握一些基本的 shell,awk,sed 等终端语法命令还是很有必要的,会省下不少的繁复的工作量。当然,这也只是我自己执行的过程,别人做的时候,很可能会避开一些没有必要的坑。

原创粉丝点击