android黑科技系列——微信定位聊天记录中照片的位置信息插件开发详解

一、前言

最近关于微信中,朋友之间发送原图就可能暴露你的位置信息,其实这个问题不在于微信,微信是为了更好的体验效果,才有发送原图功能,而对于拍照,发送普通图片微信后台都会过滤图片的exif信息,这样就不会携带位置信息了。我们本身用手机自带的相机拍摄照片默认都是会在图片中添加位置信息的。当然我们也可以手动的关闭这个功能。这一点个人觉得不能怪微信。因为更好的逆向学习,和用户体验,本文将开发一套更加好用的插件,就是选择图片直接利用微信自带的地图功能,定位图片位置。这个过程会很麻烦。但是本文会逐一详细介绍的。在介绍这个插件之前,必须了解我之前介绍的一款插件功能:如何转发小视频到朋友圈插件,本文的插件是基于这个插件的基础之上操作的。所以必须看懂这个插件原理才能继续阅读本文。

二、添加入口菜单

下面来开始操作了,首先不多说了,先给大家看看插件的效果,这样会有一个感官认识:


我们在聊天中,点击一张图片,然后会弹出一个菜单,我们需要在这个菜单中添加一项就是定位入口,那么这里的第一个突破口,就是如何找到添加这个入口呢?这个简单。我们可以通过默认的菜单文案找到关键代码,这里通过反编译微信之后,查看他的字符串信息:


然后在用Jadx打开微信全局搜索即可:


这里有多个,但是为什么选择第二个呢?因为我们点进去会发现正是我们想要的菜单,这里搜索结果不算多,可以一个个尝试查看:


这里正好对应上了,那个弹出菜单的三个选项。所以入口就在这里了,下面看看他是如何添加菜单的:


这里定义了一个内部类,然后遍历列表开始构造菜单信息,点进去方法看看如何构造:


看到了MenuItem类就可以完全确定了,这里是构造菜单了,看到这里传入的两个参数一个是菜单索引值,一个是菜单名称,构造完之后在存入到全局变量列表ktX中,后面我们需要直接操作这个ktX列表数据:


既然找到了这里添加菜单的位置,下面我们就开始利用Xposed进行hook,添加我们自定义的菜单了,但是在操作之前,我们需要解决两个问题:

第一个问题:看到上面我们需要hook的类ImageGalleryUI的内部类,这个需要我们去反编译之后的smali文件夹中找到这个内部类名称。

第二个问题:是hook的那个b方法参数是对象类型,这里需要先加载这个类类型才能进行hook操作

我们去反编译之后的smali文件夹看看这个内部类叫啥:


这里看到有十几个内部类,这里我们需要通过上面的那个内部类的方法签名判断,所以依次打开每个内部类文件进行确认,最终确认到了是ImageGalleryUI$12.smali这个类:


找到这个内部类之后,下面就是正常的hook代码了:


不过,这里需要注意,我们需要把菜单添加到最末尾,所以还需要拿到已经添加的菜单个数,也就是上面提到的那个全局菜单列表,这里用反射获取即可。而且需要保存这个列表大小信息,后面再处理菜单点击事件还要用到。

操作完成之后,我们就开始安装这个Xposed模块,重启生效,看看是否添加成功了:


看到这里是添加成功了,所以这一步就完成了,下面开始拦截这个菜单的点击事件了。继续看代码:


看到这里有很多赋值的地方,可以猜想eCN是管理菜单的核心类,点进去查看:


这里可以确定了,就是这个类管理菜单的功能,他还实现了菜单的点击实现接口,找点击菜单的回调方法:


这里看到会通过heu中的菜单列表,找到MenuItem菜单对象,然后调用het的d方法处理菜单点击事件,看看het在哪里赋值的:


看看变量kVb的定义:


果然没错,这个又是一个内部类,回调d方法,获取菜单对象和菜单索引值,然后处理菜单的点击事件。那么这里我们依然需要找到这个内部类,然后hook即可。去smali文件夹依次通过方法的签名信息找到对应的内部类文件:


这里hook就简单了:


安装这个模块,重启生效,看看点击之后的日志信息:


OK,到这里,我们就处理了菜单的点击事件了,同时把图片获取位置信息的入口操作弄完了,下面就要开始本文的重点了,如果获取这张图片位置信息了。

三、获取图片存储地址

我们知道Android中的图片会把经纬度信息保存在图片的exif中,而读取这个信息代码非常简单:


这里的重点是,如何获取我们想要操作的图片位置呢?这个就需要借助之前提到的那个转发视频插件知识了,不了解的同学一定要去查看。这里不多解释了,直接把那个插件安装之后,通过日志查看突破口,我们在聊天界面选中一张图片,然后查看日志信息:


这是当初微信视频转发的菜单拦截功能代码,这里我们通过打印日志查看,这一次我们选中图片信息:


这时候,查看日志:


看到,这里的图片地址,信息是一个特殊字符串开头的,我们可以全局搜索这个开头前缀信息:


这里看到有很多都是这个f类:


这里看到这个方法,我们不妨先来直接hook他,看看有么有日志打印信息,把最后参数和返回值都打印一下,这样来确定这个方法是否是我们想要的。这里有点靠着尝试思维,因为本来逆向就是靠猜的,而且拦截这么一个方法也不费事:


运行模块,重启生效,看看日志信息:

尽然猜对了,看到图片地址了,返回地址就是我们想要的图片地址。那么这里就简单了,我们直接hook这个方法然后保存这个方法的返回值即可,我们也发现了,这个方法会执行多次,不多我们只要在最后一次正确的保存图片地址后面处理即可。

四、获取图片的位置信息

到这里,图片地址也拿到了,那些面就简单了,开始获取经纬度信息吧,但是拿到经纬度信息之后,如何获取具体位置信息呢?这里有几种方案:

第一种方案:借助高德或者百度地图提供的SDK,可以输入经纬度就能拿到位置信息了,而且可以利用地图View进行展示,不过这种方案不可用,因为我们知道这种地图SDK使用的话都需要在xml中配置key信息。我们现在是hook代码,如果再去反编译微信,添加key,然后在回编译就太费劲了。没这个必要了。

第二种方案:借助百度提供的网页url获取位置信息,这个是百度开发的一个url请求,只要根据经纬度信息,就能返回json或者xml格式的位置信息,然后我们在利用微信内部的地图功能进行展示具体位置。

这里第二种方案是最靠谱的,不过第二种方案需要做两件事:

第一件:通过开发的url获取位置信息

获取位置信息地址:http://api.map.baidu.com/geocoder/v2/

参数location:经纬度信息,用逗号连接

参数output:返回的数据格式,可以是json,或者xml

参数pois:这个可以忽略,直接用1即可

参数ak:这个是请求位置信息需要的key值,这个需要去百度平台申请。


好了,我们直接利用图片的经纬度信息去请求这个位置信息,返回json数据,我们解析出位置主要地址,和后面的辅助地址。然后调用微信内部地图信息即可。


第二件:利用微信内部地图功能进行展示位置

我们知道微信内部用的是自家的地图功能,我们可以利用命令找到入口,首先打开内部一个地图信息,这个可以在聊天记录中发送位置信息,然后打开即可。这时候利用:adb shell dumpsys activity top 命令获取地图界面:


然后用Jadx工具全局搜索这个类即可。不过可惜的是,这里搜索没结果的:


这时候要想到了,微信是拆包了,有多个dex文件,而之前已经说过了,微信的第二个dex是打包成jar文件,放在assets目录中的:


解压jar文件拿到classes.dex就是他的第二个dex文件,我们用Jadx打开这个dex即可:


这就搜索到了,我们点进去查看即可:


那么这里就好办了,我们可以启动这个activity,通过intent传递需要的参数信息。但是这里有两个问题需要解决:

第一个问题:启动Activity需要微信中的一个activity实例,这个我们可以hook上面提到的ImageGalleryUI这个类,拿到对象实例即可。这个hook很简单,直接hook他的onResume方法,然后获取对象实例即可:


第二个问题:如何获取启动地图页面的intent中的参数信息,这个可以通过代码分析,但是这里可能携带的参数信息很多,为了避免遗漏,我们用另外一种方法就是,hook这个地图页面启动的onResume方法,拿到对象实例,然后通过getIntent方法获取当前页面的intent数据,在拿到对应的Bundle结果,可以遍历他所有的参数key和值信息,我们hook之后,随便打开微信内部一个地图信息,看看hook的参数日志都有哪些,这里我打开的是聊天记录中发送的位置信息:


这里需要注意,因为我们已经知道这个类是在第二个dex中,所以hook操作就需要拿到正确的类加载器才能加载这个类进行hook操作。而hook多dex的应用,之前已经说过了,需要先hook应用的Application的attach方法,然后拿到正确的ClassLoader,才能继续下面的hook操作。这个方法一定要记住。非常关键。

然后安装模块,重启生效,看日志信息:


这里看到的确有很多信息,不过这里分析之后,发现只有这五个参数信息是最关键的,其他参数我么可以照着抄过来就好了,关键的五个参数信息是:

kwebmap_slat:经度

kwebmap_lng:纬度

kPoiName:地图页面中展示的主地址

Kwebmap_locaion:地图页面中展示的从地址

kwebmap_scale:地图默认缩放的大小

有了这上面的信息,下面就来启动页面代码吧:


这里需要注意,启动的地图页面Activity的类变量,一定要用多dex的hook方法加载到,这个时机也要最早的。然后保存下面即可。

然后就用之前已经hook到的ImageGalleryUI这个activity实例启动即可。

 

五、插件功能流程总结

到这里我们已经完成了大部分的工作了,下面来整理整个hook流程吧:

第一步:通过聊天中查看图片信息hook方法拿到图片地址进行保存。

第二步:获取图片的exif信息,获取经纬度信息。

第三步:借助百度开发api通过经纬度信息获取具体位置信息。

第四步:有了经纬度和位置信息,启动微信内部的地图页面进行展示。

而在这个过程之前,我们还需要添加一个入口,那就是在聊天记录中点击图片查看,然后长按弹出菜单中加一个展示位置信息入口。

有了这些步骤,还不算完美,因为我们在第三步是需要请求百度开发api的,这个是一个等待过程,所以这个过程中,我们还需要加一个loading样式,不然插件体验很不好的。而我做事只求完美,不求到位。这里的loading样式怎么办呢?还是拿来主义。利用微信内部资源实现即可。这里也是一个技巧了,看我怎么操作的:

首先去微信反编译之后的资源目录下查看,找到一个合适的作为loading图片资源,这里我看到这个资源:


这个资源好看,然后我们自定义一个旋转动画,就类似于loading效果了,关于旋转动画不多说了,网上一大堆资料:


这里读取资源,可以直接用上面资源的id值即可,这个值可以通过values/public.xml中获取:


然后自定义这个loading视图:


最后在借助WindowManager来进行展示loading视图,这里可以直接代码编写布局即可:


好了,下面来看一下展示效果:


 

六、总结

好了,到这里,我们就全部介绍完了,知识点的确很多,文章也很长。因为我们做的功能太多了。哈哈,下面来总结本次操作的技术知识点:

第一、在获取hook点的时候,有时候利用Xposed进行hook尝试也是一种方案,比如本文的获取图片地址,以及获取地图启动的参数信息,都是很好的例子。不要一根筋完全去分析代码实现。hook打印参数和返回值信息也是很好的选择。

第二、多dex应用进行hook的时候,一定要记得先hook应用的Application类的attach方法,获取到正确的类加载,在进行后续的hook操作。

第三、通过经纬度信息获取具体位置信息,可以利用百度开发api获取,这个记住,以后或许还有很多地方要用到。

第四、如果在hook中想添加UI的功能,可以利用现有应用内部资源,利用代码编写布局动画即可。

好了到这里,也介绍完了插件功能了。不过可惜的是,这个不是所有的图片都能获取到位置信息的,开始的时候也说了,微信有一个发送原图功能,也就是只有原图可以获取到经纬度信息,而通过微信内部拍照,或者发送压缩图都会被微信服务器过滤exif信息。我们本地已经没办法获取到了。那么此插件就丧失了功能。最后再来看一下插件效果图:


 

注意:

本文用到的是微信6.3.9版本,最新版本可能有区别,不过逆向过程大同小异。有很多同学问了很多次Jadx打开微信会卡死怎么办?因为微信内部资源太多。所以直接打开apk会卡死的。主要是Jadx解析微信资源文件非常耗内存。所以可以解压apk之后直接用Jadx打开多个dex即可。希望以后不要再问我了。

posted @ 2017-11-11 22:15  陈程编程  阅读(7871)  评论(0编辑  收藏  举报