C#代理FiddlerCore4

使用 FiddlerCore 自定义 HTTP/HTTPS 网络代理

FiddlerCore 官网:FiddlerCore - Fiddler Proxy Engine for .NET and .NET Standard - Telerik 重试 错误原因 FiddlerCore Nuget: NuGet Gallery | FiddlerCore 4.6.2 FiddlerCore Nuget:NuGet 库 |FiddlerCore 4.6.2 FiddlerCore Nuget 包(4.6.2) 下载地址:https://pan.baidu.com/s/1ueTCsJ5Jv7wovCeWVN4gDw

fiddlercore.4.6.2.nupkg_免费高速下载|百度网盘-分享无限制 (baidu.com)

Fiddler 是个很好用的网络请求查看与调试工具,还可以写插件来扩展其功能。

Fiddler 插件开发,使用 WPF 作为 UI 控件 - J.晒太阳的猫 - 博客园

但部分场景下,需要自定义很多网络监控操作,集成到自己的程序中。这时就需要 FiddlerCore 了。

关于 Fiddler的使用,Fiddler插件开发,FiddlerCore 详细的说明,都可以看 《Fiddler权威调试指南》一书,网上可以找到电子版。

FiddlerCore 安装

FiddlerCore 的最新版本是收费版,但 nuget.org 上可以下载到之前版本(最后更新时间:2016-01-01),基础的功能是够用的,不能直接拉取的话,则需要配置本地 Nuget 源,然后把这个 Nuget 包放进去。

FiddlerCore 官网:FiddlerCore - Fiddler Proxy Engine for .NET and .NET Standard - Telerik 重试 错误原因 FiddlerCore Nuget: NuGet Gallery | FiddlerCore 4.6.2 FiddlerCore Nuget:NuGet 库 |FiddlerCore 4.6.2 FiddlerCore Nuget 包(4.6.2) 下载地址:https://pan.baidu.com/s/1ueTCsJ5Jv7wovCeWVN4gDw

img

版本 4.6.2.0(在nuget网站下载

可以拦截http/https请求(https 需要安装证书

可以多级代理(在 FiddlerApplication_BeforeRequest 中设置 oSession["X-OverrideGateway"] = “IP:Port”

可以本地代理(全局代理

可以服务器代理 (非全局代理,监听指定的端口

使用示例:

//启动Fiddler(代理端口,是否注册为系统代理,解密ssl,是否允许远程请求【作为服务器代理时传true】)

FiddlerCore 使用#

FiddlerCore 的使用非常简单,主要的就是 1 启动代理,2 监听事件,3 安装证书,4 关闭代理

  • 1 启动代理
FiddlerApplication.Startup(9898, FiddlerCoreStartupFlags.Default | FiddlerCoreStartupFlags.RegisterAsSystemProxy);

这里 FiddlerCoreStartupFlags.RegisterAsSystemProxy 是将你的这个程序(或者说 127.0.0.1:9898)注册为系统代理,所有走系统代理的,都会通过此程序。 如果不设置 FiddlerCoreStartupFlags.RegisterAsSystemProxy ,则被监控的程序,需要手动指定代理到 9898 这个端口。

  • 2 监听事件
FiddlerApplication.BeforeRequest += FiddlerApplication_BeforeRequest;
FiddlerApplication.BeforeResponse += FiddlerApplication_BeforeResponse;

private static void FiddlerApplication_BeforeResponse(Session oSession)
{
}

private static void FiddlerApplication_BeforeRequest(Session oSession)
{
}

通过这两个事件,就可以拿到 FiddlerCore 所截获的请求。然后进行操作或者记录。

img

  • 3 安装证书

如果需要监听 HTTPS 请求,需要安装证书,安装证书时,会有一个弹窗,让用户同意。

// 安装证书
public static bool InstallCertificate()
{
    if (!CertMaker.rootCertExists())
    {
        if (!CertMaker.createRootCert())
            return false;

        if (!CertMaker.trustRootCert())
            return false;
    }

    return true;
}
// 卸载证书
public static bool UninstallCertificate()
{
    if (CertMaker.rootCertExists())
    {
        if (!CertMaker.removeFiddlerGeneratedCerts(true))
            return false;
    }
    return true;
}

img

  • 4 关闭代理

这点很重要,因为如果程序结束之后代理不关闭,则无法正常上网。(因为设置了代理,但代理程序关闭了。)

if (FiddlerApplication.IsStarted())
{
    FiddlerApplication.Shutdown();
}

在真正使用的时候,以上这些操作,建议做一层抽象和封装,不然业务和网络监控的代码会耦合的太紧。

坑:

1.需要自己手动管理 session 手动 abort ,如果连接过多,后面请求进不来

2.需要自己手动释放内存,否则会内存泄露 (调用windows api SetProcessWorkingSetSize() 来释放内存

3.https证书这里有一些坑,每次启动程序都会创建新的证书的问题,解决方法:就是删除掉 CertMaker.dll, BCMakeCert.dll 这两个引用,项目中引用关系删除,然后bin文件夹中删除对应文件(https://blog.csdn.net/ReturningProdigal/article/details/108791520

4.如果本地使用服务器上的代理,需要在服务器代理程序安装证书后,把证书导出复制到本地进行安装

5.作为服务器代理时,FiddlerApplication.Startup 第四个参数要传true , 否则收不到请求

代码

using Fiddler; 
public int port = 8888;

        public Form1()
        {
            InitializeComponent();
            // 启动 FiddlerCore
            //WebProxy myProxy = new WebProxy();
            //Uri newUri = new Uri("https://cloud.tencent.com/developer/ask/sof/111248578");
            //myProxy.Address = newUri;

            if (!Fiddler.CertMaker.rootCertExists())
            {
                if (!Fiddler.CertMaker.createRootCert())
                {
                    throw new Exception("Unable to create cert for FiddlerCore.");
                }
            }
            if (!Fiddler.CertMaker.rootCertIsTrusted())
            {
                if (!Fiddler.CertMaker.trustRootCert())
                {
                    throw new Exception("Unable to install FiddlerCore's cert.");
                }
            }
            Fiddler.FiddlerApplication.Startup(8080, true, true, true);
            ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
            FiddlerApplication.BeforeRequest += FiddlerApplication_BeforeRequest;
            Fiddler.FiddlerApplication.AfterSessionComplete += FiddlerApplication_AfterSessionComplete;
            Console.WriteLine("FiddlerCore started. Press any key to exit.");
            //Console.ReadKey();

           
        }
        private static void FiddlerApplication_BeforeRequest(Session oSession)
        {
            var s = oSession.GetRequestBodyAsString();
            if (oSession.fullUrl.Contains("https://cloud.tencent.com"))
            {
                Console.WriteLine($"{oSession.RequestMethod} {oSession.fullUrl}");
                Console.WriteLine($"Request headers: {oSession.RequestHeaders.ToString()}");
                Console.WriteLine($"Response headers: {oSession.ResponseHeaders.ToString()}");
                Console.WriteLine();
            }
            //if ((!string.IsNullOrEmpty("https://cloud.tencent.com")) && oSession.m_hostIP.StartsWith("https://cloud.tencent.com"))
            //{
            //}
            //处理拦截请求,之后继续向目标url 发送,然后传回响应内容

        }
        static void FiddlerApplication_AfterSessionComplete(Session oSession)
        {
            var s = oSession.GetRequestBodyAsString();
            if (oSession.fullUrl.Contains("https://cloud.tencent.com"))
            {
                Console.WriteLine($"{oSession.RequestMethod} {oSession.fullUrl}");
                Console.WriteLine($"Request headers: {oSession.RequestHeaders.ToString()}");
                Console.WriteLine($"Response headers: {oSession.ResponseHeaders.ToString()}");
                Console.WriteLine();
            }
        }

代码2

查阅手册,看几个关键的方法和变量:

//获得Request体

oSession.GetRequestBodyAsString()

//获得Response内容

oSession.GetResponseBodyAsString()

// 修改session中的显示样式

oSession["ui-color"] = "orange";

// 移除http头部中的MQB-X5-Referer字段

oSession.oRequest.headers.Remove("MQB-X5-Referer");

// 修改http头部中的Cache-Control字段

oSession.oRequest["Cache-Control"] = "no-cache";

// 修改host

oSession.host = "example.domain";

// 修改Origin字段

oSession.oRequest["Origin"] = "http://domain";

// 删除所有的cookie

oSession.oRequest.headers.Remove("Cookie");

// 新建cookie

oSession.oRequest.headers.Add("Cookie", "username=cookiename;");

// 修改Referer字段

oSession.oRequest["Referer"] = "https://yoururl";

拦截websockets请求

FiddlerApplication_OnWebSocketMessage

  public partial class Form1 : Form
    {
        public int port = 8888;
        public Form1()
        {
          //  Iptext = new TextBox();
          //  Iptext.Text = "10"; 
            InitializeComponent();
        }
 
        private void FiddlerApplication_BeforeRequest(Session oSession)
        {
             if ((!string.IsNullOrEmpty(this.Iptext.Text))&&oSession.m_hostIP.StartsWith(this.Iptext.Text))
            {
                this.treeView1.BeginInvoke((ThreadStart)delegate ()
                {
                    
                    TreeNode tempNode = new TreeNode();
                    tempNode.Text = oSession.url+oSession.m_hostIP;
                    this.treeView1.Nodes.Add(tempNode);
                });
            }
            else
            {
                this.treeView1.BeginInvoke((ThreadStart)delegate ()
                {
                    TreeNode tempNode = new TreeNode();
                    tempNode.Text = oSession.url + oSession.m_hostIP; ;
                    this.treeView1.Nodes.Add(tempNode);
                });
            }
          
 
        }
 
 
        private void button1_Click(object sender, EventArgs e)
        {
            this.treeView1.BeginInvoke((ThreadStart)delegate ()
            {
                TreeNode tempNode = new TreeNode();
                tempNode.Text = "开始";
                this.treeView1.Nodes.Add(tempNode);
            });
            if(!Fiddler.FiddlerApplication.IsStarted())
            {
                FiddlerApplication.BeforeRequest += FiddlerApplication_BeforeRequest;
                Fiddler.FiddlerApplication.Startup(port, true, true);
            }
            else
            {
                this.treeView1.BeginInvoke((ThreadStart)delegate ()
                {
                    TreeNode tempNode = new TreeNode();
                    tempNode.Text = "不能重复开启";
                    this.treeView1.Nodes.Add(tempNode);
                });
            }
 
        }
 
        private void button1_Click_1(object sender, EventArgs e)
        {
            this.treeView1.BeginInvoke((ThreadStart)delegate ()
            {
                TreeNode tempNode = new TreeNode();
                tempNode.Text = "终止";
                this.treeView1.Nodes.Add(tempNode);
            });
            Fiddler.FiddlerApplication.Shutdown();
        }
 
        private void label1_Click(object sender, EventArgs e)
        {
 
        }
 
        private void listView1_SelectedIndexChanged(object sender, EventArgs e)
        {
 
        }
 
        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
 
        }
    }

2 获取拦截请求的域名和ip

   private void FiddlerApplication_BeforeRequest(Session oS)
        {
if ((!string.IsNullOrEmpty(this.txt_destinationHost.Text)) && oSession.m_hostIP.StartsWith(this.txt_destinationHost.Text))
             {
            }
        }

3 修改响应内容,修改返回请求头,状态,内容

    private void FiddlerApplication_BeforeRequest(Session oS)
        {
  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";  
                var Restr="返回内容";
                oS.utilDecodeResponse();
                oS.utilSetResponseBody(Restr);
           }
        }

4 开启FiddlerCore在指定端口的监听。

Fiddler.FiddlerApplication.Startup(iPort, oFCSF);

5 建立在指定端口的HTTPS的监听

oSecureEndpoint = FiddlerApplication.CreateProxyEndpoint(iSecureEndpointPort, true, sSecureEndpointHostname);

6关闭应用

Fiddler.FiddlerApplication.Shutdown();

7获取请求头信息和请求内容

oS.RequestHeaders.ToByteArray(true, true, !oS.isHTTPS)

oS.RequestBody

8 设置代理网关 提供主机:网关的端口组合,该网关应该用于代理此请求,或直接将请求发送到原始服务器。

x-overrideGateway
 oSession["X-OverrideGateway"] ="socks=127:80"

end