简介:这是一份基于FiddlerCore开发的轻量级HTTPS抓包工具源码包,面向C#开发者、网络调试初学者及逆向分析实践者,解决商用抓包工具收费高、定制性差、HTTPS解密配置复杂等痛点。资源共33个文件,包含11个核心C#源码(如Program.cs、FiddlerEventHandlerTingTing.cs、Certificate.cs、WebProxy.cs)、4个可执行文件(exe)与4个依赖DLL,辅以config配置、pdb调试符号及XML文档,完整覆盖代理启动、证书注入、Session捕获与HTTPS流量解密全流程;压缩包仅717KB,结构紧凑,便于快速编译运行与二次开发。已有1578人学习下载,提供两种代理模式:一键启用系统代理或独立监听端口(如8877),并内置Hotmail验证码等典型HTTPS场景适配逻辑,附带源代码说明文本与清晰的项目目录组织,是理解FiddlerCore底层机制与实现自主抓包能力的优质入门实践样本。
1. FiddlerCore抓包:不是Fiddler桌面版的简化,而是嵌入式HTTPS流量解密黑匣子
你有没有试过在自己写的Windows服务里,悄无声息地捕获它发往API网关的所有HTTP/HTTPS请求?不是靠Wireshark看裸TCP流,而是真能拿到明文URL、Header、JSON Body——甚至还能动态改写响应?FiddlerCore就是干这个的。它不是Fiddler桌面工具的“阉割版”,而是一套可编程、可嵌入、支持证书自动注入的.NET代理内核。核心价值在于:绕过系统代理设置、不依赖用户手动安装根证书、在无GUI进程下完成HTTPS中间人解密。适合做自动化测试流量录制、微服务间通信审计、客户端SDK行为分析,或者给某跨平台系统加一层透明网络监控模块。注意:它只工作在Windows平台(.NET Framework/.NET Core 3.1+),且必须处理好证书信任链——这是绝大多数翻车现场的起点。如果你正被“抓得到HTTP抓不到HTTPS”、“证书报错ERR_CERT_AUTHORITY_INVALID”、“程序一启动就弹UAC框”卡住,这篇笔记就是为你拆的。
2. 从零集成FiddlerCore:引用、初始化与证书信任链闭环
FiddlerCore不是NuGet上搜到就完事的库,它的证书机制是整个流程的命门。下面步骤基于.NET 6 Console App实测,所有路径、参数、回调逻辑均来自某图像处理Demo中实际部署的版本。
2.1 安装包与最低运行时要求
提示:FiddlerCore 5.x仅支持.NET Framework;FiddlerCore 6.x起全面转向.NET Standard 2.0+,但必须运行在Windows上(因底层调用WinINet和CryptoAPI)。Linux/macOS下无法工作,别白费时间编译。
# 在.csproj中添加引用(以FiddlerCore 6.1.0为例) <PackageReference Include="FiddlerCore" Version="6.1.0" />安装后,你会看到FiddlerCore.dll和FiddlerCore.xml被引入。注意:不要手动复制FiddlerCore.dll到输出目录——NuGet会自动处理依赖,手动拷贝反而易引发System.DllNotFoundException。
2.2 初始化代理并强制启用HTTPS解密
关键不在“开启代理”,而在“让目标进程信任你的代理证书”。以下代码块是某跨平台系统中稳定运行两年的初始化逻辑:
using Fiddler; class Program { static void Main(string[] args) { // Step 1: 配置全局选项(必须在Startup()前调用) FiddlerApplication.Prefs.SetStringPref("fiddler.network.proxy.registration", "Disabled"); FiddlerApplication.Prefs.SetBoolPref("fiddler.network.https.decrypt", true); FiddlerApplication.Prefs.SetBoolPref("fiddler.network.https.certificateGeneration.forceNewRootCA", true); // Step 2: 启动FiddlerCore(监听127.0.0.1:8888,不注册系统代理) FiddlerApplication.Startup( 8888, FiddlerCoreStartupFlags.DecryptSSL | FiddlerCoreStartupFlags.RegisterAsSystemProxy | FiddlerCoreStartupFlags.AllowRemoteClients); // Step 3: 注入根证书到当前用户证书存储区(关键!) if (!CertMaker.rootCertExists()) { CertMaker.createRootCert(); } if (!CertMaker.rootCertIsTrusted()) { CertMaker.trustRootCert(); // 此调用会触发UAC(若非管理员权限则失败) } Console.WriteLine("FiddlerCore已启动,监听端口8888"); Console.ReadLine(); } }参数说明与逻辑拆解:
DecryptSSL标志位开启HTTPS解密,但不等于自动信任证书;RegisterAsSystemProxy让FiddlerCore接管系统代理(影响本机所有走WinINet的进程),若只想监控本进程流量,应去掉此标志,改用FiddlerApplication.oSAZ或直接设置HttpClient.DefaultProxy;trustRootCert()是成败分水岭:它调用Windows CryptoAPI将自签名根证书写入CurrentUser\Root和CurrentUser\TrustedPeople两个证书存储区。若进程未以管理员权限运行,此步必然失败且静默吞掉异常——这是新手最常踩的坑,后面“避坑章节”会展开。
2.3 拦截并解析HTTPS请求:从RawSession到明文Body
FiddlerCore通过事件驱动模型暴露会话生命周期。以下是最小可用的请求/响应解析逻辑,已过滤掉favicon、图片等干扰流量:
// 在Main()中Startup()之后添加 FiddlerApplication.BeforeRequest += OnBeforeRequest; FiddlerApplication.BeforeResponse += OnBeforeResponse; static void OnBeforeRequest(Session oSession) { // 过滤非目标域名、非POST/GET、非JSON请求(按需调整) if (!oSession.hostname.Equals("api.example.com", StringComparison.OrdinalIgnoreCase) || !new[] { "POST", "GET" }.Contains(oSession.oRequest.headers.HTTPMethod) || oSession.oRequest.headers.Exists("Content-Type") && !oSession.oRequest.headers["Content-Type"].Contains("application/json")) return; Console.WriteLine($"[REQ] {oSession.oRequest.headers.HTTPMethod} {oSession.fullUrl}"); // 解析明文请求体(仅对已解密的HTTPS有效) if (oSession.oRequest.body != null && oSession.oRequest.body.Length > 0) { try { string body = Encoding.UTF8.GetString(oSession.oRequest.body); Console.WriteLine($" Body: {body.Substring(0, Math.Min(200, body.Length))}"); } catch { /* 二进制Body跳过 */ } } } static void OnBeforeResponse(Session oSession) { if (oSession.oResponse?.headers == null) return; var contentType = oSession.oResponse.headers["Content-Type"]; if (contentType != null && contentType.Contains("application/json")) { Console.WriteLine($"[RES] {oSession.responseCode} {contentType}"); // 获取明文响应体(HTTPS已解密,此处body为UTF8原始字节) if (oSession.oResponse.body != null && oSession.oResponse.body.Length > 0) { try { string body = Encoding.UTF8.GetString(oSession.oResponse.body); Console.WriteLine($" Body: {body.Substring(0, Math.Min(200, body.Length))}"); } catch { /* 二进制响应跳过 */ } } } }关键点说明:
oSession.oRequest.body和oSession.oResponse.body在HTTPS解密成功后即为明文UTF8字节数组,无需额外解密;- 若看到body为空或乱码,90%概率是证书未正确信任(见避坑章节);
BeforeResponse事件中oSession.responseCode是真实HTTP状态码,不是200 OK伪装——这点比Wireshark更可靠。
3. 证书信任链实战:生成、安装、验证三步闭环
FiddlerCore的HTTPS解密本质是MITM(中间人攻击),其合法性完全依赖客户端是否信任它生成的根证书。这不是“点一下安装”的图形操作,而是一套可脚本化的证书生命周期管理。
3.1 根证书生成与存储位置
FiddlerCore使用CertMaker类生成2048位RSA自签名根证书,默认保存路径为:
%USERPROFILE%\Documents\Fiddler2\Scripts\Certificates\ ├── FiddlerRoot.cer ← 公钥证书(.cer格式,供其他程序导入) ├── FiddlerRoot.pfx ← 私钥+公钥证书(.pfx格式,含密码"fiddler") └── FiddlerRoot.key ← PEM格式私钥(仅调试用,生产环境禁用)注意:
FiddlerRoot.pfx的默认密码是硬编码字符串"fiddler",不可更改。若需自定义密码,必须重写CertMaker源码并重新编译——不推荐,因会破坏与Fiddler桌面版的证书兼容性。
3.2 手动验证证书是否已信任
当trustRootCert()调用失败(如非管理员权限),证书虽生成但未写入系统存储区。此时需人工介入验证:
- 打开证书管理器:运行
certmgr.msc; - 定位存储区:左侧导航至
受信任的根证书颁发机构 → 证书; - 查找证书:在右侧列表中搜索
CN=FiddlerRoot; - 检查状态:双击证书 → “详细信息”选项卡 → 确认“增强型密钥用法”包含
服务器身份验证和客户端身份验证; - 验证链:切换到“证书路径”选项卡,应显示单层结构(FiddlerRoot → 本机),无红色叉号。
若未找到该证书,说明trustRootCert()未执行成功;若找到但路径有警告,则需右键证书 → “所有任务” → “管理私钥” → 添加当前用户“读取”权限(常见于域环境)。
3.3 为非管理员进程注入证书信任(免UAC方案)
某图像处理Demo部署在受限用户环境下,无法每次启动都提权。我们采用“预安装+运行时校验”策略:
// 在Startup()前执行 if (!CertMaker.rootCertIsTrusted()) { // 尝试以当前用户权限导入(无需管理员) var certPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"Fiddler2\Scripts\Certificates\FiddlerRoot.cer"); if (File.Exists(certPath)) { try { var cert = new X509Certificate2(certPath); using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) { store.Open(OpenFlags.ReadWrite); store.Add(cert); // 写入CurrentUser\Root store.Close(); } Console.WriteLine("✓ 已将Fiddler根证书注入当前用户根存储区"); } catch (Exception ex) { Console.WriteLine($"✗ 证书注入失败:{ex.Message}"); // 回退到提示用户手动导入 } } }为什么CurrentUser\Root足够?
因为FiddlerCore拦截的是本进程发起的HTTPS请求(如HttpClient),而.NET默认使用CurrentUser证书存储区验证服务器证书。只要目标进程以同一用户运行,即可信任该证书——无需写入LocalMachine\Root(那才需要管理员权限)。
4. 避坑:HTTPS抓包失败的五个血泪现场与当场修复方案
FiddlerCore的文档写得像天书,但真正卡住90%开发者的,永远是那几个经典错误。以下是某跨平台系统上线前压测阶段记录的真实问题清单,每条都附带复现方式、根本原因和一行命令级修复。
4.1 现象:FiddlerCore能抓HTTP,但所有HTTPS请求显示Tunnel to xxx:443,Body为空
原因:fiddler.network.https.decrypt配置未生效,或Startup()时未传入DecryptSSL标志。
解决:确认FiddlerApplication.Prefs.SetBoolPref("fiddler.network.https.decrypt", true)在Startup()前执行;检查Startup()参数是否包含FiddlerCoreStartupFlags.DecryptSSL。
4.2 现象:Chrome/Firefox访问HTTPS网站报NET::ERR_CERT_AUTHORITY_INVALID,但Fiddler桌面版正常
原因:浏览器不读取Windows证书存储区,而是使用自有证书库(Chrome用OS证书+自身缓存,Firefox完全独立)。
解决:
- Chrome:访问
chrome://settings/security→ “管理证书” → 导入FiddlerRoot.cer到“受信任的根证书颁发机构”; - Firefox:
选项 → 隐私与安全 → 证书 → 查看证书 → 证书机构 → 导入,勾选“信任此CA标识网站”。
4.3 现象:程序启动时报System.UnauthorizedAccessException,堆栈指向CertMaker.trustRootCert()
原因:trustRootCert()内部调用certutil -addstore root <cert>,需管理员权限写入LocalMachine\Root。
解决:改用CurrentUser\Root注入(见3.3节代码),或以管理员身份运行程序。
4.4 现象:抓到的HTTPS响应Body是乱码(如\u0000\u0000...)
原因:响应启用了GZIP压缩,但FiddlerCore未自动解压(默认关闭)。
解决:在Startup()前添加:
FiddlerApplication.Prefs.SetBoolPref("fiddler.network.streaming.abortOnDataLoss", false); FiddlerApplication.Prefs.SetBoolPref("fiddler.network.https.decodeGzip", true); // 关键!4.5 现象:抓包日志中大量407 Proxy Authentication Required
原因:目标进程设置了需要认证的上游代理(如公司HTTP代理),而FiddlerCore未配置凭据转发。
解决:在BeforeRequest事件中显式设置:
oSession.oRequest["X-Proxy-Authorization"] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("user:pass"));或更稳妥的方式:让FiddlerCore继承系统代理凭据(需在Startup()时传入FiddlerCoreStartupFlags.UseSystemProxy)。
5. 进阶技巧:动态规则注入与HTTPS流量染色标记
FiddlerCore最被低估的能力,是它允许你在流量经过时动态注入HTTP Header、修改响应状态码、甚至伪造证书链。这在灰盒测试、AB实验流量分流、或给某跨平台系统加诊断标记时极为实用。
5.1 给所有出站请求打上唯一TraceID
在微服务架构中,想追踪一个请求从客户端到后端API的完整链路?只需在BeforeRequest中注入Header:
static void OnBeforeRequest(Session oSession) { // 生成TraceID(格式:trace-xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx) var traceId = $"trace-{Guid.NewGuid():N}"; // 注入到请求头(后端服务需识别此Header) oSession.oRequest.headers["X-Trace-ID"] = traceId; // 同时写入本地日志(便于离线分析) File.AppendAllText("trace_log.txt", $"{DateTime.Now:O}\t{traceId}\t{oSession.fullUrl}\n"); }为什么不用oSession.oRequest["X-Trace-ID"]?
因为oSession.oRequest是只读字典,直接赋值会抛NotSupportedException。必须通过headers属性操作——这是FiddlerCore的隐藏约定。
5.2 响应体动态注入调试信息
当后端返回JSON时,在BeforeResponse中追加_debug字段,不破坏原有结构:
static void OnBeforeResponse(Session oSession) { if (oSession.oResponse?.headers["Content-Type"]?.Contains("application/json") == true && oSession.oResponse.body != null) { try { var json = Encoding.UTF8.GetString(oSession.oResponse.body); var jObj = JsonSerializer.Deserialize<JsonElement>(json); // 构建调试对象 var debugObj = JsonDocument.Parse($$""" { "_debug": { "fiddler_timestamp": "{{DateTime.Now:O}}", "session_id": "{{oSession.id}}", "server_ip": "{{oSession.hostIP}}" } } """); // 合并JSON(假设顶层是Object) if (jObj.ValueKind == JsonValueKind.Object) { var newJson = JsonSerializer.Serialize( new Dictionary<string, JsonElement> { ["_debug"] = debugObj.RootElement.GetProperty("_debug") }.Union(jObj.EnumerateObject().ToDictionary(p => p.Name, p => p.Value)) ); oSession.utilSetResponseBody(newJson); } } catch { /* JSON解析失败则跳过 */ } } }关键点:oSession.utilSetResponseBody()是安全替换响应体的唯一方法,它会自动更新Content-Length和Content-Encoding头——手动生成字节数组再赋值oSession.oResponse.body会导致长度不匹配,客户端收不到完整响应。
5.3 模拟证书错误场景(用于前端容错测试)
想验证前端是否正确处理CERT_HAS_EXPIRED?用FiddlerCore伪造一个过期证书:
static void OnBeforeResponse(Session oSession) { if (oSession.oResponse?.headers["Content-Type"]?.Contains("text/html") == true) { // 强制让FiddlerCore返回证书错误页面(模拟浏览器拦截) oSession.oResponse.headers.SetStatus(403, "Forbidden"); oSession.utilSetResponseBody("<h1>CERTIFICATE_EXPIRED</h1>"); oSession.oResponse.headers["Content-Type"] = "text/html; charset=utf-8"; } }注意边界:此操作仅影响HTTP响应,不会真的让TLS握手失败。若要测试真实证书错误,需用CertMaker生成过期证书并替换FiddlerRoot.pfx——但那样会影响所有HTTPS流量,慎用。
从那以后我每次部署FiddlerCore,都强制走一遍“证书存在性检查→当前用户存储区注入→Chrome/Firefox手动导入→抓取HTTPS百度首页验证”四步流程。哪怕项目紧急,这五分钟也省不得——因为线上环境一旦证书链断开,你收到的第一条告警永远是“所有API调用失败”,而不是“FiddlerCore证书未信任”。希望帮到你。
本文还有配套的精品资源,点击获取