academic逆向

 2024-07-27    0 条评论    232 浏览 ChatGPT

没事逛github,发现了一个项目,遂看能不能逆向成接口,

发现他发送第一个ws请求,会返回

{"msg":"send_hash"}

然后这时候,需要发送一个session_hash,多次调试之后,发现session_hash可以随机生成,没有限制,这时候我们再次请求

{
    "fn_index": 9,
    "session_hash": "mge4bxrbarh"
}

然后,重点:再次请求的时候,需要把刚才生成的session_hash替换成刚才生成的session_hash,再次请求

图3的代码如下:

{
    "data": [
        null,
        4096,
        "gemini-pro",
        "你好",
        "",
        1,
        1,
        [],
        null,
        "Serve me as a writing and programming assistant.",
        "",
        null
    ],
    "event_data": null,
    "fn_index": 9,
    "session_hash": "mge4bxrbarh"
}

这时候,就可以收到返回值了

但是发现他返回的是html格式,也没有遵循openai的格式返回;

写了个C#的程序,以供自己调用,等后期有时间再搞个页面


using System;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;

/// <summary>
/// academic逆向
/// icon_hash="1871189255"
/// WebSocket 连接
/// </summary>
class Program
{
    private static readonly TimeSpan PongWait = TimeSpan.FromSeconds(60);

    static async Task Main(string[] args)
    {
        //var client = new Client("wss://test.cn/queue/join");
        //await client.ConnectAsync();

        //var messageChan = client.SendMessage("你是gpt4吗?", "gpt-4");

        //await foreach (var msg in messageChan)
        //{
        //    Console.WriteLine($"Message: {msg}");
        //}

        while (true)
        {
            Console.Write("请输入您的问题 (输入 'exit' 退出): ");
            string question = Console.ReadLine();
            if (question.ToLower() == "exit")
                break;
            try
            {
                using (var client = new Client("wss://test.cn/queue/join"))
                {
                    await client.ConnectAsync();
                    var messageChan = client.SendMessage(question, "gpt-4");
                    await foreach (var msg in messageChan)
                    {
                        Console.WriteLine($"回答: {msg}");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发生错误: {ex.Message}");
            }
        }
        Console.WriteLine("程序已退出。");
    }
}

public class Client : IDisposable
{
    private readonly Uri _addr;
    private readonly string _sessionHash;
    private ClientWebSocket _webSocket;
    private readonly Channel<string> _receiveChannel;

    private const int MaxMessageSize = 64 * 1024; // 64 KB
    private static readonly TimeSpan PongWait = TimeSpan.FromSeconds(60);
    private static readonly TimeSpan PingPeriod = TimeSpan.FromSeconds((PongWait.TotalSeconds * 9) / 10);
    private static readonly TimeSpan WaitTimeout = TimeSpan.FromSeconds(30);

    public Client(string addr)
    {
        _addr = new Uri(addr);
        _sessionHash = Guid.NewGuid().ToString();
        _receiveChannel = Channel.CreateUnbounded<string>();
    }

    public async Task ConnectAsync()
    {
        _webSocket = new ClientWebSocket();
        _webSocket.Options.KeepAliveInterval = TimeSpan.Zero; // Disable built-in keep-alive

        await _webSocket.ConnectAsync(_addr, CancellationToken.None);
        _ = Task.Run(ReadPumpAsync);
        _ = Task.Run(WritePumpAsync);
    }

    public async IAsyncEnumerable<string> SendMessage(string message, string model)
    {
        var responseChannel = Channel.CreateUnbounded<string>();
        _ = Task.Run(() => HandleMessageExchangeAsync(message, model, responseChannel.Writer));
        await foreach (var msg in responseChannel.Reader.ReadAllAsync())
        {
            yield return msg;
        }
    }

    private async Task HandleMessageExchangeAsync(string message, string model, ChannelWriter<string> responseWriter)
    {
        try
        {
            if (!await PerformHandshakeAsync())
            {
                await responseWriter.WriteAsync("Error: handshake failed");
                return;
            }

            if (!await SendAiRequestAsync(message, model))
            {
                await responseWriter.WriteAsync("Error: sending AI request failed");
                return;
            }

            await ProcessResponsesAsync(responseWriter);
        }
        finally
        {
            responseWriter.Complete();
        }
    }

    private async Task<bool> PerformHandshakeAsync()
    {
        if (!await WaitForMessageAsync("send_hash")) return false;

        var sessionHashMessage = new
        {
            fn_index = 16,
            session_hash = _sessionHash
        };
        if (!await SendJsonAsync(sessionHashMessage)) return false;

        if (!await WaitForMessageAsync("estimation")) return false;
        if (!await WaitForMessageAsync("send_data")) return false;

        return true;
    }

    private async Task<bool> SendAiRequestAsync(string message, string model)
    {
        var aiRequest = new
        {
            data = new object[]
            {
                null, 4096, model, message, "", 1, 1, new string[][] {},
                null, "Serve me as a writing and programming assistant.", "", null
            },
            fn_index = 16,
            session_hash = _sessionHash
        };

        return await SendJsonAsync(aiRequest);
    }

    private async Task ProcessResponsesAsync(ChannelWriter<string> responseWriter)
    {
        await foreach (var msg in _receiveChannel.Reader.ReadAllAsync())
        {
            var response = JsonSerializer.Deserialize<AiResponse>(msg);
            if (response == null)
            {
                await responseWriter.WriteAsync("Error: unmarshalling response");
                return;
            }

            switch (response.msg)
            {
                case "process_starts":
                    // Process has started, wait for generating messages
                    break;
                case "process_generating":
                case "process_completed":
                    if (!response.success)
                    {
                        await responseWriter.WriteAsync($"Error from server: {response.output}");
                        return;
                    }
                    var latestResponse = ExtractLatestResponse(response);
                    if (!string.IsNullOrEmpty(latestResponse))
                    {
                        await responseWriter.WriteAsync(latestResponse);
                    }
                    if (response.msg == "process_completed")
                    {
                        return;
                    }
                    break;
                default:
                    Console.WriteLine($"Unexpected message type: {response.msg}");
                    break;
            }
        }
    }

    private static string ExtractLatestResponse(AiResponse response)
    {
        if (response.output?.data?.Length <= 1)
        {
            return string.Empty;
        }

        var conversations = response.output.data[1] as JsonElement?;
        if (conversations == null || !conversations.HasValue || conversations.Value.GetArrayLength() == 0)
        {
            return string.Empty;
        }

        string latestResponse = null;
        var conversationsArray = conversations.Value.EnumerateArray().ToArray();

        if (conversationsArray.Length == 1)
        {
            var conversation = conversationsArray[0].EnumerateArray().ToArray();
            if (conversation.Length > 1)
            {
                latestResponse = conversation[1].GetString();
            }
        }
        else
        {
            var latestConversation = conversationsArray[^1].EnumerateArray().ToArray();
            if (latestConversation.Length > 1)
            {
                latestResponse = latestConversation[1].GetString();
            }
        }

        if (!string.IsNullOrEmpty(latestResponse))
        {
            // 移除HTML标签
            latestResponse = System.Text.RegularExpressions.Regex.Replace(latestResponse, "<.*?>", string.Empty);
        }
        return latestResponse;
    }

    private async Task<bool> WaitForMessageAsync(string expectedMsg)
    {
        using var cts = new CancellationTokenSource(WaitTimeout);
        await foreach (var msg in _receiveChannel.Reader.ReadAllAsync(cts.Token))
        {
            var response = JsonSerializer.Deserialize<AiResponse>(msg);
            if (response?.msg == expectedMsg)
            {
                return true;
            }
        }

        return false;
    }

    private async Task<bool> SendJsonAsync(object message)
    {
        var json = JsonSerializer.Serialize(message);
        Console.WriteLine($"Sending: {json}");
        var buffer = Encoding.UTF8.GetBytes(json);

        try
        {
            await _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error sending message: {ex.Message}");
            return false;
        }
    }

    private async Task ReadPumpAsync()
    {
        var buffer = new byte[MaxMessageSize];

        try
        {
            while (_webSocket.State == WebSocketState.Open)
            {
                var result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                if (result.MessageType == WebSocketMessageType.Close)
                {
                    await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                    break;
                }

                var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                await _receiveChannel.Writer.WriteAsync(message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error in ReadPump: {ex.Message}");
        }
        finally
        {
            _receiveChannel.Writer.Complete();
        }
    }

    private async Task WritePumpAsync()
    {
        var buffer = new byte[1]; // Ping frame payload is empty or minimal
        var cts = new CancellationTokenSource();

        try
        {
            while (_webSocket.State == WebSocketState.Open)
            {
                await Task.Delay(PingPeriod);

                if (_webSocket.State == WebSocketState.Open)
                {
                    try
                    {
                        await _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Binary, true, cts.Token);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"Error sending Ping: {ex.Message}");
                        cts.Cancel();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error in WritePump: {ex.Message}");
        }
    }

    public void Dispose()
    {
        _webSocket?.Dispose();
    }
}

public class AiResponse
{
    public string msg { get; set; }
    public AiOutput output { get; set; }
    public bool success { get; set; }
}

public class AiOutput
{
    public object[] data { get; set; }
    public bool is_generating { get; set; }
}

项目链接:点击这里