• (转)MitmProxy+APPnium安装使用


    MitmProxy+APPnium安装使用

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/jiageibuuuyi/article/details/99716357

    MitmProxy+APPnium安装使用指导手册

    1、环境搭建

    1.1、环境准备

    Windows 10版本 64位系统

    1. jdk1.6.0 (64位)
    2. android-sdk_r24.3.4-windows
    3. Node.js
    4. appium
    5. .net framework
    6. Appium-Python-Client
    7. mitmproxy

    1.2、jdk安装

    1. 下载jdk包,根据自己的系统选择对应版本,jdk版本8.0比较合适。
    2. 一路傻瓜式安装,注意安装路径不要有空格,不要有中文。jdk和jre不要放在一个文件夹下
    3. 设置三个环境变量,我的电脑>选择“属性”->“高级”->“环境变量”->“系统变量”->“新建”

    JAVA_HOME----D:Javajdk1.6.0” (根据自己安装路径填写)

    img

    CLASSPATH— .;%JAVA_HOME%lib;%JAVA_HOME%lib ools.jar;

    img

    PATH-----;%JAVA_HOME%in;%JAVA_HOME%jrein;

    在path路径下加上面那两个

    ​ 4.打开cmd验证是否安装成功,输入java -version,然后输入javac

    能显示版本号和下面的帮助信息说明安装成功

    img


     

    1.3、android-sdk下载安装

    1.3.1、Android SDK简介

    Android SDK(Software Development Kit,软件开发工具包)被软件开发工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。它提供了 Android API 库和开发工具构建,测试和调试应用程序。简单来讲,Android SDK 可以看做用于开发和运行 Android 应用的一个软件。

    1.3.2、下载SDK

    1.目前官网上已经没有单独的SDK下载安装包了。目前官网推荐的是下载包含有Android SDK的Android Studio。官网下载传送门

    Android Studio 的一个功能非常强大的用来做安卓开发试调的官方出品的工具。Ps:官网下载,需科学(翻)上(墙)网

    **2.由于这个Android Studio 的安装包过大且官网下载需要翻墙,而咱们在用Appium 的时候实际上只需要用到SDK 工具而已。所以推荐大家去另外一个国内的Android 工具的下载网站下载。传送门**

    进入页面后点击【Android SDK 工具】按钮,然后在弹出的选择框内点击【SDK Tools】按钮,然后界面会自动跳转到SDK 的下载界面,选择适合自己的版本进行下载

    img

    将下载后的安装包解压到相应的目录下,如下图:

    img

    1.3.3、安装Android 版本

    进入SDK的解压目录下,双击“SDK Manager.exe”打开SDK管理器

    img

    在使用这个管理器的时候,需要使用科学,才能进行下载安装;或者你也可以在上面推荐的那个下载SDK的国内安卓工具网站上下载适合你的版本的Android SDK 镜像,网站传送门(可根据夜神模拟器使用的安卓版本下载)

    img

    1.3.4、下载aapt

    ​ 1.在android-sdk里面双击SDK-manager,下载buidl-tools

    img

    ​ 2.勾选build-tools,随便选一个版本

    img

    1.3.4、SDK环境配置

    在解压完成后,就可以开始配置环境变量了。方法和设置JAVA JDK的环境变量的方法类似。

    “右键 我的电脑”—“属性”—“高级系统设置”—“环境变量”—“系统变量”—“新建”

    SDK 需要对环境变量做如下更改(win 10):
    1 创建ANDROID_HOME,值是你的刚刚SDK的解压目录,比如     E:Androidandroid-sdk-windows
    
    • 1
    • 2

    img

    在新建完ANDROID_HOME之后,找到Path变量,然后双击打开Path 变量

    将SDK的platform-tools和tool的路径添加到Path 变量内(这两个目录在你通过SDK Manager安装更新Android SDK Tools和Android SDK Platform-tools之后就会有了)
    1 新建 %ANDROID_HOME%platform-tools
    2 新建 %ANDROID_HOME%	ools
    3 新建 %ANDROID_HOME%uild—tools28.0.0
    
    • 1
    • 2
    • 3
    • 4

    img

    在cmd输入adb可以查看对应版本号

    img

    输入aapt出现如下界面,说明环境OK了

    img

    1.3.5、adb连接夜神模拟器

    1. 输入adb version命令,查看adb版本,我电脑的adb版本为1.0.39

      夜神模拟器怎么连接adb

    2. 输入nox_adb version 命令,查看模拟器的adb版本,发现模拟器的版本是1.0.36与android-sdk的adb版本不一致

      夜神模拟器怎么连接adb

    3. 首先进入android-sdk的platform-tools目录下复制adb.exe

      夜神模拟器怎么连接adb

    4. 进入模拟器的安装目录下,修改nox_adb.exe的名字为nox_adb.exe.bak

      夜神模拟器怎么连接adb

    5. 把android-sdk的adb.exe文件复制到桌面一份,修改adb.exe的名称为nox_adb.exe,再把桌面修改完名称的nox_adb.exe复制到模拟器的Noxin目录下

      夜神模拟器怎么连接adb

    6. 再次在cmd窗口中输入命令nox_adb version ,发现替换完文件后的版本变为1.0.39了,版本一致了

      夜神模拟器怎么连接adb

    7. 打开模拟器

      夜神模拟器怎么连接adb

    8. 在cmd中输入 adb devices,可以发现模拟器设备了,已经连接上了,大功告成

      夜神模拟器怎么连接adb


    1.4、安装node.js

    下载官网地址:https://nodejs.org/en/download/23

    img

    下载后一路傻瓜式安装,安装完成后,运行cmd,输入node –v查看版本号,然后输入npm

    img

    出现如上图信息,表示node.js安装成功。npm是一个node包管理和分发工具,有了npm,后面就可以输入指令在线安装appium(打开 cmd输入:npm install –g appium但是一般不推荐这种,下载比较慢,所以用下面这种客户端安装)


    1.5、安装appium

    下载安装地址:https://bitbucket.org/appium/appium.app/downloads/16

    img

    直接双击appium-installer.exe文件安装就好,桌面会生成一个appium的图标,启动后界面显示如下

    img


    1.6、安装.net framework

    Appium是用.net开发的,所以需要安装.net framework4.5,下载地址:https://www.microsoft.com/zh-cn/download/details.aspx?id=3065332

    img


    1.7、appium-doctor

    1. appium安装好后,找到这个文件目录D:appiumAppium ode_modules.bin
    2. 将上面的地址添加到环境变量path下
    3. 打卡cmd,输入appium-doctor,检查环境是否OK,出现如下图所示,说明环境OK

    img

    1.8、安装Appium-Python-Client

    安装python库:pip install Appium-Python-Client
    img


    1.9、安装mitmproxy

    安装 python 的 mitmproxy 包除了会得到 mitmproxy 工具外,还会得到开发定制脚本所需要的包依赖,其安装过程并不复杂。

    首先需要安装好 python,版本需要不低于 3.6,且安装了附带的包管理工具 pip。不同操作系统安装 python 3 的方式不一,参考 python 的下载页,这里不做展开,假设你已经准备好这样的环境了。

    安装开始。

    在 linux 中:

    sudo pip3 install mitmproxy
    
    • 1

    在 windows 中,以管理员身份运行 cmd 或 power shell:

    pip3 install mitmproxy
    
    • 1

    安装结束。

    完成后,系统将拥有 mitmproxymitmdumpmitmweb 三个命令,由于 mitmproxy 命令不支持在 windows 系统中运行(这没关系,不用担心),我们可以拿 mitmdump 测试一下安装是否成功,执行:

    mitmdump --version
    
    • 1

    应当可以看到类似于这样的输出:

    Mitmproxy: 4.0.1
    Python:    3.6.5
    OpenSSL:   OpenSSL 1.1.0h  27 Mar 2018
    Platform:  Windows-10-10.0.16299-SP0
    
    • 1
    • 2
    • 3
    • 4

    1.10、证书安装

    手机抓包大家都知道要安装证书就是相当于一个护照有了它你才可以出国旅游,在你安装好mitmdump (我们这里用win系统作为例子因为win只可以用mitmdump和mitmweb)

    首先我们要把手机和电脑链接在同一个网络下比如我的电脑和Wifi都在连接在shangwang这个wifi下面

    img

    填写上你的正确的ip地址和抓包开启的端口号默认8080端口也可使用其他端口

    img

    img

    我这里是192.168.1.105所以服务器输入192.168.1.105端口号使用默认端口号8080就可以了如果大家想使用其他端口号也可以使用

    mitmdump -p ****
    
    • 1

    进行端口指定,防止8080端口被其他的软件所占用。

    手机配置好以后我们就开始使用了首先是访问mitm.it网站安装证书

    img

    选择你的手机机型,我这里是苹果机型所以选择第一个下载安装好证书以后就可访问网站了,如果你没有安装好证书就访问其他网页其他网页是阻止访问的,注意:苹果机型是需要设置证书信任的11.x版本都是在设置->通用->关于本机->证书信任里面设置信任证书。

    2、操作指导

    1. mitmproxy操作
    2. appnium操作

    2.1、mitmproxy

    mitmproxy 就是用于 MITM 的 proxy,MITM 即中间人攻击(Man-in-the-middle attack)。用于中间人攻击的代理首先会向正常的代理一样转发请求,保障服务端与客户端的通信,其次,会适时的查、记录其截获的数据,或篡改数据,引发服务端或客户端特定的行为。

    不同于 fiddler 或 wireshark 等抓包工具,mitmproxy 不仅可以截获请求帮助开发者查看、分析,更可以通过自定义脚本进行二次开发。举例来说,利用 fiddler 可以过滤出浏览器对某个特定 url 的请求,并查看、分析其数据,但实现不了高度定制化的需求,类似于:“截获对浏览器对该 url 的请求,将返回内容置空,并将真实的返回内容存到某个数据库,出现异常时发出邮件通知”。而对于 mitmproxy,这样的需求可以通过载入自定义 python 脚本轻松实现。

    但 mitmproxy 并不会真的对无辜的人发起中间人攻击,由于 mitmproxy 工作在 HTTP 层,而当前 HTTPS 的普及让客户端拥有了检测并规避中间人攻击的能力,所以要让 mitmproxy 能够正常工作,必须要让客户端(APP 或浏览器)主动信任 mitmproxy 的 SSL 证书,或忽略证书异常,这也就意味着 APP 或浏览器是属于开发者本人的——显而易见,这不是在做黑产,而是在做开发或测试。

    那这样的工具有什么实际意义呢?据我所知目前比较广泛的应用是做仿真爬虫,即利用手机模拟器、无头浏览器来爬取 APP 或网站的数据,mitmpproxy 作为代理可以拦截、存储爬虫获取到的数据,或修改数据调整爬虫的行为。

    事实上,以上说的仅是 mitmproxy 以正向代理模式工作的情况,通过调整配置,mitmproxy 还可以作为透明代理、反向代理、上游代理、SOCKS 代理等,但这些工作模式针对 mitmproxy 来说似乎不大常用,故本文仅讨论正向代理模式。

    “安装 mitmproxy”这句话是有歧义的,既可以指“安装 mitmproxy 工具”,也可以指“安装 python 的 mitmproxy 包”,注意后者是包含前者的。

    如果只是拿 mitmproxy 做一个替代 fiddler 的工具,没有什么定制化的需求,那完全只需要“安装 mitmproxy 工具”即可,去 mitmproxy 官网 上下载一个 installer 便可开箱即用,不需要提前准备好 python 开发环境。但显然,这不是这里要讨论的,我们需要的是“安装 python 的 mitmproxy 包”。

    2.1.1、启动

    要启动 mitmproxy 用 mitmproxymitmdumpmitmweb 这三个命令中的任意一个即可,这三个命令功能一致,且都可以加载自定义脚本,唯一的区别是交互界面的不同。

    mitmproxy 命令启动后,会提供一个命令行界面,用户可以实时看到发生的请求,并通过命令过滤请求,查看请求数据。形如:

    img

    mitmweb 命令启动后,会提供一个 web 界面,用户可以实时看到发生的请求,并通过 GUI 交互来过滤请求,查看请求数据。形如:

    img

    mitmdump 命令启动后——你应该猜到了,没有界面,程序默默运行,所以 mitmdump 无法提供过滤请求、查看数据的功能,只能结合自定义脚本,默默工作。

    由于 mitmproxy 命令的交互操作稍显繁杂且不支持 windows 系统,而我们主要的使用方式又是载入自定义脚本,并不需要交互,所以原则上说只需要 mitmdump 即可,但考虑到有交互界面可以更方便排查错误,所以这里以 mitmweb 命令为例。实际使用中可以根据情况选择任何一个命令。

    启动 mitmproxy:

    mitmweb
    
    • 1

    应当看到如下输出:

    Web server listening at http://127.0.0.1:8081/
    Proxy server listening at http://*:8080
    
    
    • 1
    • 2
    • 3

    mitmproxy 绑定了 *:8080 作为代理端口,并提供了一个 web 交互界面在 127.0.0.1:8081

    现在可以测试一下代理,让 Chrome 以 mitmproxy 为代理并忽略证书错误。为了不影响平时正常使用,我们不去改 Chrome 的配置,而是通过命令行带参数起一个 Chrome。如果你不使用 Chrome 而是其他浏览器,也可以搜一下对应的启动参数是什么,应该不会有什么坑。此外示例仅以 windows 系统为例,因为使用 linux 或 mac 开发的同学应该更熟悉命令行的使用才对,应当能自行推导出在各自环境中对应的操作。

    由于 Chrome 要开始赴汤蹈火走代理了,为了方便继续在 web 界面上与 mitmproxy 交互,我们委屈求全使用 Edge 或其他浏览器打开 127.0.0.1:8081。插一句,我用 Edge 实在是因为机器上没其他浏览器了(IE 不算),Edge 有一个默认禁止访问回环地址的狗屁设定,详见解决方案

    接下来关闭所有 Chrome 窗口,否则命令行启动时的附加参数将失效。打开 cmd,执行:

    "C:Program Files (x86)GoogleChromeApplicationchrome.exe" --proxy-server=127.0.0.1:8080 --ignore-certificate-errors
    
    
    • 1
    • 2

    前面那一长串是 Chrome 的的安装路径,应当根据系统实际情况修改,后面两参数设置了代理地址并强制忽略掉证书错误。用 Chrome 打开一个网站,可以看到:

    img

    同时在 Edge 上可以看到:

    img

    2.1.2、脚本

    完成了上述工作,我们已经具备了操作 mitmproxy 的基本能力 了。接下来开始开发自定义脚本,这才是 mitmproxy 真正强大的地方。

    脚本的编写需要遵循 mitmproxy 规定的套路,这样的套路有两个。

    第一个是,编写一个 py 文件供 mitmproxy 加载,文件中定义了若干函数,这些函数实现了某些 mitmproxy 提供的事件,mitmproxy 会在某个事件发生时调用对应的函数,形如:

    import mitmproxy.http
    from mitmproxy import ctx
    
    num = 0
    
    
    def request(flow: mitmproxy.http.HTTPFlow):
        global num
        num = num + 1
        ctx.log.info("We've seen %d flows" % num)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    第二个是,编写一个 py 文件供 mitmproxy 加载,文件定义了变量 addons,addons 是个数组,每个元素是一个类实例,这些类有若干方法,这些方法实现了某些 mitmproxy 提供的事件,mitmproxy 会在某个事件发生时调用对应的方法。这些类,称为一个个 addon,比如一个叫 Counter 的 addon:

    import mitmproxy.http
    from mitmproxy import ctx
    
    
    class Counter:
        def __init__(self):
            self.num = 0
    
        def request(self, flow: mitmproxy.http.HTTPFlow):
            self.num = self.num + 1
            ctx.log.info("We've seen %d flows" % self.num)
    
    
    addons = [
        Counter()
    ]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里强烈建议使用第二种套路,直觉上就会感觉第二种套路更为先进,使用会更方便也更容易管理和拓展。况且这也是官方内置的一些 addon 的实现方式。

    我们将上面第二种套路的示例代码存为 addons.py,再重新启动 mitmproxy:

    mitmweb -s addons.py
    
    
    • 1
    • 2

    当浏览器使用代理进行访问时,就应该能看到控制台里有类似这样的日志:

    Web server listening at http://127.0.0.1:8081/
    Loading script addons.py
    Proxy server listening at http://*:8080
    We've seen 1 flows
    ……
    ……
    We've seen 2 flows
    ……
    We've seen 3 flows
    ……
    We've seen 4 flows
    ……
    ……
    We've seen 5 flows
    ……
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这就说明自定义脚本生效了。

    2.1.3、事件

    上述的脚本估计不用我解释相信大家也看明白了,就是当 request 发生时,计数器加一,并打印日志。这里对应的是 request 事件,那拢共有哪些事件呢?不多,也不少,这里详细介绍一下。

    事件针对不同生命周期分为 5 类。“生命周期”这里指在哪一个层面看待事件,举例来说,同样是一次 web 请求,我可以理解为“HTTP 请求 -> HTTP 响应”的过程,也可以理解为“TCP 连接 -> TCP 通信 -> TCP 断开”的过程。那么,如果我想拒绝来个某个 IP 的客户端请求,应当注册函数到针对 TCP 生命周期 的 tcp_start 事件,又或者,我想阻断对某个特定域名的请求时,则应当注册函数到针对 HTTP 声明周期的 http_connect 事件。其他情况同理。

    下面一段估计会又臭又长,如果你没有耐心看完,那至少看掉针对 HTTP 生命周期的事件,然后跳到示例

    2.1.3.1、 针对 HTTP 生命周期
    def http_connect(self, flow: mitmproxy.http.HTTPFlow):
    
    
    • 1
    • 2

    (Called when) 收到了来自客户端的 HTTP CONNECT 请求。在 flow 上设置非 2xx 响应将返回该响应并断开连接。CONNECT 不是常用的 HTTP 请求方法,目的是与服务器建立代理连接,仅是 client 与 proxy 的之间的交流,所以 CONNECT 请求不会触发 request、response 等其他常规的 HTTP 事件。

    def requestheaders(self, flow: mitmproxy.http.HTTPFlow):
    
    
    • 1
    • 2

    (Called when) 来自客户端的 HTTP 请求的头部被成功读取。此时 flow 中的 request 的 body 是空的。

    def request(self, flow: mitmproxy.http.HTTPFlow):
    
    
    • 1
    • 2

    (Called when) 来自客户端的 HTTP 请求被成功完整读取。

    def responseheaders(self, flow: mitmproxy.http.HTTPFlow):
    
    
    • 1
    • 2

    (Called when) 来自服务端的 HTTP 响应的头部被成功读取。此时 flow 中的 response 的 body 是空的。

    def response(self, flow: mitmproxy.http.HTTPFlow):
    
    
    • 1
    • 2

    (Called when) 来自服务端端的 HTTP 响应被成功完整读取。

    def error(self, flow: mitmproxy.http.HTTPFlow):
    
    
    • 1
    • 2

    (Called when) 发生了一个 HTTP 错误。比如无效的服务端响应、连接断开等。注意与“有效的 HTTP 错误返回”不是一回事,后者是一个正确的服务端响应,只是 HTTP code 表示错误而已。

    2.1.3.2、 针对 TCP 生命周期
    def tcp_start(self, flow: mitmproxy.tcp.TCPFlow):
    
    
    • 1
    • 2

    (Called when) 建立了一个 TCP 连接。

    def tcp_message(self, flow: mitmproxy.tcp.TCPFlow):
    
    
    • 1
    • 2

    (Called when) TCP 连接收到了一条消息,最近一条消息存于 flow.messages[-1]。消息是可修改的。

    def tcp_error(self, flow: mitmproxy.tcp.TCPFlow):
    
    
    • 1
    • 2

    (Called when) 发生了 TCP 错误。

    def tcp_end(self, flow: mitmproxy.tcp.TCPFlow):
    
    
    • 1
    • 2

    (Called when) TCP 连接关闭。

    2.1.3.3、 针对 Websocket 生命周期
    def websocket_handshake(self, flow: mitmproxy.http.HTTPFlow):
    
    
    • 1
    • 2

    (Called when) 客户端试图建立一个 websocket 连接。可以通过控制 HTTP 头部中针对 websocket 的条目来改变握手行为。flow 的 request 属性保证是非空的的。

    def websocket_start(self, flow: mitmproxy.websocket.WebSocketFlow):
    
    
    • 1
    • 2

    (Called when) 建立了一个 websocket 连接。

    def websocket_message(self, flow: mitmproxy.websocket.WebSocketFlow):
    
    
    • 1
    • 2

    (Called when) 收到一条来自客户端或服务端的 websocket 消息。最近一条消息存于 flow.messages[-1]。消息是可修改的。目前有两种消息类型,对应 BINARY 类型的 frame 或 TEXT 类型的 frame。

    def websocket_error(self, flow: mitmproxy.websocket.WebSocketFlow):
    
    
    • 1
    • 2

    (Called when) 发生了 websocket 错误。

    def websocket_end(self, flow: mitmproxy.websocket.WebSocketFlow):
    
    
    • 1
    • 2

    (Called when) websocket 连接关闭。

    2.1.3.4、 针对网络连接生命周期
    def clientconnect(self, layer: mitmproxy.proxy.protocol.Layer):
    
    
    • 1
    • 2

    (Called when) 客户端连接到了 mitmproxy。注意一条连接可能对应多个 HTTP 请求。

    def clientdisconnect(self, layer: mitmproxy.proxy.protocol.Layer):
    
    
    • 1
    • 2

    (Called when) 客户端断开了和 mitmproxy 的连接。

    def serverconnect(self, conn: mitmproxy.connections.ServerConnection):
    
    
    • 1
    • 2

    (Called when) mitmproxy 连接到了服务端。注意一条连接可能对应多个 HTTP 请求。

    def serverdisconnect(self, conn: mitmproxy.connections.ServerConnection):
    
    
    • 1
    • 2

    (Called when) mitmproxy 断开了和服务端的连接。

    def next_layer(self, layer: mitmproxy.proxy.protocol.Layer):
    
    
    • 1
    • 2

    (Called when) 网络 layer 发生切换。你可以通过返回一个新的 layer 对象来改变将被使用的 layer。详见 layer 的定义

    2.1.3.5、 通用生命周期
    def configure(self, updated: typing.Set[str]):
    
    
    • 1
    • 2

    (Called when) 配置发生变化。updated 参数是一个类似集合的对象,包含了所有变化了的选项。在 mitmproxy 启动时,该事件也会触发,且 updated 包含所有选项。

    def done(self):
    
    
    • 1
    • 2

    (Called when) addon 关闭或被移除,又或者 mitmproxy 本身关闭。由于会先等事件循环终止后再触发该事件,所以这是一个 addon 可以看见的最后一个事件。由于此时 log 也已经关闭,所以此时调用 log 函数没有任何输出。

    def load(self, entry: mitmproxy.addonmanager.Loader):
    
    
    • 1
    • 2

    (Called when) addon 第一次加载时。entry 参数是一个 Loader 对象,包含有添加选项、命令的方法。这里是 addon 配置它自己的地方。

    def log(self, entry: mitmproxy.log.LogEntry):
    
    
    • 1
    • 2

    (Called when) 通过 mitmproxy.ctx.log 产生了一条新日志。小心不要在这个事件内打日志,否则会造成死循环。

    def running(self):
    
    
    • 1
    • 2

    (Called when) mitmproxy 完全启动并开始运行。此时,mitmproxy 已经绑定了端口,所有的 addon 都被加载了。

    def update(self, flows: typing.Sequence[mitmproxy.flow.Flow]):
    
    
    • 1
    • 2

    (alled when) 一个或多个 flow 对象被修改了,通常是来自一个不同的 addon。

    2.1.3.6、示例

    估计看了那么多的事件你已经晕了,正常,鬼才会记得那么多事件。事实上考虑到 mitmproxy 的实际使用场景,大多数情况下我们只会用到针对 HTTP 生命周期的几个事件。再精简一点,甚至只需要用到 http_connectrequestresponse 三个事件就能完成大多数需求了。

    这里以一个稍微有点黑色幽默的例子,覆盖这三个事件,展示如果利用 mitmproxy 工作。

    需求是这样的:

    1. 因为百度搜索是不靠谱的,所有当客户端发起百度搜索时,记录下用户的搜索词,再修改请求,将搜索词改为“360 搜索”;
    2. 因为 360 搜索还是不靠谱的,所有当客户端访问 360 搜索时,将页面中所有“搜索”字样改为“请使用谷歌”。
    3. 因为谷歌是个不存在的网站,所有就不要浪费时间去尝试连接服务端了,所有当发现客户端试图访问谷歌时,直接断开连接。
    4. 将上述功能组装成名为 Joker 的 addon,并保留之前展示名为 Counter 的 addon,都加载进 mitmproxy。

    第一个需求需要篡改客户端请求,所以实现一个 request 事件:

    def request(self, flow: mitmproxy.http.HTTPFlow):
        # 忽略非百度搜索地址
        if flow.request.host != "www.baidu.com" or not flow.request.path.startswith("/s"):
            return
    
        # 确认请求参数中有搜索词
        if "wd" not in flow.request.query.keys():
            ctx.log.warn("can not get search word from %s" % flow.request.pretty_url)
            return
    
        # 输出原始的搜索词
        ctx.log.info("catch search word: %s" % flow.request.query.get("wd"))
        # 替换搜索词为“360搜索”
        flow.request.query.set_all("wd", ["360搜索"])
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    第二个需求需要篡改服务端响应,所以实现一个 response 事件:

    def response(self, flow: mitmproxy.http.HTTPFlow):
        # 忽略非 360 搜索地址
        if flow.request.host != "www.so.com":
            return
    
        # 将响应中所有“搜索”替换为“请使用谷歌”
        text = flow.response.get_text()
        text = text.replace("搜索", "请使用谷歌")
        flow.response.set_text(text) 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第三个需求需要拒绝客户端请求,所以实现一个 http_connect 事件:

    def http_connect(self, flow: mitmproxy.http.HTTPFlow):
        # 确认客户端是想访问 www.google.com
        if flow.request.host == "www.google.com":
            # 返回一个非 2xx 响应断开连接
            flow.response = http.HTTPResponse.make(404)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    为了实现第四个需求,我们需要将代码整理一下,即易于管理也易于查看。

    创建一个 joker.py 文件,内容为:

    import mitmproxy.http
    from mitmproxy import ctx, http
    
    
    class Joker:
        def request(self, flow: mitmproxy.http.HTTPFlow):
            if flow.request.host != "www.baidu.com" or not flow.request.path.startswith("/s"):
                return
    
            if "wd" not in flow.request.query.keys():
                ctx.log.warn("can not get search word from %s" % flow.request.pretty_url)
                return
    
            ctx.log.info("catch search word: %s" % flow.request.query.get("wd"))
            flow.request.query.set_all("wd", ["360搜索"])
    
        def response(self, flow: mitmproxy.http.HTTPFlow):
            if flow.request.host != "www.so.com":
                return
    
            text = flow.response.get_text()
            text = text.replace("搜索", "请使用谷歌")
            flow.response.set_text(text)
    
        def http_connect(self, flow: mitmproxy.http.HTTPFlow):
            if flow.request.host == "www.google.com":
                flow.response = http.HTTPResponse.make(404)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    创建一个 counter.py 文件,内容为:

    import mitmproxy.http
    from mitmproxy import ctx
    
    
    class Counter:
        def __init__(self):
            self.num = 0
    
        def request(self, flow: mitmproxy.http.HTTPFlow):
            self.num = self.num + 1
            ctx.log.info("We've seen %d flows" % self.num)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    创建一个 addons.py 文件,内容为:

    import counter
    import joker
    
    addons = [
        counter.Counter(),
        joker.Joker(),
    ]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    将三个文件放在相同的文件夹,在该文件夹内启动命令行,运行:

    mitmweb -s addons.py
    
    
    • 1
    • 2

    老规矩,关闭所有 Chrome 窗口,从命令行中启动 Chrome 并指定代理且忽略证书错误。

    测试一下运行效果:

    img

    img

    img


    2.2、Appnium

    2.2.1、启动APP

    2.2.1.1、获取apk包名

    1. 将准备测试的APK放到D盘某个目录,如D: est
    2. 打开cmd,输入指令aapt dump badging D: estxxx.apk(APK的全名,如手机淘宝.apk)
    3. 以手机淘宝.apk为例,如下图
    4. 这里就可以看到apk的包名:com.taobao.taobao

    img

    注:老司机可以直接把apk放在桌面上,输入指令后拖到cmd框

    2.2.1.2、获取launcherActivity

    ​ 1.接着上一步操作,cmd屏幕拖到中间l找到auncherActivity

    ​ 2.这里可以看到,淘宝的launcherActivity值为com.taobao.tao.welcome.Welcome

    img

    2.2.1.3、写脚本

    1. platformName:这里是android的apk
    2. deviceName:手机设备名称,通过adb devices查看
    3. platformVersion:android系统的版本号
    4. appPackage:apk包名
    5. appActivity:apk的launcherActivity

    img

    2.2.1.4、运行appium

    ​ 1.启动appium,右上角点三角形按钮,变成正方形,就是启动状态。

    img

    ​ 2.确认手机连上电脑

    img

    ​ 3.在pycharm运行脚本,随后在手机上会弹出安装下面两个软件的提示,安装后,桌面上多两个图标。那么恭喜你启动成功!

    img

    ​ 4.接着会看到淘宝app已经启动啦,有木有小激动~~

    2.2.1.5、代码

    # coding=utf-8
    
    from appium import webdriver
    
    desired_caps = {
                    'platformName': 'Android',
                    'deviceName': '30d4e606',
                    'platformVersion': '5.0',
                    # apk包名
                    'appPackage': 'com.taobao.taobao',
                    # apk的launcherActivity
                    'appActivity': 'com.taobao.tao.welcome.Welcome'
                    }
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个地址是怎么来的呢?

    img

    2.2.2、定位元素

    uiautomatorviewer是android-sdk自带的一个元素定位工具,非常简单好用,使用uiautomatorviewer,你可以检查一个应用的UI来查看应用的布局和组件以及相关的属性。

    2.2.2.1、启动uiautomatorviewer.bat

    ​ 1.打开目录D:androidsdkandroid-sdk-windows ools

    img

    ​ 2.双击启动,启动之后出现如下界面

    img

    ​ 3如果不喜欢双击启动的话,也可以在cmd里面通过指令启动,先cd到tools目录下,然后输入uiautomatorviewer.bat回车后启动服务

    img

    2.2.2.2、连接模拟器

    ​ 1.cmd打开输入adb devices,确认模拟器已连上

    ​ 2.打开手机淘宝页面,让屏幕处于点亮状态

    ​ 3.点左上角安卓机器人按钮Devices Screenshot按钮刷新页面

    img

    2.2.2.3、定位元素

    ​ 1.移动鼠标到需要定位的元素上,如搜索输入框

    img

    2.右下角可以看到元素对应的属性

    text:搭配新宠不能缺
    resource-id:com.taobao.taobao:id/home_searchedit
    class:android.widget.EditText
    
    
    • 1
    • 2
    • 3
    • 4

    2.2.2.4、点搜索框

    ​ 1.前面一篇启动app后,休眠五秒,等待页面加载完成

    ​ 2.通过id来定位到搜索框,然后点击

    img

    2.2.2.5、代码

    # coding=utf-8
    from appium import webdriver
    import time
    
    
    desired_caps = {
                    'platformName': 'Android',
                    'deviceName': '30d4e606',
                    'platformVersion': '5.0',
                    'appPackage': 'com.taobao.taobao',
                    'appActivity': 'com.taobao.tao.welcome.Welcome',
                    }
    
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
    # 休眠五秒等待页面加载完成
    time.sleep(5)
    driver.find_element_by_id("com.taobao.taobao:id/home_searchedit").click()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.2.2.6、元素定位

    ​ appium的webdriver提供了11种元素定位方法,在selenium的基础上扩展了三个,可以在pycharm里面输入driver.find_element_by然后会自动匹配出来

    img


    3、Demo

    抓取得到APP数据

    首先通过Fiddler抓包分析想要的数据在说明url的请求中:

    可以发现数据在请求https://entree.igetget.com/bauhinia/h5/college/course中

    在这里插入图片描述

    记住该信息方便下面进行代码编撰。

    3.1、环境说明

    本机IP:
    在这里插入图片描述

    夜神模拟器代理配置:

    在这里插入图片描述

    查看APK信息

    在这里插入图片描述

    在这里插入图片描述

    确认模拟器连接电脑

    3.2、代码

    joker.py

    import json
    import mitmproxy.http
    
    
    
    class Joker:
        #针对响应动作抓包
        def response(self, flow: mitmproxy.http.HTTPFlow):
            #过滤所有响应,只针对存在想要数据的url响应进行解析
            if flow.request.url == 'https://entree.igetget.com/bauhinia/h5/college/course':
                # ctx.log.info(str(type(flow.request.data.content)))
                res = flow.response.text    #获取响应内容
                parse(res)  #调用解析函数
    
    
    #解析函数
    def parse(text):
        res = json.loads(text).get('c').get('list')
        for i in res:
            result = {}
            teacher_list = []
            teacher_dict = {}
            result['name'] = i.get('name')
            teacher_dict['name'] = i.get('lecturer_name')
            teacher_dict['indtoduce'] = i.get('lecturer_title')
            teacher_list.append(teacher_dict)
            result['teacher'] = teacher_list
            result['introduce'] = i.get('highlight')
            result['price'] = str(int(i.get('price'))/100)
            result['period'] = i.get('phase_num')
            with open('test.json', 'a+', encoding='utf8') as f:
                json.dump(result, f, indent=4, ensure_ascii=False)
                f.write('
    
    ')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    addon.py

    import joker
    
    addons = [
        joker.Joker(),
    ]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    app_contorl.py

    import time
    from appium import webdriver
    
    class AppConn:
    
        def __init__(self):
            self.desired_caps = {
                'platformName': 'Android',          #系统名
                'deviceName': '127.0.0.1:62001',    #设备名 通过adb devices查看
                'platformVersion': '5.0',           #android系统的版本号
                'appPackage': 'com.luojilab.player',    #apk包名
                'appActivity': 'com.luojilab.business.welcome.SplashActivity' #apk的launcherActivity
            }
    
            self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', self.desired_caps)
    
    #点击事件,选取指定的元素进行点击
        def click(self, college):
            time.sleep(5)
            self.driver.find_element_by_id('com.luojilab.player:id/classImageView').click()
            time.sleep(1)
            # a = self.driver.find_element_by_xpath('//android.widget.TextView[@text="%s"]'%college).click()
            # print(a)
    
    #滑动事件,滑动屏幕刷新储更多的数据
        def move(self, num):
            l = (self.driver.get_window_size()['width'], self.driver.get_window_size()['height'])
            x1 = int(l[0] * 0.5)  # x坐标
            y1 = int(l[1] * 0.25)  # 起始y坐标
            y2 = int(l[1] * 0.75)
            for i in range(num):
                self.driver.swipe(x1, y2, x1, y1, 1000)
                time.sleep(0.2)
    
    
    if __name__ == '__main__':
        test = AppConn()
        # college_list = ['商学院25', '能力学院60', '人文学院', '视野学院', '商学院']
        test.click('商学院25')
        test.move(10)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    3.3、启动命令

    1. cmd启动mimtproxy

    在这里插入图片描述

    mitmweb --web-port 8889 -p 8888 -s addons.py
    
    
    • 1
    • 2
    1. 启动Appnium

    在这里插入图片描述

    1. 执行app_control.py

    2. APPnium开始操作apk

    在这里插入图片描述

    1. 抓包处理数据并保存

    在这里插入图片描述

    以上大部分内容皆整理于以下链接

    参考链接

    https://cnblogs.com/yoyoketang/p/6128725.html
    https://www.cnblogs.com/yoyoketang/p/6128730.html
    https://www.cnblogs.com/yoyoketang/p/6128735.html
    https://www.cnblogs.com/yoyoketang/p/6128741.html
    https://cnblogs.com/grandlulu/p/9525417.html
    https://jingyan.baidu.com/article/cb5d6105a3365f005c2fe0f7.html
    https://blog.csdn.net/weixin_36662706/article/details/81042352

  • 相关阅读:
    【设计模式】备忘录
    统计ip的发送频率和该ip发送的有效消息(去除相似消息)的数目
    Android之消息推送聊天实现
    Dictionary通过下标获取key和value
    SGU 271 水题。。。。
    二叉树递归和非递归遍历
    C#与SSL
    正则表达式总结
    SQL Server User Accounts
    嵌入式领域中各种文件系统的比较
  • 原文地址:https://www.cnblogs.com/showker/p/11561302.html
Copyright © 2020-2023  润新知