TCG decrypt [skip ci]

This commit is contained in:
DismissedLight
2023-11-23 23:34:31 +08:00
parent 74ac738236
commit e0f967341e
2 changed files with 219 additions and 3 deletions

View File

@@ -7,9 +7,7 @@ namespace Snap.Hutao.Test.BaseClassLibrary;
[TestClass]
public class JsonSerializeTest
{
private TestContext? testContext;
public TestContext? TestContext { get => testContext; set => testContext = value; }
public TestContext? TestContext { get; set; }
private readonly JsonSerializerOptions AlowStringNumberOptions = new()
{

View File

@@ -0,0 +1,218 @@
using System;
using System.Buffers.Binary;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
namespace Snap.Hutao.Test.IncomingFeature;
[TestClass]
public sealed class GeniusInvokationDecoding
{
public TestContext? TestContext { get; set; }
/// <summary>
/// https://www.bilibili.com/video/av278125720
/// </summary>
[TestMethod]
public unsafe void GeniusInvokationShareCodeDecoding()
{
// 51 bytes obfuscated data
byte[] bytes = Convert.FromBase64String("BCHBwxQNAYERyVANCJGBynkOCZER2pgOCrFx8poQChGR9bYQDEGB9rkQDFKRD7oRDeEB");
// ---------------------------------------------
// | Data | Caesar Cipher Key |
// |----------|-------------------|
// | 50 Bytes | 1 Byte |
// ---------------------------------------------
// Data:
// 00000100 00100001 11000001 11000011 00010100
// 00001101 00000001 10000001 00010001 11001001
// 01010000 00001101 00001000 10010001 10000001
// 11001010 01111001 00001110 00001001 10010001
// 00010001 11011010 10011000 00001110 00001010
// 10110001 01110001 11110010 10011010 00010000
// 00001010 00010001 10010001 11110101 10110110
// 00010000 00001100 01000001 10000001 11110110
// 10111001 00010000 00001100 01010010 10010001
// 00001111 10111010 00010001 00001101 11100001
// ---------------------------------------------
// Caesar Cipher Key:
// 00000001
// ---------------------------------------------
fixed (byte* ptr = bytes)
{
// Reinterpret as 50 byte actual data and 1 deobfuscate key byte
EncryptedDataAndKey* data = (EncryptedDataAndKey*)ptr;
byte* dataPtr = data->Data;
// ----------------------------------------------------------
// | First | Second | Padding |
// |-----------|----------|---------|
// | 25 Bytes | 25 Bytes | 1 Byte |
// ----------------------------------------------------------
// We are doing two things here:
// 1. Retrieve actual data by subtracting key
// 2. Store data into two halves by alternating between them
// ----------------------------------------------------------
// What we will get after this step:
// ----------------------------------------------------------
// First:
// 00000011 11000000 00010011 00000000 00010000
// 01001111 00000111 10000000 01111000 00001000
// 00010000 10010111 00001001 01110000 10011001
// 00001001 10010000 10110101 00001011 10000000
// 10111000 00001011 10010000 10111001 00001100
// ----------------------------------------------------------
// Second:
// 00100000 11000010 00001100 10000000 11001000
// 00001100 10010000 11001001 00001101 10010000
// 11011001 00001101 10110000 11110001 00001111
// 00010000 11110100 00001111 01000000 11110101
// 00001111 01010001 00001110 00010000 11100000
// ----------------------------------------------------------
RearrangeBuffer rearranged = default;
byte* pFirst = rearranged.First;
byte* pSecond = rearranged.Second;
for (int i = 0; i < 50; i++)
{
// Determine which half are we going to insert
byte** ppTarget = i % 2 == 0 ? &pFirst : &pSecond;
// (actual data = data - key) and store it directly to the target half
**ppTarget = unchecked((byte)(dataPtr[i] - data->Key));
(*ppTarget)++;
}
// Prepare decoded data result storage
DecryptedData decoded = default;
ushort* pDecoded = decoded.Data;
// ----------------------------------------------------------
// | Data |
// |----------| x 17 = 51 Bytes
// | 3 Bytes |
// ----------------------------------------------------------
// Grouping each 3 bytes and read out as 2 ushort with
// 12 bits each (Big Endian)
// ----------------------------------------------------------
// 00000011 1100·0000 00010011|
// 00000000 0001·0000 01001111|
// 00000111 1000·0000 01111000|
// 00001000 0001·0000 10010111|
// 00001001 0111·0000 10011001|
// 00001001 1001·0000 10110101|
// 00001011 1000·0000 10111000|
// 00001011 1001·0000 10111001|
// 00001100 0010·0000 11000010|
// 00001100 1000·0000 11001000|
// 00001100 1001·0000 11001001|
// 00001101 1001·0000 11011001|
// 00001101 1011·0000 11110001|
// 00001111 0001·0000 11110100|
// 00001111 0100·0000 11110101|
// 00001111 0101·0001 00001110|
// 00010000 1110·0000 -padding|[padding32]
// ----------------------------------------------------------
// reinterpret as DecodeGroupingHelper for each 3 bytes
DecodeGroupingHelper* pGroup = (DecodeGroupingHelper*)&rearranged;
for (int i = 0; i < 17; i++)
{
(ushort first, ushort second) = pGroup->GetData();
*pDecoded = first;
*(pDecoded + 1) = second;
pDecoded += 2;
pGroup++;
}
// Now we get
// 60, 19, 1,
// 79,120,120,
// 129,151,151,
// 153,153,181,
// 184,184,185,
// 185,194,194,
// 200,200,201,
// 201,217,217,
// 219,241,241,
// 244,244,245,
// 245,270,270,
StringBuilder stringBuilder = new();
for (int i = 0; i < 33; i++)
{
stringBuilder
.AppendFormat(CultureInfo.InvariantCulture, "{0,3}", decoded.Data[i])
.Append(',');
if (i % 11 == 10)
{
stringBuilder.Append('\n');
}
}
TestContext?.WriteLine(stringBuilder.ToString(0, stringBuilder.Length - 1));
ushort[] resultArray = new ushort[33];
Span<ushort> result = new((ushort*)&decoded, 33);
result.CopyTo(resultArray);
ushort[] testKnownResult =
#if NET8_0_OR_GREATER
[
060, 019, 001, 079, 120, 120, 129, 151, 151, 153, 153,
181, 184, 184, 185, 185, 194, 194, 200, 200, 201, 201,
217, 217, 219, 241, 241, 244, 244, 245, 245, 270, 270,
];
#else
{
060, 019, 001, 079, 120, 120, 129, 151, 151, 153, 153,
181, 184, 184, 185, 185, 194, 194, 200, 200, 201, 201,
217, 217, 219, 241, 241, 244, 244, 245, 245, 270, 270,
};
#endif
CollectionAssert.AreEqual(resultArray, testKnownResult);
}
}
private struct EncryptedDataAndKey
{
public unsafe fixed byte Data[50];
public byte Key;
}
private struct RearrangeBuffer
{
public unsafe fixed byte First[25];
public unsafe fixed byte Second[25];
// Make it 51 bytes
// allow to be group as 17 DecodeGroupingHelper later
public byte padding;
// prevent accidently int32 cast access violation
public byte paddingTo32;
}
private struct DecodeGroupingHelper
{
public unsafe fixed byte Data[3];
public unsafe (ushort First, ushort Second) GetData()
{
fixed (byte* ptr = Data)
{
uint value = BinaryPrimitives.ReverseEndianness((*(uint*)ptr) & 0x00FFFFFF) >> 8; // keep low 24 bits only
return ((ushort)((value >> 12) & 0x0FFF), (ushort)(value & 0x0FFF));
}
}
}
private struct DecryptedData
{
public unsafe fixed ushort Data[33];
}
}