超碰人人人人人,亚洲AV午夜福利精品一区二区,亚洲欧美综合区丁香五月1区,日韩欧美亚洲系列

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C#開(kāi)發(fā)完整的Socks5代理客戶端與服務(wù)端——客戶端

freeflydom
2025年5月12日 9:11 本文熱度 99

本機(jī)流量劫持

通過(guò)系統(tǒng)開(kāi)啟手動(dòng)代理

通過(guò)c#程序打開(kāi)Windows的手動(dòng)代理, 并且設(shè)置端口號(hào)和IP地址,這樣只要客戶端監(jiān)聽(tīng)該端口就可以獲取到本機(jī)的Http的流量數(shù)據(jù)。

通過(guò)對(duì)注冊(cè)表的修改,來(lái)開(kāi)啟本機(jī)的手動(dòng)代理,并且設(shè)置端口,Ip設(shè)置為本機(jī),因?yàn)榭蛻舳耸潜緳C(jī)啟動(dòng)的,端口設(shè)置不沖突的即可。

黑名單則是設(shè)置哪些域名或者IP段不走代理,我們這里先把局域網(wǎng)的排除掉。

// 引入Windows API
[DllImport("wininet.dll")]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
public const int INTERNET_OPTION_SETTINGS_CHANGED = 39;
public const int INTERNET_OPTION_REFRESH = 37;
// 設(shè)置系統(tǒng)代理
public static void SetProxy(string proxyServer, bool enable)
{
    const string userRoot = "HKEY_CURRENT_USER";
    const string subkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
    const string keyName = userRoot + "\\" + subkey;
    // 設(shè)置代理服務(wù)器地址和端口
    Microsoft.Win32.Registry.SetValue(keyName, "ProxyServer", proxyServer);
    // 啟用或禁用代理
    Microsoft.Win32.Registry.SetValue(keyName, "ProxyEnable", enable ? 1 : 0);
    // 通知系統(tǒng)設(shè)置已更改
    InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
    InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
}
/// <summary>
/// 黑名單
/// </summary>
/// <param name="exceptions"></param>
public static void SetProxyExceptions(string exceptions)
{
    const string userRoot = "HKEY_CURRENT_USER";
    const string subkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
    const string keyName = userRoot + "\\" + subkey;
    Microsoft.Win32.Registry.SetValue(keyName, "ProxyOverride", exceptions);
}
 SystemProxy.SetProxy($"127.0.0.1:{App.SettingsModel.LocalPort}", true);
 SystemProxy.SetProxyExceptions("localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*;<local>");
客戶端開(kāi)啟對(duì)應(yīng)TCP服務(wù)
 _tcpListener = new TcpListener(IPAddress.Any, _httpProxyPort);
 _tcpListener.Start();

主要就是開(kāi)啟一個(gè)監(jiān)聽(tīng)服務(wù),讓操作系統(tǒng)將對(duì)應(yīng)的流量轉(zhuǎn)發(fā)到我們的Socks5客戶端。

將http報(bào)文進(jìn)行解析,獲取到請(qǐng)求的targetHost和Port,這樣才能后面在和Socks5服務(wù)端握手的時(shí)候才能告知對(duì)方需要代理的遠(yuǎn)端信息

解析系統(tǒng)的Http請(qǐng)求
    
    /// <summary>
    /// 解析http請(qǐng)求報(bào)文,提取關(guān)鍵信息
    /// </summary>
    /// <param name="request">http請(qǐng)求</param>
    /// <param name="host">請(qǐng)求主機(jī)</param>
    /// <param name="port">請(qǐng)求主機(jī)端口號(hào)</param>
    /// <returns>是否解析成功</returns>
    private bool TryParseHttpRequest(string request, out string host, out int port)
    {
        host = null;
        port = 0;
        // 解析 CONNECT 請(qǐng)求(如 CONNECT example.com:443 HTTP/1.1)
        if (request.StartsWith("CONNECT"))
        {
            var parts = request.Split(' ')[1].Split(':');
            host = parts[0];
            port = int.Parse(parts[1]);
            return true;
        }
        // 2. 處理 GET/POST 請(qǐng)求(HTTP)
        using (var reader = new StringReader(request))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                // 從 Host 頭提取目標(biāo)
                if (line.StartsWith("Host:", StringComparison.OrdinalIgnoreCase))
                {
                    var hostParts = line.Substring(5).Trim().Split(':');
                    host = hostParts[0];
                    if (hostParts.Length > 1)
                        port = int.Parse(hostParts[1]);
                    return true;
                }
                // 空行表示頭結(jié)束
                if (string.IsNullOrWhiteSpace(line))
                    break;
            }
        }
        // 3. 舊式HTTP/1.0請(qǐng)求可能沒(méi)有Host頭,從URL解析
        if (request.StartsWith("GET ") || request.StartsWith("POST "))
        {
            var url = request.Split(' ')[1];
            if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
            {
                host = uri.Host;
                port = uri.Port;
                return true;
            }
        }
        return false;
    }
遠(yuǎn)程Socks5服務(wù)端握手

這里我們采用的是帶有認(rèn)證的握手,需要服務(wù)端也開(kāi)啟認(rèn)證配置,這里對(duì)于握手協(xié)議和認(rèn)證還不清楚的可以看我集合的上面一篇文章

將http的遠(yuǎn)程信息作為握手信息與服務(wù)端建立連接,讓服務(wù)端建立與目標(biāo)的連接代理。

    /// <summary>
    /// 帶有身份驗(yàn)證的登錄
    /// </summary>
    /// <param name="socks5Stream"></param>
    /// <param name="targetHost"></param>
    /// <param name="targetPort"></param>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private async Task PerformSocks5Handshake(NetworkStream socks5Stream,
                                              string targetHost,
                                              int targetPort,
                                              string username,
                                              string password)
    {
        // === 1. 協(xié)商認(rèn)證方法 ===
        // 發(fā)送支持的認(rèn)證方法:無(wú)認(rèn)證(0x00) 和 用戶名/密碼(0x02)
        var authMethods = new byte[] { 0x05, 0x02, 0x00, 0x02 };
        await socks5Stream.WriteAsync(authMethods, 0, authMethods.Length);
        // 讀取服務(wù)器選擇的認(rèn)證方法
        var authResponse = new byte[2];
        await socks5Stream.ReadAsync(authResponse, 0, 2);
        if (authResponse[1] == 0xFF)
            throw new Exception("SOCKS5服務(wù)器不支持任何提供的認(rèn)證方法");
        // === 2. 用戶名/密碼認(rèn)證 ===
        if (authResponse[1] == 0x02)
        {
            // 構(gòu)建認(rèn)證請(qǐng)求包
            var authRequest = new byte[3 + username.Length + password.Length];
            authRequest[0] = 0x01; // 認(rèn)證子協(xié)商版本
            authRequest[1] = (byte)username.Length;
            Encoding.ASCII.GetBytes(username).CopyTo(authRequest, 2);
            authRequest[2 + username.Length] = (byte)password.Length;
            Encoding.ASCII.GetBytes(password).CopyTo(authRequest, 3 + username.Length);
            await socks5Stream.WriteAsync(authRequest, 0, authRequest.Length);
            // 讀取認(rèn)證響應(yīng)
            var authResult = new byte[2];
            await socks5Stream.ReadAsync(authResult, 0, 2);
            if (authResult[1] != 0x00)
                throw new Exception("SOCKS5用戶名/密碼認(rèn)證失敗");
        }
        // === 3. 發(fā)送連接請(qǐng)求 ===
        var request = new byte[7 + targetHost.Length];
        request[0] = 0x05; // VER
        request[1] = 0x01; // CMD=CONNECT
        request[2] = 0x00; // RSV
        request[3] = 0x03; // ATYP=域名
        request[4] = (byte)targetHost.Length;
        Encoding.ASCII.GetBytes(targetHost).CopyTo(request, 5);
        BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)targetPort)).CopyTo(request, 5 + targetHost.Length);
        await socks5Stream.WriteAsync(request, 0, request.Length);
        // === 4. 讀取連接響應(yīng) ===
        var response = new byte[10];
        await socks5Stream.ReadAsync(response, 0, 10);
        if (response[1] != 0x00)
            throw new Exception($"SOCKS5連接失敗 (狀態(tài)碼: {response[1]})");
    }
交換流量

所謂的交換流量就是把遠(yuǎn)程代理的流量和本機(jī)的請(qǐng)求流量通過(guò)客戶端作為中間人來(lái)轉(zhuǎn)發(fā)

    private async Task ForwardDataAsync(NetworkStream src, NetworkStream dest)
    {
        var buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = await src.ReadAsync(buffer, 0, buffer.Length)) > 0)
        {
            await dest.WriteAsync(buffer, 0, bytesRead);
        }
    }
await Task.WhenAny(ForwardDataAsync(httpStream, socks5Stream),ForwardDataAsync(socks5Stream, httpStream)
代理流量顯示

客戶端也需要顯示上傳和下載流量的一些顯示,我們這里簡(jiǎn)單點(diǎn),因?yàn)槲覀冎伴_(kāi)發(fā)的服務(wù)端是有基于用戶的流量統(tǒng)計(jì)的,所以只需要把數(shù)據(jù)獲取到就行,一般情況下為了性能,也可以做雙端統(tǒng)計(jì)減少壓力。

我們這里通過(guò)SSE將用戶的流量信息基于用戶名推送到客戶端。

public async Task ConnectAsync(string remoteAddress, string userName)
{
    _httpClient = new HttpClient(new HttpClientHandler
    {
        Proxy = new WebProxy($"http://{remoteAddress}:5000"), // 明確指定代理
    });
    _cts = new CancellationTokenSource();
    try
    {
        using var response = await _httpClient
            .GetAsync($"http://{remoteAddress}:5000/account/flow/{userName}", HttpCompletionOption.ResponseHeadersRead,_cts.Token);
        if (response.IsSuccessStatusCode)
        {
            using var stream = await response.Content.ReadAsStreamAsync();
            using var reader = new StreamReader(stream);
            while (!_cts.Token.IsCancellationRequested)
            {
                var line = await reader.ReadLineAsync();
                if (!string.IsNullOrEmpty(line))
                {
                    MessageReceived?.Invoke(line);
                }
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"SSE連接錯(cuò)誤: {ex.Message}");
    }
}

驗(yàn)證

遠(yuǎn)端開(kāi)啟服務(wù)端

開(kāi)啟客戶端

添加一些配置
包括:服務(wù)端IP,服務(wù)端Port,本地代理Port,用戶名密碼

開(kāi)啟客戶端

可以看到代理成功,走的是本機(jī)的代理和服務(wù)端的代理請(qǐng)求

結(jié)尾

因?yàn)楸旧泶聿捎玫男薷南到y(tǒng)代理設(shè)置是一種基礎(chǔ)設(shè)置,所謂可能存在下面影響:
僅影響支持系統(tǒng)代理的應(yīng)用(部分UWP應(yīng)用、游戲等會(huì)繞過(guò)
無(wú)法代理非HTTP/HTTPS流量(如DNS、UDP

源碼地址

https://github.com/BruceQiu1996/Socks5Server

?轉(zhuǎn)自https://www.cnblogs.com/qwqwQAQ/p/18867762


該文章在 2025/5/12 9:12:31 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專(zhuān)業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車(chē)隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類(lèi)企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷(xiāo)售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved