如何利用mitmproxy来批量修改Android中HTTP流量
背景
有时候我们常常在调试Android程序时,常常需要对API返回的response进行修改,以达到测试特殊情况的目的。比如有时候我们需要某个字符串显示超过某个限制来看看此时Android上面显示是否正常,有时候我们需要特定高度的图片来显示页面是否异常。
对于这些场景,其实通过调试工具mitmproxy就能完成。利用mitmproxy可以让我们的APP开发调试事半功倍。对于从来没有听说过mitmproxy的朋友,推荐查看我之前的两篇博客:如何调试 Android 上 HTTP(S) 流量 和 mitmproxy基础实践教程。
之前所讲的关于mitmproxy的使用教程都是比较基础的。比如我们可以拦截某些特定的request,然后修改这个request的某些属性,有必要还可以修改response的某些属性,这样就可以解决本文开头所讲的场景里面的问题。但是,这样做的效率很低,我在手机上发起某个API请求,然后被电脑上的mitmproxy截获了,然后我更改这个请求返回的response,然后这个response就会到了手机上了。
Mitmproxy的Inline Scripts 特性介绍
这样听起来好像有点麻烦。可以不可以自动的修改这些resquest和respones。当然可以!这就是我们今天要说的主题,mitmproxy还一个非常极客的特性,叫做 Inline Scripts。有了它,我们就不要在手机和电脑上来来回回地调试了。mitmproxy的Inline Scripts可以让我们对通过mitmproxy的所有HTTP(S)流量进行可编程的定制,这里的script指的是python脚本。Inline Scripts是一种靠HTTP中的事件进行驱动的API,这有点类似于Android的Activity的生命周期。Inline Scripts提供了HTTP请求中各个时间点的hook函数,比如HTTP请求启动的时候,request到达mitmproxy的时候,response到达mitmproxy的时候等等。下面我就简单介绍一下Inline Scripts中的一些Event:
Inline Scripts 中常用的Event介绍
- start(context, argv)
HTTP请求开始启动的时候,这个event在所有event之前。 - clientconnect(context, root_layer)
客户端向代理(mitmproxy)建立一个connection的时候,一个connection可以对应多个request。 - request(context, flow)
客户端的HTTP请求被代理(mitmproxy)接收到的时候。flow里面包含了request对象,比如request的方法,request的url,参数等等。 - serverconnect(context, server_conn)
代理(mitmproxy)向服务器端建立一个connection的时候,同理一个connection可以对应多个request。 - responseheaders(context, flow)
当服务器的response header被代理(mitmproxy)接收的时候,这个event在接下来的response event之前。 - response(context, flow)
当服务器的response被代理(mitmproxy)接收的时候,这个event在responseheaders事件之后。 - error(context, flow)
当flow出现异常的时候会产生该事件。比如connection被中断等。 - serverdisconnect(context, server_conn)
当代理(mitmproxy)断开到服务器的连接时 - clientdisconnect(context, root_layer)
当客户端断开到代理(mitmproxy)的连接时 - done(context, argv)
Inline Scripts被关闭的时候。
Inline Scripts 使用示例
下面的示例为了说明 mitmproxy 中的 Inline Scripts是如何使用的。具体思路:
- 首先我们将已有的相关API 流量通过mitmproxy截获
- 将这些API中response中特性属性的value全部改为我们想要的value
假设我们要测试的某个请求返回的数据格式如下:
{"data":[{"key":"value1"},{"key":"value2"},{"key":"value3"}]}
因此接下来我们就可以写相应的python脚本(change.py)了:
from libmproxy.protocol.http import decoded #来自mitmproxy中的库 from libmproxy.protocol.http import HTTPResponse import json #mitmproxy中代理所有HTTP请求的response全部会经过这里 def response(context, flow): #如果request的url里面包含了某个关键字(需要按照你的需要设置) if 'KEYWORD' in flow.request.pretty_url(hostheader=True): #解码请求的response with decoded(flow.response): #使用json封装response body = json.loads(flow.response.content) if body['data']: list = body['data'] for item in list: if item['key']: #将符合条件的属性进行更改 item['key']='value_special' #将更改后的数据重新封装为response flow.response.content = json.dumps(body)
然后咱们再结合之前两篇教程,我们只需执行以下命令就可以完成我们的需求:
1 </div> </td> | </tr> </tbody> </table> </div> </div> ./mitmproxy -b YOUR_LOCAL_IP_ADDRESS -p PORT -s change.py 当这个命令行运行的时候,每次手机请求这个api,请求经过mitmproxy的时候,mitmproxy会把请求转发给服务器端,接着服务器端讲 response返回给mitmproxy。mitmproxy拿到数据之后,将response中的数据按照前面的python脚本进行了更改,最后这个被修改过的response回到手机上了。 这一切都是自动的,你只需拿着手机发起请求就行了。 来自:https://greenrobot.me/devpost/how-to-use-mitmproxy-custom-android-api-call/ |