Files
wx-win-agent/node-agent/WxAgent.Core/WcdbConfigCipher.cs
T

52 lines
2.2 KiB
C#

using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace WxAgent.Core;
/// <summary>Decodes bounded Windows WCDB Config.Cipher blobs. Candidates still require page HMAC verification.</summary>
public static class WcdbConfigCipher
{
public const string TypeName = "com.Tencent.WCDB.Config.Cipher";
public const int MaximumBlobLength = 1024;
// Windows 4.1.x storage layout, independently verified against each target DB before use.
private static readonly byte[] Mask = Convert.FromHexString("d2c7442458020000004889442450488b450048844c2448488944254048584c24");
public static IReadOnlyList<WcdbKeyCandidate> Decode(ReadOnlySpan<byte> blob)
{
if (blob.Length is 0 or > MaximumBlobLength) return [];
var decoded = blob.ToArray();
try
{
for (var i = 0; i < decoded.Length; i++) decoded[i] ^= Mask[i % Mask.Length];
var candidates = new HashSet<WcdbKeyCandidate>();
foreach (Match match in Regex.Matches(Encoding.ASCII.GetString(decoded), "[xX]'([0-9a-fA-F]{64,192})'", RegexOptions.None, TimeSpan.FromSeconds(1)))
{
var run = match.Groups[1].Value.ToLowerInvariant();
var starts = new HashSet<int> { 0 };
if (run.Length > 96)
{
for (var start = 0; start + 64 <= run.Length; start += 32) starts.Add(start);
starts.Add(run.Length - 64);
}
foreach (var start in starts)
{
var keyHex = run.Substring(start, 64);
var key = Convert.FromHexString(keyHex);
var distinct = key.Distinct().Count();
var allZero = key.All(byteValue => byteValue == 0);
var allOne = key.All(byteValue => byteValue == 0xff);
if (distinct < 15 || allZero || allOne) continue;
candidates.Add(new WcdbKeyCandidate(keyHex,
start + 96 <= run.Length ? run.Substring(start + 64, 32) : null));
}
}
return candidates.ToArray();
}
finally
{
CryptographicOperations.ZeroMemory(decoded);
}
}
}