
简介面向需要抓包分析、HTTPS解密与代理调试的C#开发者这份基于FiddlerCore的抓包工具源码与可执行程序将常见抓包场景的处理逻辑整理成可直接运行的示例。作者参考社区开源示例并结合自身数天学习后完成免费分享。包体共33个文件包括11个C#源代码文件、4个DLL依赖、4个可执行程序、4个配置文件以及调试符号和XML文档压缩后仅717KB下载后即可快速编译或直接使用。该工具提供两种抓包模式通过系统代理自动捕获全部Session以及调用WebProxy.Start(8877)自定义代理端口实现指定流量监听同时包含证书生成与信任设置相关代码可辅助分析HTTPS加密流量。源码中对系统代理与自定义代理的切换位置做了标注目录按功能模块划分适合二次开发与学习。目前已有1577人学习对想入门FiddlerCore或需要轻量抓包工具的学习者来说是一份可运行、易扩展的参考实现。1. 用 FiddlerCore 抓包当你的程序需要自己动手看流量时很多人第一反应是打开 Fiddler 经典版点两下就能看到 HTTPS 请求为什么还要碰 FiddlerCore一种很常见的场景是你是做客户端或服务端开发的想在自动化测试里校验某个请求头是否被正确带上或者你的 WinForms/服务程序需要把指定进程的 HTTP 流量实时解析出来做监控这时候手工开一个 Fiddler 再让人去点按键流程就断了。FiddlerCore 本质上是把 Fiddler 的抓包引擎代理、HTTPS 解密、请求响应拦截封装成一组 .NET 可调用的 API让你在自己的进程里起一个抓包服务。它解决的是“流量解析自动化”这件事而不是“临时看一眼”的事。适合谁被测试脚本、数据采集、接口调试工具、协议分析这类需求反复折腾的开发。不适合谁只想偶尔抓一次包看看请求长什么样的人那种场景老老实实开 Fiddler 或 Charles 反而更快。这个话题相关的检索长尾里最常被提到的其实是“FiddlerCore 抓不到包”“FiddlerCore 证书失效”所以这篇文章会花大量篇幅在代理配置和证书处理上这才是 FiddlerCore 真正的坑区。2. 把 FiddlerCore 跑起来从 NuGet 包到第一个可见的抓包日志2.1 为什么选 FiddlerCore 而不是自己写 Socket 代理如果你曾经试过用 TcpListener 做中间人代理你会发现短时间内能抓到明文 HTTP一旦遇到 HTTPS 就完全歇菜因为 TLS 握手需要你同时伪造服务端证书和客户端信任链这件事自己实现的成本非常高。FiddlerCore 的价值在于它内置了完整的 HTTPS 中间人解密链路包含证书生成、信任安装、TLS 终止、HTTP 解析和会话重建。网络检索里频繁出现“wireshark 抓包及分析”“fiddler 抓包工具 web 端使用”大多数做法是在外部工具里完成但外部工具的问题在于它是给人类交互设计的。FiddlerCore 则可以把这套能力嵌进你自己的程序里让它成为功能的一部分。举例来说你可以写一个控制台程序启动后自动抓取本机指定端口上的流量把请求和响应直接落成日志文件整个过程不需要人打开任何界面。另一个常见的选型理由是对比很多人问“FiddlerCore 和 Fiddler 有什么区别”Fiddler 是完整 GUI 应用FiddlerCore 只是它的引擎库。FiddlerCore 不提供界面你要自己写界面或者干脆不要界面。它还更轻量核心 DLL 大概几百 KB 级别不依赖 Fiddler 客户端的安装。2.2 最小可运行工程引用、初始化与代理端口用 Visual Studio 创建一个 .NET Framework 4.7.2 或 .NET 6 的控制台项目通过 NuGet 安装 FiddlerCore 包。不同版本对运行时要求有差异老版本要求 .NET Framework新版本已经支持 .NET 6/8但 WinForms 场景下很多人仍停留在 .NET Framework 4.8这会导致后面遇到一些兼容性坑具体在避坑章展开。using System; using Fiddler; class Program { static void Main(string[] args) { // 1. 启动 FiddlerCore监听 8877 端口 FiddlerCore.Startup(8877, FiddlerCoreStartupFlags.Default); // 2. 挂上请求开始事件用来打印每个请求的 URL FiddlerApplication.BeforeRequest delegate(Session oSession) { Console.WriteLine($[REQ] {oSession.fullUrl}); }; // 3. 挂上响应完成事件用来打印状态码 FiddlerApplication.ResponseFinal delegate(Session oSession) { Console.WriteLine($[RES] {oSession.fullUrl} - {oSession.responseCode}); }; // 4. 把系统代理设置为本机 8877 端口这样浏览器和大多数应用才会走过来 FiddlerApplication.SetAsSystemProxy(8877, true); Console.WriteLine(FiddlerCore is running. Press any key to stop...); Console.ReadKey(); // 5. 退出前恢复系统代理并卸载 FiddlerApplication.SetAsSystemProxy(0, false); FiddlerApplication.Shutdown(); } }这段代码是 FiddlerCore 最常见的最小骨架。FiddlerCore.Startup(8877, ...)第一个参数是监听端口第二个是启动标志。FiddlerCoreStartupFlags.Default会启用系统代理设置、请求响应解密等默认行为。BeforeRequest在请求刚进来还没发出去时触发ResponseFinal在响应结束、body 缓存完整时触发。SetAsSystemProxy负责把操作系统的 IE/系统代理解析指向本地 8877 端口这一步决定了浏览器流量能不能被截获。参数方面需要说明三点端口建议避开常用服务端口8877 是 FiddlerCore 默认推荐但实际上你可以用任意可用端口关键是别和本机已有服务冲突SetAsSystemProxy传入的 int 参数是端口号第二个 bool 参数表示是否同时处理 PAC 脚本FiddlerCoreStartupFlags.Default这个标志组合里已经包含DecryptSSL用来启用 HTTPS 解密能力。2.3 绕过系统代理的流量如何把指定进程的流量引过来SetAsSystemProxy生效后走系统代理的客户端流量会被截获但有些程序不走系统代理比如很多游戏、UWP 应用、自研网络库。常见做法是设置一个“强制代理”的环境变量或者在代码里手动指定代理。// 对 .NET 的 HttpClient 手动指定代理 var handler new HttpClientHandler { Proxy new WebProxy(127.0.0.1, 8877), UseProxy true }; var client new HttpClient(handler); var resp await client.GetAsync(https://example.com);关键是HttpClientHandler的Proxy属性。FiddlerCore 启动后相当于一个 HTTP 代理服务器任何把网关设到127.0.0.1:8877的客户端都会被它接管。但如果客户端不读系统代理也不读环境变量比如原生 TCP 实现的协议FiddlerCore 就无能为力了这时候需要路由器流量镜像或者 TAP 虚拟网卡方案不在 FiddlerCore 的能力范围内。Sysinternals 工具可以辅助确认某个进程的网络连接是否指向了你的代理端口。先启动 FiddlerCore再用TCPVIEW.EXE查看目标进程的连接如果看到大量指向本机 8877 端口的连接说明流量已进入你的抓包服务。这一步是排查“设置代理了但没抓到包”问题的第一步。3. HTTPS 解密证书信任链与中间人攻击的工程实现3.1 FiddlerCore 的证书机制是怎么工作的HTTPS 抓包的核心是中间人攻击的工程化实现。FiddlerCore 启动时会生成一对根证书根证书的私钥保存在本地。当客户端发起 HTTPS 连接时FiddlerCore 用这个根证书临时签发一张仿冒的站点证书替换掉原本的网站证书。浏览器或客户端之所以不报警是因为你的系统根证书库里已经信任了 FiddlerCore 的根证书。检索长尾里频繁出现“charles 鸿蒙系统抓包”“app 抓包失败”“charles 激活成功后无法抓包”这些问题在 FiddlerCore 里一模一样。核心矛盾是证书密钥必须被系统信任。FiddlerCore 提供CertMaker静态类来管理证书生成和安装。// 确保根证书存在 if (!CertMaker.rootCertExists()) { // 创建一个新的根证书有效期默认是 10 年 CertMaker.createRootCert(); } // 把根证书安装到当前用户的受信任根证书列表 bool installed CertMaker.trustRootCert(); Console.WriteLine($Trust root cert: {installed});rootCertExists检查本地是否已有根证书createRootCert在第一次使用时生成。值得注意在服务器或者 CI 环境里调用trustRootCert是修改系统级信任库的行为如果不想影响整个系统可以只在应用内做证书校验而不安装到系统。例如把生成的 certificate 导出成文件加载到 HttpClient 的回调里动态校验这样不影响机器上的其他软件。3.2 解密条件与 SessionFlag哪些请求要解密、哪些放行FiddlerCore 不是所有 HTTPS 请求都能直接解密。Android 7.0 以上的应用默认不信任用户级 CA 证书除非应用在 Manifest 里声明networkSecurityConfig信任用户证书。iOS 也是如此App 如果开了 SSL Pinning就算系统信任了你的根证书服务端证书校验时比对的是内置的公钥指纹而不是系统信任链照样校验失败。FiddlerCore 对这种场景有两个应对方向一是在BeforeRequest里手工覆盖证书错误二是配合外部注入工具绕过 Pinning。前者是 FiddlerCore 能力范围内的常见做法FiddlerApplication.BeforeRequest delegate(Session oSession) { // 如果目标站点是测试环境允许证书错误 if (oSession.HostnameIs(test-api.example.com)) { // 绕过证书校验继续解密 oSession[X-Ignore-Certificate] true; } // 打印完整请求头 Console.WriteLine(oSession.oRequest.headers.ToString()); };X-Ignore-Certificate是一个标准的 Fiddler 内部标记设置了以后跳过证书链验证类似浏览器里的“继续前往”。但这条只对服务端返回非法证书时有效如果客户端应用自己做了 Pinning在代码里校验证书公钥或证书指纹那 FiddlerCore 伪造的证书照样不通过。业界常见做法是用 Frida 脚本在运行时 Hook 掉证书校验函数配合 FiddlerCore 做代理这套方案里 FiddlerCore 负责代理链路Frida 负责客户端侧校验绕行。3.3 明文流量的上限为什么有些 HTTPS 流只能看到 CONNECT抓包时看到很多CONNECT隧道会话而看不到具体 URL是新手最容易困惑的现象。HTTP 代理协议里CONNECT 是客户端向代理发出“我要建立到目标服务器的隧道”的指令。代理返回 200 后客户端直接在同一连接上和目标服务器做 TLS 握手。FiddlerCore 若不解密这个 TLS它只看到二进制密文能显示的只有目标主机名和端口号。出现这种情况的原因基本有两个一是该会话被标记为不解密常见的标记是oSession[X-Ignore-Certificate]不存在且DecryptSSL标志未全局开启二是客户端自己也发起了CONNECT但后续的 TLS 握手失败常见原因是根证书不在客户端信任链里。排查方法是看会话详情里的HTTPS列如果显示Tunnel to而不是可展开的请求头就说明没有解密成功。4. 改造流量请求头注入、响应替换与本地文件落盘4.1 在 BeforeRequest 里改请求加头、改 URL、改 BodyFiddlerCore 最常见的生产用法是在转发前修改请求。例如给所有请求统一附加一个追踪 Header或者把某个接口的响应在本地 Mock 掉。这类需求用 Fiddler GUI 也能做但写进代码后可以随自动化测试重复执行。FiddlerApplication.BeforeRequest delegate(Session oSession) { // 给所有请求加一个自定义头用于链路追踪 oSession.oRequest.headers.Add(X-Trace-Id, Guid.NewGuid().ToString(N)); // 对特定域名做 URL 重写方便本地调试 if (oSession.HostnameIs(api.example.com)) { oSession.fullUrl oSession.fullUrl.Replace( https://api.example.com/, http://127.0.0.1:5000/); } // 修改 POST 请求体这里只演示字符串替换 if (oSession.fullUrl.Contains(/login) oSession.HTTPMethodIs(POST)) { string body oSession.GetRequestBodyAsString(); body body.Replace(\env\:\prod\, \env\:\test\); oSession.utilSetRequestBody(body); } };oRequest.headers是缺省可写的集合Add方法直接追加 Header 名和值。fullUrl是完整 URL重写时要注意HostnameIs是只读判断属性它做的是精确域名匹配如果请求用 IP 访问就要换一种判断方式。GetRequestBodyAsString在请求体较大时开销不小每次调用都会有编码转换建议只在需要改 Body 时才使用判断oSession.HTTPMethodIs(POST)是一种便宜的预筛。4.2 在 BeforeResponse 里改响应篡改接口数据实现自动化 Mock做前端联调时后端接口还没就绪是常态。用 FiddlerCore 在响应阶段替换数据相当于内嵌了一个可编程 Mock Server。FiddlerApplication.BeforeResponse delegate(Session oSession) { // 只处理 JSON 接口 if (oSession.fullUrl.Contains(/api/v1/orders) oSession.oResponse.headers.HTTPResponseCode 200) { string json {\code\:0,\data\:{\orderId\:\mock-001\}}; // 注意一定先修改 Content-Type 和 Content-Length再写入 Body oSession.oResponse.headers[Content-Type] application/json; charsetutf-8; oSession.utilSetResponseBody(json); } };BeforeResponse触发时机是响应头已经解析完、但 Body 还没完全读取。utilSetResponseBody会重写 Body 并自动修正Content-Length。这里有个容易漏掉的地方如果你改了 Body 但响应头里原有的Content-Encoding: gzip没有去掉或修改接收端解压会失败界面显示乱码或直接报错。处理办法是在重写 Body 前去响应头里删除Content-Encoding。oSession.oResponse.headers.Remove(Content-Encoding);很多人在这一步翻车因为utilSetResponseBody虽然会修正长度但不会自动帮你解除压缩编码。你不去掉Content-Encoding: gzip就等于告诉客户端“这个 JSON 是 gzip 压缩的”客户端尝试解压一串普通 ASCII 字符串自然会失败。所以改响应体的标准操作是移除压缩编码 → 设置新 Content-Type → 写入新 Body。4.3 将会话导出为 SAZ 或 HAR给测试报告留证据抓包之后的关键问题是怎么存档。FiddlerCore 可以把每次会话导出为 SAZFiddler 自己的压缩存档格式或 HAR 文件。HAR 的好处是纯 JSON可以被很多开源报告工具直接消费。// 在 ResponseFinal 里把每个会话追加写入一个 HAR 收集器 FiddlerApplication.ResponseFinal delegate(Session oSession) { // 条件过滤只关心业务 API不做静态资源 if (!oSession.fullUrl.Contains(/api/)) return; // 用 Fiddler 自带的压缩格式落盘 oSession.SaveSession(sessions/ DateTime.Now.Ticks .saz, false); };SaveSession的第一个参数是完整文件路径第二个布尔参数表示是否覆盖重名文件。文件名用Ticks保证唯一性。HAR 导出更简单FiddlerApplication 内部维护了一个GetHAR方法但更常见的是用第三方库遍历 Sessions。自己遍历时要注意一个Session对象记录的是单个 HTTP 事务请求 响应而不是一条 TCP 连接不要混淆。5. FiddlerCore 实战避坑五个高频翻车点与排查路径5.1 抓不到 localhost 流量被系统环回地址例外规则干掉了现象设置了系统代理浏览器访问 www.example.com 能看到请求但访问http://localhost:8080或者http://127.0.0.1时 FiddlerCore 完全没动静。原因Windows 系统有一个“绕过本地地址代理”的默认设置IE/Edge 和大部分 .NET HttpClient 在访问环回地址时会直接连接不经过代理。这个行为和 FiddlerCore 无关是 WinINET 的默认策略。解决在SetAsSystemProxy之后手动把这个绕过清单里的localhost和127.0.0.1移除。常见做法是直接修改注册表HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyOverride把localhost;127.0.0.1;*.local里的本机条目删掉。也可以换一种思路访问时不要用 localhost改用机器的局域网 IP因为代理解析只对环回地址做了绕过。5.2 明明设置了系统代理但外部程序不走代理现象Chrome 和 Edge 都能抓到但某个自研客户端、Python requests 脚本、Java 服务发起的请求一直看不到。原因系统代理只对读取 WinINET/WinHTTP 配置的程序生效。很多网络库或 SDK 默认使用自己的代理设置比如 Pythonrequests读取环境变量Java 的HttpClient默认不读系统代理。还有一类程序完全绕开代理服务器直接走 Socket 连接。解决对于这类程序在进程启动环境变量里显式指定 HTTP_PROXY 和 HTTPS_PROXY 指向127.0.0.1:8877。对于库代码里指定代理。对于直接裸 Socket 的应用只有两个方案一是做透明代理用 netsh 做端口转发让所有流量被重定向到 FiddlerCore 监听端口上二是放弃抓包改用 WFP 驱动的抓包工具。FiddlerCore 本身不自带透明代理能力。5.3 证书被信任但手机/App 仍然提示证书无效现象电脑上抓 https 网页没问题换成 Android 手机配置了代理、装了根证书很多 App 的请求仍然失败报证书错误或直接连不上。原因Android App 开发时如果不特别指定系统默认只信任系统级 CA不信任用户级 CA。你把证书装在“用户证书”分区浏览器会认但大多数 App 不认。另外如果 App 开启了 TLS Pinning服务端证书指纹校验失败信任任何 CA 都没用。解决针对 Android 调试场景业界通用做法是 root 手机后把用户证书复制到系统证书目录/system/etc/security/cacerts/并设置权限。路径是/data/misc/user/0/cacerts-added/下复制对应哈希文件。注意 Android 10 以后系统证书目录是只读的需要先挂载为可读写或者使用 Magisk 模块方案。这个操作有设备变砖的风险务必先做备份。5.4 抓包能跑但程序内存一直在涨Session 对象堆积不释放现象程序运行一小时后内存占用从 200MB 涨到 2GB。流量特别大的时候界面越来越卡。原因FiddlerCore 的Session对象非常“重”它内部保存了请求头、请求体、响应头、响应体、连接状态等大量引用。默认情况下会话结束后Session对象不会自动从内存里移除你需要手动清理。解决在ResponseFinal事件处理完后立即清理该 Session 的 Body 缓冲。常见做法是主动调用oSession.ReleaseResponseBody和oSession.ReleaseRequestBody只保留头信息和元数据。如果只需要 URL 和状态码可以直接减少对 Body 属性访问不要每次都用GetResponseBodyAsString()去读那会把整个 Body 加载到内存字符串里。对于要长期运行的服务程序建议每处理完 1000 个会话就调用一次GC.Collect()不要依赖系统自动回收。FiddlerApplication.ResponseFinal delegate(Session oSession) { // 业务处理只提取关键信息 Console.WriteLine(${oSession.responseCode} {oSession.fullUrl}); // 释放体积较大的请求/响应体对象 oSession.ReleaseRequestBody(); oSession.ReleaseResponseBody(); };注意ReleaseRequestBody和ReleaseResponseBody之后如果再去读取 Body 会得到空字符串或异常所以一定要在业务处理完之后再调用。如果你之后还要导出 SAZ这些方法会影响导出内容的完整性需要权衡是否需要保留。5.5 响应阶段篡改 Body 导致抓包进程崩溃现象BeforeResponse里调用utilSetResponseBody后偶发出现异常提示“Header is read-only”或者直接进程崩溃。原因BeforeResponse触发时响应头已经发送给客户端了此时oResponse.headers处于只读状态。FiddlerCore 文档里有个隐藏约束如果想改响应头必须在BeforeResponse的早期阶段做而且某些状态下是不允许改的。崩溃多数是因为同时改 Header 和 Body 的时机冲突。解决把响应篡改逻辑从BeforeResponse移到ResponseFinal里做因为ResponseFinal的 Headers 是可写的。但这会带来一个副作用客户端已经收到了原始响应再改 Body 只影响存档日志不会改变已经到达客户端的数据。所以严格来说修改响应内容必须在BeforeResponse阶段完成只是如果连 Header 一起改就要判断oSession.oResponse.headers的Locked状态。// 安全写法先检查 Header 是否可写 if (!oSession.oResponse.headers.Locked) { oSession.oResponse.headers.Remove(Content-Encoding); oSession.oResponse.headers[Content-Type] application/json; charsetutf-8; }6. 把 FiddlerCore 用得更顺手性能基线、白名单与埋点统计最后一个章节聊点实在的。FiddlerCore 长期运行的稳定性问题我用过一次比较狠的场景连续跑 48 小时抓取某网关全量 API 请求会话总数超过 300 万条。那一次让我意识到三件重要的事。第一件事是代理层面的白名单。不是所有流量都值得解析静态资源图片、JS、CSS会拖垮系统代理的转发效率。做法是在BeforeRequest里快速丢弃不需要的会话先判断 Host 或 URL 后缀命中静态资源就直接oSession.Abort()不进入后续解析链路。这样 CPU 占用从 40% 降到 8%。Abort会关闭客户端连接客户端需要重新发起请求对浏览器来说是刷新一下的事对 API 调用方可能会报错因此白名单策略要谨慎。第二件事是性能基线。FiddlerCore 的吞吐上限和 TLS 解密强相关而握手瓶颈在 CPU。一个很有意思的经验是如果只是抓明文 HTTP单核处理 2000 QPS 没问题一旦启用 HTTPS 解密在不做任何并发优化的默认配置下单核 QPS 能掉到 500 以下。所以性能测试环境里抓包服务一定要单独部署不要和被测服务抢 CPU。另外可以给抓包线程设置更高优先级但别设到最高级否则会导致系统代理响应极快、反向拖垮自己的业务线程。第三件事是给自己留一条“后悔药”路径。每次修改请求或响应前先把原始 Body 备份一份到本地文件这样线上问题排查时至少能回放原始流量。见过太多因为响应篡改导致前端永远只能看到造出来的假数据等到发现时原始数据已经查不到了。FiddlerCore 的 Session 对象在ReleaseResponseBody之后不能再回读所以备份必须在处理链的前半段完成。我现在的个人习惯是每次上线抓包工具第一周只做日志和统计不主动篡改任何请求响应。等确认流量结构、证书行为都稳定了再加 Mock 和重写逻辑。为什么这么做因为抓包工具最容易被忽略的问题是它对网络行为有隐蔽影响。用户可能不会意识到代理这层本身可能就是故障源头把抓包本身搞成了一次网络故障的元凶。FiddlerCore 是很强大的库但越是强大的中间人工具越需要克制地使用——先观察流量一周再做任何改动希望帮到你。本文还有配套的精品资源点击获取