1.简介

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。Flutter应用程序是用Dart编写的,这是一种由Google在7年多前创建的语言。Flutter是Google使用Dart语言开发的移动应用开发框架,使用一套Dart代码就能快速构建高性能、高保真的iOS和Android应用程序。

HTTP应用层的抓包已经成为日常工作测试与调试中的重要一环,最近接触新项目突然之间发现之前的抓包手段都不好使了,顿时模块与模块之间的前端与服务之间的交互都变成了不可见,整个人都好像被蒙住了眼睛。

2.验证是否走代理

Flutter 应用的网络请求是不走手机的系统代理的,也就是说你在系统设置中设置了代理地址和端口号后 Flutter 也不会走你的代理,而抓包是必须要设置代理的,然后走代理我们才可以成功的抓到包,现在人家都不从你这里走,累死你都抓不到。

方法一:首先我们使用正常的抓包流程:通过fiddler进行抓包,可以看到,只抓到一些图片和一些没有用处的乱七八糟的文件,那么很有可能他不走代理。

还有一种方法可以判断APP是否为无代理请求模式:以fiddler为例,当我们配置好fiddler证书、模拟器wifi配置好ip和端口后,客户端关闭fiddler抓包工具,如果该APP还可以正常运行说明请求为无代理模式。

宏哥查了一下现在使用Flutter的应用程序,发现好多程序都用它,宏哥就选择了某鱼这一款APP。

按照之前的宏哥配置,模拟器配置了代理而且这个代理是走Fiddler的,如果宏哥没有启动Fiddler如果是走代理的应用程序,就会出现网络问题,如果是不走代理的应用程序,就可以正常访问网络。具体操作步骤如下:

1.宏哥没有启动Fiddler,然后用浏览器访问百度,出现网络问题,因为代理的网络走到Fiddler这里,Fiddler不通,出现网络问题。如下图所示:

2.宏哥没有启动Fiddler,然后启动应用某鱼APP,正常访问网络,因为不走代理的网络,Fiddler启动不启动对其没有影响,不会出现网络问题。如下图所示:

通过以上对比,我们确认了这款某鱼APP不走我们手机设置的代理,因此我们就不可能抓到它的包了。

3.为什么http请求没有通过wifi走代理?

为什么http请求没有通过wifi走代理呢,因为之前安卓原生使用的一些http框架都是正常走代理的啊,那是不是有可能代码中有api方法可以设置请求不走代理,于是乎就研读了一下Flutter中http相关的源码,最终找到了答案。

3.1http请求源码跟踪

http.dart中的HttpClient是一个抽象类,成员方法的具体实现在http_impl.dart中,http的get请求实现如下:

  1. Future<HttpClientRequest> getUrl(Uri url) => _openUrl("get", url);
  2.  
  3. Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
  4.  
  5. .
  6.  
  7. .
  8.  
  9. .
  10.  
  11. // Check to see if a proxy server should be used for this connection.
  12.  
  13. var proxyConf = const _ProxyConfiguration.direct();
  14.  
  15. if (_findProxy != null) {
  16.  
  17. // TODO(sgjesse): Keep a map of these as normally only a few
  18.  
  19. // configuration strings will be used.
  20.  
  21. try {
  22.  
  23. proxyConf = new _ProxyConfiguration(_findProxy(uri));
  24.  
  25. } catch (error, stackTrace) {
  26.  
  27. return new Future.error(error, stackTrace);
  28.  
  29. }
  30.  
  31. }
  32.  
  33. return _getConnection(uri.host, port, proxyConf, isSecure)
  34.  
  35. .then((_ConnectionInfo info) {
  36.  
  37. .
  38.  
  39. .
  40.  
  41. .
  42.  
  43. });
  44.  
  45. }

首先,我们可以发现方法中有一行注释// Check to see if a proxy server should be used for this connection.,意思是“检查是否应该使用代理服务器进行此连接”;

然后,有一个proxyConf对象初始化和根据_findProxy来创建新的proxyConf对象的语句,然后通过_getConnection(uri.host, port, proxyConf, isSecure)来创建连接,_getConnection的源码如下:

  1. Future<_ConnectionInfo> _getConnection(String uriHost, int uriPort,
  2.  
  3. _ProxyConfiguration proxyConf, bool isSecure) {
  4.  
  5. Iterator<_Proxy> proxies = proxyConf.proxies.iterator;
  6.  
  7. Future<_ConnectionInfo> connect(error) {
  8.  
  9. if (!proxies.moveNext()) return new Future.error(error);
  10.  
  11. _Proxy proxy = proxies.current;
  12.  
  13. String host = proxy.isDirect ? uriHost : proxy.host;
  14.  
  15. int port = proxy.isDirect ? uriPort : proxy.port;
  16.  
  17. return _getConnectionTarget(host, port, isSecure)
  18.  
  19. .connect(uriHost, uriPort, proxy, this)
  20.  
  21. // On error, continue with next proxy.
  22.  
  23. .catchError(connect);
  24.  
  25. }
  26.  
  27. return connect(new HttpException("No proxies given"));
  28.  
  29. }

从代码中我们可以看到根据代理配置信息来将请求的host和port进行重置,然后创建真实的连接。

跟踪以上源码我们发现dart中http请求是否走代理是需要配置的,而_findProxy变量和配置的代理信息有关。

http__impl.dart文件中的_HttpClient类中定义了_findProxy的默认值

  1. Function _findProxy = HttpClient.findProxyFromEnvironment;

HttpClient类中findProxyFromEnvironment方法的实现

  1. static String findProxyFromEnvironment(Uri url,
  2.  
  3. {Map<String, String> environment}) {
  4.  
  5. HttpOverrides overrides = HttpOverrides.current;
  6.  
  7. if (overrides == null) {
  8.  
  9. return _HttpClient._findProxyFromEnvironment(url, environment);
  10.  
  11. }
  12.  
  13. return overrides.findProxyFromEnvironment(url, environment);
  14.  
  15. }

_HttpClient类中_findProxyFromEnvironment方法的实现

  1. static String _findProxyFromEnvironment(
  2.  
  3. Uri url, Map<String, String> environment) {
  4.  
  5. checkNoProxy(String option) {
  6.  
  7. if (option == null) return null;
  8.  
  9. Iterator<String> names = option.split(",").map((s) => s.trim()).iterator;
  10.  
  11. while (names.moveNext()) {
  12.  
  13. var name = names.current;
  14.  
  15. if ((name.startsWith("[") &&
  16.  
  17. name.endsWith("]") &&
  18.  
  19. "[${url.host}]" == name) ||
  20.  
  21. (name.isNotEmpty && url.host.endsWith(name))) {
  22.  
  23. return "DIRECT";
  24.  
  25. }
  26.  
  27. }
  28.  
  29. return null;
  30.  
  31. }
  32.  
  33. checkProxy(String option) {
  34.  
  35. if (option == null) return null;
  36.  
  37. option = option.trim();
  38.  
  39. if (option.isEmpty) return null;
  40.  
  41. int pos = option.indexOf("://");
  42.  
  43. if (pos >= 0) {
  44.  
  45. option = option.substring(pos + 3);
  46.  
  47. }
  48.  
  49. pos = option.indexOf("/");
  50.  
  51. if (pos >= 0) {
  52.  
  53. option = option.substring(0, pos);
  54.  
  55. }
  56.  
  57. // Add default port if no port configured.
  58.  
  59. if (option.indexOf("[") == 0) {
  60.  
  61. var pos = option.lastIndexOf(":");
  62.  
  63. if (option.indexOf("]") > pos) option = "$option:1080";
  64.  
  65. } else {
  66.  
  67. if (option.indexOf(":") == -1) option = "$option:1080";
  68.  
  69. }
  70.  
  71. return "PROXY $option";
  72.  
  73. }
  74.  
  75. // Default to using the process current environment.
  76.  
  77. if (environment == null) environment = _platformEnvironmentCache;
  78.  
  79. String proxyCfg;
  80.  
  81. String noProxy = environment["no_proxy"];
  82.  
  83. if (noProxy == null) noProxy = environment["NO_PROXY"];
  84.  
  85. if ((proxyCfg = checkNoProxy(noProxy)) != null) {
  86.  
  87. return proxyCfg;
  88.  
  89. }
  90.  
  91. if (url.scheme == "http") {
  92.  
  93. String proxy = environment["http_proxy"];
  94.  
  95. if (proxy == null) proxy = environment["HTTP_PROXY"];
  96.  
  97. if ((proxyCfg = checkProxy(proxy)) != null) {
  98.  
  99. return proxyCfg;
  100.  
  101. }
  102.  
  103. } else if (url.scheme == "https") {
  104.  
  105. String proxy = environment["https_proxy"];
  106.  
  107. if (proxy == null) proxy = environment["HTTPS_PROXY"];
  108.  
  109. if ((proxyCfg = checkProxy(proxy)) != null) {
  110.  
  111. return proxyCfg;
  112.  
  113. }
  114.  
  115. }
  116.  
  117. return "DIRECT";
  118.  
  119. }

从以上代码中可以发现代理配置从environment中读取,设置代理时必须指定http_proxy或https_proxy等。而从_openUrl方法实现中proxyConf = new _ProxyConfiguration(_findProxy(uri));得出默认情况下environment是为空的,所以要想在Flutter的http请求中使用代理,则要指定相应的代理配置,即设置httpClient.findProxy的值。示例代码:

  1. _getHttpData() async {
  2.  
  3. var httpClient = new HttpClient();
  4.  
  5. httpClient.findProxy = (url) {
  6.  
  7. return HttpClient.findProxyFromEnvironment(url, environment: {"http_proxy": 'http://192.168.124.7:8888',});
  8.  
  9. };
  10.  
  11. var uri =
  12.  
  13. new Uri.http('t.weather.sojson.com', '/api/weather/city/101210101');
  14.  
  15. var request = await httpClient.getUrl(uri);
  16.  
  17. var response = await request.close();
  18.  
  19. if (response.statusCode == 200) {
  20.  
  21. print('请求成功');
  22.  
  23. var responseBody = await response.transform(Utf8Decoder()).join();
  24.  
  25. print('responseBody = $responseBody');
  26.  
  27. } else {
  28.  
  29. print('请求失败');
  30.  
  31. }
  32.  
  33. }

以上代码设置后即可使用Fiddler或Charles抓包了。

敲黑板!!!代码中已设置代理,手机wifi不再需要进行代理设置;192.168.124.7该IP为我们需要抓包的Charles所在电脑IP。

查了好多资料绝大多数是在代码中设置代理,或者是代码设置了,然后让其走手机代理,或许这对于开发很容易但是对于测试,或者别人家的APP或许就不是很容易了。下面我们看看下边的方案。

4.使用VPN

使用VPN将终端设备的流量转发到代理服务器。说的好听点就是使用VPN,难听点就是使用Drony工具强行使APP走代理。

优势:使用VPN软件不用添加其他测试。

劣势:终端上的VPN默认会直接对所有流量进行转发,要进行合理的配置可能需要额外的学习成本。

  因为我们的测试对象是手机移动APP,因为我们的测试对象是手机移动APP,所以我们首先要在手机上安装一个VPN,这里使用一个十分方便的VPN软件drony (介绍在这里https://github.com/SuppSandroB/sandrop/wiki/Drony-FAQ),drony会在你的手机上创建一个VPN,将手机上的所有流量都重定向到drony自身(不是流向vpn服务器) ,这样drony就可以管理所有手机上的网络流量,甚至可以对手机上不同APP的流量进行单独配置。

4.1下载安装Drony

1.下载对应的安装包到手机上安装好,宏哥这里还是用夜神模拟器做演示,访问其下载地址:https://drony.soft112.com/ 翻不了墙的,用这个地址下载:https://www.appsapk.com/drony-1-3-155/,如下图所示:

2.下载安装包并安装好。安装完成后打开软件,如下图所示:

4.2配置drony转发

1.打开Drony(处于OFF状态),切换到SETTINGS(无法点击,试试左右滑动切换到SETTING),如下图所示:

2.选择Networks,点击Wi-Fi,如下图所示:

3.点击Wi-Fi,进入配置界面,如果是真机或者你有多个热点可以连接都可以在这里显示,这个就和我们手机连接WiFi一样。在网络列表中选择点击当前手机wifi连接的网络 (需要确保该网络与Fiddler代理服务器网络是联通的)。如下图所示:

4.由于宏哥这里是模拟器,因此需要宏哥编辑一下,在这界面选中那个VirtWifi(虚拟WiFi)长按,弹出Edit和Delete。如下图所示:

5.点击“Edit”。进入网络详情设置(Network details),如下图所示:

6.设置代理hosetname,默认是电脑局域网ip,也就是Fiddler安装电脑的IP,如下图所示:

7.设置代理Port,fiddler 默认是 8888,如下图所示:

8.设置 Proxy type,注意Proxy type代理方式要选择 Plain http proxy。如下图所示:

敲黑板!!!!最上边的Proxy type,选择代理模式为手动(Manual),如下图所示:

9.设置Filter default value为Direct all,如下图所示:

10.设置Rules,点击下面的Rule设置应用规则。如下图所示:

11.点击“Edit filter Rules”,。进入添加规则页面,如下图所示:

12.默认您的规则里应该是空的,这里直接点击上面的加号添加一个规则(符合规则要求的才会被转发),点击右上角的加号图标,如下图所示:

13.点击右上角的加号图标后,进入过滤规则添加界面,如下图所示:

(1)在Network id处 选择当前wifi的SSID

(2)Action 选择 Local proxy chain

(3)Application 选择需要强制代理的APP

(4)Hostname 及 Port 不填 表示所有的都会被强制代理,因为APP可能会使用其他的网络协议不一定都是http,可能不希望把所有流量都引流到http代理服务器,这个时候就会使用这个配置指定ip及端口才转发

14.添加好以后,点击右上角的保存图标,如下图所示:

15.点击“保存”后,跳转到规则界面,如下图所示:

16.启动Drony:返回到SETTING主页,滑动到LOG页,点击下面“OFF”按钮,

17.点击“确定”,使其处于ON的状态(表示启用),如下图所示:

4.3开启代理抓包软件

宏哥这里代理抓包软件使用的是Fiddler。Fiddler的使用这里不再介绍,需要打开远程代理,并在手机中安装Fiddler根证书。这里宏哥就不做赘述,前边都有详细的介绍

经过上面到配置,这些APP的HTTP流量我们就可以通过代理抓包软件获取,https流量也可能正常解码。

5.小结

宏哥这里只是提供一种思路供你学习和实践。 好了,今天时间也不早了,宏哥就讲解和分享到这里,感谢你耐心地阅读!!!

6.拓展

6.1如何下载google play上的apk安装包

之前一直没有从Google Play上下载过apk文件,也不知道怎么下载,带来过不便,今天下载查了一下资料,并亲自实践,发现很简单。

前提:能FQ访问Google。

共分两个步骤:

1,访问Google play

https://play.google.com/store/apps

搜索你想要的应用。

打开应用详情页,复制URL地址到步骤2。

2,访问

https://apps.evozi.com/apk-downloader/

将步骤1中的链接粘贴到这个URL中的输入框,点击按钮(蓝色)解析出下载apk的链接,再点击下载链接(绿色)就下载到你的电脑了。

《吐血整理》高级系列教程-吃透Fiddler抓包教程(31)-Fiddler如何抓取Android系统中Flutter应用程序的包的更多相关文章

  1. 使用Fiddler抓取Android模拟器中的Android_APP请求

    对Fiddler的设置:在https://www.telerik.com/download/fiddler网站上下载Fiddler,输入内容后点击下面按钮进行下载: 下载成功后,打开Fiddler进行 ...

  2. 吐血整理 Delphi系列书籍 118本(全)

    Delphi 教程 系列书籍 网友(老帅)整理 001_<Delhpi6数据库设计思想与实践> 002_<Delphi6应用开发指南> 003_<Delphi6开发人员指 ...

  3. 在RedHat.Enterprise.Linux_v6.3系统中安装Oracle_11gR2教程

    在RedHat.Enterprise.Linux_v6.3系统中安装Oracle_11gR2教程 本教程提供PDF格式下载: 在RedHat.Enterprise.Linux_v6.3系统中安装Ora ...

  4. Android系统编程入门系列之应用环境及开发环境介绍

        作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...

  5. XamarinSQLite教程在Xamarin.Android项目中提取数据库文件

    XamarinSQLite教程在Xamarin.Android项目中提取数据库文件 由于不能直接打开该文件,开发者需要先将数据库文件从Android系统中提取出来.操作步骤如下. (5)选择MyDoc ...

  6. Android系统编程入门系列之应用初始化Application

    在上一篇文章中我们了解到Android系统启动应用的时候,会首先加载AndroidManifest.xml清单文件中的一系列信息,在清单文件中如果不指定<application></ ...

  7. Android系统编程入门系列之加载界面Activity

    上回说到应用初始化加载及其生命周期,在Android系统调用Applicaiton.onCreate()之后,继续创建并加载清单文件中注册的首个界面即主Activity,也可称之为入口界面.主Acti ...

  8. Android系统编程入门系列之应用内数据保存数据库

    上篇文章已经介绍了如何使用SharedPreferences存储键值对形式的轻量级数据,对于那些相同结构的多组数据,类似于存储Java中定义的类的多个对象属性值,如果按照键值对的形式一条条读写,需要分 ...

  9. Android系统编程入门系列之硬件交互——通信硬件Bluetooth

    通信硬件NFC的文章,虽然可以在Android系统中通过非直接接触的形式与支持NFC硬件的设备通信,但是也只能交互一些简短的标签内容,对大量的持续性数据,却并不能很好的支持.因此针对这个弊端,可以考虑 ...

随机推荐

  1. java学习第二天多态.day09

    接口 接口总结 接口表示一种规约(规范.标准),它里面定义了一些列抽象方法(功能),它可以被多个类实现. 1接口名称首写字母用I,表示一个接口,后命名使用驼峰命名 2.接口中定义的都是抽象方法,所以可 ...

  2. C++ 一键关闭屏幕

    Demo下载地址:http://pan.baidu.com/s/1vN4wF #include <windows.h> #include "resource.h" LR ...

  3. mybatisplus-Service CRUD 接口

    通用 Service CRUD 封装IService (opens new window)接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前 ...

  4. CF915G Coprime Arrays (莫比乌斯反演)

    CF915G Coprime Arrays 题解 (看了好半天终于看懂了) 我们先对于每一个i想,那么 我们设 我们用莫比乌斯反演 有了这个式子,可比可以求出△ans呢?我们注意到,由于那个(i/d) ...

  5. 【java】学习路径39-Buffered缓冲输出流

    import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; pu ...

  6. metasploit进行局域网远控

    用metasploit进行局域网远程控制 Metasploit是一款开源的安全漏洞检测工具,可以帮助安全和IT专业人士识别安全性问题,验证漏洞的缓解措施,并管理专家驱动的安全性进行评估,提供真正的安全 ...

  7. C语言小游戏:贪吃蛇

    #include <graphics.h> #include <conio.h> #include <stdio.h> #define WIDTH 40 //设置宽 ...

  8. 大促活动如何抵御大流量 DDoS 攻击?

    每一次活动大促带来的迅猛流量,对技术人而言都是一次严峻考验.如果在活动期间遭受黑产恶意DDoS攻击,无疑是雪上加霜.电商的特性是业务常态下通常不会遭受大流量DDoS攻击,且对延迟敏感,因此只需要在活动 ...

  9. HK32F030MF4P6的Linux GCC工具链和VSCode开发环境

    HK32F030MF4P6简介 航顺的 HK32F030MF4P6, TSSOP20封装, Arm Cortex M0 内核, 内建32MHz时钟, 16K Flash, 2K RAM(实际上可用的有 ...

  10. HCNP Routing&Switching之ARP安全

    前文我们了解了IP安全相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16652367.html:今天我们来聊一聊ARP安全相关话题: 什么是ARP? ...