DecryptionHelper.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Security.Cryptography;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace WechatPCMsgBakTool.Helpers
  9. {
  10. public class DecryptionHelper
  11. {
  12. const int IV_SIZE = 16;
  13. const int HMAC_SHA1_SIZE = 20;
  14. const int KEY_SIZE = 32;
  15. const int AES_BLOCK_SIZE = 16;
  16. const int DEFAULT_ITER = 64000;
  17. const int DEFAULT_PAGESIZE = 4096; //4048数据 + 16IV + 20 HMAC + 12
  18. const string SQLITE_HEADER = "SQLite format 3";
  19. public static byte[]? GetWechatKey()
  20. {
  21. Process? process = ProcessHelper.GetProcess("WeChat");
  22. if (process == null)
  23. {
  24. return null;
  25. }
  26. ProcessModule? module = ProcessHelper.FindProcessModule(process.Id, "WeChatWin.dll");
  27. if (module == null)
  28. {
  29. return null;
  30. }
  31. string? version = module.FileVersionInfo.FileVersion;
  32. if (version == null)
  33. {
  34. return null;
  35. }
  36. //这里加的是版本偏移量,兼容不同版本把这个加给改了
  37. long baseAddress = (long)module.BaseAddress + 62031872;
  38. byte[]? bytes = ProcessHelper.ReadMemoryDate(process.Handle, (IntPtr)baseAddress, 8);
  39. if (bytes != null)
  40. {
  41. IntPtr baseAddress2 = (IntPtr)(((long)bytes[7] << 56) + ((long)bytes[6] << 48) + ((long)bytes[5] << 40) + ((long)bytes[4] << 32) + ((long)bytes[3] << 24) + ((long)bytes[2] << 16) + ((long)bytes[1] << 8) + (long)bytes[0]);
  42. byte[]? twoGet = ProcessHelper.ReadMemoryDate(process.Handle, baseAddress2, 32);
  43. if (twoGet != null)
  44. {
  45. string key = BytesToHex(twoGet);
  46. return twoGet;
  47. }
  48. }
  49. return null;
  50. }
  51. public static byte[] DecryptDB(byte[] db_file_bytes, byte[] password_bytes)
  52. {
  53. //数据库头16字节是盐值
  54. var salt = db_file_bytes.Take(16).ToArray();
  55. //HMAC验证时用的盐值需要亦或0x3a
  56. byte[] hmac_salt = new byte[16];
  57. for (int i = 0; i < salt.Length; i++)
  58. {
  59. hmac_salt[i] = (byte)(salt[i] ^ 0x3a);
  60. }
  61. //计算保留段长度
  62. int reserved = IV_SIZE;
  63. reserved += HMAC_SHA1_SIZE;
  64. reserved = ((reserved % AES_BLOCK_SIZE) == 0) ? reserved : ((reserved / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
  65. //密钥扩展,分别对应AES解密密钥和HMAC验证密钥
  66. byte[] key = new byte[KEY_SIZE];
  67. byte[] hmac_key = new byte[KEY_SIZE];
  68. OpenSSLInterop.PKCS5_PBKDF2_HMAC_SHA1(password_bytes, password_bytes.Length, salt, salt.Length, DEFAULT_ITER, key.Length, key);
  69. OpenSSLInterop.PKCS5_PBKDF2_HMAC_SHA1(key, key.Length, hmac_salt, hmac_salt.Length, 2, hmac_key.Length, hmac_key);
  70. int page_no = 0;
  71. int offset = 16;
  72. Console.WriteLine("开始解密...");
  73. var hmac_sha1 = HMAC.Create("HMACSHA1");
  74. hmac_sha1!.Key = hmac_key;
  75. List<byte> decrypted_file_bytes = new List<byte>();
  76. while (page_no < db_file_bytes.Length / DEFAULT_PAGESIZE)
  77. {
  78. byte[] decryped_page_bytes = new byte[DEFAULT_PAGESIZE];
  79. byte[] going_to_hashed = new byte[DEFAULT_PAGESIZE - reserved - offset + IV_SIZE + 4];
  80. db_file_bytes.Skip((page_no * DEFAULT_PAGESIZE) + offset).Take(DEFAULT_PAGESIZE - reserved - offset + IV_SIZE).ToArray().CopyTo(going_to_hashed, 0);
  81. var page_bytes = BitConverter.GetBytes(page_no + 1);
  82. page_bytes.CopyTo(going_to_hashed, DEFAULT_PAGESIZE - reserved - offset + IV_SIZE);
  83. //计算分页的Hash
  84. var hash_mac_compute = hmac_sha1.ComputeHash(going_to_hashed, 0, going_to_hashed.Count());
  85. //取出分页中存储的Hash
  86. var hash_mac_cached = db_file_bytes.Skip((page_no * DEFAULT_PAGESIZE) + DEFAULT_PAGESIZE - reserved + IV_SIZE).Take(hash_mac_compute.Length).ToArray();
  87. //对比两个Hash
  88. if (!hash_mac_compute.SequenceEqual(hash_mac_cached))
  89. {
  90. Console.WriteLine("Hash错误...");
  91. return decrypted_file_bytes.ToArray();
  92. }
  93. else
  94. {
  95. Console.WriteLine($"解密第[{page_no + 1}]页");
  96. if (page_no == 0)
  97. {
  98. var header_bytes = Encoding.ASCII.GetBytes(SQLITE_HEADER);
  99. header_bytes.CopyTo(decryped_page_bytes, 0);
  100. }
  101. var encrypted_content = db_file_bytes.Skip((page_no * DEFAULT_PAGESIZE) + offset).Take(DEFAULT_PAGESIZE - reserved - offset).ToArray();
  102. var iv = db_file_bytes.Skip((page_no * DEFAULT_PAGESIZE) + (DEFAULT_PAGESIZE - reserved)).Take(16).ToArray();
  103. var decrypted_content = DecryptionHelper.AESDecrypt(encrypted_content, key, iv);
  104. decrypted_content.CopyTo(decryped_page_bytes, offset);
  105. var reserved_bytes = db_file_bytes.Skip((page_no * DEFAULT_PAGESIZE) + DEFAULT_PAGESIZE - reserved).Take(reserved).ToArray();
  106. reserved_bytes.CopyTo(decryped_page_bytes, DEFAULT_PAGESIZE - reserved);
  107. }
  108. page_no++;
  109. offset = 0;
  110. foreach (var item in decryped_page_bytes)
  111. {
  112. decrypted_file_bytes.Add(item);
  113. }
  114. }
  115. return decrypted_file_bytes.ToArray();
  116. }
  117. public static byte[] AESDecrypt(byte[] content, byte[] key, byte[] iv)
  118. {
  119. Aes rijndaelCipher = Aes.Create();
  120. rijndaelCipher.Mode = CipherMode.CBC;
  121. rijndaelCipher.Padding = PaddingMode.None;
  122. rijndaelCipher.KeySize = 256;
  123. rijndaelCipher.BlockSize = 128;
  124. rijndaelCipher.Key = key;
  125. rijndaelCipher.IV = iv;
  126. ICryptoTransform transform = rijndaelCipher.CreateDecryptor();
  127. byte[] plain_bytes = transform.TransformFinalBlock(content, 0, content.Length);
  128. return plain_bytes;
  129. }
  130. private static string BytesToHex(byte[] bytes)
  131. {
  132. return BitConverter.ToString(bytes, 0).Replace("-", string.Empty).ToLower().ToUpper();
  133. }
  134. }
  135. }