Files
dps_lib/include/SqrReqScript.hpp
2026-04-22 19:36:06 +08:00

498 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "BASE64.hpp"
#include "Version.h"
#include <asio.hpp>
#include <asio/basic_socket_streambuf.hpp>
#include <asio/ssl.hpp>
#include <chrono>
#include <ctime>
#include <iostream>
#include <sstream>
#include <zlib.h>
using namespace asio;
using asio::ip::tcp;
// 单个字符解密
static char CutcodeChar(char c, int key, int key2) {
char a = (((c - 1) ^ key) ^ key2) - key;
return a;
}
// 解密
void Cutecode(char *pstr, int *pkey, int len, int keyLength) {
if (len == -1)
len = strlen(pstr);
for (int i = 0; i < len; i++) {
*(pstr + i) = CutcodeChar(*(pstr + i), pkey[i % keyLength],
pkey[(i + 18) % keyLength]);
}
}
// 加密函数
static std::string r_encryptDecrypt(const std::string &input,
const std::string &key) {
std::string output = input;
for (size_t i = 0; i < input.size(); i++) {
output[i] = input[i] ^ key[i % key.size()]; // 使用异或运算进行加密
}
return output;
}
static std::string ReqScriptUrl(const std::vector<std::string> &domains) {
for (const auto &domain : domains) {
try {
asio::io_context io_context;
asio::ssl::context ctx(asio::ssl::context::tlsv12_client);
ctx.set_verify_mode(asio::ssl::verify_none);
// 启用 TLS 1.0/1.1/1.2/1.3 支持
ctx.set_options(asio::ssl::context::default_workarounds |
asio::ssl::context::no_sslv2 |
asio::ssl::context::no_sslv3 |
asio::ssl::context::single_dh_use);
asio::ssl::stream<asio::ip::tcp::socket> socket(io_context, ctx);
// 设置 SNI
if (!SSL_set_tlsext_host_name(socket.native_handle(), domain.c_str())) {
asio::error_code ec{static_cast<int>(::ERR_get_error()),
asio::error::get_ssl_category()};
throw asio::system_error{ec};
}
asio::ip::tcp::resolver resolver(io_context);
auto endpoints = resolver.resolve(domain, "https");
// 连接超时设置
bool connect_timeout = false;
asio::steady_timer connect_timer(io_context);
connect_timer.expires_from_now(std::chrono::seconds(10));
connect_timer.async_wait([&](const asio::error_code &ec) {
if (!ec) {
connect_timeout = true;
socket.lowest_layer().close();
}
});
asio::error_code connect_ec;
asio::async_connect(socket.lowest_layer(), endpoints,
[&](asio::error_code ec, asio::ip::tcp::endpoint) {
connect_ec = ec;
connect_timer.cancel();
});
io_context.run();
io_context.reset();
if (connect_timeout) {
throw std::runtime_error("连接超时");
}
if (connect_ec) {
throw std::system_error(connect_ec);
}
// SSL握手超时设置
bool handshake_timeout = false;
asio::steady_timer handshake_timer(io_context);
handshake_timer.expires_from_now(std::chrono::seconds(10));
handshake_timer.async_wait([&](const asio::error_code &ec) {
if (!ec) {
handshake_timeout = true;
socket.lowest_layer().close();
}
});
asio::error_code handshake_ec;
socket.async_handshake(asio::ssl::stream_base::client,
[&](asio::error_code ec) {
handshake_ec = ec;
handshake_timer.cancel();
});
io_context.run();
io_context.reset();
if (handshake_timeout) {
throw std::runtime_error("SSL握手超时");
}
if (handshake_ec) {
throw std::system_error(handshake_ec);
}
std::string request = "POST /script/getservicenew HTTP/1.1\r\n";
request += "Host: " + domain + "\r\n";
request += "ve: " + std::string(DPS_SCRIPT_VERSION) + "\r\n";
request += "Content-Length: 0\r\n";
request += "Content-Type: application/x-www-form-urlencoded\r\n";
request += "Connection: close\r\n";
request += "\r\n";
asio::write(socket, asio::buffer(request));
// 响应超时设置
bool response_timeout = false;
asio::steady_timer response_timer(io_context);
response_timer.expires_from_now(std::chrono::seconds(10));
response_timer.async_wait([&](const asio::error_code &ec) {
if (!ec) {
response_timeout = true;
socket.lowest_layer().close();
}
});
asio::streambuf response;
asio::error_code read_ec;
asio::async_read(socket, response, [&](asio::error_code ec, std::size_t) {
read_ec = ec;
response_timer.cancel();
});
io_context.run();
if (response_timeout) {
throw std::runtime_error("响应超时");
}
if (read_ec && read_ec != asio::error::eof) {
throw std::system_error(read_ec);
}
// 解析响应头部
std::istream response_stream(&response);
std::string http_version;
unsigned int status_code;
std::string status_message;
response_stream >> http_version >> status_code;
std::getline(response_stream, status_message); // 读取状态行剩余部分
response_stream.ignore(); // 跳过状态行后的换行符
// 读取并解析头部字段
std::string header_line;
std::map<std::string, std::string> headers;
while (std::getline(response_stream, header_line) &&
header_line != "\r") {
// 去掉行末的\r
if (!header_line.empty() &&
header_line[header_line.size() - 1] == '\r') {
header_line.resize(header_line.size() - 1);
}
// 分割头部字段
size_t colon_pos = header_line.find(':');
if (colon_pos != std::string::npos) {
std::string key = header_line.substr(0, colon_pos);
std::string value = header_line.substr(colon_pos + 1);
// 去除值前面的空格
size_t start = value.find_first_not_of(" \t");
if (start != std::string::npos) {
value = value.substr(start);
}
headers[key] = value;
}
}
if (status_code == 200) {
// 读取响应体根据Content-Length
size_t content_length = 0;
auto cl_it = headers.find("Content-Length");
if (cl_it != headers.end()) {
content_length = std::stoul(cl_it->second);
}
// 计算剩余需要读取的字节数
size_t bytes_read = response.size();
size_t bytes_to_read =
content_length > bytes_read ? content_length - bytes_read : 0;
if (bytes_to_read > 0) {
// 继续读取剩余数据
response_timer.expires_from_now(std::chrono::seconds(10));
response_timer.async_wait([&](const asio::error_code &ec) {
if (!ec) {
response_timeout = true;
socket.lowest_layer().close();
}
});
asio::error_code read_body_ec;
asio::async_read(socket, response,
asio::transfer_exactly(bytes_to_read),
[&](asio::error_code ec, std::size_t) {
read_body_ec = ec;
response_timer.cancel();
});
io_context.run();
if (response_timeout) {
throw std::runtime_error("读取响应体超时");
}
if (read_body_ec && read_body_ec != asio::error::eof) {
throw std::system_error(read_body_ec);
}
}
// 提取响应体字符串
std::istreambuf_iterator<char> eos;
std::string response_body(
std::istreambuf_iterator<char>(response_stream), eos);
return response_body;
} else {
std::cerr << "错误的请求 " << domain
<< " failed with status code: " << status_code << std::endl;
}
} catch (const std::exception &e) {
std::cerr << "错误的连接 " << domain << ": " << e.what() << std::endl;
}
}
return "";
}
static std::string make_request(const std::string &host,
const std::string &port,
const std::string &path) {
asio::io_context io_context;
tcp::resolver resolver(io_context);
tcp::resolver::results_type endpoints = resolver.resolve(host, port);
tcp::socket socket(io_context);
asio::connect(socket, endpoints);
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This
// will allow us to treat all data up until the EOF as the content.
std::string request = "GET " + path + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "Connection: close\r\n\r\n";
// Send the request.
asio::write(socket, asio::buffer(request));
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
asio::streambuf response;
asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
throw std::runtime_error("Invalid response");
}
if (status_code != 200) {
throw std::runtime_error("Response returned with status code " +
std::to_string(status_code));
}
// Read the response headers, which are terminated by a blank line.
asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
while (std::getline(response_stream, header) && header != "\r") {
// std::cout << header << "\n";
}
// std::cout << "\n";
// Write whatever content we already have to output.
std::ostringstream response_body;
if (response.size() > 0) {
response_body << &response;
}
// Read until EOF, writing data to output as we go.
asio::error_code error;
while (asio::read(socket, response, asio::transfer_at_least(1), error)) {
response_body << &response;
}
if (error != asio::error::eof) {
throw asio::system_error(error);
}
return response_body.str();
}
static std::string ReqScript(std::string url) {
std::vector<std::string> UrlData;
Tool::Split(url, UrlData, ",");
try {
std::string host = UrlData[0];
std::string port = UrlData[1];
std::string path = "/" + UrlData[2];
std::string response = make_request(host, port, path);
// std::cout << "Response body:\n"
// << response << "\n";
return response;
} catch (std::exception &e) {
// std::cerr << "Exception: " << e.what() << "\n";
return "null";
}
}
std::vector<unsigned char> base64_decode(const std::string &encoded) {
BIO *bio, *b64;
std::vector<unsigned char> result(encoded.size());
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bio = BIO_new_mem_buf(encoded.c_str(), encoded.size());
bio = BIO_push(b64, bio);
int len = BIO_read(bio, result.data(), encoded.size());
result.resize(len);
BIO_free_all(bio);
return result;
}
// zlib解压函数
static std::string gzip_decompress(std::vector<unsigned char> compressed) {
z_stream zs;
memset(&zs, 0, sizeof(zs));
if (inflateInit2(&zs, 16 + MAX_WBITS) != Z_OK) {
// throw std::runtime_error("inflateInit failed");
return "null";
}
zs.next_in = (Bytef *)compressed.data();
zs.avail_in = compressed.size();
int ret;
char outbuffer[32768];
std::string outstring;
do {
zs.next_out = reinterpret_cast<Bytef *>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = inflate(&zs, 0);
if (outstring.size() < zs.total_out) {
outstring.append(outbuffer, zs.total_out - outstring.size());
}
} while (ret == Z_OK);
inflateEnd(&zs);
if (ret != Z_STREAM_END) {
// throw std::runtime_error("Exception during zlib decompression: (" +
// std::to_string(ret) + ") " + zs.msg);
return "null";
}
return outstring;
}
static std::string rsaDecrypt(std::vector<unsigned char> encryptedData,
const std::string &publicKeyStr) {
RSA *rsa = RSA_new();
BIO *bio = BIO_new_mem_buf(const_cast<char *>(publicKeyStr.c_str()), -1);
PEM_read_bio_RSA_PUBKEY(bio, &rsa, NULL, NULL);
int rsaSize = RSA_size(rsa);
std::string decryptedData(rsaSize, 0);
int decryptedSize =
RSA_public_decrypt(encryptedData.size(), encryptedData.data(),
reinterpret_cast<unsigned char *>(&decryptedData[0]),
rsa, RSA_PKCS1_PADDING);
if (decryptedSize == -1) {
std::cerr << "Error decrypting data" << std::endl;
return "";
}
RSA_free(rsa);
BIO_free(bio);
decryptedData.resize(decryptedSize);
return decryptedData;
}
static void ReqSquirrelScript(HSQUIRRELVM v) {
std::vector<std::string> domains = {"dps.senzo.online"};
std::string StrBuf = ReqScriptUrl(domains);
std::vector<unsigned char> compressed = base64_decode(StrBuf);
std::string original_data = gzip_decompress(compressed);
nlohmann::json Jso = nlohmann::json::parse(original_data);
std::string Key = Jso["key"].dump();
Key = Key.substr(1, Key.length() - 2);
std::string Script = Jso["getBaseScriptStr"].dump();
Script = Script.substr(1, Script.length() - 2);
std::string pub = R"(-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDafyp7gGautPZZ3I3IlYLf8Qyw
xGigvg0rkmXPaP34C6sHi//GLuYjwM6AUJTtbfo0pCNmLqBbCiiuzkBXEqM+GeS2
+7zhu1yeEXv+i9iySFPbYydy851uVip7oqsbNM4iGYpS5ERND9XYuhSGUFI5p9ik
Nsvz+z7r4iT2rd8vrwIDAQAB
-----END PUBLIC KEY-----)";
std::string KeyString = rsaDecrypt(base64_decode(Key), pub);
std::vector<std::string> BaseDataBuffer;
Tool::Split(Script, BaseDataBuffer, "$$$$$");
size_t Ds = BaseDataBuffer.size();
int RealKey[20] = {0};
// 转换字符串到数组
for (size_t i = 0; i < KeyString.length() && i < 20; ++i) {
RealKey[i] = KeyString[i] - '0'; // 将字符转换为对应的数字
}
std::vector<std::string> BaseData;
for (size_t i = 0; i < (Ds - 1); i++) {
std::string str = BaseDataBuffer[i];
if (i % 2 != 0) {
// 得到有多少个逗号
std::vector<std::string> Data;
Tool::Split(str, Data, ", ");
size_t DDs = Data.size();
char *nutstr = new char[DDs + 1];
for (size_t s = 0; s < DDs; s++) {
nutstr[s] = char(atoi(Data[s].c_str()));
}
Cutecode(nutstr, RealKey, DDs, 20); // 解密
nutstr[DDs] = '\0';
std::string RealStr(nutstr, DDs + 1);
delete[] nutstr;
BaseData.push_back(RealStr);
} else {
BaseData.push_back(str);
}
}
for (size_t i = 0; i < BaseData.size(); i += 2) {
std::string Path = BaseData[i];
std::string Script = BaseData[i + 1];
// std::cout << Path << std::endl;
if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar *)(Script.c_str()),
Script.length(), (SQChar *)(Path.c_str()),
true))) {
sq_pushroottable(v);
sq_call(v, 1, 1, 1);
sq_pop(v, 1);
}
}
}