C# FiddlerCore 抓取
本文目的
记录FiddlerCore怎样实现以下功能:
抓取本机的请求响应
抓取本机局域网内其它设备的请求响应
情景介绍
用C#调用FiddlerCore,开发出软件,本软件可以抓取本机(运行本软件的电脑)上的http/https请求响应,可以抓取同一局域网内设备(如本机局域网ip为192.168.1.2,路由器ip为192.168.1.1,同一路由器下还连接了一部手机,其ip为192.168.1.3,那么,这个手机的http/https也是可以抓取的)。
添加引用
需要的库文件:
BCMakeCert.dll
CertMaker.dll
FiddlerCore4.dll
FiddlerCore4.pdb
FiddlerCore4.xml
using Fiddler;
初始化FiddlerCore
//https代理
Proxy oSecureEndpoint;
//主机名
string sSecureEndpointHostname = "localhost";
//伪装https服务器(别人这么说,我也没搞明白这个技术细节)
int iSecureEndpointPort = 8877;
//代理端口
int iStartPort = 8888;
//FiddlerCore抓取到的会话不会缓存,所以,要自己维护一个会话列表,来保存所关心的请求
List<Session> oAllSessions = new List<Session>();
//初始化Fiddler
private void InitFiddler()
{
//这个名字随便
FiddlerApplication.SetAppDisplayName("test");
//绑定事件处理————当发起请求之前
FiddlerApplication.BeforeRequest += On_BeforeRequest;
//绑定事件处理————当会话结束之后
FiddlerApplication.AfterSessionComplete += On_AfterSessionComplete;
//-----------处理证书-----------
//伪造的证书
X509Certificate2 oRootCert;
//如果没有伪造过证书并把伪造的证书加入本机证书库中
if(null== CertMaker.GetRootCertificate())
{
//创建伪造证书
CertMaker.createRootCert();
//重新获取
oRootCert = CertMaker.GetRootCertificate();
//打开本地证书库
X509Store certStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadWrite);
try
{
//将伪造的证书加入到本地的证书库
certStore.Add(oRootCert);
}
finally
{
certStore.Close();
}
}
else
{
//以前伪造过证书,并且本地证书库中保存过伪造的证书
oRootCert = CertMaker.GetRootCertificate();
}
//-----------------------------
//指定伪造证书
FiddlerApplication.oDefaultClientCertificate = oRootCert;
//忽略服务器证书错误
CONFIG.IgnoreServerCertErrors = true;
//信任证书
CertMaker.trustRootCert();
//看字面意思知道是啥,但实际起到啥作用。。。鬼才知道,官方例程里有这句,加上吧,管它呢。
FiddlerApplication.Prefs.SetBoolPref("fiddler.network.streaming.abortifclientaborts", true);
//启动代理服务————启动参数1:捕捉https;启动参数2:允许局域网其他终端连入本代理
FiddlerApplication.Startup(iStartPort, FiddlerCoreStartupFlags.DecryptSSL | FiddlerCoreStartupFlags.AllowRemoteClients | FiddlerCoreStartupFlags.Default, null);
//创建https代理
oSecureEndpoint = FiddlerApplication.CreateProxyEndpoint(iSecureEndpointPort, true, oRootCert);
}
实现事件处理
//封包发送之前事件————基于这个Session oS可以做很多很多事
private void On_BeforeRequest(Session oS)
{
oS.bBufferResponse = false;
Monitor.Enter(oAllSessions);
if (oS.fullUrl.ToLower().Length >= 0)
{
oAllSessions.Add(oS);
}
Monitor.Exit(oAllSessions);
if (oS.oRequest.pipeClient.LocalPort == iSecureEndpointPort && oS.hostname == sSecureEndpointHostname)
{
oS.utilCreateResponseAndBypassServer();
oS.oResponse.headers.SetStatus(200, "Ok");
oS.oResponse["Content-Type"] = "text/html; charset=UTF-8";
oS.oResponse["Cache-Control"] = "private, max-age=0";
oS.utilSetResponseBody("<html><body>Request for httpS://" + sSecureEndpointHostname + ":" + iSecureEndpointPort.ToString() + " received. Your request was:<br /><plaintext>" + oS.oRequest.headers.ToString());
}
}
//封包响应结束事件————同样,基于这个Session oS可以做很多很多事
private void On_AfterSessionComplete(Session oS)
{
string respStr = "";
respStr = oS.GetResponseBodyAsString();
if (oS.bHasResponse && respStr.Length > 0)
{
Invoke(new Action(() =>
{
textBox1.AppendText($"{oS.fullUrl}\r\n");
}));
}
if (oAllSessions.Count > 300)
{
Monitor.Enter(oAllSessions);
oAllSessions.Clear();
Monitor.Exit(oAllSessions);
GC.Collect();
}
}
启动、停止
到这里,程序已经准备好了。现在我们来启动它。
冷启动
程序刚运行起来,从未将自身作为系统代理。这时,只需要调用InitFiddler就行了。
彻底停止
由于本程序将自身设置为系统代理了,当本程序退出时,如果你没有做善后处理,那么系统代理还在Internet选项——>连接——>局域网设置——>代理服务器的设置里躺着,可由于你的程序已经退出了,这个系统代理指向的是一个没有在工作的代理,那么此路不同,你可能会浏览器打不开网页,通过本机代理上网的其他设备也无法发起http请求。因此,当你确定不再抓取请求的时候,要做善后处理。
FiddlerApplication.Shutdown();
关于这个,官方有段建议:
Shuts down the FiddlerCore proxy and disposes it. Note: If there’s any traffic in progress while you’re calling this method, your background threads are likely to blow up with ObjectDisposedExceptions or NullReferenceExceptions. In many cases, you’re better off simply calling oProxy.Detach() and letting the garbage collector clean up when your program exits.
意思是:当你调用这个方法的时候,如果存在任何正在处理的通信,那么你的后台线程很有可能带着ObjectDisposedExceptions异常或NullReferenceExceptions异常而崩溃。
在的大多数情况下,你最好只是简单地调用一下oProxy.Detach() 然后当你的程序退出时让GC来处理。
热启动、停止
有时候,程序可能需要暂停一下抓取,但并不完全退出程序,只是暂时不让它作为代理了。这时候有两种方法:
方式一:解绑事件处理函数
//解绑事件处理————当发起请求之前
FiddlerApplication.BeforeRequest -= On_BeforeRequest;
//解绑事件处理————当会话结束之后
FiddlerApplication.AfterSessionComplete -= On_AfterSessionComplete;
这种方式只是不再介入请求和响应了,实际上,所有的会话还是通过本程序的代理走的流量。
方式二:解除系统代理
暂停代理
if(FiddlerApplication.oProxy.IsAttached)
{
FiddlerApplication.oProxy.Detach();
oSecureEndpoint.Detach();
}
这种方式是将本程序的代理彻底解除,当代理解除之后,所有的请求都将不再走本程序。
当你需要再启动代理的时候,重新设置一下代理即可:
重设代理
if(!FiddlerApplication.oProxy.IsAttached)
{
FiddlerApplication.oProxy.Attach();
oSecureEndpoint.Attach();
}
终端设置
现在程序已经准备好拦截抓取了,就等着连入请求了。
抓取本机请求
一般情况下,浏览器的代理设置都默认为使用IE代理设置,如果不是,需要手动设置一下:
代理服务器IP设置为localhost或127.0.0.1
端口设置为8888,也就是iStartPort = 8888指定的端口号
抓取非本机请求
这种场景,我只实验成功了抓取同一局域网内的终端请求。
保证目标终端与代理机处于同一网段
如果你的软件运行的计算机(我们称它为代理机)连接在一台192.168.1.1的路由器下,那么你的代理机的局域网IP应该是192.168.1.X,那么你必须要将要抓取的终端(它可以是同一局域网内的另一台计算机,可以是通过WiFi上网的手机、iPad等,我们称它为目标终端)通过网线或者WiFi连接到同一个路由器下。
下载证书
在目标终端上,打开浏览器,输入网址,格式为:代理机的局域网IP:代理端口,比如192.168.1.5:8888
这时,会看到一个网页,这是FiddlerCore生成的一个网页,提供伪造证书下载的页面:
Fiddler Echo Service
GET / HTTP/1.1
Host: localhost:8888
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _ga=GA1.1.1679095215.1533305166
This page returned a HTTP/200 response
Originating Process Information: 360se:3664
To configure Fiddler as a reverse proxy instead of seeing this page, see Reverse Proxy Setup
You can download the FiddlerRoot certificate
注意这个页面最下方的You can download the FiddlerRoot certificate带有一个超链接,这个连接就可以下载伪造的证书。
安装证书
这个就不说了,不同的系统、设备,安装证书的方法不尽相同,百度一下就知道了。
设置代理
同上,不同系统设备的设置方法不尽相同,百度之。
但重要的参数不能填错:
代理服务器ip就填代理机的IP
代理端口就填你FiddlerApplication.Startup时给的端口,也就是代码中的iStartPort,8888
真正开始抓取
启动软件,打开目标机上的浏览器、app等,看看你软件里的On_BeforeRequest 和 On_AfterSessionComplete 能干些啥事吧~
待实现
截至写本文的时候,我依然没搞定将Fiddler作为广域网代理服务器来抓取来自外网IP的代理数据。而网上有资料说这个功能也是可以实现的。以后再试验一下吧。
我想原因应该是跨请求跨路由器网段了,需要做端口映射、防火墙设置之类的东西吧。当然,只是暂时猜测,真正原因还有待查明。
项目合作
本人在爬虫、网页自动化以及验证码识别方面经验还算丰富,有合适的项目的话,可以联系我,备注fiddler
微信