基于Android 的 HTTP/HTTPS 流量的监控与转存

一.目的

通过流量分析软件Mitmproxy的学习与应用,最终要完成写一个python插件实现Android的HTTP、HTTPS流量的监控与转存。

实现规划(大概两周进行深入了解):

第一周

了解、熟悉mitmproxy,尝试用Genymotion创建虚拟机,测试mitmproxy功能

第二周

进一步学习mitmproxy,并且学习写插件,实现真机的流量检测并完成转存到本地

二.什么是Mitmproxy

Mitmproxy是一种拦截代理的工具,类似于WireShark,包括有对HTTP和HTTPS的拦截、修改、保存、重播、转发等功能。

Mitmproxy是一组为HTTP/1、HTTP/2和WebSockets提供交互式、支持SSL/TLS的拦截代理的工具,类似于WireShark、Filddler,只不过它是一个控制台的形式操作。另外,它还有两个非常有用的组件,一个是mitmdump,它是mitmproxy的命令行接口,可以直接抓取请求数据;另一个是mitmweb,它是一个web程序,通过它可以清楚地观察mitmproxy抓取的请求数据。

对Mitmproxy的学习能够帮助我们深入理解网络通信,学会对Android端流量的实时监控与调试,不仅可以提高个人的技能和知识,还可以为组织提供更强大和灵活的网络监控和分析能力,从而确保数据安全和业务连续性。

我的Mitmproxy的学习包括两个大的阶段,第一步是了解这个工具并且学会安装使用;第二步是学会用python写脚本完成其他需求,并最终实现需要的转存流量。具体内容如下:

(1)Mitmproxy的功能

·拦截HTTP和HTTPS请求和响应,并实时修改它们;

·保存完整的HTTP对话,以便以后重播和分析;

·重播HTTP会话的客户端;

·重播以前录制的服务器的HTTP响应;

·将流量转发到指定服务器的反向代理模式;

·macOS和Linux上的透明代理模式;

·使用Python对HTTP流量进行脚本化更改;

·用于拦截的SSL/TLS证书是动态生成的。

(2)Mitmproxy的安装与使用

我的电脑环境是Windows 11,使用的目标是在电脑端监听Android手机的http和https流量,所以我只针对这种情况说明安装和使用步骤:

  •  在github上下载Mitmproxy的release包到本地。
  •  在浏览器中开启代理服务,输入本地ip和默认的8080端口,然后在终端使用命令

mitmweb -p 8080

开启mitmproxy,接着访问http://mitm.it/,下载Windows下的mitmproxy-ca-cert.p12证书。

③ 取消电脑端的代理,将手机与电脑连接同一个wifi,并开启代理为电脑端的IP地址和8080端口。手机端同样访问http://mitm.it/下载Android的证书。

④ 准备好上面步骤后,在电脑端使用命令

mitmweb -p 8080

在本地启动一个服务,打开了一个web界面,然后在手机端访问web就能在电脑端查看到数据流量包详情。

(3)插件设计

A 实现功能

设计一个Python脚本,配合mitmweb当做插件使用。需要实现:

  1. 能对监听的Android端的流量进行转存;
  2. 能按照当天日期分类进行保存到本地文件;
  3. 每一个session保存为一个文件夹,文件夹名是以1开始的数字id;
  4. 每个session文件夹中包含四个文件,分别存储request_body、response_body、request和response的头信息、通信的端口和ip信息。其中头信息还应该包含请求行和响应行,端口信息要准确显示手机端的实际IP和端口,以及访问web服务器的ip和端口;
  5. 额外添加一个报错日志。

B 总体设计

代码的流程图如下图所示:

设计思路:

1.脚本主要是一个插件类Counter,它定义了在特定事件发生时执行的操作,类中包含load()、request()、response()、log_error()、get_session_id()、creat_session_dir()等几个函数,来实现具体功能。最后通过将Counter()实例添加到addons列表中,我们告诉Mitmweb在运行时加载并使用这个插件。

2.类中函数load()函数,根据当前路径以及运行时间,创建数据存放目录,同时创建报错日志文件date_error_log.txt。

3.request()类函数,利用函数get_session_id()得到当前session的分配id,然后调用creat_session_id()创建此次session的文件夹。接着创建request_body.txt,从flow中写入信息。然后将request的header写入headers.txt(先创建)。

4.response()类函数,根据当前session_id创建response_body.txt,从flow中写入body信息,然后向headers.txt中写入response的header信息。接着从flow中获取手机IP、端口、服务器IP、服务器域名、服务器端口等信息,写入新创建的metadata.txt。

5.log_error()类函数,检测到错误,向当前session_id的文件中写入错误信息,以及在总的error_log.txt中写入信息。

6.get_session_id()和create_session_dir()两个类函数分别是实现获取当前session的id以及创建对应的文件夹。

如何实现:

(1)load()函数的第2行代码使用os.path.join()来将当前工作目录(通过os.path.abspath("") 获得)与 "log" 子目录和当前日期(格式为 "%Y%m%d")进行拼接。这样确保日志被存储在每天特定的目录中。第3行代码创建由log_dir指定的日志目录,exist_ok=True参数确保如果目录已经存在,函数不会引发异常。第5、6行代码类似,是创建名为date_error_log.txt文件。

def load(self, loader):
    log_dir = os.path.join(os.path.abspath(""), "log", time.strftime("%Y%m%d"))
os.makedirs(log_dir, exist_ok=True)

    error_log_file_name = f"{time.strftime('%Y%m%d')}_error_log.txt"
    self.error_log_file = os.path.join(log_dir, error_log_file_name)

(2)request()函数(response()与之类似),其中第3行把时间戳写入flow流,接着5-6行调用get_session_id()得到id,传参调用create_session_dir()创建文件夹,并写入flow流。第13行得到路径(其实也就是创建文件)。14行使用open()函数打开request_body_file文件以供写入,并将返回的文件对象赋值给变量f。mode="w" 表示以写入模式打开文件,如果文件不存在则创建它。encoding="ISO-8859-1" 指定了文件的编码格式为ISO-8859-1。15行检查流对象flow的请求内容是否存在,16-17表示存在就将流对象flow的请求内容以UTF-8编码进行解码,并将解码后的内容写入到打开的文件f中,18-19行是不存在就将捕获异常并调用名为 log_error的方法,传递相应的参数来记录错误日志。21-27与上述差不多,是创建headers.txt文件,写入有关request的头信息。

def request(self, flow: mitmproxy.http.HTTPFlow):
    # 记录请求时间戳
    flow.metadata["request_timestamp"] = time.time()
    
    if "session_id" not in flow.metadata:
        session_id = self.get_session_id()
        self.sessions[flow] = self.create_session_dir(session_id)
        flow.metadata["session_id"] = session_id
    
    session_dir = self.sessions[flow]
    
    # Save request body
    request_body_file = os.path.join(session_dir, "request_body.txt")
    with open(request_body_file, mode="w", encoding="ISO-8859-1") as f:
        if flow.request.content:
            try:
                f.write(flow.request.content.decode("utf-8"))
            except UnicodeDecodeError as e:
                self.log_error(session_dir, flow, "request", str(e))

    # Save headers
    headers_file = os.path.join(session_dir, "headers.txt")
    with open(headers_file, mode="w", encoding="utf-8") as f:
        f.write(f"{flow.request.method} {flow.request.url}\n")
        for name, value in flow.request.headers.items():
            f.write(f"{name}: {value}\n")
        f.write("\n\n")
    
    # 增加请求计数
    self.request_count += 1

三.功能测试

将一台Android手机与电脑端连接相同的wifi,并且手机端设置代理ip为电脑ip,端口为8080 ,电脑端用命令行执行:

mitmweb -s demo.py

在浏览器出现web监听界面,然后手机端在浏览器中访问web,电脑端出现截包信息,退出后查看电脑本地文件详情。

① 电脑端监听结果,成功实现监听:

② 本地保存的文件情况

可以看到,成功在log文件下创建了一个20230710的文件夹,里面有编号1-128的session文件夹,同时有一个总的20230710_error_log.txt文件,另外在session文件夹中有header.txt、metadata.txt、request_body.txt和response_body.txt。

③本地headers.txt文件详情:

可以看到mitmweb上的headers和本地保存的完全一致:

④ metadata.txt详情,可以看到文件中记录了手机的IP和端口,以及服务器端的IP、域名、端口信息:

源代码见本人github仓库:beichenxin (beichenxin) / Repositories · GitHub

  • 25
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值