Skip to content

🛡️ FBro资源拦截与篡改完整指南

深度控制网络资源请求,实现高级的内容拦截、修改和替换功能


📋 概述

FBro浏览器提供了强大的资源拦截和篡改功能,通过重写GetResourceHandler方法,开发者可以完全控制网页资源的加载过程。这个功能可以实现网页内容替换、资源重定向、内容过滤、广告拦截等高级功能。

🎯 主要功能

  • 完整资源拦截:拦截所有类型的网络请求(HTML、CSS、JS、图片等)
  • 内容动态替换:实时替换网页内容和资源
  • 自定义响应:自定义HTTP响应头、状态码和内容
  • 流式数据处理:支持大文件的分块处理
  • 精准URL匹配:支持精确URL匹配和模式匹配
  • 多种篡改策略:支持完整替换、部分修改、条件拦截

📱 应用场景

应用场景实现效果技术优势
内容过滤屏蔽不良内容和广告实时拦截,零延迟
页面改版动态修改网页布局和样式无需修改源网站
API模拟模拟后端API响应前端独立开发测试
数据注入向页面注入自定义数据增强用户体验
安全防护拦截恶意资源和脚本主动安全防护
离线功能缓存资源实现离线访问提升访问速度

🔧 核心实现架构

资源拦截流程图

网页请求 → GetResourceHandler → 判断是否拦截 → 创建资源处理器 → 自定义响应
    ↓                ↓                    ↓                 ↓            ↓
原始请求        URL匹配检查        设置回调处理器        处理请求      返回内容

关键组件说明

  1. GetResourceHandler:资源拦截入口点
  2. FBroResourceEventCallback:资源处理回调类
  3. IFBroSharpResourceHandler:资源处理器接口
  4. IFBroSharpResponse:HTTP响应对象
  5. 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应用场景需求。

如果文档对您有帮助,欢迎 请喝咖啡 ☕ | 软件发布 | 源码购买