498 lines
15 KiB
C++
498 lines
15 KiB
C++
#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);
|
||
}
|
||
}
|
||
} |