4 Commits

Author SHA1 Message Date
Suxue
affafe324f fix bug 2024-01-09 20:49:30 +08:00
Suxue
ef3c84a724 v0.9.7.0 Releases!
1.优化解密的内存开销,同时修复大于2gb文件解密出错的问题。
2.现在起工作区加载联系人也开始为异步操作了。
3.现在起工作区查看聊天记录改为分页查看了。
4.保底版本支持3.9.8.25。
2024-01-09 20:33:00 +08:00
Suxue
0f70591cdb v0.9.6.4 Release!
1.新增部分消息容错。
2.新增已删除但有消息记录的人员记录导出。
2024-01-06 11:25:14 +08:00
Suxue
029403e8ae v0.9.6.3 Release!
1.新增引用消息支持。
2.词云分词异常时错误提示。
2023-12-23 00:07:43 +08:00
15 changed files with 544 additions and 157 deletions

View File

@@ -12,6 +12,7 @@ using Newtonsoft.Json;
using WechatBakTool.ViewModel;
using System.Security.Policy;
using System.Windows;
using System.Xml.Linq;
namespace WechatBakTool.Export
{
@@ -157,29 +158,88 @@ namespace WechatBakTool.Export
}
HtmlBody += string.Format("<p class=\"content\">{0}</p>", title);
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/recorditem");
try
{
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/recorditem");
if (findNode != null)
{
if (findNode.Count > 0)
{
XmlDocument itemObj = new XmlDocument();
itemObj.LoadXml(findNode[0]!.InnerText);
XmlNodeList? itemNode = itemObj.DocumentElement.SelectNodes("/recordinfo/datalist/dataitem");
if (itemNode.Count > 0)
{
foreach (XmlNode node in itemNode)
{
string nodeMsg;
string name = node["sourcename"].InnerText;
if (node.Attributes["datatype"].InnerText == "1")
nodeMsg = node["datadesc1"].InnerText;
else if (node.Attributes["datatype"].InnerText == "2")
nodeMsg = "不支持的消息";
else
nodeMsg = node["datatitle"].InnerText;
HtmlBody += string.Format("<p class=\"content\">{0}{1}</p>", name, nodeMsg);
}
}
}
}
}
catch
{
HtmlBody += string.Format("<p class=\"content\">{0}</p>", "解析异常");
}
}
}
}
}
else if (msg.SubType == 57)
{
using (var decoder = LZ4Decoder.Create(true, 64))
{
byte[] target = new byte[10240];
int res = 0;
if (msg.CompressContent != null)
res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
byte[] data = target.Skip(0).Take(res).ToArray();
string xml = Encoding.UTF8.GetString(data);
if (!string.IsNullOrEmpty(xml))
{
xml = xml.Replace("\n", "");
XmlDocument xmlObj = new XmlDocument();
xmlObj.LoadXml(xml);
if (xmlObj.DocumentElement != null)
{
string title = "";
XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
if (findNode != null)
{
if (findNode.Count > 0)
{
XmlDocument itemObj = new XmlDocument();
itemObj.LoadXml(findNode[0]!.InnerText);
XmlNodeList? itemNode = itemObj.DocumentElement.SelectNodes("/recordinfo/datalist/dataitem");
if (itemNode.Count > 0)
{
foreach (XmlNode node in itemNode)
{
string nodeMsg;
string name = node["sourcename"].InnerText;
if (node.Attributes["datatype"].InnerText == "1")
nodeMsg = node["datadesc1"].InnerText;
else if (node.Attributes["datatype"].InnerText == "2")
nodeMsg = "不支持的消息";
else
nodeMsg = node["datatitle"].InnerText;
HtmlBody += string.Format("<p class=\"content\">{0}{1}</p>", name, nodeMsg);
}
}
title = findNode[0]!.InnerText;
}
}
HtmlBody += string.Format("<p class=\"content\">{0}</p>", title);
XmlNode? type = xmlObj.DocumentElement.SelectSingleNode("/msg/appmsg/refermsg/type");
if(type != null)
{
XmlNode? source = xmlObj.DocumentElement.SelectSingleNode("/msg/appmsg/refermsg/displayname");
XmlNode? text = xmlObj.DocumentElement.SelectSingleNode("/msg/appmsg/refermsg/content");
if(type.InnerText == "1" && source != null && text != null)
{
HtmlBody += string.Format("<p class=\"content\">[引用]{0}:{1}</p>", source.InnerText, text.InnerText);
}
else if(type.InnerText != "1" && source != null && text != null)
{
HtmlBody += string.Format("<p class=\"content\">[引用]{0}:非文本消息类型-{1}</p>", source.InnerText, type);
}
else
{
HtmlBody += string.Format("<p class=\"content\">未知的引用消息</p>");
}
}
}

View File

@@ -19,12 +19,12 @@ namespace WechatBakTool.Helpers
{
public class DecryptionHelper
{
const int IV_SIZE = 16;
const long IV_SIZE = 16;
const int HMAC_SHA1_SIZE = 20;
const int KEY_SIZE = 32;
const int AES_BLOCK_SIZE = 16;
const int DEFAULT_ITER = 64000;
const int DEFAULT_PAGESIZE = 4096; //4048数据 + 16IV + 20 HMAC + 12
const long DEFAULT_PAGESIZE = 4096; //4048数据 + 16IV + 20 HMAC + 12
const string SQLITE_HEADER = "SQLite format 3";
public static byte[]? GetWechatKey(string pid, int find_key_type, string account)
{
@@ -136,41 +136,116 @@ namespace WechatBakTool.Helpers
throw new Exception("搜索不到微信账号,请确认用户名是否正确,如错误请重新新建工作区,务必确认账号是否正确");
}
}
else if (find_key_type == 3)
{
string searchString = "-----BEGIN PUBLIC KEY-----";
}
return null;
}
public static byte[] DecryptDB(byte[] db_file_bytes, byte[] password_bytes)
public static void DecryptDB(string file, string to_file, byte[] password_bytes)
{
//数据库头16字节是盐值
var salt = db_file_bytes.Take(16).ToArray();
byte[] salt_key = new byte[16];
FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
fileStream.Read(salt_key, 0, 16);
//HMAC验证时用的盐值需要亦或0x3a
byte[] hmac_salt = new byte[16];
for (int i = 0; i < salt.Length; i++)
for (int i = 0; i < salt_key.Length; i++)
{
hmac_salt[i] = (byte)(salt[i] ^ 0x3a);
hmac_salt[i] = (byte)(salt_key[i] ^ 0x3a);
}
//计算保留段长度
int reserved = IV_SIZE;
long reserved = IV_SIZE;
reserved += HMAC_SHA1_SIZE;
reserved = ((reserved % AES_BLOCK_SIZE) == 0) ? reserved : ((reserved / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
//密钥扩展分别对应AES解密密钥和HMAC验证密钥
byte[] key = new byte[KEY_SIZE];
byte[] hmac_key = new byte[KEY_SIZE];
OpenSSLInterop.PKCS5_PBKDF2_HMAC_SHA1(password_bytes, password_bytes.Length, salt, salt.Length, DEFAULT_ITER, key.Length, key);
OpenSSLInterop.PKCS5_PBKDF2_HMAC_SHA1(password_bytes, password_bytes.Length, salt_key, salt_key.Length, DEFAULT_ITER, key.Length, key);
OpenSSLInterop.PKCS5_PBKDF2_HMAC_SHA1(key, key.Length, hmac_salt, hmac_salt.Length, 2, hmac_key.Length, hmac_key);
int page_no = 0;
int offset = 16;
long page_no = 0;
long offset = 16;
Console.WriteLine("开始解密...");
var hmac_sha1 = HMAC.Create("HMACSHA1");
hmac_sha1!.Key = hmac_key;
List<byte> decrypted_file_bytes = new List<byte>();
while (page_no < db_file_bytes.Length / DEFAULT_PAGESIZE)
FileStream tofileStream = new FileStream(to_file, FileMode.OpenOrCreate, FileAccess.Write);
using (fileStream)
{
try
{
// 当前分页小于计算分页数
while (page_no < fileStream.Length / DEFAULT_PAGESIZE)
{
// 读内容
byte[] decryped_page_bytes = new byte[DEFAULT_PAGESIZE];
byte[] going_to_hashed = new byte[DEFAULT_PAGESIZE - reserved - offset + IV_SIZE + 4];
fileStream.Seek((page_no * DEFAULT_PAGESIZE) + offset, SeekOrigin.Begin);
fileStream.Read(going_to_hashed, 0, Convert.ToInt32(DEFAULT_PAGESIZE - reserved - offset + IV_SIZE));
// 分页标志
var page_bytes = BitConverter.GetBytes(page_no + 1);
page_bytes = page_bytes.Take(4).ToArray();
page_bytes.CopyTo(going_to_hashed, DEFAULT_PAGESIZE - reserved - offset + IV_SIZE);
var hash_mac_compute = hmac_sha1.ComputeHash(going_to_hashed, 0, going_to_hashed.Length);
// 取分页hash
byte[] hash_mac_cached = new byte[hash_mac_compute.Length];
fileStream.Seek((page_no * DEFAULT_PAGESIZE) + DEFAULT_PAGESIZE - reserved + IV_SIZE, SeekOrigin.Begin);
fileStream.Read(hash_mac_cached, 0, hash_mac_compute.Length);
if (!hash_mac_compute.SequenceEqual(hash_mac_cached) && page_no == 0)
{
Console.WriteLine("Hash错误...");
return;
}
else
{
if (page_no == 0)
{
var header_bytes = Encoding.ASCII.GetBytes(SQLITE_HEADER);
header_bytes.CopyTo(decryped_page_bytes, 0);
}
// 加密内容
byte[] page_content = new byte[DEFAULT_PAGESIZE - reserved - offset];
fileStream.Seek((page_no * DEFAULT_PAGESIZE) + offset, SeekOrigin.Begin);
fileStream.Read(page_content, 0, Convert.ToInt32(DEFAULT_PAGESIZE - reserved - offset));
// iv
byte[] iv = new byte[16];
fileStream.Seek((page_no * DEFAULT_PAGESIZE) + (DEFAULT_PAGESIZE - reserved), SeekOrigin.Begin);
fileStream.Read(iv, 0, 16);
var decrypted_content = AESDecrypt(page_content, key, iv);
decrypted_content.CopyTo(decryped_page_bytes, offset);
// 保留
byte[] reserved_byte = new byte[reserved];
fileStream.Seek((page_no * DEFAULT_PAGESIZE) + DEFAULT_PAGESIZE - reserved, SeekOrigin.Begin);
fileStream.Read(reserved_byte, 0, Convert.ToInt32(reserved));
reserved_byte.CopyTo(decryped_page_bytes, Convert.ToInt32(DEFAULT_PAGESIZE - reserved));
tofileStream.Write(decryped_page_bytes, 0, decryped_page_bytes.Length);
}
page_no++;
offset = 0;
}
}catch(Exception ex)
{
File.AppendAllText("err.log", "page=>" + page_no.ToString() + "\r\n");
File.AppendAllText("err.log", "size=>" + fileStream.Length.ToString() + "\r\n");
File.AppendAllText("err.log", "postion=>" + ((page_no * DEFAULT_PAGESIZE) + offset).ToString() + "\r\n");
File.AppendAllText("err.log", ex.ToString() + "\r\n");
}
}
/*
* 旧版解密
while (page_no < fileStream.Length / DEFAULT_PAGESIZE)
{
byte[] decryped_page_bytes = new byte[DEFAULT_PAGESIZE];
byte[] going_to_hashed = new byte[DEFAULT_PAGESIZE - reserved - offset + IV_SIZE + 4];
@@ -179,7 +254,6 @@ namespace WechatBakTool.Helpers
page_bytes.CopyTo(going_to_hashed, DEFAULT_PAGESIZE - reserved - offset + IV_SIZE);
//计算分页的Hash
var hash_mac_compute = hmac_sha1.ComputeHash(going_to_hashed, 0, going_to_hashed.Length);
//取出分页中存储的Hash
var hash_mac_cached = db_file_bytes.Skip((page_no * DEFAULT_PAGESIZE) + DEFAULT_PAGESIZE - reserved + IV_SIZE).Take(hash_mac_compute.Length).ToArray();
//对比两个Hash
if (!hash_mac_compute.SequenceEqual(hash_mac_cached))
@@ -208,8 +282,9 @@ namespace WechatBakTool.Helpers
{
decrypted_file_bytes.Add(item);
}
}
return decrypted_file_bytes.ToArray();
}*/
tofileStream.Close();
tofileStream.Dispose();
}
public static byte[] AESDecrypt(byte[] content, byte[] key, byte[] iv)
{
@@ -297,16 +372,8 @@ namespace WechatBakTool.Helpers
{
FileInfo info = new FileInfo(file);
viewModel.LabelStatus = "正在解密" + info.Name;
var db_bytes = File.ReadAllBytes(file);
var decrypted_file_bytes = DecryptDB(db_bytes, key);
if (decrypted_file_bytes == null || decrypted_file_bytes.Length == 0)
{
Console.WriteLine("解密后的数组为空");
}
else
{
File.WriteAllBytes(Path.Combine(decPath, info.Name), decrypted_file_bytes);
}
string to_file = Path.Combine(decPath, info.Name);
DecryptDB(file,to_file, key);
}
}
}

View File

@@ -15,7 +15,10 @@ namespace WechatBakTool.Helpers
public static string FromDevicePath(string devicePath)
{
var drive = Array.Find(DriveInfo.GetDrives(), d => devicePath.StartsWith(d.GetDevicePath(), StringComparison.InvariantCultureIgnoreCase));
var drive = Array.Find(
DriveInfo.GetDrives(), d =>
devicePath.StartsWith(d.GetDevicePath() + "\\", StringComparison.InvariantCultureIgnoreCase)
);
return drive != null ?
devicePath.ReplaceFirst(drive.GetDevicePath(), drive.GetDriveLetter()) :
null;

View File

@@ -114,6 +114,8 @@ namespace WechatBakTool.Model
{
[Column("localId")]
public int LocalId { get; set; }
[Column("MsgSequence")]
public int MsgSequence { get; set; }
[Column("Type")]
public int Type { get; set; }
[Column("SubType")]
@@ -158,6 +160,15 @@ namespace WechatBakTool.Model
public string Reserved0 { get; set; } = "";
}
public class WXContactHT
{
public string UserName { get; set; } = "";
public string NickName { get; set; } = "";
public string LastMsg { get; set; } = "";
public int FileCount { get; set; } = 1;
public string AvatarString { get; set; } = "";
public bool Hidden { get; set; } = false;
}
[Table("Contact")]
public class WXContact
{

View File

@@ -39,7 +39,7 @@
</Button>
<Label Margin="210,350,0,0" Content="其他选项:" FontWeight="Bold" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<CheckBox Margin="215,380,0,0" Content="打包资源文件夹(功能规划中)" IsEnabled="False" HorizontalAlignment="Left" VerticalAlignment="Top" />
<CheckBox Margin="215,405,0,0" Content="手动模式(功能规划中)" IsEnabled="False" HorizontalAlignment="Left" VerticalAlignment="Top" />
<CheckBox Name="cb_manual" Checked="cb_manual_Checked" Margin="215,405,0,0" Content="手动模式" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Label Name="lab_status" Content="{Binding LabelStatus}" HorizontalAlignment="Left" Margin="30,450,0,0" VerticalAlignment="Top"/>
</Grid>
</Page>

View File

@@ -97,6 +97,7 @@ namespace WechatBakTool.Pages
private void btn_create_worksapce_Click(object sender, RoutedEventArgs e)
{
ViewModel.IsEnable = false;
Task.Run(() => {
if (ViewModel.KeyType != -1)
{
@@ -147,5 +148,25 @@ namespace WechatBakTool.Pages
ViewModel.IsEnable = true;
});
}
private void cb_manual_Checked(object sender, RoutedEventArgs e)
{
MessageBox.Show("该功能仅限用于网络安全研究用途使用,红队同学请在合规授权下进行相关操作","重要提醒!!!!!!!!!");
if (MessageBox.Show("我确认获取到合规授权,仅用于网络安全用途使用", "信息确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
if (File.Exists("auth.txt"))
{
}
else
{
MessageBox.Show("未完成声明文件,请先确认声明", "错误");
}
}
else
{
cb_manual.IsChecked = false;
}
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using WechatBakTool.Model;
namespace WechatBakTool.Pages
{
public class MsgTemplateSelector : DataTemplateSelector
{
public override DataTemplate? SelectTemplate(object item, DependencyObject container)
{
FrameworkElement? element = container as FrameworkElement;
if (element != null && item != null && item is WXMsg)
{
WXMsg? wxmsg = item as WXMsg;
if (wxmsg == null)
return null;
if (wxmsg.Type == 1)
return
element.FindResource("MsgText") as DataTemplate;
else if (wxmsg.Type == 3)
return
element.FindResource("MsgImage") as DataTemplate;
else
return
element.FindResource("MsgText") as DataTemplate;
}
return null;
}
}
}

View File

@@ -3,11 +3,12 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WechatBakTool.Pages"
xmlns:local="clr-namespace:WechatBakTool.Pages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="720"
Title="Workspace" Background="White">
<Page.Resources>
<local:MsgTemplateSelector x:Key="MsgTemplateSelector"/>
<Style TargetType="ToggleButton" x:Key="ComboxStyleBtn">
<Setter Property="Template">
<Setter.Value>
@@ -164,9 +165,8 @@
</DataTemplate>
<DataTemplate x:Key="MsgImage">
<Grid Margin="0">
<Image Width="40" Height="40" Margin="10" VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Avatar}" />
<Label Margin="60,8,0,0" FontWeight="Bold" VerticalAlignment="Top" HorizontalAlignment="Left" Content="{Binding NickName}" Width="130"/>
<Label Margin="60,25,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="140" Content="{Binding LastMsg}"/>
<Label Margin="60,25,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="140" Content="1111"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="MsgAudio">
@@ -214,7 +214,7 @@
</ListView.Resources>
</ListView>
<Label Content="{Binding WXContact.NickName}" HorizontalAlignment="Left" Margin="258,21,0,0" VerticalAlignment="Top"/>
<ListView x:Name="list_msg" Margin="230,60,0,60" Background="Transparent" BorderThickness="0,1,0,1" BorderBrush="#BB2775b6" ItemTemplate="{DynamicResource MsgText}">
<ListView x:Name="list_msg" Margin="230,60,0,60" Background="Transparent" BorderThickness="0,1,0,1" BorderBrush="#BB2775b6" ItemTemplateSelector="{StaticResource MsgTemplateSelector}" ItemsSource="{Binding WXMsgs}" ScrollViewer.ScrollChanged="list_msg_ScrollChanged" >
</ListView>
<ComboBox Name="cb_export" Width="120" Height="30" Style="{StaticResource ComboBoxStyle}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="30,15" ItemsSource="{Binding ExportItems}" SelectedItem="{Binding SelectExportItem}" DisplayMemberPath="Name" SelectedValuePath="Value" IsEnabled="{Binding SelectContact}" Background="#2775b6" />

View File

@@ -25,6 +25,10 @@ using WordCloudSharp;
using System.Drawing;
using System.Windows.Controls;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using System.Drawing.Imaging;
using System.Threading;
using System.Runtime.CompilerServices;
namespace WechatBakTool.Pages
{
@@ -35,6 +39,9 @@ namespace WechatBakTool.Pages
{
public WXUserReader? UserReader;
private WorkspaceViewModel ViewModel = new WorkspaceViewModel();
private int PageSize = 100;
private int Postion = 0;
private bool Loading = false;
public Workspace()
{
ViewModel.ExportItems = new System.Collections.ObjectModel.ObservableCollection<ExportItem> {
@@ -54,7 +61,11 @@ namespace WechatBakTool.Pages
UserReader = new WXUserReader(config);
if (config.Decrypt)
{
ViewModel.Contacts = UserReader.GetWXContacts();
ViewModel.Contacts = null;
Task.Run(() => {
ViewModel.Contacts = UserReader.GetWXContacts();
});
}
}
}
@@ -70,14 +81,43 @@ namespace WechatBakTool.Pages
private void list_users_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ViewModel.WXMsgs.Clear();
Postion = 0;
ViewModel.ExportCount = "";
ViewModel.WXContact = list_users.SelectedItem as WXContact;
if(ViewModel.WXContact == null || UserReader == null)
{
loadMsg();
if (ViewModel.WXMsgs.Count == 0)
return;
}
private void loadMsg()
{
Loading = true;
ViewModel.WXContact = list_users.SelectedItem as WXContact;
if (ViewModel.WXContact == null || UserReader == null)
return;
List<WXMsg>? list = UserReader.GetWXMsgs(ViewModel.WXContact.UserName, Postion, PageSize);
// Trace.WriteLine(string.Format("{0}->{1}", PageSize, Postion));
if (list == null)
return;
if (list.Count == 0)
return;
foreach (WXMsg w in list)
{
ViewModel.WXMsgs.Add(w);
}
List<WXMsg>? msgs = UserReader.GetWXMsgs(ViewModel.WXContact.UserName);
list_msg.ItemsSource = msgs;
Postion = int.Parse(list.Max(x => x.CreateTime).ToString());
list_msg.ScrollIntoView(list[0]);
Task.Run(() => {
Thread.Sleep(500);
Loading = false;
});
}
private void txt_find_user_TextChanged(object sender, TextChangedEventArgs e)
@@ -89,7 +129,25 @@ namespace WechatBakTool.Pages
if (txt_find_user.Text == "搜索...")
findName = "";
ViewModel.Contacts = UserReader.GetWXContacts(findName);
Task.Run(() =>
{
ViewModel.Contacts = UserReader.GetWXContacts(findName);
// 保底回落搜索已删除人员
if (ViewModel.Contacts.Count == 0)
{
var i = UserReader.GetWXMsgs(txt_find_user.Text);
if (i != null)
{
var g = i.GroupBy(x => x.StrTalker);
ViewModel.Contacts = new System.Collections.ObjectModel.ObservableCollection<WXContact>();
foreach (var x in g)
{
string name = x.Key;
ViewModel.Contacts.Add(new WXContact() { UserName = name, NickName = name });
}
}
}
});
}
private void txt_find_user_GotFocus(object sender, RoutedEventArgs e)
@@ -115,9 +173,9 @@ namespace WechatBakTool.Pages
export.SetMsg(UserReader, ViewModel.WXContact, ViewModel);
export.SetEnd();
export.Save(path);
}catch(Exception ex)
}
catch (Exception ex)
{
File.AppendAllText("1.log", ex.Message);
MessageBox.Show(ex.Message);
}
@@ -164,20 +222,30 @@ namespace WechatBakTool.Pages
var jieba = new JiebaSegmenter();
Counter<string> counter = new Counter<string>();
ViewModel.ExportCount = "词频统计ing...";
List<WXMsg>? msgs = UserReader.GetWXMsgs(ViewModel.WXContact.UserName);
if(msgs!= null)
try
{
foreach(WXMsg msg in msgs)
ViewModel.ExportCount = "词频统计ing...";
List<WXMsg>? msgs = UserReader.GetWXMsgs(ViewModel.WXContact.UserName);
if (msgs != null)
{
if(msg.Type == 1)
foreach (WXMsg msg in msgs)
{
List<string> list = jieba.Cut(msg.StrContent).ToList();
counter.Add(list);
if (msg.Type == 1)
{
List<string> list = jieba.Cut(msg.StrContent).ToList();
counter.Add(list);
}
}
}
}
catch
{
ViewModel.ExportCount = "异常";
MessageBox.Show("词频统计发生异常,请检查字典文件是否存在", "错误");
return;
}
var orderBy = counter.MostCommon();
ViewModel.ExportCount = "移除部分词语...";
string[] remove_string_list = setting.RemoveKey.Split(",");
@@ -260,6 +328,73 @@ namespace WechatBakTool.Pages
MessageBox.Show("用户所有表情预下载完毕");
});
}
/*
if (UserReader != null && ViewModel.WXContact != null)
{
Task.Run(() =>
{
List<WXMsg> msgs = UserReader.GetWXMsgs(ViewModel.WXContact.UserName).ToList();
List<WXContactHT> users = new List<WXContactHT>();
if (File.Exists("WXContact.json"))
{
string text = File.ReadAllText("WXContact.json");
text = text.Substring(8, text.Length - 8);
users = JsonConvert.DeserializeObject<List<WXContactHT>>(text);
}
int i = 1; int all = 1;
List<WXMsg> tmp = new List<WXMsg>();
foreach (WXMsg m in msgs)
{
m.BytesExtra = null;
tmp.Add(m);
if (all % 10000 == 0)
{
File.WriteAllText(ViewModel.WXContact.UserName + "-" + i.ToString() + ".json", string.Format("showMsg({0})", JsonConvert.SerializeObject(tmp)));
tmp.Clear();
i++;
}
all++;
}
if (users!.Find(x => x.UserName == ViewModel.WXContact.UserName) == null)
{
WXContactHT html = new WXContactHT();
html.NickName = ViewModel.WXContact.NickName;
html.UserName = ViewModel.WXContact.UserName;
html.LastMsg = ViewModel.WXContact.LastMsg;
if (ViewModel.WXContact.Avatar != null)
{
using (var ms = new MemoryStream())
{
ViewModel.WXContact.Avatar.StreamSource.CopyTo(ms);
byte[] bytes = new byte[ms.Length];
ms.Write(bytes, 0, bytes.Length);
html.AvatarString = Convert.ToBase64String(bytes);
}
}
html.FileCount = i;
users.Add(html);
}
File.WriteAllText(ViewModel.WXContact.UserName + "-" + i.ToString() + ".json", string.Format("showMsg({0})", JsonConvert.SerializeObject(tmp)));
File.WriteAllText("WXContact.json", string.Format("getUser({0})", JsonConvert.SerializeObject(users)));
MessageBox.Show("json已导出");
});
}
*/
}
private void list_msg_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (ViewModel.WXMsgs.Count == 0)
return;
if (e.VerticalOffset + e.ViewportHeight == e.ExtentHeight && !Loading)
{
// 滚动条到达底部的处理逻辑
loadMsg();
}
}
}
}

View File

@@ -14,6 +14,7 @@
- [x] 群聊
- [x] 系统消息
- [x] 文件
- [x] 引用/转发消息
- [x] 表情(需要预下载)
如果有什么好的建议或意见或者遇到什么问题欢迎提issue看到会回。
@@ -24,10 +25,11 @@
<br/>
### 免责声明
**本项目仅供学习使用,严禁商业使用**<br/>
**本项目仅供学习、研究使用严禁商业使用**<br/>
**用于网络安全用途的,请确保在国家法律法规下使用**<br/>
**本项目完全免费,问你要钱的都是骗子**<br/>
**使用本项目初衷是作者研究微信数据库的运行使用,您使用本软件导致的后果,包含但不限于数据损坏,记录丢失等问题,作者不承担相关责任。**<br/>
**因软件特殊性质,请在使用时获得微信账号所有人授权**<br/>
**因软件特殊性质,请在使用时获得微信账号所有人授权,你当确保不侵犯他人个人隐私权,后果自行承担**<br/>
<br/>
### 隐私声明
@@ -44,6 +46,7 @@ C# + .NET6.0 + WPF <br/>
- [ ] 性能优化
- [ ] 打包资源文件夹
- [ ] 手动模式(合适离线分析)
- [ ] 年度报告类分析(等美术资源中,没有资源不做)
<br/>
### 部分问题
@@ -58,8 +61,7 @@ A基本上都是因为刚迁移完缓存没写入到数据库导致的
<br/>
### 使用说明
**本说明为新版本说明,即将发版**<br/>
0.安装.NET Desktop Runtime(如已经安装忽略)<br/>
0.安装.NET Desktop Runtime(注意是6.0版本的Desktop Runtime如已经安装忽略)<br/>
1.打开微信,并登录。<br/>
2.在软件左侧下方点击**新建工作区**<br/>
3.在**新建工作区界面**,选择要创建工作区的微信进程,并**确认下方微信号是否正确**<br/>
@@ -77,6 +79,7 @@ A基本上都是因为刚迁移完缓存没写入到数据库导致的
4. 解密微信图片 [吾爱破解chenhahacjl/微信 DAT 图片解密 C#](https://www.52pojie.cn/forum.php?mod=viewthread&tid=1507922)
5. 参考了句柄名称实现,注意获取句柄别看这里,#10 这个issue就是血泪 [huiyadanli/RevokeMsgPatcher](https://github.com/huiyadanli/RevokeMsgPatcher)
6. 参考了句柄获取 [FuzzySecurity/Sharp-Suite](https://github.com/FuzzySecurity/Sharp-Suite)
7. 这个获取秘钥更通用一些 [SnowMeteors/GetWeChatKey](https://github.com/SnowMeteors/GetWeChatKey) ,用户名不是很稳定
### 其他声明
[1] 理论支持所有64位版本指用户名推断和公钥头推断地址直接获取方式需要version.json支持更新不是很及时。

View File

@@ -16,6 +16,9 @@ namespace WechatBakTool.ViewModel
[NotifyPropertyChangedFor(nameof(LabelStatus))]
private WXContact? wXContact = null;
[ObservableProperty]
private ObservableCollection<WXMsg> wXMsgs = new ObservableCollection<WXMsg>();
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(LabelStatus))]
private string exportCount = "";

View File

@@ -330,6 +330,25 @@ namespace WechatBakTool
}
return tmp;
}
public List<WXMsg>? GetWXMsgs(string uid,int time,int page)
{
List<WXMsg> tmp = new List<WXMsg>();
for (int i = 0; i <= 99; i++)
{
SQLiteConnection? con = getCon("MSG" + i.ToString());
if (con == null)
return tmp;
List<WXMsg>? wXMsgs = null;
string query = "select * from MSG where StrTalker=? and CreateTime>? Limit ?";
wXMsgs = con.Query<WXMsg>(query, uid, time, page);
if (wXMsgs.Count != 0) {
return ProcessMsg(wXMsgs, uid);
}
}
return tmp;
}
public List<WXMsg>? GetWXMsgs(string uid,string msg = "")
{
List<WXMsg> tmp = new List<WXMsg>();
@@ -356,98 +375,107 @@ namespace WechatBakTool
wXMsgs = con.Query<WXMsg>(query, uid, string.Format("%{0}%", msg));
}
foreach (WXMsg w in wXMsgs)
tmp.AddRange(ProcessMsg(wXMsgs, uid));
}
return tmp;
}
private List<WXMsg> ProcessMsg(List<WXMsg> msgs,string uid)
{
foreach (WXMsg w in msgs)
{
if (UserNameCache.ContainsKey(w.StrTalker))
{
if (UserNameCache.ContainsKey(w.StrTalker))
WXContact? contact = UserNameCache[w.StrTalker] as WXContact;
if (contact != null)
{
WXContact? contact = UserNameCache[w.StrTalker] as WXContact;
if (contact != null)
if (contact.Remark != "")
w.NickName = contact.Remark;
else
w.NickName = contact.NickName;
w.StrTalker = contact.UserName;
}
}
else
{
w.NickName = uid;
}
// 群聊处理
if (uid.Contains("@chatroom"))
{
string userId = "";
if (w.BytesExtra == null)
continue;
string sl = BitConverter.ToString(w.BytesExtra).Replace("-", "");
ProtoMsg protoMsg;
using (MemoryStream stream = new MemoryStream(w.BytesExtra))
{
protoMsg = ProtoBuf.Serializer.Deserialize<ProtoMsg>(stream);
}
if (protoMsg.TVMsg != null)
{
foreach (TVType _tmp in protoMsg.TVMsg)
{
if (contact.Remark != "")
w.NickName = contact.Remark;
else
w.NickName = contact.NickName;
if (_tmp.Type == 1)
userId = _tmp.TypeValue;
}
}
// 群聊处理
if (uid.Contains("@chatroom"))
if (!w.IsSender)
{
string userId = "";
if (w.BytesExtra == null)
continue;
string sl = BitConverter.ToString(w.BytesExtra).Replace("-", "");
ProtoMsg protoMsg;
using (MemoryStream stream = new MemoryStream(w.BytesExtra))
if (UserNameCache.ContainsKey(userId))
{
protoMsg = ProtoBuf.Serializer.Deserialize<ProtoMsg>(stream);
}
if (protoMsg.TVMsg != null)
{
foreach (TVType _tmp in protoMsg.TVMsg)
{
if (_tmp.Type == 1)
userId = _tmp.TypeValue;
}
}
if (!w.IsSender)
{
if (UserNameCache.ContainsKey(userId))
{
WXContact? contact = UserNameCache[userId] as WXContact;
if (contact != null)
w.NickName = contact.Remark == "" ? contact.NickName : contact.Remark;
}
else
{
w.NickName = userId;
}
}
}
// 发送人名字处理
if (w.IsSender)
w.NickName = "我";
w.DisplayContent = w.StrContent;
// 额外格式处理
if (w.Type != 1)
{
if (w.Type == 10000)
{
w.Type = 1;
w.NickName = "系统消息";
w.DisplayContent = w.StrContent.Replace("<revokemsg>", "").Replace("</revokemsg>", "");
}
else if (w.Type == 49 && (w.SubType == 6 || w.SubType == 19 || w.SubType == 40))
{
WXSessionAttachInfo? attachInfos = GetWXMsgAtc(w);
if (attachInfos == null)
{
w.DisplayContent = "附件不存在";
}
else
{
w.DisplayContent = Path.Combine(UserBakConfig!.UserResPath, attachInfos.attachPath);
}
WXContact? contact = UserNameCache[userId] as WXContact;
if (contact != null)
w.NickName = contact.Remark == "" ? contact.NickName : contact.Remark;
}
else
{
w.DisplayContent = "[界面未支持格式]Type=" + w.Type;
w.NickName = userId;
}
}
tmp.Add(w);
}
// 发送人名字处理
if (w.IsSender)
w.NickName = "我";
w.DisplayContent = w.StrContent;
// 额外格式处理
if (w.Type != 1)
{
if (w.Type == 10000)
{
w.Type = 1;
w.NickName = "系统消息";
w.DisplayContent = w.StrContent.Replace("<revokemsg>", "").Replace("</revokemsg>", "");
}
else if (w.Type == 49 && (w.SubType == 6 || w.SubType == 19 || w.SubType == 40))
{
WXSessionAttachInfo? attachInfos = GetWXMsgAtc(w);
if (attachInfos == null)
{
w.DisplayContent = "附件不存在";
}
else
{
w.DisplayContent = Path.Combine(UserBakConfig!.UserResPath, attachInfos.attachPath);
}
}
else
{
w.DisplayContent = "[界面未支持格式]Type=" + w.Type;
}
}
}
return tmp;
return msgs;
}
public List<WXSessionAttachInfo>? GetWXMsgAtc()
{

View File

@@ -18,7 +18,7 @@ namespace WechatBakTool
{
private UserBakConfig UserBakConfig = new UserBakConfig();
public WXWorkspace(string path,string account = "") {
string checkResult = Init(path, account);
string checkResult = Init(path, false, account);
if (checkResult != "")
new Exception(checkResult);
}
@@ -28,7 +28,7 @@ namespace WechatBakTool
UserBakConfig = userBakConfig;
}
public void DecryptDB(string pid,int type,CreateWorkViewModel viewModel)
public void DecryptDB(string pid,int type,CreateWorkViewModel viewModel,string pwd = "")
{
if (UserBakConfig == null)
{
@@ -39,7 +39,20 @@ namespace WechatBakTool
{
byte[]? key = null;
viewModel.LabelStatus = "正在获取秘钥需要1 - 10秒左右";
key = DecryptionHelper.GetWechatKey(pid, type, UserBakConfig.Account);
if(pwd == "")
key = DecryptionHelper.GetWechatKey(pid, type, UserBakConfig.Account);
else
{
key = new byte[pwd.Length / 2];
for(int i = 0;i<pwd.Length / 2; i++)
{
key[i] = Convert.ToByte(pwd.Substring(i * 2, 2), 16);
}
}
#if DEBUG
File.WriteAllText("key.log", BitConverter.ToString(key!));
#endif
if (key == null)
{
throw new Exception("获取到的密钥为空,获取失败");
@@ -104,7 +117,7 @@ namespace WechatBakTool
}
}
}
private string Init(string path,string account = "")
private string Init(string path,bool manual,string account = "")
{
string curPath = AppDomain.CurrentDomain.BaseDirectory;
string md5 = GetMd5Hash(path);

View File

@@ -6,9 +6,9 @@
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<AssemblyVersion>0.9.6.2</AssemblyVersion>
<FileVersion>0.9.6.2</FileVersion>
<Version>0.9.6.2</Version>
<AssemblyVersion>0.9.7.0</AssemblyVersion>
<FileVersion>0.9.7.0</FileVersion>
<Version>0.9.7.0</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -11,5 +11,8 @@
},{
"Version":"3.9.8.15",
"BaseAddr": 64997904
},{
"Version":"3.9.8.25",
"BaseAddr": 65002192
}
]