🛡️ FBro资源拦截与篡改完整指南
深度控制网络资源请求,实现高级的内容拦截、修改和替换功能
📋 概述
FBro浏览器提供了强大的资源拦截和篡改功能,通过重写GetResourceHandler方法,开发者可以完全控制网页资源的加载过程。这个功能可以实现网页内容替换、资源重定向、内容过滤、广告拦截等高级功能。
🎯 主要功能
- 完整资源拦截:拦截所有类型的网络请求(HTML、CSS、JS、图片等)
- 内容动态替换:实时替换网页内容和资源
- 自定义响应:自定义HTTP响应头、状态码和内容
- 流式数据处理:支持大文件的分块处理
- 精准URL匹配:支持精确URL匹配和模式匹配
- 多种篡改策略:支持完整替换、部分修改、条件拦截
📱 应用场景
| 应用场景 | 实现效果 | 技术优势 |
|---|---|---|
| 内容过滤 | 屏蔽不良内容和广告 | 实时拦截,零延迟 |
| 页面改版 | 动态修改网页布局和样式 | 无需修改源网站 |
| API模拟 | 模拟后端API响应 | 前端独立开发测试 |
| 数据注入 | 向页面注入自定义数据 | 增强用户体验 |
| 安全防护 | 拦截恶意资源和脚本 | 主动安全防护 |
| 离线功能 | 缓存资源实现离线访问 | 提升访问速度 |
🔧 核心实现架构
资源拦截流程图
网页请求 → GetResourceHandler → 判断是否拦截 → 创建资源处理器 → 自定义响应
↓ ↓ ↓ ↓ ↓
原始请求 URL匹配检查 设置回调处理器 处理请求 返回内容关键组件说明
- GetResourceHandler:资源拦截入口点
- FBroResourceEventCallback:资源处理回调类
- IFBroSharpResourceHandler:资源处理器接口
- IFBroSharpResponse:HTTP响应对象
- Stream处理:数据流读写控制
📖 方法详解
GetResourceHandler方法
csharp
/// <summary>
/// 资源请求拦截处理器
/// 当浏览器请求任何资源时触发此方法
/// </summary>
/// <param name="browser">发起请求的浏览器实例</param>
/// <param name="frame">发起请求的框架</param>
/// <param name="request">HTTP请求对象,包含URL、方法、头部等信息</param>
/// <param name="set_callback">资源处理器设置接口,用于设置自定义处理器</param>
public override void GetResourceHandler(IFBroSharpBrowser browser,
IFBroSharpFrame frame,
IFBroSharpRequest request,
IFBroSharpResourceHandler set_callback)
{
// 获取请求的URL
string requestUrl = request.GetURL();
Console.WriteLine($"资源请求拦截: {requestUrl}");
// 根据URL判断是否需要拦截和处理
if (ShouldInterceptResource(requestUrl))
{
// 创建自定义资源处理器
var resourceCallback = CreateResourceHandler(requestUrl, request);
// 设置资源处理器,接管该请求的响应
set_callback.SetCallback(resourceCallback);
Console.WriteLine($"✅ 资源已拦截: {requestUrl}");
}
else
{
// 不拦截,让浏览器正常处理
Console.WriteLine($"⏭️ 资源放行: {requestUrl}");
}
}
/// <summary>
/// 判断是否应该拦截该资源
/// </summary>
/// <param name="url">请求URL</param>
/// <returns>是否拦截</returns>
private bool ShouldInterceptResource(string url)
{
// 示例:拦截百度首页
if (url == "https://www.baidu.com/")
return true;
// 示例:拦截所有图片资源
if (url.EndsWith(".jpg") || url.EndsWith(".png") || url.EndsWith(".gif"))
return true;
// 示例:拦截特定域名下的所有资源
if (url.Contains("ads.example.com"))
return true;
// 示例:拦截所有CSS文件
if (url.EndsWith(".css"))
return true;
return false;
}
/// <summary>
/// 创建资源处理器
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="request">原始请求对象</param>
/// <returns>资源处理器实例</returns>
private FBroResourceEventCallback CreateResourceHandler(string url, IFBroSharpRequest request)
{
if (url == "https://www.baidu.com/")
{
return new HtmlReplacementHandler();
}
else if (url.EndsWith(".css"))
{
return new CssInterceptHandler();
}
else if (url.Contains("ads."))
{
return new AdBlockHandler();
}
else
{
return new GenericResourceHandler(url);
}
}🎨 资源处理器实现
基础HTML替换处理器
csharp
/// <summary>
/// HTML内容替换处理器
/// 用于完全替换网页内容
/// </summary>
public class HtmlReplacementHandler : FBroSharpResourceEvent
{
private string htmlContent;
private bool isDataWritten = false;
public HtmlReplacementHandler()
{
// 自定义HTML内容
htmlContent = GenerateCustomHtml();
}
/// <summary>
/// 生成自定义HTML内容
/// </summary>
/// <returns>HTML字符串</returns>
private string GenerateCustomHtml()
{
return @"
<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>FBro资源篡改示例</title>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 0;
padding: 20px;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background: white;
padding: 40px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
max-width: 600px;
text-align: center;
}
h1 {
color: #333;
margin-bottom: 20px;
}
.highlight {
color: #667eea;
font-weight: bold;
}
.info {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
}
button {
background: #667eea;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
margin: 10px;
transition: background 0.3s;
}
button:hover {
background: #5a6fd8;
}
</style>
</head>
<body>
<div class='container'>
<h1>🛡️ FBro资源篡改成功!</h1>
<p class='highlight'>原本的百度页面已被完全替换</p>
<div class='info'>
<h3>📊 拦截信息</h3>
<p><strong>拦截时间:</strong> <span id='timestamp'></span></p>
<p><strong>原始URL:</strong> https://www.baidu.com/</p>
<p><strong>处理器:</strong> HtmlReplacementHandler</p>
</div>
<button onclick='showDetails()'>查看技术详情</button>
<button onclick='testRequest()'>测试AJAX请求</button>
<div id='details' style='display:none; margin-top:20px; text-align:left;'>
<h4>🔧 技术实现</h4>
<ul>
<li>✅ 使用GetResourceHandler拦截请求</li>
<li>✅ 自定义FBroResourceEventCallback处理器</li>
<li>✅ 动态生成HTML响应内容</li>
<li>✅ 设置正确的Content-Type和响应头</li>
<li>✅ 支持UTF-8编码和中文显示</li>
</ul>
</div>
</div>
<script>
// 显示当前时间
document.getElementById('timestamp').textContent = new Date().toLocaleString('zh-CN');
function showDetails() {
const details = document.getElementById('details');
details.style.display = details.style.display === 'none' ? 'block' : 'none';
}
function testRequest() {
fetch('/api/test')
.then(response => response.text())
.then(data => alert('请求结果: ' + data))
.catch(error => alert('请求失败: ' + error.message));
}
// 页面加载完成后的欢迎提示
window.onload = function() {
setTimeout(() => {
console.log('🎉 FBro资源篡改示例页面加载完成');
}, 1000);
};
</script>
</body>
</html>";
}
/// <summary>
/// 开始处理资源请求
/// </summary>
/// <param name="flag">请求标识</param>
public override void Start(long flag)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 开始处理HTML替换请求 - Flag: {flag}");
}
/// <summary>
/// 取消资源请求处理
/// </summary>
/// <param name="flag">请求标识</param>
public override void Cancel(long flag)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] HTML替换请求被取消 - Flag: {flag}");
}
/// <summary>
/// 结束资源请求处理
/// </summary>
/// <param name="flag">请求标识</param>
public override void End(long flag)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] HTML替换请求处理完成 - Flag: {flag}");
}
/// <summary>
/// 设置响应头信息
/// </summary>
/// <param name="flag">请求标识</param>
/// <param name="response">响应对象</param>
/// <param name="response_length">响应内容长度</param>
/// <param name="redirectUrl">重定向URL(如果需要)</param>
public override void GetResponseHeaders(long flag, IFBroSharpResponse response,
ref long response_length, ref string redirectUrl)
{
try
{
// 获取UTF-8编码的字节数组
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(htmlContent);
// 设置响应内容长度
response_length = contentBytes.Length;
// 设置响应MIME类型
response.SetMimeType("text/html");
// 设置HTTP状态码
response.SetStatus(200);
// 设置响应头
response.SetHeaderByName("Content-Type", "text/html; charset=utf-8", true);
response.SetHeaderByName("Cache-Control", "no-cache", true);
response.SetHeaderByName("X-FBro-Intercepted", "true", true);
response.SetHeaderByName("X-FBro-Handler", "HtmlReplacementHandler", true);
Console.WriteLine($"✅ 响应头设置完成 - 内容长度: {response_length} 字节");
}
catch (Exception ex)
{
Console.WriteLine($"❌ 设置响应头失败: {ex.Message}");
}
}
/// <summary>
/// 打开资源处理器
/// </summary>
/// <param name="flag">请求标识</param>
/// <param name="request">请求对象</param>
/// <param name="handle_request">是否处理请求</param>
/// <param name="callback">回调接口</param>
/// <returns>是否成功打开</returns>
public override bool Open(long flag, IFBroSharpRequest request,
ref bool handle_request, IFBroSharpCallback callback)
{
Console.WriteLine($"📂 打开资源处理器 - URL: {request.GetURL()}");
handle_request = true;
return false;
}
/// <summary>
/// 处理资源请求
/// </summary>
/// <param name="flag">请求标识</param>
/// <param name="request">请求对象</param>
/// <param name="callback">回调接口</param>
/// <returns>是否开始处理</returns>
public override bool ProcessRequest(long flag, IFBroSharpRequest request, IFBroSharpCallback callback)
{
Console.WriteLine($"🔄 开始处理请求 - Method: {request.GetMethod()}, URL: {request.GetURL()}");
// 继续处理
callback.Continue();
// 返回true表示开始处理响应
return true;
}
/// <summary>
/// 读取响应数据
/// </summary>
/// <param name="flag">请求标识</param>
/// <param name="dataOut">输出数据流</param>
/// <param name="bytesRead">实际读取的字节数</param>
/// <param name="callback">读取回调</param>
/// <returns>是否继续读取</returns>
public override bool Read(long flag, Stream dataOut, out int bytesRead,
IFBroSharpResourceReadCallback callback)
{
callback.Dispose();
// 如果数据已经写入过,直接返回false结束
if (isDataWritten)
{
bytesRead = 0;
Console.WriteLine("📝 数据已全部写入完成");
return false;
}
try
{
// 将HTML内容转换为UTF-8字节数组
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(htmlContent);
int contentLength = contentBytes.Length;
Console.WriteLine($"📤 准备写入数据 - 内容长度: {contentLength}, 缓冲区大小: {dataOut.Length}");
// 检查缓冲区是否足够
if (contentLength <= dataOut.Length)
{
// 一次性写入所有数据
dataOut.Write(contentBytes, 0, contentLength);
bytesRead = contentLength;
isDataWritten = true;
Console.WriteLine($"✅ 数据写入完成 - 写入字节数: {bytesRead}");
return true;
}
else
{
// 数据太大,需要分块写入
dataOut.Write(contentBytes, 0, (int)dataOut.Length);
bytesRead = (int)dataOut.Length;
// 保存剩余数据,准备下次写入
byte[] remainingBytes = new byte[contentLength - (int)dataOut.Length];
Array.Copy(contentBytes, (int)dataOut.Length, remainingBytes, 0, remainingBytes.Length);
htmlContent = System.Text.Encoding.UTF8.GetString(remainingBytes);
Console.WriteLine($"📦 分块写入 - 本次写入: {bytesRead}, 剩余: {remainingBytes.Length}");
return true;
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ 数据写入失败: {ex.Message}");
bytesRead = 0;
return false;
}
}
/// <summary>
/// 跳过指定字节数的数据
/// </summary>
/// <param name="flag">请求标识</param>
/// <param name="bytes_to_skip">要跳过的字节数</param>
/// <param name="bytes_skipped">实际跳过的字节数</param>
/// <param name="callback">跳过回调</param>
/// <returns>是否成功跳过</returns>
public override bool Skip(long flag, long bytes_to_skip, ref long bytes_skipped,
IFBroSharpResourceSkipCallback callback)
{
Console.WriteLine($"⏭️ 跳过数据请求 - 要跳过: {bytes_to_skip} 字节");
bytes_skipped = 0;
return false;
}
}CSS样式拦截处理器
csharp
/// <summary>
/// CSS样式拦截和修改处理器
/// 用于动态修改网页样式
/// </summary>
public class CssInterceptHandler : FBroSharpResourceEvent
{
private string cssContent;
private bool isDataWritten = false;
public CssInterceptHandler()
{
// 自定义CSS样式
cssContent = GenerateCustomCss();
}
/// <summary>
/// 生成自定义CSS样式
/// </summary>
/// <returns>CSS字符串</returns>
private string GenerateCustomCss()
{
return @"
/* FBro自定义样式 - 让所有网页更加美观 */
* {
transition: all 0.3s ease !important;
}
body {
font-family: 'Microsoft YaHei', 'Segoe UI', Arial, sans-serif !important;
line-height: 1.6 !important;
color: #333 !important;
}
/* 隐藏所有广告 */
[class*='ad'], [id*='ad'], [class*='banner'], .advertisement {
display: none !important;
}
/* 美化链接 */
a {
color: #667eea !important;
text-decoration: none !important;
border-bottom: 1px solid transparent !important;
}
a:hover {
border-bottom-color: #667eea !important;
transform: translateY(-1px) !important;
}
/* 美化按钮 */
button, input[type='button'], input[type='submit'] {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
color: white !important;
padding: 8px 16px !important;
border-radius: 6px !important;
cursor: pointer !important;
font-weight: 500 !important;
}
button:hover, input[type='button']:hover, input[type='submit']:hover {
transform: translateY(-2px) !important;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4) !important;
}
/* 美化输入框 */
input[type='text'], input[type='password'], input[type='email'], textarea {
border: 2px solid #e1e5e9 !important;
border-radius: 6px !important;
padding: 8px 12px !important;
font-size: 14px !important;
}
input[type='text']:focus, input[type='password']:focus,
input[type='email']:focus, textarea:focus {
border-color: #667eea !important;
outline: none !important;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
}
/* 页面加载动画 */
@keyframes fbro-fade-in {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
body > * {
animation: fbro-fade-in 0.6s ease-out !important;
}
/* 添加水印 */
body::after {
content: '🛡️ Protected by FBro' !important;
position: fixed !important;
bottom: 10px !important;
right: 10px !important;
background: rgba(102, 126, 234, 0.1) !important;
padding: 4px 8px !important;
border-radius: 4px !important;
font-size: 12px !important;
color: #667eea !important;
z-index: 9999 !important;
pointer-events: none !important;
}
";
}
public override void GetResponseHeaders(long flag, IFBroSharpResponse response,
ref long response_length, ref string redirectUrl)
{
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(cssContent);
response_length = contentBytes.Length;
response.SetMimeType("text/css");
response.SetStatus(200);
response.SetHeaderByName("Content-Type", "text/css; charset=utf-8", true);
response.SetHeaderByName("X-FBro-Enhanced", "true", true);
Console.WriteLine($"✨ CSS样式拦截 - 长度: {response_length} 字节");
}
public override bool ProcessRequest(long flag, IFBroSharpRequest request, IFBroSharpCallback callback)
{
callback.Continue();
return true;
}
public override bool Read(long flag, Stream dataOut, out int bytesRead,
IFBroSharpResourceReadCallback callback)
{
callback.Dispose();
if (isDataWritten)
{
bytesRead = 0;
return false;
}
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(cssContent);
dataOut.Write(contentBytes, 0, contentBytes.Length);
bytesRead = contentBytes.Length;
isDataWritten = true;
Console.WriteLine($"🎨 CSS内容已注入 - {bytesRead} 字节");
return true;
}
// 其他必需方法的简化实现
public override void Start(long flag) { }
public override void Cancel(long flag) { }
public override void End(long flag) { }
public override bool Open(long flag, IFBroSharpRequest request, ref bool handle_request, IFBroSharpCallback callback) { return false; }
public override bool Skip(long flag, long bytes_to_skip, ref long bytes_skipped, IFBroSharpResourceSkipCallback callback) { return false; }
}广告拦截处理器
csharp
/// <summary>
/// 广告拦截处理器
/// 返回空内容或替代内容来阻止广告加载
/// </summary>
public class AdBlockHandler : FBroSharpResourceEvent
{
private static readonly string[] AD_KEYWORDS = {
"ads", "advertisement", "banner", "popup", "tracking",
"analytics", "doubleclick", "adsystem", "googlesyndication"
};
private string blockedContent = "";
private bool isDataWritten = false;
public override void GetResponseHeaders(long flag, IFBroSharpResponse response,
ref long response_length, ref string redirectUrl)
{
// 返回空内容
response_length = 0;
response.SetStatus(204); // No Content
response.SetMimeType("text/plain");
response.SetHeaderByName("X-FBro-AdBlocked", "true", true);
Console.WriteLine("🚫 广告已拦截");
}
public override bool ProcessRequest(long flag, IFBroSharpRequest request, IFBroSharpCallback callback)
{
string url = request.GetURL();
Console.WriteLine($"🛡️ 拦截广告请求: {url}");
callback.Continue();
return true;
}
public override bool Read(long flag, Stream dataOut, out int bytesRead,
IFBroSharpResourceReadCallback callback)
{
callback.Dispose();
bytesRead = 0; // 返回空内容
return false;
}
// 其他方法省略...
public override void Start(long flag) { }
public override void Cancel(long flag) { }
public override void End(long flag) { }
public override bool Open(long flag, IFBroSharpRequest request, ref bool handle_request, IFBroSharpCallback callback) { return false; }
public override bool Skip(long flag, long bytes_to_skip, ref long bytes_skipped, IFBroSharpResourceSkipCallback callback) { return false; }
}图片替换处理器
csharp
/// <summary>
/// 图片资源替换处理器
/// 用于替换或优化图片资源
/// </summary>
public class ImageReplacementHandler : FBroSharpResourceEvent
{
private byte[] imageData;
private bool isDataWritten = false;
private string mimeType;
public ImageReplacementHandler(string imagePath = null)
{
if (string.IsNullOrEmpty(imagePath))
{
// 使用默认的占位图片(base64编码的小图片)
imageData = GenerateDefaultImage();
mimeType = "image/png";
}
else
{
LoadImageFromFile(imagePath);
}
}
/// <summary>
/// 生成默认占位图片(1x1像素PNG)
/// </summary>
/// <returns>图片字节数组</returns>
private byte[] GenerateDefaultImage()
{
// 1x1透明PNG的base64数据
string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
return Convert.FromBase64String(base64);
}
/// <summary>
/// 从文件加载图片
/// </summary>
/// <param name="imagePath">图片文件路径</param>
private void LoadImageFromFile(string imagePath)
{
try
{
imageData = File.ReadAllBytes(imagePath);
// 根据文件扩展名确定MIME类型
string ext = Path.GetExtension(imagePath).ToLower();
mimeType = ext switch
{
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".webp" => "image/webp",
".svg" => "image/svg+xml",
_ => "image/png"
};
Console.WriteLine($"📷 图片已加载: {imagePath} ({imageData.Length} 字节)");
}
catch (Exception ex)
{
Console.WriteLine($"❌ 图片加载失败: {ex.Message}");
imageData = GenerateDefaultImage();
mimeType = "image/png";
}
}
public override void GetResponseHeaders(long flag, IFBroSharpResponse response,
ref long response_length, ref string redirectUrl)
{
response_length = imageData.Length;
response.SetMimeType(mimeType);
response.SetStatus(200);
response.SetHeaderByName("Cache-Control", "public, max-age=3600", true);
response.SetHeaderByName("X-FBro-ImageReplaced", "true", true);
Console.WriteLine($"🖼️ 图片响应头设置完成 - {response_length} 字节");
}
public override bool ProcessRequest(long flag, IFBroSharpRequest request, IFBroSharpCallback callback)
{
callback.Continue();
return true;
}
public override bool Read(long flag, Stream dataOut, out int bytesRead,
IFBroSharpResourceReadCallback callback)
{
callback.Dispose();
if (isDataWritten)
{
bytesRead = 0;
return false;
}
if (imageData.Length <= dataOut.Length)
{
dataOut.Write(imageData, 0, imageData.Length);
bytesRead = imageData.Length;
isDataWritten = true;
Console.WriteLine($"✅ 图片数据写入完成");
return true;
}
else
{
// 处理大图片的分块写入
dataOut.Write(imageData, 0, (int)dataOut.Length);
bytesRead = (int)dataOut.Length;
// 创建剩余数据数组
byte[] remaining = new byte[imageData.Length - (int)dataOut.Length];
Array.Copy(imageData, (int)dataOut.Length, remaining, 0, remaining.Length);
imageData = remaining;
Console.WriteLine($"📦 图片分块写入 - 本次: {bytesRead}, 剩余: {remaining.Length}");
return true;
}
}
// 其他方法省略...
public override void Start(long flag) { }
public override void Cancel(long flag) { }
public override void End(long flag) { }
public override bool Open(long flag, IFBroSharpRequest request, ref bool handle_request, IFBroSharpCallback callback) { return false; }
public override bool Skip(long flag, long bytes_to_skip, ref long bytes_skipped, IFBroSharpResourceSkipCallback callback) { return false; }
}🎯 实际应用场景
场景1:内容审查和过滤
csharp
/// <summary>
/// 内容审查处理器
/// 自动过滤和替换敏感内容
/// </summary>
public class ContentFilterHandler : FBroSharpResourceEvent
{
private string originalContent;
private string filteredContent;
private bool isDataWritten = false;
// 敏感词列表
private static readonly string[] SENSITIVE_WORDS = {
"敏感词1", "敏感词2", "不当内容"
};
public ContentFilterHandler(string content)
{
originalContent = content;
filteredContent = FilterContent(content);
}
/// <summary>
/// 过滤内容中的敏感词
/// </summary>
/// <param name="content">原始内容</param>
/// <returns>过滤后的内容</returns>
private string FilterContent(string content)
{
string result = content;
foreach (string word in SENSITIVE_WORDS)
{
// 用星号替换敏感词
result = result.Replace(word, new string('*', word.Length));
}
// 添加内容审查标识
result = result.Replace("</body>",
@"<div style='position:fixed;bottom:0;right:0;background:rgba(255,0,0,0.1);padding:5px;font-size:12px;'>
⚠️ 内容已审查
</div></body>");
Console.WriteLine($"🔍 内容过滤完成 - 原始长度: {content.Length}, 过滤后: {result.Length}");
return result;
}
// 实现必需的方法...
public override void GetResponseHeaders(long flag, IFBroSharpResponse response,
ref long response_length, ref string redirectUrl)
{
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(filteredContent);
response_length = contentBytes.Length;
response.SetMimeType("text/html");
response.SetStatus(200);
response.SetHeaderByName("X-Content-Filtered", "true", true);
}
public override bool ProcessRequest(long flag, IFBroSharpRequest request, IFBroSharpCallback callback)
{
callback.Continue();
return true;
}
public override bool Read(long flag, Stream dataOut, out int bytesRead,
IFBroSharpResourceReadCallback callback)
{
callback.Dispose();
if (isDataWritten)
{
bytesRead = 0;
return false;
}
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(filteredContent);
dataOut.Write(contentBytes, 0, contentBytes.Length);
bytesRead = contentBytes.Length;
isDataWritten = true;
return true;
}
// 其他方法省略...
public override void Start(long flag) { }
public override void Cancel(long flag) { }
public override void End(long flag) { }
public override bool Open(long flag, IFBroSharpRequest request, ref bool handle_request, IFBroSharpCallback callback) { return false; }
public override bool Skip(long flag, long bytes_to_skip, ref long bytes_skipped, IFBroSharpResourceSkipCallback callback) { return false; }
}场景2:API模拟和测试
csharp
/// <summary>
/// API模拟处理器
/// 用于前端开发时模拟后端API响应
/// </summary>
public class ApiMockHandler : FBroSharpResourceEvent
{
private string jsonResponse;
private bool isDataWritten = false;
public ApiMockHandler(string apiPath)
{
jsonResponse = GenerateMockResponse(apiPath);
}
/// <summary>
/// 根据API路径生成模拟响应
/// </summary>
/// <param name="apiPath">API路径</param>
/// <returns>JSON响应字符串</returns>
private string GenerateMockResponse(string apiPath)
{
// 根据API路径返回不同的模拟数据
if (apiPath.Contains("/api/user"))
{
return @"{
""success"": true,
""data"": {
""id"": 12345,
""name"": ""张三"",
""email"": ""zhangsan@example.com"",
""avatar"": ""https://via.placeholder.com/64"",
""role"": ""admin"",
""lastLogin"": """ + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + @"""
},
""message"": ""获取用户信息成功""
}";
}
else if (apiPath.Contains("/api/products"))
{
return @"{
""success"": true,
""data"": [
{
""id"": 1,
""name"": ""商品1"",
""price"": 99.99,
""stock"": 100,
""category"": ""电子产品""
},
{
""id"": 2,
""name"": ""商品2"",
""price"": 149.99,
""stock"": 50,
""category"": ""数码配件""
}
],
""total"": 2,
""page"": 1,
""message"": ""获取商品列表成功""
}";
}
else
{
return @"{
""success"": false,
""message"": ""API路径不存在"",
""code"": 404
}";
}
}
public override void GetResponseHeaders(long flag, IFBroSharpResponse response,
ref long response_length, ref string redirectUrl)
{
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(jsonResponse);
response_length = contentBytes.Length;
response.SetMimeType("application/json");
response.SetStatus(200);
response.SetHeaderByName("Content-Type", "application/json; charset=utf-8", true);
response.SetHeaderByName("Access-Control-Allow-Origin", "*", true);
response.SetHeaderByName("X-Mock-API", "true", true);
Console.WriteLine($"🎭 API模拟响应已准备 - {response_length} 字节");
}
public override bool ProcessRequest(long flag, IFBroSharpRequest request, IFBroSharpCallback callback)
{
Console.WriteLine($"🔄 处理API模拟请求: {request.GetURL()}");
callback.Continue();
return true;
}
public override bool Read(long flag, Stream dataOut, out int bytesRead,
IFBroSharpResourceReadCallback callback)
{
callback.Dispose();
if (isDataWritten)
{
bytesRead = 0;
return false;
}
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(jsonResponse);
dataOut.Write(contentBytes, 0, contentBytes.Length);
bytesRead = contentBytes.Length;
isDataWritten = true;
Console.WriteLine($"📤 API模拟数据已发送");
return true;
}
// 其他方法省略...
public override void Start(long flag) { }
public override void Cancel(long flag) { }
public override void End(long flag) { }
public override bool Open(long flag, IFBroSharpRequest request, ref bool handle_request, IFBroSharpCallback callback) { return false; }
public override bool Skip(long flag, long bytes_to_skip, ref long bytes_skipped, IFBroSharpResourceSkipCallback callback) { return false; }
}🏗️ 高级资源管理器
智能资源路由器
csharp
/// <summary>
/// 智能资源路由器
/// 根据URL模式和规则智能选择处理器
/// </summary>
public class SmartResourceRouter
{
private readonly Dictionary<string, Func<IFBroSharpRequest, FBroSharpResourceEvent>> routeHandlers;
private readonly List<RouteRule> routeRules;
public SmartResourceRouter()
{
routeHandlers = new Dictionary<string, Func<IFBroSharpRequest, FBroSharpResourceEvent>>();
routeRules = new List<RouteRule>();
InitializeDefaultRoutes();
}
/// <summary>
/// 初始化默认路由规则
/// </summary>
private void InitializeDefaultRoutes()
{
// HTML页面替换
AddRoute("*.baidu.com", request => new HtmlReplacementHandler());
// CSS样式增强
AddRoute("*.css", request => new CssInterceptHandler());
// 广告拦截
AddRoute("*ads*", request => new AdBlockHandler());
AddRoute("*doubleclick*", request => new AdBlockHandler());
AddRoute("*googlesyndication*", request => new AdBlockHandler());
// 图片替换
AddRoute("*.jpg", request => new ImageReplacementHandler());
AddRoute("*.png", request => new ImageReplacementHandler());
// API模拟
AddRoute("*/api/*", request => new ApiMockHandler(request.GetURL()));
Console.WriteLine($"✅ 初始化完成 - 注册了 {routeRules.Count} 个路由规则");
}
/// <summary>
/// 添加路由规则
/// </summary>
/// <param name="pattern">URL匹配模式</param>
/// <param name="handlerFactory">处理器工厂方法</param>
public void AddRoute(string pattern, Func<IFBroSharpRequest, FBroSharpResourceEvent> handlerFactory)
{
var rule = new RouteRule(pattern, handlerFactory);
routeRules.Add(rule);
Console.WriteLine($"➕ 添加路由规则: {pattern}");
}
/// <summary>
/// 查找匹配的处理器
/// </summary>
/// <param name="request">HTTP请求</param>
/// <returns>资源处理器(如果匹配)</returns>
public FBroSharpResourceEvent FindHandler(IFBroSharpRequest request)
{
string url = request.GetURL();
foreach (var rule in routeRules)
{
if (rule.IsMatch(url))
{
Console.WriteLine($"🎯 路由匹配: {rule.Pattern} -> {url}");
return rule.CreateHandler(request);
}
}
Console.WriteLine($"⏭️ 无匹配路由: {url}");
return null;
}
/// <summary>
/// 获取路由统计信息
/// </summary>
/// <returns>统计信息字符串</returns>
public string GetRouteStatistics()
{
var stats = new StringBuilder();
stats.AppendLine("=== 资源路由统计 ===");
stats.AppendLine($"总路由规则数: {routeRules.Count}");
for (int i = 0; i < routeRules.Count; i++)
{
var rule = routeRules[i];
stats.AppendLine($"{i + 1}. {rule.Pattern} (匹配次数: {rule.MatchCount})");
}
return stats.ToString();
}
}
/// <summary>
/// 路由规则类
/// </summary>
public class RouteRule
{
public string Pattern { get; }
public Func<IFBroSharpRequest, FBroSharpResourceEvent> HandlerFactory { get; }
public int MatchCount { get; private set; }
public RouteRule(string pattern, Func<IFBroSharpRequest, FBroSharpResourceEvent> handlerFactory)
{
Pattern = pattern;
HandlerFactory = handlerFactory;
MatchCount = 0;
}
/// <summary>
/// 检查URL是否匹配当前规则
/// </summary>
/// <param name="url">要检查的URL</param>
/// <returns>是否匹配</returns>
public bool IsMatch(string url)
{
bool match = MatchPattern(url, Pattern);
if (match)
{
MatchCount++;
}
return match;
}
/// <summary>
/// 创建处理器实例
/// </summary>
/// <param name="request">HTTP请求</param>
/// <returns>处理器实例</returns>
public FBroSharpResourceEvent CreateHandler(IFBroSharpRequest request)
{
return HandlerFactory(request);
}
/// <summary>
/// 模式匹配算法
/// 支持通配符 * 和 ?
/// </summary>
/// <param name="text">要匹配的文本</param>
/// <param name="pattern">匹配模式</param>
/// <returns>是否匹配</returns>
private bool MatchPattern(string text, string pattern)
{
// 简单的通配符匹配实现
if (pattern.Contains("*"))
{
string[] parts = pattern.Split('*');
int lastIndex = 0;
foreach (string part in parts)
{
if (string.IsNullOrEmpty(part)) continue;
int index = text.IndexOf(part, lastIndex, StringComparison.OrdinalIgnoreCase);
if (index == -1) return false;
lastIndex = index + part.Length;
}
return true;
}
else
{
return text.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
}🔄 完整的浏览器事件类实现
集成资源管理的BrowserEvent
csharp
using System;
using System.Collections.Generic;
using System.Reflection;
using FBroSharp.Event;
using FBroSharp.Lib;
/// <summary>
/// 资源篡改功能集成的浏览器事件处理类
/// </summary>
public class ResourceInterceptorBrowserEvent : FBroBrowserEvent
{
private readonly SmartResourceRouter resourceRouter;
private readonly List<FBroSharpBrowser> activeBrowsers;
private readonly Dictionary<string, int> interceptStatistics;
public ResourceInterceptorBrowserEvent()
{
resourceRouter = new SmartResourceRouter();
activeBrowsers = new List<FBroSharpBrowser>();
interceptStatistics = new Dictionary<string, int>();
Console.WriteLine("🚀 资源拦截器初始化完成");
}
/// <summary>
/// 浏览器创建后事件
/// </summary>
/// <param name="browser">浏览器实例</param>
/// <param name="extrainfo">额外信息</param>
public override void OnAfterCreated(IFBroSharpBrowser browser, IFBroSharpDictionaryValue extrainfo)
{
Console.WriteLine($"🌐 浏览器创建: {MethodBase.GetCurrentMethod().Name}");
if (!extrainfo.GetBool("是否为后台"))
{
activeBrowsers.Add((FBroSharpBrowser)browser);
Console.WriteLine($"✅ 前台浏览器已注册 - 总数: {activeBrowsers.Count}");
}
else
{
Console.WriteLine("🔧 后台浏览器创建");
}
}
/// <summary>
/// 浏览器关闭前事件
/// </summary>
/// <param name="browser">浏览器实例</param>
public override void OnBeforeClose(IFBroSharpBrowser browser)
{
Console.WriteLine($"🔄 浏览器关闭: {MethodBase.GetCurrentMethod().Name}");
// 从活动列表中移除浏览器
for (int i = activeBrowsers.Count - 1; i >= 0; i--)
{
var activeBrowser = activeBrowsers[i];
if (activeBrowser.IsSame(browser))
{
activeBrowsers.RemoveAt(i);
Console.WriteLine($"❌ 浏览器已从活动列表移除 - 剩余: {activeBrowsers.Count}");
break;
}
}
// 输出拦截统计信息
PrintInterceptStatistics();
}
/// <summary>
/// 浏览器关闭控制
/// </summary>
/// <param name="browser">浏览器实例</param>
/// <returns>是否允许关闭</returns>
public override bool DoClose(IFBroSharpBrowser browser)
{
return false; // 允许关闭
}
/// <summary>
/// 资源请求处理器 - 核心拦截逻辑
/// </summary>
/// <param name="browser">浏览器实例</param>
/// <param name="frame">框架实例</param>
/// <param name="request">HTTP请求</param>
/// <param name="set_callback">资源处理器设置接口</param>
public override void GetResourceHandler(IFBroSharpBrowser browser,
IFBroSharpFrame frame,
IFBroSharpRequest request,
IFBroSharpResourceHandler set_callback)
{
string url = request.GetURL();
string method = request.GetMethod();
Console.WriteLine($"🔍 资源请求: {method} {url}");
try
{
// 使用智能路由器查找处理器
var handler = resourceRouter.FindHandler(request);
if (handler != null)
{
// 设置自定义资源处理器
set_callback.SetCallback(handler);
// 更新统计信息
UpdateInterceptStatistics(url);
Console.WriteLine($"✅ 资源已拦截并处理: {url}");
}
else
{
// 记录未拦截的请求(可选)
Console.WriteLine($"⏭️ 资源放行: {url}");
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ 资源处理异常: {ex.Message}");
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
}
}
/// <summary>
/// 更新拦截统计信息
/// </summary>
/// <param name="url">被拦截的URL</param>
private void UpdateInterceptStatistics(string url)
{
try
{
// 提取域名用于统计
var uri = new Uri(url);
string domain = uri.Host;
if (interceptStatistics.ContainsKey(domain))
{
interceptStatistics[domain]++;
}
else
{
interceptStatistics[domain] = 1;
}
}
catch
{
// 如果URL解析失败,使用"其他"分类
string key = "其他";
if (interceptStatistics.ContainsKey(key))
{
interceptStatistics[key]++;
}
else
{
interceptStatistics[key] = 1;
}
}
}
/// <summary>
/// 打印拦截统计信息
/// </summary>
private void PrintInterceptStatistics()
{
if (interceptStatistics.Count == 0)
{
Console.WriteLine("📊 无拦截统计数据");
return;
}
Console.WriteLine("📊 资源拦截统计:");
Console.WriteLine("==========================================");
var sortedStats = interceptStatistics.OrderByDescending(kv => kv.Value);
foreach (var stat in sortedStats)
{
Console.WriteLine($" {stat.Key}: {stat.Value} 次");
}
int totalIntercepts = interceptStatistics.Values.Sum();
Console.WriteLine("==========================================");
Console.WriteLine($"总拦截次数: {totalIntercepts}");
}
/// <summary>
/// 添加自定义路由规则
/// </summary>
/// <param name="pattern">URL模式</param>
/// <param name="handlerFactory">处理器工厂</param>
public void AddCustomRoute(string pattern, Func<IFBroSharpRequest, FBroSharpResourceEvent> handlerFactory)
{
resourceRouter.AddRoute(pattern, handlerFactory);
Console.WriteLine($"🔧 添加自定义路由: {pattern}");
}
/// <summary>
/// 获取当前活动浏览器数量
/// </summary>
/// <returns>活动浏览器数量</returns>
public int GetActiveBrowserCount()
{
return activeBrowsers.Count;
}
/// <summary>
/// 获取拦截统计报告
/// </summary>
/// <returns>详细统计报告</returns>
public string GetDetailedStatistics()
{
var report = new System.Text.StringBuilder();
report.AppendLine("=== FBro资源拦截详细报告 ===");
report.AppendLine($"报告生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
report.AppendLine($"活动浏览器数量: {activeBrowsers.Count}");
report.AppendLine();
// 路由统计
report.AppendLine(resourceRouter.GetRouteStatistics());
report.AppendLine();
// 域名拦截统计
report.AppendLine("=== 域名拦截统计 ===");
if (interceptStatistics.Count > 0)
{
var sortedStats = interceptStatistics.OrderByDescending(kv => kv.Value);
foreach (var stat in sortedStats)
{
double percentage = (double)stat.Value / interceptStatistics.Values.Sum() * 100;
report.AppendLine($"{stat.Key}: {stat.Value} 次 ({percentage:F1}%)");
}
int totalIntercepts = interceptStatistics.Values.Sum();
report.AppendLine($"总拦截次数: {totalIntercepts}");
}
else
{
report.AppendLine("暂无拦截数据");
}
report.AppendLine("========================================");
return report.ToString();
}
}⚠️ 注意事项
1. 性能优化
- 内存管理:及时释放大文件的内存占用,避免内存泄漏
- 缓存策略:对频繁访问的资源进行缓存,提高响应速度
- 异步处理:对于大文件处理,考虑使用异步方式避免阻塞
- 资源限制:设置合理的文件大小限制,防止系统资源耗尽
2. 安全考虑
- 输入验证:对所有输入内容进行安全验证,防止XSS和注入攻击
- 权限控制:确保只有授权的请求才能触发资源替换
- 敏感信息保护:避免在替换内容中暴露敏感信息
- HTTPS处理:注意HTTPS网站的证书验证问题
3. 兼容性
- 编码处理:正确处理UTF-8、GBK等各种字符编码
- MIME类型:为不同类型的资源设置正确的Content-Type
- 浏览器差异:测试不同浏览器内核的兼容性
- 响应头完整性:确保所有必要的HTTP响应头都已设置
4. 调试和监控
- 详细日志:记录所有拦截操作,便于问题排查
- 性能监控:监控资源处理的响应时间和成功率
- 错误处理:妥善处理各种异常情况,避免程序崩溃
- 统计分析:定期分析拦截统计数据,优化拦截策略
🏆 最佳实践
1. 资源处理器设计原则
csharp
// ✅ 好的实践:状态管理清晰
public class GoodResourceHandler : FBroSharpResourceEvent
{
private readonly string content;
private bool isDataWritten = false;
public GoodResourceHandler(string content)
{
this.content = content ?? throw new ArgumentNullException(nameof(content));
}
// 清晰的状态控制逻辑...
}
// ❌ 避免:全局状态和内存泄漏
public class BadResourceHandler : FBroSharpResourceEvent
{
private static string sharedContent; // 避免静态变量
private byte[] largeData; // 记得及时释放
}2. 错误处理最佳实践
csharp
public override bool Read(long flag, Stream dataOut, out int bytesRead,
IFBroSharpResourceReadCallback callback)
{
callback.Dispose();
bytesRead = 0;
try
{
// 安全的数据处理逻辑
if (string.IsNullOrEmpty(content))
{
return false;
}
byte[] data = System.Text.Encoding.UTF8.GetBytes(content);
// 检查缓冲区大小
if (data.Length > dataOut.Length)
{
Console.WriteLine($"⚠️ 数据过大,需要分块处理: {data.Length} > {dataOut.Length}");
// 实现分块处理逻辑
}
dataOut.Write(data, 0, data.Length);
bytesRead = data.Length;
return true;
}
catch (Exception ex)
{
Console.WriteLine($"❌ 数据写入失败: {ex.Message}");
return false;
}
}3. 资源路由管理
csharp
// 使用配置文件管理路由规则
public void LoadRoutesFromConfig(string configPath)
{
try
{
var config = JsonConvert.DeserializeObject<RouteConfig>(
File.ReadAllText(configPath));
foreach (var route in config.Routes)
{
AddRoute(route.Pattern, request =>
CreateHandlerByType(route.HandlerType, route.Parameters));
}
}
catch (Exception ex)
{
Console.WriteLine($"⚠️ 配置加载失败,使用默认路由: {ex.Message}");
InitializeDefaultRoutes();
}
}4. 性能监控
csharp
public class PerformanceMonitor
{
private readonly Dictionary<string, TimeSpan> processingTimes = new();
public void RecordProcessingTime(string handlerType, TimeSpan duration)
{
processingTimes[handlerType] = duration;
if (duration.TotalMilliseconds > 1000) // 超过1秒警告
{
Console.WriteLine($"⚠️ 处理器性能警告: {handlerType} 耗时 {duration.TotalMilliseconds}ms");
}
}
}📈 扩展功能建议
1. 缓存机制
- 实现LRU缓存策略
- 支持缓存过期时间
- 提供缓存统计和清理功能
2. 配置化管理
- JSON配置文件支持
- 热更新配置功能
- 配置验证和错误提示
3. 插件化架构
- 动态加载处理器插件
- 插件生命周期管理
- 插件间通信机制
4. 监控和分析
- 实时性能监控
- 拦截成功率统计
- 异常报告和分析
通过以上完整的实现方案,您可以构建一个功能强大、安全可靠的FBro资源拦截和篡改系统,满足各种复杂的Web应用场景需求。