mirror of
https://github.com/Megghy/MegghysAPI.git
synced 2025-12-06 22:26:56 +08:00
fix: 修复文件列表为空时的返回值处理并添加图像处理相关依赖
This commit is contained in:
565
Modules/GeetestCrypto.cs
Normal file
565
Modules/GeetestCrypto.cs
Normal file
@@ -0,0 +1,565 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MegghysAPI.Modules
|
||||
{
|
||||
internal static class GeetestCrypto
|
||||
{
|
||||
private const string RsaN = "00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B59706592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81";
|
||||
private const string RsaE = "010001";
|
||||
private const string AesKey = "1234567890123456";
|
||||
private static readonly byte[] AesIv =
|
||||
{
|
||||
48, 48, 48, 48, 48, 48, 48, 48,
|
||||
48, 48, 48, 48, 48, 48, 48, 48
|
||||
};
|
||||
|
||||
private const string Base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()";
|
||||
private const int Mask1 = 7274496;
|
||||
private const int Mask2 = 9483264;
|
||||
private const int Mask3 = 19220;
|
||||
private const int Mask4 = 235;
|
||||
|
||||
public static string ClickCalculate(string key, string gt, string challenge)
|
||||
{
|
||||
if (string.IsNullOrEmpty(challenge) || challenge.Length < 2)
|
||||
{
|
||||
throw new ArgumentException("challenge 长度必须至少为 2", nameof(challenge));
|
||||
}
|
||||
|
||||
var passTime = (int)(Random.Shared.NextDouble() * 700.0 + 1300.0);
|
||||
var challengePrefix = challenge[..^2];
|
||||
var md5Source = string.Concat(gt, challengePrefix, passTime.ToString());
|
||||
var rpBytes = MD5.HashData(Encoding.UTF8.GetBytes(md5Source));
|
||||
var rp = Convert.ToHexString(rpBytes).ToLowerInvariant();
|
||||
|
||||
var payload = new Dictionary<string, object?>
|
||||
{
|
||||
["lang"] = "zh-cn",
|
||||
["passtime"] = passTime,
|
||||
["a"] = key,
|
||||
["tt"] = string.Empty,
|
||||
["ep"] = BuildEpObject(),
|
||||
["h9s9"] = "1816378497",
|
||||
["rp"] = rp,
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(payload);
|
||||
return Encrypt(json);
|
||||
}
|
||||
|
||||
public static string SlideCalculate(int distance, string gt, string challenge, ReadOnlySpan<byte> c, string s)
|
||||
{
|
||||
if (distance < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(distance), "distance 必须大于等于 0");
|
||||
}
|
||||
if (string.IsNullOrEmpty(challenge) || challenge.Length < 2)
|
||||
{
|
||||
throw new ArgumentException("challenge 长度必须至少为 2", nameof(challenge));
|
||||
}
|
||||
|
||||
var track = GetSlideTrack(distance);
|
||||
var passTime = track[track.Count - 1][2];
|
||||
var encryptedTrack = TrackEncrypt(track);
|
||||
var aa = FinalEncrypt(encryptedTrack, c, s);
|
||||
var userResponse = UserResponse(distance, challenge);
|
||||
|
||||
var challengePrefix = challenge[..^2];
|
||||
var md5Source = string.Concat(gt, challengePrefix, passTime.ToString());
|
||||
var rpBytes = MD5.HashData(Encoding.UTF8.GetBytes(md5Source));
|
||||
var rp = Convert.ToHexString(rpBytes).ToLowerInvariant();
|
||||
|
||||
var payload = new Dictionary<string, object?>
|
||||
{
|
||||
["lang"] = "zh-cn",
|
||||
["userresponse"] = userResponse,
|
||||
["passtime"] = passTime,
|
||||
["imgload"] = NextIntInclusive(100, 200),
|
||||
["aa"] = aa,
|
||||
["ep"] = BuildEpObject(),
|
||||
["rp"] = rp,
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(payload);
|
||||
return Encrypt(json);
|
||||
}
|
||||
|
||||
private static Dictionary<string, object?> BuildEpObject()
|
||||
{
|
||||
return new Dictionary<string, object?>
|
||||
{
|
||||
["v"] = "9.1.8-bfget5",
|
||||
["$_E_"] = false,
|
||||
["me"] = true,
|
||||
["ven"] = "Google Inc. (Intel)",
|
||||
["ren"] = "ANGLE (Intel, Intel(R) HD Graphics 520 Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
["fp"] = new object[] { "move", 483, 149, 1702019849214L, "pointermove" },
|
||||
["lp"] = new object[] { "up", 657, 100, 1702019852230L, "pointerup" },
|
||||
["em"] = new Dictionary<string, object?>
|
||||
{
|
||||
["ph"] = 0,
|
||||
["cp"] = 0,
|
||||
["ek"] = "11",
|
||||
["wd"] = 1,
|
||||
["nt"] = 0,
|
||||
["si"] = 0,
|
||||
["sc"] = 0,
|
||||
},
|
||||
["tm"] = new Dictionary<string, object?>
|
||||
{
|
||||
["a"] = 1702019845759L,
|
||||
["b"] = 1702019845951L,
|
||||
["c"] = 1702019845951L,
|
||||
["d"] = 0,
|
||||
["e"] = 0,
|
||||
["f"] = 1702019845763L,
|
||||
["g"] = 1702019845785L,
|
||||
["h"] = 1702019845785L,
|
||||
["i"] = 1702019845785L,
|
||||
["j"] = 1702019845845L,
|
||||
["k"] = 1702019845812L,
|
||||
["l"] = 1702019845845L,
|
||||
["m"] = 1702019845942L,
|
||||
["n"] = 1702019845946L,
|
||||
["o"] = 1702019845954L,
|
||||
["p"] = 1702019846282L,
|
||||
["q"] = 1702019846282L,
|
||||
["r"] = 1702019846287L,
|
||||
["s"] = 1702019846288L,
|
||||
["t"] = 1702019846288L,
|
||||
["u"] = 1702019846288L,
|
||||
},
|
||||
["dnf"] = "dnf",
|
||||
["by"] = 0,
|
||||
};
|
||||
}
|
||||
|
||||
private static string Encrypt(string json)
|
||||
{
|
||||
var rsaEncryptedKey = RsaEncrypt(AesKey);
|
||||
var aesEncryptedData = AesEncrypt(json);
|
||||
var base64Part = CustomBase64(aesEncryptedData);
|
||||
return base64Part + rsaEncryptedKey;
|
||||
}
|
||||
|
||||
private static byte[] AesEncrypt(string data)
|
||||
{
|
||||
using var aes = Aes.Create();
|
||||
aes.Mode = CipherMode.CBC;
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
aes.Key = Encoding.UTF8.GetBytes(AesKey);
|
||||
aes.IV = AesIv;
|
||||
|
||||
using var encryptor = aes.CreateEncryptor();
|
||||
var inputBytes = Encoding.UTF8.GetBytes(data);
|
||||
return encryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
|
||||
}
|
||||
|
||||
private static string RsaEncrypt(string data)
|
||||
{
|
||||
var modulus = TrimLeadingZero(HexToBytes(RsaN));
|
||||
var exponent = TrimLeadingZero(HexToBytes(RsaE));
|
||||
|
||||
using var rsa = RSA.Create();
|
||||
rsa.ImportParameters(new RSAParameters
|
||||
{
|
||||
Modulus = modulus,
|
||||
Exponent = exponent
|
||||
});
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(data);
|
||||
var encrypted = rsa.Encrypt(bytes, RSAEncryptionPadding.Pkcs1);
|
||||
return Convert.ToHexString(encrypted).ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static byte[] TrimLeadingZero(byte[] value)
|
||||
{
|
||||
if (value.Length > 0 && value[0] == 0)
|
||||
{
|
||||
var trimmed = new byte[value.Length - 1];
|
||||
Buffer.BlockCopy(value, 1, trimmed, 0, trimmed.Length);
|
||||
return trimmed;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string CustomBase64(ReadOnlySpan<byte> input)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var padding = string.Empty;
|
||||
var len = input.Length;
|
||||
var ptr = 0;
|
||||
|
||||
while (ptr < len)
|
||||
{
|
||||
if (ptr + 2 < len)
|
||||
{
|
||||
var c = (input[ptr] << 16) + (input[ptr + 1] << 8) + input[ptr + 2];
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask1)]);
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask2)]);
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask3)]);
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask4)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var remainder = len % 3;
|
||||
if (remainder == 2)
|
||||
{
|
||||
var c = (input[ptr] << 16) + (input[ptr + 1] << 8);
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask1)]);
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask2)]);
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask3)]);
|
||||
padding = ".";
|
||||
}
|
||||
else if (remainder == 1)
|
||||
{
|
||||
var c = input[ptr] << 16;
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask1)]);
|
||||
sb.Append(Base64Table[GetIntByMask(c, Mask2)]);
|
||||
padding = "..";
|
||||
}
|
||||
}
|
||||
|
||||
ptr += 3;
|
||||
}
|
||||
|
||||
sb.Append(padding);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static int GetIntByMask(int value, int mask)
|
||||
{
|
||||
var result = 0;
|
||||
for (var bit = 23; bit >= 0; bit--)
|
||||
{
|
||||
if (ChooseBit(mask, bit) == 1)
|
||||
{
|
||||
result = (result << 1) | ChooseBit(value, bit);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int ChooseBit(int value, int bit)
|
||||
{
|
||||
return (value >> bit) & 1;
|
||||
}
|
||||
|
||||
private static byte[] HexToBytes(string hex)
|
||||
{
|
||||
var cleaned = hex.Replace("\n", string.Empty)
|
||||
.Replace("\r", string.Empty)
|
||||
.Replace(" ", string.Empty);
|
||||
if (cleaned.Length % 2 != 0)
|
||||
{
|
||||
throw new ArgumentException("十六进制字符串长度必须为偶数", nameof(hex));
|
||||
}
|
||||
|
||||
var bytes = new byte[cleaned.Length / 2];
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
bytes[i] = Convert.ToByte(cleaned.Substring(i * 2, 2), 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static int NextIntInclusive(int minValue, int maxValue)
|
||||
{
|
||||
if (minValue > maxValue)
|
||||
{
|
||||
throw new ArgumentException("minValue 必须小于等于 maxValue");
|
||||
}
|
||||
return Random.Shared.Next(minValue, maxValue + 1);
|
||||
}
|
||||
|
||||
private static List<List<int>> GetSlideTrack(int distance)
|
||||
{
|
||||
if (distance < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(distance));
|
||||
}
|
||||
|
||||
var slideTrack = new List<List<int>>();
|
||||
var x1 = NextIntInclusive(-50, -10);
|
||||
var y1 = NextIntInclusive(-50, -10);
|
||||
slideTrack.Add(new List<int> { x1, y1, 0 });
|
||||
slideTrack.Add(new List<int> { 0, 0, 0 });
|
||||
|
||||
var count = 30 + distance / 2;
|
||||
var t = NextIntInclusive(50, 100);
|
||||
var currentX = 0;
|
||||
var currentY = 0;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var sep = i / (double)count;
|
||||
double x;
|
||||
if (Math.Abs(sep - 1.0) < double.Epsilon)
|
||||
{
|
||||
x = distance;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = (1.0 - Math.Pow(2.0, -10.0 * sep)) * distance;
|
||||
}
|
||||
|
||||
var xRounded = (int)Math.Round(x);
|
||||
t += NextIntInclusive(10, 20);
|
||||
|
||||
if (xRounded == currentX)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
slideTrack.Add(new List<int> { xRounded, currentY, t });
|
||||
currentX = xRounded;
|
||||
}
|
||||
|
||||
var last = slideTrack[slideTrack.Count - 1];
|
||||
slideTrack.Add(new List<int>(last));
|
||||
|
||||
return slideTrack;
|
||||
}
|
||||
|
||||
private static string TrackEncrypt(List<List<int>> track)
|
||||
{
|
||||
static List<List<int>> ProcessTrack(List<List<int>> trackData)
|
||||
{
|
||||
var result = new List<List<int>>();
|
||||
var o = 0;
|
||||
|
||||
for (var s = 0; s < trackData.Count - 1; s++)
|
||||
{
|
||||
var e = trackData[s + 1][0] - trackData[s][0];
|
||||
var n = trackData[s + 1][1] - trackData[s][1];
|
||||
var r = trackData[s + 1][2] - trackData[s][2];
|
||||
|
||||
if (e == 0 && n == 0 && r == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e == 0 && n == 0)
|
||||
{
|
||||
o += r;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(new List<int> { e, n, r + o });
|
||||
o = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (o != 0 && result.Count > 0)
|
||||
{
|
||||
var lastItem = result[result.Count - 1];
|
||||
result.Add(new List<int> { lastItem[0], lastItem[1], o });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static string EncodeValue(int t)
|
||||
{
|
||||
const string chars = "()*,-./0123456789:?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqr";
|
||||
var n = chars.Length;
|
||||
var r = new StringBuilder();
|
||||
|
||||
var i = Math.Abs(t);
|
||||
var oVal = i / n;
|
||||
var o = oVal >= n ? n - 1 : oVal;
|
||||
if (o >= chars.Length)
|
||||
{
|
||||
o = chars.Length - 1;
|
||||
}
|
||||
|
||||
if (o > 0)
|
||||
{
|
||||
r.Append(chars[o]);
|
||||
}
|
||||
|
||||
var s = new StringBuilder();
|
||||
if (t < 0)
|
||||
{
|
||||
s.Append('!');
|
||||
}
|
||||
if (r.Length > 0)
|
||||
{
|
||||
s.Append('$');
|
||||
}
|
||||
|
||||
s.Append(r);
|
||||
s.Append(chars[i % n]);
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
static char? EncodePair(IReadOnlyList<int> values)
|
||||
{
|
||||
var pairs = new List<int[]>
|
||||
{
|
||||
new[] { 1, 0 },
|
||||
new[] { 2, 0 },
|
||||
new[] { 1, -1 },
|
||||
new[] { 1, 1 },
|
||||
new[] { 0, 1 },
|
||||
new[] { 0, -1 },
|
||||
new[] { 3, 0 },
|
||||
new[] { 2, -1 },
|
||||
new[] { 2, 1 },
|
||||
};
|
||||
const string chars = "stuvwxyz~";
|
||||
|
||||
for (var idx = 0; idx < pairs.Count; idx++)
|
||||
{
|
||||
var pair = pairs[idx];
|
||||
if (values[0] == pair[0] && values[1] == pair[1])
|
||||
{
|
||||
return chars[idx];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static void ProcessElement(IReadOnlyList<int> item, StringBuilder r, StringBuilder i, StringBuilder o)
|
||||
{
|
||||
var encodedPair = EncodePair(item);
|
||||
if (encodedPair.HasValue)
|
||||
{
|
||||
i.Append(encodedPair.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
r.Append(EncodeValue(item[0]));
|
||||
i.Append(EncodeValue(item[1]));
|
||||
}
|
||||
o.Append(EncodeValue(item[2]));
|
||||
}
|
||||
|
||||
var processed = ProcessTrack(track);
|
||||
var rBuilder = new StringBuilder();
|
||||
var iBuilder = new StringBuilder();
|
||||
var oBuilder = new StringBuilder();
|
||||
|
||||
foreach (var item in processed)
|
||||
{
|
||||
ProcessElement(item, rBuilder, iBuilder, oBuilder);
|
||||
}
|
||||
|
||||
return string.Concat(rBuilder, "!!", iBuilder, "!!", oBuilder);
|
||||
}
|
||||
|
||||
private static string FinalEncrypt(string t, ReadOnlySpan<byte> e, string n)
|
||||
{
|
||||
if (e.Length < 5 || string.IsNullOrEmpty(n))
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
var s = e[0];
|
||||
var a = e[2];
|
||||
var m = e[4];
|
||||
var originalLength = t.Length;
|
||||
var builder = new StringBuilder(t);
|
||||
|
||||
var index = 0;
|
||||
while (index <= n.Length - 2)
|
||||
{
|
||||
var hexPair = n.Substring(index, 2);
|
||||
index += 2;
|
||||
var c = Convert.ToByte(hexPair, 16);
|
||||
var u = (char)c;
|
||||
|
||||
var ll = ((ulong)s * c * c + (ulong)a * c + m) % (ulong)originalLength;
|
||||
builder.Insert((int)ll, u);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string UserResponse(int key, string challenge)
|
||||
{
|
||||
if (challenge.Length < 2)
|
||||
{
|
||||
throw new ArgumentException("challenge 长度必须至少为 2", nameof(challenge));
|
||||
}
|
||||
|
||||
var chars = challenge.ToCharArray();
|
||||
var lastTwo = new[] { chars[chars.Length - 2], chars[chars.Length - 1] };
|
||||
|
||||
var r = new List<int>();
|
||||
foreach (var c in lastTwo)
|
||||
{
|
||||
var code = (int)c;
|
||||
r.Add(code > 57 ? code - 87 : code - 48);
|
||||
}
|
||||
|
||||
var n = 36 * r[0] + r[1];
|
||||
var a = key + n;
|
||||
|
||||
var underscores = new List<List<char>>
|
||||
{
|
||||
new List<char>(),
|
||||
new List<char>(),
|
||||
new List<char>(),
|
||||
new List<char>(),
|
||||
new List<char>()
|
||||
};
|
||||
var charSet = new HashSet<char>();
|
||||
var idx = 0;
|
||||
|
||||
for (var i = 0; i < chars.Length - 2; i++)
|
||||
{
|
||||
var c = chars[i];
|
||||
if (charSet.Add(c))
|
||||
{
|
||||
underscores[idx].Add(c);
|
||||
idx = (idx + 1) % 5;
|
||||
}
|
||||
}
|
||||
|
||||
var f = a;
|
||||
var d = 4;
|
||||
var result = new StringBuilder();
|
||||
var weights = new List<int> { 1, 2, 5, 10, 50 };
|
||||
|
||||
while (f > 0)
|
||||
{
|
||||
if (d >= weights.Count)
|
||||
{
|
||||
throw new InvalidOperationException("权重数组越界,请检查输入参数有效性");
|
||||
}
|
||||
|
||||
if (f >= weights[d])
|
||||
{
|
||||
if (underscores[d].Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException($"五元组数组 {d} 号位置无可用字符,请确保输入字符串包含足够多的唯一字符");
|
||||
}
|
||||
|
||||
result.Append(underscores[d][0]);
|
||||
f -= weights[d];
|
||||
}
|
||||
else
|
||||
{
|
||||
underscores.RemoveAt(d);
|
||||
weights.RemoveAt(d);
|
||||
if (d > 0)
|
||||
{
|
||||
d -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("权重数组耗尽,无法继续处理");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user