没事逛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; }
}
项目链接:点击这里



💬 评论