Suxue 1 rok pred
rodič
commit
1929ef64fe

+ 2 - 2
Analyse.xaml

@@ -1,9 +1,9 @@
-<Window x:Class="WechatPCMsgBakTool.Analyse"
+<Window x:Class="WechatBakTool.Analyse"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:WechatPCMsgBakTool"
+        xmlns:local="clr-namespace:WechatBakTool"
         mc:Ignorable="d"
         WindowStartupLocation="CenterScreen"
         Title="溯雪微信备份工具-分析" Height="450" Width="900">

+ 9 - 5
Analyse.xaml.cs

@@ -11,9 +11,9 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Shapes;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Model;
 
-namespace WechatPCMsgBakTool
+namespace WechatBakTool
 {
     /// <summary>
     /// Analyse.xaml 的交互逻辑
@@ -31,10 +31,14 @@ namespace WechatPCMsgBakTool
 
         private void btn_analyse_Click(object sender, RoutedEventArgs e)
         {
-            List<WXContact>? contacts = UserReader.GetWXContacts();
-            List<WXMsgGroup> list = UserReader.GetWXMsgGroup().OrderByDescending(x => x.MsgCount).ToList();
-            if(contacts == null)
+            var tmp = UserReader.GetWXContacts();
+            List<WXContact> contacts;
+            if (tmp == null)
                 contacts = new List<WXContact>();
+            else
+                contacts = tmp.ToList();
+            
+            List<WXMsgGroup> list = UserReader.GetWXMsgGroup().OrderByDescending(x => x.MsgCount).ToList();
 
             foreach (WXMsgGroup item in list)
             {

+ 3 - 3
App.xaml

@@ -1,8 +1,8 @@
-<Application x:Class="WechatPCMsgBakTool.App"
+<Application x:Class="WechatBakTool.App"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:local="clr-namespace:WechatPCMsgBakTool"
-             StartupUri="Main.xaml">
+             xmlns:local="clr-namespace:WechatBakTool"
+             StartupUri="Main2.xaml">
     <Application.Resources>
          
     </Application.Resources>

+ 1 - 1
App.xaml.cs

@@ -6,7 +6,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Windows;
 
-namespace WechatPCMsgBakTool
+namespace WechatBakTool
 {
     /// <summary>
     /// Interaction logic for App.xaml

+ 32 - 12
Helpers/DecryptionHelper.cs

@@ -10,9 +10,9 @@ using System.Text;
 using System.Text.Json.Serialization;
 using System.Threading.Tasks;
 using System.Windows;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Model;
 
-namespace WechatPCMsgBakTool.Helpers
+namespace WechatBakTool.Helpers
 {
     public class DecryptionHelper
     {
@@ -23,13 +23,9 @@ namespace WechatPCMsgBakTool.Helpers
         const int DEFAULT_ITER = 64000;
         const int DEFAULT_PAGESIZE = 4096; //4048数据 + 16IV + 20 HMAC + 12
         const string SQLITE_HEADER = "SQLite format 3";
-        public static byte[]? GetWechatKey(bool mem_find_key,string account)
+        public static byte[]? GetWechatKey(string pid, bool mem_find_key, string account)
         {
-            Process? process = ProcessHelper.GetProcess("WeChat");
-            if (process == null)
-            {
-                return null;
-            }
+            Process process = Process.GetProcessById(int.Parse(pid));
             ProcessModule? module = ProcessHelper.FindProcessModule(process.Id, "WeChatWin.dll");
             if (module == null)
             {
@@ -41,7 +37,7 @@ namespace WechatPCMsgBakTool.Helpers
                 return null;
             }
 
-            
+
 
             if (!mem_find_key)
             {
@@ -73,7 +69,7 @@ namespace WechatPCMsgBakTool.Helpers
             else
             {
                 List<int> read = ProcessHelper.FindProcessMemory(process.Handle, module, account);
-                if(read.Count >= 2)
+                if (read.Count >= 2)
                 {
                     byte[] buffer = new byte[8];
                     int key_offset = read[1] - 64;
@@ -82,7 +78,7 @@ namespace WechatPCMsgBakTool.Helpers
                         ulong addr = BitConverter.ToUInt64(buffer, 0);
 
                         byte[] key_bytes = new byte[32];
-                        if(ProcessHelper.ReadProcessMemory(process.Handle, (IntPtr)addr, key_bytes, key_bytes.Length, out _))
+                        if (ProcessHelper.ReadProcessMemory(process.Handle, (IntPtr)addr, key_bytes, key_bytes.Length, out _))
                         {
                             return key_bytes;
                         }
@@ -90,11 +86,12 @@ namespace WechatPCMsgBakTool.Helpers
                 }
                 else
                 {
-                    MessageBox.Show("搜索不到微信账号,请确认用户名是否正确,如错误请重新新建工作区,务必确认账号是否正确", "错误");
+                    throw new Exception("搜索不到微信账号,请确认用户名是否正确,如错误请重新新建工作区,务必确认账号是否正确");
                 }
             }
             return null;
         }
+
         public static byte[] DecryptDB(byte[] db_file_bytes, byte[] password_bytes)
         {
             //数据库头16字节是盐值
@@ -286,6 +283,29 @@ namespace WechatPCMsgBakTool.Helpers
             }
             return saveFilePath;
         }
+        public static void DecryUserData(byte[] key, string source, string to)
+        {
+            string dbPath = source;
+            string decPath = to;
+            if (!Directory.Exists(decPath))
+                Directory.CreateDirectory(decPath);
+
+            string[] filePath = Directory.GetFiles(dbPath);
+            foreach (string file in filePath)
+            {
+                FileInfo info = new FileInfo(file);
+                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);
+                }
+            }
+        }
     }
 
 }

+ 1 - 1
Helpers/DevicePathMapper.cs

@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace WechatPCMsgBakTool.Helpers
+namespace WechatBakTool.Helpers
 {
     public static class DevicePathMapper
     {

+ 1 - 1
Helpers/NativeAPI.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Runtime.InteropServices;
 
-namespace WechatPCMsgBakTool.Helpers
+namespace WechatBakTool.Helpers
 {
     public class NativeAPI
     {

+ 2 - 2
Helpers/NativeAPIHelper.cs

@@ -5,9 +5,9 @@ using System.Linq;
 using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading.Tasks;
-using static WechatPCMsgBakTool.Helpers.NativeAPI;
+using static WechatBakTool.Helpers.NativeAPI;
 
-namespace WechatPCMsgBakTool.Helpers
+namespace WechatBakTool.Helpers
 {
     public class NativeAPIHelper
     {

+ 1 - 1
Helpers/OpenSSLInterop.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Text;
 
-namespace WechatPCMsgBakTool.Helpers
+namespace WechatBakTool.Helpers
 {
     public class OpenSSLInterop
     {

+ 1 - 22
Helpers/ProcessHelper.cs

@@ -9,31 +9,10 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 
-namespace WechatPCMsgBakTool.Helpers
+namespace WechatBakTool.Helpers
 {
     public class ProcessHelper
     {
-        
-        public static Process? GetProcess(string ProcessName)
-        {
-            Process[] processes = Process.GetProcessesByName(ProcessName);
-            if (processes.Length == 0)
-                return null;
-            else if(processes.Length > 1) {
-                SelectWechat selectWechat = new SelectWechat();
-                MessageBox.Show("检测到有多个微信,请选择本工作区对应的微信");
-                selectWechat.ShowDialog();
-                if (selectWechat.SelectProcess == null)
-                    return null;
-
-                Process? p = processes.ToList().Find(x => x.Id.ToString() == selectWechat.SelectProcess.ProcessId);
-                if (p == null)
-                    return null;
-                return p;
-            }
-            else
-                return processes[0];
-        }
         public static ProcessModule? FindProcessModule(int ProcessId, string ModuleName)
         {
             Process process = Process.GetProcessById(ProcessId);

+ 3 - 3
Helpers/ToolsHelper.cs

@@ -7,11 +7,11 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace WechatPCMsgBakTool.Helpers
+namespace WechatBakTool.Helpers
 {
     public class ToolsHelper
     {
-        public static TaskFactory factory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(10));
+        private static TaskFactory factory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(10));
         public static string DecodeVoice(string source,string pcm,string to)
         {
             string ffmpeg = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Tools", "ffmpeg.exe");
@@ -45,7 +45,7 @@ namespace WechatPCMsgBakTool.Helpers
 
     }
 
-    public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
+    partial class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
     {
         // Indicates whether the current thread is processing work items.
         [ThreadStatic]

+ 0 - 185
Helpers/WechatDBHelper.cs

@@ -1,185 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading.Tasks;
-using WechatPCMsgBakTool.Model;
-
-namespace WechatPCMsgBakTool.Helpers
-{
-    public class WechatDBHelper
-    {
-        private static string ResPath = "";
-        private static string CurrentPath = AppDomain.CurrentDomain.BaseDirectory;
-        private static string UserWorkPath = "";
-        private static int MaxMediaDBCount = 0;
-        private static int MaxMsgDBCount = 0;
-        public static DBInfo GetDBInfo()
-        {
-            return new DBInfo() { MaxMediaDBCount = MaxMediaDBCount, MaxMsgDBCount = MaxMsgDBCount, UserPath = UserWorkPath, ResPath = ResPath };
-        }
-
-        public static DBInfo GetDBinfoOnLocal(string path)
-        {
-            string md5 = GetMd5Hash(path);
-            string tmpPath = Path.Combine(CurrentPath, md5);
-
-            string decPath = Path.Combine(tmpPath, "DecDB");
-            string[] files = Directory.GetFiles(decPath);
-            int media = 0;
-            int msg = 0;
-            foreach(string file in files)
-            {
-                FileInfo fileInfo = new FileInfo(file);
-                if(fileInfo.Extension == ".db")
-                {
-                    string name = fileInfo.Name.Replace(".db", "");
-                    if(name.Substring(0,3) == "MSG")
-                    {
-                        name = name.Replace("MSG", "");
-                        int currentDB = int.Parse(name);
-                        if(currentDB > msg)
-                            msg = currentDB;
-                        continue;
-                    }
-                    if(name.Substring(0,8)== "MediaMSG")
-                    {
-                        name = name.Replace("MediaMSG", "");
-                        int currentDB = int.Parse(name);
-                        if (currentDB > media)
-                            media = currentDB;
-                        continue;
-                    }
-                }
-            }
-            return new DBInfo() { MaxMediaDBCount = media, MaxMsgDBCount = msg, UserPath = tmpPath, ResPath = path };
-        }
-
-        public static void CreateUserWorkPath(string path)
-        {
-            ResPath = path;
-            string md5 = GetMd5Hash(path);
-            string tmpPath = Path.Combine(CurrentPath, md5);
-            if (!Directory.Exists(tmpPath))
-            {
-                Directory.CreateDirectory(tmpPath);
-            }
-            UserWorkPath = tmpPath;
-        }
-
-        public static string MoveUserData(string path)
-        {
-            if(UserWorkPath != "")
-            {
-                //创建db库
-                string db = Path.Combine(UserWorkPath, "DB");
-                if (!Directory.Exists(db))
-                {
-                    Directory.CreateDirectory(db);
-                }
-
-                //核心数据库查找
-                List<string> dbPathArray = new List<string>();
-
-                string userDBPath = Path.Combine(path, "Msg");
-                if (!Directory.Exists(userDBPath))
-                    return "用户目录不存在,创建失败";
-
-                string mainDB = Path.Combine(userDBPath, "MicroMsg.db");
-                if (!File.Exists(mainDB))
-                    return "微信主数据库不存在,创建失败";
-                else
-                    dbPathArray.Add(mainDB);
-
-                string actDB = Path.Combine(userDBPath, "MultiSearchChatMsg.db");
-                if(!File.Exists(actDB))
-                    return "微信附件数据库不存在,创建失败";
-                else
-                    dbPathArray.Add(actDB);
-
-                string dbmsg = Path.Combine(userDBPath, "Multi");
-                bool mediaDBExists = false;
-                bool msgDBExists = false;
-                for(int i = 0; i < 100; i++)
-                {
-                    string mediaDBPath = Path.Combine(dbmsg, string.Format("MediaMSG{0}.db", i.ToString()));
-                    string msgDBPath = Path.Combine(dbmsg, string.Format("MSG{0}.db", i.ToString()));
-
-                    mediaDBExists = File.Exists(mediaDBPath);
-                    msgDBExists = File.Exists(msgDBPath);
-
-                    if (i == 0 && !mediaDBExists && !msgDBExists)
-                    {
-                        return "微信聊天记录数据不存在,创建失败";
-                    }
-
-                    if(mediaDBExists)
-                        dbPathArray.Add(mediaDBPath);
-
-                    if (msgDBExists)
-                        dbPathArray.Add(msgDBPath);
-
-                    if (!msgDBExists && !msgDBExists)
-                        break;
-                }
-
-                foreach(string dbPath in dbPathArray) { 
-                    FileInfo file = new FileInfo(dbPath);
-                    string to = Path.Combine(db, file.Name);
-                    if(!File.Exists(to))
-                        File.Copy(dbPath, to);
-                }
-                return "";
-
-            }
-            return "请复制目录至文本框内";
-        }
-        public static void DecryUserData(byte[] key,string source,string to)
-        {
-            string dbPath = source;
-            string decPath = to;
-            if(!Directory.Exists(decPath))
-                Directory.CreateDirectory(decPath);
-
-            string[] filePath = Directory.GetFiles(dbPath);
-            foreach (string file in filePath)
-            {
-                FileInfo info = new FileInfo(file);
-                var db_bytes = File.ReadAllBytes(file);
-                var decrypted_file_bytes = DecryptionHelper.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);
-                }
-            }
-        }
-        private static string GetMd5Hash(string input)
-        {
-            using (MD5 md5Hash = MD5.Create())
-            {
-                // Convert the input string to a byte array and compute the hash.
-                byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
-
-                // Create a new Stringbuilder to collect the bytes
-                // and create a string.
-                StringBuilder sBuilder = new StringBuilder();
-
-                // Loop through each byte of the hashed data 
-                // and format each one as a hexadecimal string.
-                for (int i = 0; i < data.Length; i++)
-                {
-                    sBuilder.Append(data[i].ToString("x2"));
-                }
-
-                // Return the hexadecimal string.
-                return sBuilder.ToString();
-            }
-        }
-    }
-}

+ 4 - 4
HtmlExport.cs

@@ -6,11 +6,11 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using WechatPCMsgBakTool.Interface;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Interface;
+using WechatBakTool.Model;
 using System.Xml;
 
-namespace WechatPCMsgBakTool
+namespace WechatBakTool
 {
     public class HtmlExport : IExport
     {
@@ -64,7 +64,7 @@ namespace WechatPCMsgBakTool
 
             foreach (var msg in msgList)
             {
-                HtmlBody += string.Format("<div class=\"msg\"><p class=\"nickname\">{0} <span style=\"padding-left:10px;\">{1}</span></p>", msg.IsSender ? "我" : Session.NickName, TimeStampToDateTime(msg.CreateTime).ToString("yyyy-MM-dd HH:mm:ss"));
+                HtmlBody += string.Format("<div class=\"msg\"><p class=\"nickname\">{0} <span style=\"padding-left:10px;\">{1}</span></p>", msg.IsSender ? "我" : msg.NickName, TimeStampToDateTime(msg.CreateTime).ToString("yyyy-MM-dd HH:mm:ss"));
 
                 if (msg.Type == 1)
                     HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", msg.StrContent);

+ 2 - 2
Interface/ExportInterface.cs

@@ -3,9 +3,9 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Model;
 
-namespace WechatPCMsgBakTool.Interface
+namespace WechatBakTool.Interface
 {
     public interface IExport
     {

+ 0 - 42
Main.xaml

@@ -1,42 +0,0 @@
-<Window x:Class="WechatPCMsgBakTool.Main"
-        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:WechatPCMsgBakTool"
-        mc:Ignorable="d"
-        WindowStartupLocation="CenterScreen"
-        Title="溯雪微信备份工具" Height="450" Width="800">
-    <Grid>
-        <ListView Name="list_workspace" Margin="15,50,0,20" HorizontalAlignment="Left" Width="230" Grid.RowSpan="2" SelectionChanged="list_workspace_SelectionChanged">
-            <ListView.View>
-                <GridView>
-                    <GridViewColumn Header="原始id" Width="140" DisplayMemberBinding="{Binding UserName,Mode=TwoWay}" />
-                    <GridViewColumn Header="是否解密" Width="80" DisplayMemberBinding="{Binding Decrypt,Mode=TwoWay}" />
-                </GridView>
-            </ListView.View>
-        </ListView>
-        <Label Content="工作区:" HorizontalAlignment="Left" Margin="15,15,0,0" VerticalAlignment="Top" Height="25" Width="58"/>
-        <Button Content="新增" Width="50" HorizontalAlignment="Left" Margin="194,20,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.343,0.521" Height="19" Click="Button_Click_1"/>
-        <Label Content="用户路径:-" Name="user_path" HorizontalAlignment="Left" Margin="278,68,0,0" VerticalAlignment="Top" Height="25" Width="500"/>
-        <Button Content="解密" IsEnabled="False" Width="50" HorizontalAlignment="Left" Margin="285,20,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.343,0.521" Name="btn_decrypt" Click="btn_decrypt_Click" Height="19"/>
-        <Button Content="读取" IsEnabled="False" Width="50" HorizontalAlignment="Left" Margin="285,47,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.343,0.521" Name="btn_read" Click="btn_read_Click" Height="19" />
-
-        <ListView Name="list_sessions" Margin="278,130,0,20" HorizontalAlignment="Left" Width="290" MouseDoubleClick="list_sessions_MouseDoubleClick">
-            <ListView.View>
-                <GridView>
-                    <GridViewColumn Header="昵称" Width="120" DisplayMemberBinding="{Binding NickName}" />
-                    <GridViewColumn Header="原始id" Width="140" DisplayMemberBinding="{Binding UserName}" />
-                </GridView>
-            </ListView.View>
-        </ListView>
-        <Button Content="导出所选人员聊天记录" HorizontalAlignment="Left" Margin="609,130,0,0" VerticalAlignment="Top" Width="140" Click="Button_Click"/>
-        <Label Content="搜索:" HorizontalAlignment="Left" Margin="278,92,0,0" VerticalAlignment="Top"/>
-        <TextBox Name="find_user" HorizontalAlignment="Left" Margin="323,96,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="194" Height="20"/>
-        <Button Name="btn_search" Content="搜索" HorizontalAlignment="Left" Margin="525,96,0,0" VerticalAlignment="Top" Width="43" Click="btn_search_Click"/>
-        <Button Name="btn_analyse" Content="消息分析工具" HorizontalAlignment="Left" Margin="609,160,0,0" VerticalAlignment="Top" Width="140" Click="btn_analyse_Click"/>
-        <CheckBox Name="cb_del_search" Content="已删除人员强制从记录搜索" HorizontalAlignment="Left" Margin="610,99,0,0" VerticalAlignment="Top"/>
-        <RadioButton Name="rb_find_file" GroupName="find_addr_function" Content="使用version.json进行基址查找" HorizontalAlignment="Left" Margin="348,22,0,0" VerticalAlignment="Top" IsChecked="True"/>
-        <RadioButton Name="rb_find_mem" GroupName="find_addr_function" Content="使用推定进行基址查找【推荐】" HorizontalAlignment="Left" Margin="546,22,0,0" VerticalAlignment="Top" Checked="rb_find_mem_Checked"/>
-    </Grid>
-</Window>

+ 0 - 303
Main.xaml.cs

@@ -1,303 +0,0 @@
-using K4os.Compression.LZ4;
-using K4os.Compression.LZ4.Encoders;
-using K4os.Compression.LZ4.Streams;
-using Newtonsoft.Json;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Reflection.PortableExecutable;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Xml;
-using WechatPCMsgBakTool.Helpers;
-using WechatPCMsgBakTool.Interface;
-using WechatPCMsgBakTool.Model;
-
-namespace WechatPCMsgBakTool
-{
-    /// <summary>
-    /// Main.xaml 的交互逻辑
-    /// </summary>
-    public partial class Main : Window
-    {
-        private UserBakConfig? CurrentUserBakConfig = null;
-        private WXUserReader? UserReader = null;
-        private ObservableCollection<UserBakConfig> userBakConfigs = new ObservableCollection<UserBakConfig>();
-        public Main()
-        {
-            Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
-            InitializeComponent();
-            LoadWorkspace();
-            this.Title += $" {Application.ResourceAssembly.GetName().Version}";
-        }
-
-        private void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
-        {
-            MessageBox.Show("发生了未知错误,记录已写入到根目录err.log,如果可以,欢迎反馈给开发人员,非常感谢", "错误");
-            File.AppendAllText("err.log", "\r\n\r\n\r\n=============================\r\n");
-            File.AppendAllText("err.log", string.Format("异常时间:{0}\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
-            File.AppendAllText("err.log", e.Exception.ToString());
-            return;
-        }
-
-        private void LoadWorkspace()
-        {
-            userBakConfigs.Clear();
-            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "workspace");
-            if (Directory.Exists(path))
-            {
-                string[] files = Directory.GetFiles(path);
-                foreach(string file in files)
-                {
-                    string type = file.Substring(file.Length - 5, 5);
-                    if(type == ".json")
-                    {
-                        string jsonString = File.ReadAllText(file);
-                        UserBakConfig? userBakConfig = null;
-                        try
-                        {
-                            userBakConfig = JsonConvert.DeserializeObject<UserBakConfig>(jsonString);
-                        }
-                        catch
-                        {
-                            MessageBox.Show("读取到异常工作区文件,请确认备份数据是否正常\r\n文件路径:" + file,"错误");
-                        }
-                        if(userBakConfig != null)
-                        {
-                            userBakConfigs.Add(userBakConfig);
-                        }
-                    }
-                }
-            }
-            list_workspace.ItemsSource = userBakConfigs;
-        }
-
-        private void btn_decrypt_Click(object sender, RoutedEventArgs e)
-        {
-            if(CurrentUserBakConfig != null)
-            {
-                if (!CurrentUserBakConfig.Decrypt)
-                {
-                    bool? mem_find_key = rb_find_mem.IsChecked;
-                    if(mem_find_key == null)
-                    {
-                        MessageBox.Show("请选择key获取方式");
-                        return;
-                    }
-                    byte[]? key = null;
-                    try
-                    {
-                        key = DecryptionHelper.GetWechatKey((bool)mem_find_key,CurrentUserBakConfig.Account);
-                        if (key == null)
-                            MessageBox.Show("获取到的秘钥为空");
-                        File.AppendAllText("debug.log", BitConverter.ToString(key, 0));
-                    }
-                    catch (Exception ex)
-                    {
-                        if(ex.Source == "Newtonsoft.Json")
-                        {
-                            MessageBox.Show("版本文件读取失败,请检查版本文件内容是否为正确的json格式", "错误");
-                        }
-                        else
-                        {
-                            MessageBox.Show(ex.Message);
-                        }
-                        return;
-                    }
-                    //byte[]? key = DecryptionHelper.GetWechatKey();
-                    if (key == null)
-                    {
-                        MessageBox.Show("微信密钥获取失败,请检查微信是否打开,或者版本不兼容");
-                        return;
-                    }
-                    string key_string = BitConverter.ToString(key, 0).Replace("-", string.Empty).ToLower().ToUpper();
-                    string source = Path.Combine(CurrentUserBakConfig.UserWorkspacePath, "OriginalDB");
-                    string to = Path.Combine(CurrentUserBakConfig.UserWorkspacePath, "DecDB");
-                    try
-                    {
-                        WechatDBHelper.DecryUserData(key, source, to);
-                        MessageBox.Show("解密完成,请点击读取数据");
-                        CurrentUserBakConfig.Decrypt = true;
-                        WXWorkspace.SaveConfig(CurrentUserBakConfig);
-                        LoadWorkspace();
-                    }
-                    catch (Exception ex)
-                    {
-                        MessageBox.Show("解密过程出现错误:" + ex.Message);
-                    }
-                }
-            }
-        }
-
-        private void btn_read_Click(object sender, RoutedEventArgs e)
-        {
-            if(CurrentUserBakConfig == null)
-            {
-                MessageBox.Show("请先选择工作区");
-                return;
-            }
-            UserReader = new WXUserReader(CurrentUserBakConfig);
-            list_sessions.ItemsSource = UserReader.GetWXContacts();
-        }
-
-        private void list_workspace_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            CurrentUserBakConfig = list_workspace.SelectedItem as UserBakConfig;
-            if(CurrentUserBakConfig != null)
-            {
-                user_path.Content = "用户路径:" + CurrentUserBakConfig.UserResPath;
-                if (CurrentUserBakConfig.Decrypt)
-                {
-                    btn_decrypt.IsEnabled = false;
-                    btn_read.IsEnabled = true;
-                }
-                else
-                {
-                    btn_decrypt.IsEnabled = true;
-                    btn_read.IsEnabled = false;
-                }
-            }
-        }
-
-        private void list_sessions_MouseDoubleClick(object sender, MouseButtonEventArgs e)
-        {
-
-        }
-
-        private void Button_Click(object sender, RoutedEventArgs e)
-        {
-            WXContact? wXContact = list_sessions.SelectedItem as WXContact;
-            if(UserReader == null)
-            {
-                MessageBox.Show("请先点击读取已解密工作区");
-                return;
-            }
-            if(wXContact == null || CurrentUserBakConfig == null)
-            {
-                MessageBox.Show("请先选择要导出的联系人");
-                return;
-            }
-
-            IExport export = new HtmlExport();
-            export.InitTemplate(wXContact);
-            export.SetMsg(UserReader, wXContact);
-            export.SetEnd();
-            //string path = UserReader.GetSavePath(wXContact);
-            string path = Path.Combine(CurrentUserBakConfig.UserWorkspacePath, wXContact.UserName + ".html");
-            export.Save(path);
-            MessageBox.Show("导出完成");
-        }
-
-        private void Button_Click_1(object sender, RoutedEventArgs e)
-        {
-            SelectWechat selectWechat = new SelectWechat();
-            selectWechat.ShowDialog();
-            if(selectWechat.SelectProcess != null)
-            {
-                string path = selectWechat.SelectProcess.DBPath.Replace("\\Msg\\MicroMsg.db", "");
-                try
-                {
-                    WXWorkspace wXWorkspace = new WXWorkspace(path, selectWechat.SelectProcess.Account);
-                    wXWorkspace.MoveDB();
-                    MessageBox.Show("创建工作区成功");
-                    LoadWorkspace();
-                }
-                catch (Exception)
-                {
-                    MessageBox.Show("创建工作区失败,请检查路径是否正确");
-                }
-            }
-        }
-
-        private void btn_search_Click(object sender, RoutedEventArgs e)
-        {
-            if(UserReader == null)
-            {
-                MessageBox.Show("请先读取工作区数据");
-                return;
-            }
-            if(cb_del_search.IsChecked != null)
-            {
-                if (!(bool)cb_del_search.IsChecked)
-                    list_sessions.ItemsSource = UserReader.GetWXContacts(find_user.Text);
-                else
-                {
-                    List<WXMsg>? wXMsgs = UserReader.GetWXMsgs(find_user.Text);
-                    if(wXMsgs != null)
-                    {
-                        if(wXMsgs.Count > 0)
-                        {
-                            List<WXContact> wXContacts = new List<WXContact>() { new WXContact() { NickName = wXMsgs[0].StrTalker, UserName = wXMsgs[0].StrTalker } };
-                            list_sessions.ItemsSource = wXContacts;
-                        }
-                    }
-                }
-
-            }
-            
-        }
-
-        private void btn_analyse_Click(object sender, RoutedEventArgs e)
-        {
-            if(UserReader == null || CurrentUserBakConfig == null)
-            {
-                MessageBox.Show("请先读取数据");
-                return;
-            }
-            Analyse analyse = new Analyse(CurrentUserBakConfig, UserReader);
-            analyse.Show();
-        }
-
-        private void rb_find_mem_Checked(object sender, RoutedEventArgs e)
-        {
-            if(CurrentUserBakConfig!= null)
-            {
-                if (string.IsNullOrEmpty(CurrentUserBakConfig.Account))
-                {
-                    MessageBox.Show("使用该功能需要填写用户名,请务必确认用户名已经正确填写,否则请重建工作区");
-                    return;
-                }
-            }
-        }
-
-        private void Button_Click_2(object sender, RoutedEventArgs e)
-        {
-            var list = UserReader.GetWXChatRooms();
-            var users = UserReader.GetWXContacts();
-            Hashtable hashtable = new Hashtable();
-            foreach(var u in users)
-            {
-                hashtable[u.UserName] = u;
-            }
-            foreach(var room in list)
-            {
-                if(room.ChatRoomName == "20647511469@chatroom")
-                {
-                    string[] ids = room.UserNameList.Split("^G");
-                    foreach(string id in ids) {
-                        if (hashtable.ContainsKey(id))
-                        {
-                            WXContact? contact = hashtable[id] as WXContact;
-                            Debug.WriteLine($"{id} 是 ${contact.NickName}");
-                        }
-                        else
-                        {
-                            Debug.WriteLine("不存在");
-                        }
-                    }
-                }
-            }
-        }
-    }
-}

+ 3 - 3
Main2.xaml

@@ -1,9 +1,9 @@
-<Window x:Class="WechatPCMsgBakTool.Main2"
+<Window x:Class="WechatBakTool.Main2"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:WechatPCMsgBakTool"
+        xmlns:local="clr-namespace:WechatBakTool"
         mc:Ignorable="d" WindowStartupLocation="CenterScreen" WindowStyle="None" WindowState="Normal" Background="Transparent" AllowsTransparency="True" ResizeMode="NoResize"
         Title="Main2" Height="550" Width="950" >
     <Window.Resources>
@@ -125,7 +125,7 @@
         <Grid Width="230" Background="#2775b6" HorizontalAlignment="Left" IsHitTestVisible="True">
             <ListView BorderThickness="0" Background="Transparent" Margin="0,0,0,85" Name="list_workspace" ItemTemplate="{DynamicResource ListViewItemContentTemplate}" SelectionChanged="list_workspace_SelectionChanged"/>
             <Grid Name="new_workspace" Width="170" Height="40" VerticalAlignment="Bottom" Margin="30,45" IsHitTestVisible="True">
-                <Rectangle Name="new_workspace_fill" Fill="Transparent"  RadiusX="0" RadiusY="0" Stroke="White" StrokeDashArray="5" >
+                <Rectangle Name="new_workspace_fill" Fill="Transparent"  RadiusX="0" RadiusY="0" Stroke="White" StrokeDashArray="5" MouseDown="new_workspace_fill_MouseDown">
                     <Rectangle.Triggers>
                         <EventTrigger RoutedEvent="Rectangle.MouseEnter">
                             <BeginStoryboard>

+ 17 - 8
Main2.xaml.cs

@@ -13,9 +13,9 @@ using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Model;
 
-namespace WechatPCMsgBakTool
+namespace WechatBakTool
 {
     /// <summary>
     /// Main2.xaml 的交互逻辑
@@ -37,7 +37,7 @@ namespace WechatPCMsgBakTool
             Application.Current.Shutdown();
         }
 
-        private void LoadWorkspace()
+        public void LoadWorkspace()
         {
             userBakConfigs.Clear();
             string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "workspace");
@@ -72,16 +72,25 @@ namespace WechatPCMsgBakTool
         private void list_workspace_SelectionChanged(object sender, SelectionChangedEventArgs e)
         {
             UserBakConfig? config = list_workspace.SelectedItem as UserBakConfig;
-            if(config != null)
+            if(config == null)
             {
-                CurrentUserBakConfig = config;
-                MainFrame.Navigate(new Uri("pack://application:,,,/Pages/Workspace.xaml"));
+                MainFrame.Navigate(new Uri("pack://application:,,,/Pages/Welcome.xaml?datatime=" + DateTime.Now.Ticks));
+                return;
             }
-            else
+            if (!config.Decrypt)
             {
-                MessageBox.Show("工作区配置文件异常,请确认工作区配置是否正常", "错误", MessageBoxButton.OK);
+                MessageBox.Show("请先到创建工作区进行解密");
+                MainFrame.Navigate(new Uri("pack://application:,,,/Pages/CreateWork.xaml?datatime=" + DateTime.Now.Ticks));
                 return;
             }
+
+            CurrentUserBakConfig = config;
+            MainFrame.Navigate(new Uri("pack://application:,,,/Pages/Workspace.xaml?datatime=" + DateTime.Now.Ticks));
+        }
+
+        private void new_workspace_fill_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+            MainFrame.Navigate(new Uri("pack://application:,,,/Pages/CreateWork.xaml?datatime=" + DateTime.Now.Ticks));
         }
     }
 }

+ 1 - 1
Model/Common.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace WechatPCMsgBakTool.Model
+namespace WechatBakTool.Model
 {
     public class ProcessInfo
     {

+ 25 - 0
Model/ProtobufModel.cs

@@ -0,0 +1,25 @@
+using ProtoBuf;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WechatBakTool.Model
+{
+    [ProtoContract]
+    public class TVType
+    {
+        [ProtoMember(1)]
+        public int Type;
+        [ProtoMember(2)]
+        public string TypeValue = "";
+    }
+
+    [ProtoContract]
+    public class ProtoMsg
+    {
+        [ProtoMember(3)]
+        public List<TVType>? TVMsg;
+    }
+}

+ 20 - 3
Model/WXModel.cs

@@ -5,8 +5,10 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Media.Imaging;
 
-namespace WechatPCMsgBakTool.Model
+namespace WechatBakTool.Model
 {
     public class UserBakConfig : INotifyPropertyChanged
     {
@@ -31,6 +33,11 @@ namespace WechatPCMsgBakTool.Model
         }
     }
 
+    public class WXCount
+    {
+        public int Count { get; set; }
+    }
+
     public class WXMsgGroup
     {
         [Column("StrTalker")]
@@ -41,6 +48,14 @@ namespace WechatPCMsgBakTool.Model
         public string NickName { get; set; } = "";
     }
 
+    [Table("ContactHeadImg1")]
+    public class ContactHeadImg
+    {
+        public string usrName { get; set; } = "";
+        public int createTime { get; set; }
+        public byte[]? smallHeadBuf { get; set; }
+    }
+
     public class WXUserInfo
     {
         public string UserName { get; set; } = "";
@@ -121,6 +136,7 @@ namespace WechatPCMsgBakTool.Model
         public byte[]? CompressContent { get; set; }
         [Column("BytesExtra")]
         public byte[]? BytesExtra { get; set; }
+        public string NickName { get; set; } = "";
     }
 
     [Table("ChatRoom")]
@@ -158,8 +174,9 @@ namespace WechatPCMsgBakTool.Model
         public string LastMsg { get; set; } = "";
         [Column("ExtraBuf")]
         public byte[]? ExtraBuf { get; set; }
-        [Column("smallHeadImgUrl")]
-        public string Avatar { get; set; } = "";
+        public BitmapImage? Avatar { get; set; }
+        [Column("Remark")]
+        public string Remark { get; set; } = "";
     }
 
     [Table("ContactHeadImgUrl")]

+ 43 - 0
Pages/CreateWork.xaml

@@ -0,0 +1,43 @@
+<Page x:Class="WechatBakTool.Pages.CreateWork"
+      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+      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.ViewModel"
+      mc:Ignorable="d" 
+      d:DesignHeight="550" d:DesignWidth="800"
+      Title="Welcome" Background="White">
+    <Page.Resources>
+        <local:GetKeyConverter x:Key="getKeyConverterKey" />
+    </Page.Resources>
+    <Grid>
+        <Label FontSize="20" Margin="30,15" Content="新建工作区" HorizontalAlignment="Left" VerticalAlignment="Top" />
+        <Label Margin="30,55,0,0" Content="请选择要创建工作区的微信,可以通过微信路径判断是哪一个微信哦!" HorizontalAlignment="Left" VerticalAlignment="Top" />
+        <ListView Name="list_process" Margin="35,95,35,0" VerticalAlignment="Top" Height="160" ItemsSource="{Binding ProcessInfos}" SelectionChanged="list_process_SelectionChanged" SelectedItem="{Binding SelectProcess}">
+            <ListView.View>
+                <GridView>
+                    <GridViewColumn Header="进程名" Width="120" DisplayMemberBinding="{Binding ProcessName}" />
+                    <GridViewColumn Header="PID" Width="80" DisplayMemberBinding="{Binding ProcessId}" />
+                    <GridViewColumn Header="路径" Width="430" DisplayMemberBinding="{Binding DBPath}" />
+                </GridView>
+            </ListView.View>
+        </ListView>
+        <Label Margin="30,275,0,0" Content="选择微信后,请确认下方自动获取的微信名是否正确。不正确请修改!" FontWeight="Bold" HorizontalAlignment="Left" VerticalAlignment="Top"/>
+        <TextBox x:Name="txt_username" Margin="35,300,0,0" Width="280" HorizontalAlignment="Left" VerticalAlignment="Top" BorderThickness="0,0,0,1" Text="{Binding UserName}" />
+
+        <Label Margin="30,350,0,0" Content="请选择解密方式:" FontWeight="Bold" HorizontalAlignment="Left" VerticalAlignment="Top"/>
+        <RadioButton Margin="35,380,0,0" Content="固定地址查找" HorizontalAlignment="Left" VerticalAlignment="Top" GroupName="rb_find_key" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsChecked="{Binding KeyType, Converter={StaticResource ResourceKey=getKeyConverterKey}, ConverterParameter=1}" />
+        <RadioButton Margin="35,405,0,0" Content="用户名推断查找" HorizontalAlignment="Left" VerticalAlignment="Top" GroupName="rb_find_key" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsChecked="{Binding KeyType, Converter={StaticResource ResourceKey=getKeyConverterKey}, ConverterParameter=2}"/>
+
+        <Button Name="btn_create_worksapce" Margin="0,0,35,50" Height="60" Width="100" HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="创建工作区" BorderThickness="0" Background="#2775b6" Foreground="White" Click="btn_create_worksapce_Click">
+            <Button.Resources>
+                <Style TargetType="{x:Type Border}">
+                    <Setter Property="CornerRadius" Value="8"/>
+                </Style>
+            </Button.Resources>
+        </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" />
+    </Grid>
+</Page>

+ 135 - 0
Pages/CreateWork.xaml.cs

@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using WechatBakTool.Helpers;
+using WechatBakTool.Model;
+using WechatBakTool.ViewModel;
+
+namespace WechatBakTool.Pages
+{
+    /// <summary>
+    /// CreateWork.xaml 的交互逻辑
+    /// </summary>
+    public partial class CreateWork : Page
+    {
+        private CreateWorkViewModel ViewModel = new CreateWorkViewModel();
+        public CreateWork()
+        {
+            DataContext = ViewModel;
+            InitializeComponent();
+            GetWechatProcessInfos();
+        }
+
+        private void GetWechatProcessInfos()
+        {
+            ViewModel.ProcessInfos.Clear();
+            Process[] processes = Process.GetProcessesByName("wechat");
+            foreach (Process p in processes)
+            {
+                var lHandles = NativeAPIHelper.GetHandleInfoForPID((uint)p.Id);
+                foreach (var h in lHandles)
+                {
+                    string name = NativeAPIHelper.FindHandleName(h, p);
+                    if (name != "")
+                    {
+                        // 预留handle log
+                        if (File.Exists("handle.log"))
+                        {
+                            File.AppendAllText("handle.log", string.Format("{0}|{1}|{2}|{3}\n", p.Id, h.ObjectTypeIndex, h.HandleValue, name));
+                        }
+                        if (name.Contains("\\MicroMsg.db") && name.Substring(name.Length - 3, 3) == ".db")
+                        {
+                            ProcessInfo info = new ProcessInfo();
+                            info.ProcessId = p.Id.ToString();
+                            info.ProcessName = p.ProcessName;
+                            info.DBPath = DevicePathMapper.FromDevicePath(name);
+                            ViewModel.ProcessInfos.Add(info);
+                        }
+                    }
+                }
+            }
+        }
+
+        private void list_process_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (ViewModel.SelectProcess != null)
+            {
+                string[] name_raw = ViewModel.SelectProcess.DBPath.Split("\\");
+                ViewModel.UserName = name_raw[name_raw.Length - 3];
+
+                FileInfo fileInfo = new FileInfo(ViewModel.SelectProcess.DBPath);
+                DirectoryInfo msgParent = fileInfo.Directory!.Parent!;
+                DirectoryInfo[] accounts = msgParent.GetDirectories();
+
+                DirectoryInfo? newUserName = null;
+                foreach ( DirectoryInfo account in accounts )
+                {
+                    if(account.Name.Contains("account_")) {
+                        if(newUserName == null)
+                            newUserName = account;
+                        else
+                        {
+                            if (newUserName.LastWriteTime < account.LastWriteTime)
+                                newUserName = account;
+                        }
+                    }
+                }
+                if(newUserName != null)
+                {
+                    ViewModel.UserName = newUserName.Name.Split("_")[1];
+                }
+            }
+        }
+
+        private void btn_create_worksapce_Click(object sender, RoutedEventArgs e)
+        {
+            if(ViewModel.KeyType != -1)
+            {
+                if (ViewModel.SelectProcess != null)
+                {
+                    string path = ViewModel.SelectProcess.DBPath.Replace("\\Msg\\MicroMsg.db", "");
+                    try
+                    {
+                        //创建工作区
+                        WXWorkspace wXWorkspace = new WXWorkspace(path, ViewModel.UserName);
+                        //DB移动
+                        wXWorkspace.MoveDB();
+                        //开始解密数据库
+                        try
+                        {
+                            wXWorkspace.DecryptDB(ViewModel.SelectProcess.ProcessId, ViewModel.KeyType);
+
+                            MessageBox.Show("创建工作区成功");
+                            ((Main2)Window.GetWindow(this)).LoadWorkspace();
+                        }
+                        catch (Exception ex)
+                        {
+                            MessageBox.Show(ex.Message);
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        MessageBox.Show("创建工作区失败,请检查路径是否正确");
+                    }
+                }
+            }
+            else
+            {
+                MessageBox.Show("请选择Key获取方式", "错误");
+            }
+        }
+    }
+}

+ 3 - 3
Pages/Welcome.xaml

@@ -1,11 +1,11 @@
-<Page x:Class="WechatPCMsgBakTool.Pages.Welcome"
+<Page x:Class="WechatBakTool.Pages.Welcome"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       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:WechatPCMsgBakTool.Pages"
+      xmlns:local="clr-namespace:WechatBakTool.Pages"
       mc:Ignorable="d" 
-      d:DesignHeight="450" d:DesignWidth="800"
+      d:DesignHeight="550" d:DesignWidth="800"
       Title="Welcome" Background="White">
 
     <Grid>

+ 1 - 1
Pages/Welcome.xaml.cs

@@ -14,7 +14,7 @@ using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
 using System.Windows.Shapes;
 
-namespace WechatPCMsgBakTool.Pages
+namespace WechatBakTool.Pages
 {
     /// <summary>
     /// Welcome.xaml 的交互逻辑

+ 31 - 11
Pages/Workspace.xaml

@@ -1,11 +1,11 @@
-<Page x:Class="WechatPCMsgBakTool.Pages.Workspace"
+<Page x:Class="WechatBakTool.Pages.Workspace"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       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:WechatPCMsgBakTool.Pages"
+      xmlns:local="clr-namespace:WechatBakTool.Pages"
       mc:Ignorable="d" 
-      d:DesignHeight="450" d:DesignWidth="800"
+      d:DesignHeight="450" d:DesignWidth="720"
       Title="Workspace" Background="White">
     <Page.Resources>
         <Style x:Key="RepeatButtonTransparent" TargetType="{x:Type RepeatButton}">
@@ -51,8 +51,10 @@
         </DataTemplate>
         <DataTemplate x:Key="MsgText">
             <Grid Margin="0">
-                <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,8,0,0" FontWeight="Bold" VerticalAlignment="Top" Content="{Binding NickName}"/>
+                <Label Margin="60,25,0,8" VerticalAlignment="Top" HorizontalAlignment="Left" Width="380">
+                    <TextBlock Text="{Binding StrContent}" TextWrapping="Wrap"  />
+                </Label>
             </Grid>
         </DataTemplate>
         <DataTemplate x:Key="MsgImage">
@@ -124,12 +126,30 @@
                 </Style>
             </ListView.Resources>
         </ListView>
-        <Label Content="{Binding WXContact.UserName}" HorizontalAlignment="Left" Margin="258,21,0,0" VerticalAlignment="Top"/>
-        <ListView x:Name="list_msg" Margin="230,60,0,0" Background="Aqua" BorderThickness="0">
-            <Grid Margin="0">
-                <Label Margin="0,8,0,0" FontWeight="Bold" VerticalAlignment="Top" Content="溯雪" Width="130"/>
-                <Label Margin="60,25,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="140" Content="{Binding LastMsg}"/>
-            </Grid>
+        <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>
+        <Button Name="btn_export" Width="90" Height="30" Content="导出HTML" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="20,15" Click="btn_export_Click" IsEnabled="{Binding SelectContact}">
+            <Button.Resources>
+                <Style TargetType="{x:Type Border}">
+                    <Setter Property="CornerRadius" Value="3"/>
+                </Style>
+            </Button.Resources>
+        </Button>
+        <Button x:Name="btn_open_workspace" Width="80" Height="30" Content="打开文件夹" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,390,15" Click="btn_open_workspace_Click">
+            <Button.Resources>
+                <Style TargetType="{x:Type Border}">
+                    <Setter Property="CornerRadius" Value="3"/>
+                </Style>
+            </Button.Resources>
+        </Button>
+        <Button x:Name="btn_analyse" Width="80" Height="30" Content="旧版消息工具" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,292,15" Click="btn_analyse_Click" >
+            <Button.Resources>
+                <Style TargetType="{x:Type Border}">
+                    <Setter Property="CornerRadius" Value="3"/>
+                </Style>
+            </Button.Resources>
+        </Button>
     </Grid>
 </Page>

+ 39 - 5
Pages/Workspace.xaml.cs

@@ -3,6 +3,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
+using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -15,11 +16,11 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
-using System.Windows.Shapes;
-using WechatPCMsgBakTool.Model;
-using WechatPCMsgBakTool.ViewModel;
+using WechatBakTool.Interface;
+using WechatBakTool.Model;
+using WechatBakTool.ViewModel;
 
-namespace WechatPCMsgBakTool.Pages
+namespace WechatBakTool.Pages
 {
     /// <summary>
     /// Workspace.xaml 的交互逻辑
@@ -60,7 +61,7 @@ namespace WechatPCMsgBakTool.Pages
                 return;
             }
             List<WXMsg>? msgs = UserReader.GetWXMsgs(ViewModel.WXContact.UserName);
-            ListViewItem i = new ListViewItem();
+            list_msg.ItemsSource = msgs;
         }
 
         private void txt_find_user_TextChanged(object sender, TextChangedEventArgs e)
@@ -82,5 +83,38 @@ namespace WechatPCMsgBakTool.Pages
 
             Debug.WriteLine(ViewModel.SearchString);
         }
+
+        private void btn_export_Click(object sender, RoutedEventArgs e)
+        {
+            if(ViewModel.WXContact == null || UserReader == null)
+            {
+                MessageBox.Show("请选择联系人", "错误");
+                return;
+            }
+            IExport export = new HtmlExport();
+            export.InitTemplate(ViewModel.WXContact);
+            export.SetMsg(UserReader, ViewModel.WXContact);
+            export.SetEnd();
+            //string path = UserReader.GetSavePath(wXContact);
+            string path = Path.Combine(Main2.CurrentUserBakConfig!.UserWorkspacePath, ViewModel.WXContact.UserName + ".html");
+            export.Save(path);
+            MessageBox.Show("导出完成");
+        }
+
+        private void btn_open_workspace_Click(object sender, RoutedEventArgs e)
+        {
+            Process.Start("explorer.exe ", Main2.CurrentUserBakConfig!.UserWorkspacePath);
+        }
+
+        private void btn_analyse_Click(object sender, RoutedEventArgs e)
+        {
+            if (UserReader == null || Main2.CurrentUserBakConfig == null)
+            {
+                MessageBox.Show("请先读取数据");
+                return;
+            }
+            Analyse analyse = new Analyse(Main2.CurrentUserBakConfig, UserReader);
+            analyse.Show();
+        }
     }
 }

+ 50 - 37
README.md

@@ -1,51 +1,64 @@
-# WechatPCMsgBakTool
-微信PC聊天记录备份工具,仅支持Windows
 
-- 支持3.9.6.33版本后,若版本更新可在version.json添加版本号和地址即可完成新版本支持
-- 支持用户名推定key位置,无视版本,创建工作区请正常录入微信号
-- 导出图片、视频、音频、分享链接
-- 导出Html文件
-- 支持聊天频率分析,全消息库内容搜索
-
-**本项目仅做学习使用,主要供个人备份自己的微信记录,请勿用于非法用途。**
+# WechatBakTool
+基于C#开发的微信聊天记录备份分析工具,努力做最好用的微信备份工具。
 
-**本项目严禁商用**
+- 理论支持64位版本所有微信[1]
+- 工作区概念,支持多微信切换操作。
+- 支持导出Html文件
+- 支持聊天频率分析,全消息库内容搜索
+- 目前支持以下类型消息解析
+- [x] 文本消息
+- [x] 图片
+- [x] 语音
+- [x] 分享链接
+- [x] 群聊
+- [ ] 文件
+- [ ] 表情
 
 如果有什么好的建议或意见,或者遇到什么问题,欢迎提issue,看到会回。
 
-### 近期开发规划
-- 【进行中】UI、界面交互全面更新;
-- 【进行中】群聊支持;
-- 各种消息记录完善
-- 工作区更新逻辑完善
-- 自定义HTML模版
-- 各种数据信息统计
-
 > [!NOTE]
 > 反馈群:815054692<br/>
-> 如果觉得不错,欢迎右上角点个star!这是对作者的鼓励,谢谢!
+> 如果觉得不错,欢迎右上角点个star!这是对作者的鼓励,谢谢!<br/>
 <br/>
 
-### 使用
-<p>1.打开微信,并登录。</p>
-<p>2.在工作区上方点击新增,选择要创建的工作区微信</p>
-<p>3.如同时运行多个微信,请选择微信,请注意通过路径进行区别</p>
-<p>4.选中刚刚创建的工作区,点击解密。(如当前多开微信,请选择对应的微信进行解密)</p>
-<p>5.选中刚刚创建的工作区,点击读取</p>
-<p><b>尽情使用吧!</b></p>
+### 免责声明
+
+**本项目仅供学习使用,严禁商业使用**<br/>
+**使用本项目初衷是作者研究微信数据库的运行使用,您使用本软件导致的后果,包含但不限于数据损坏,记录丢失等问题,作者不承担相关责任。**<br/>
+**因软件特殊性质,请在使用时获得微信账号所有人授权。**
 <br/>
 
-### 注意
-<p>本项目基于.NET开发,需要安装.NET Desktop Runtime,如未安装,双击EXE时会提示。</p>
-<p>如果使用过程中发生崩溃,请删除工作区试一下,工作区即根据用户名在运行目录下生成的md5文件夹。</p>
-<p>已解密的工作区可以直接读取。</p>
-<p>再次强调,主要用于个人备份自己微信使用,请勿用于非法用途,严禁商用!</p>
+### 近期开发规划
+本项目技术栈为:
+C# + .NET6.0 + WPF MVVM(目前MVVM不是特别完全!莫喷!) <br/>
+- [x] ~~新版本UI界面开发~~
+- [ ] 完善各类消息支持
+- [ ] 性能优化
+- [ ] 打包资源文件夹
+- [ ] 手动模式(合适离线分析)
+<br/>
+
+### 使用说明
+**本说明为新版本说明,即将发版**<br/>
+0.安装.NET Desktop Runtime(如已经安装忽略)<br/>
+1.打开微信,并登录。<br/>
+2.在软件左侧下方点击**新建工作区**,<br/>
+3.在**新建工作区界面**,选择要创建工作区的微信进程,并**确认下方微信号是否正确**<br/>
+4.解密方式**推荐选择用户名推断查找**!该方式理论支持所有64位版本微信。**但该模式需要确保微信账号正确**<br/>
+5.新手请忽略其他选项,直接**点击创建工作区**,程序会自动进行工作区创建、解密。<br/><br/>
+**工作区创建完毕,点击左侧工作区,尽情使用吧!**<br/>
 <br/>
 
 ### 参考/引用
-都是站在大佬们的肩膀上完成的项目,本项目 参考/引用 了以下 项目/文章 内代码。
-##### [Mr0x01/WXDBDecrypt.NET](https://github.com/Mr0x01/WXDBDecrypt.NET)
-##### [AdminTest0/SharpWxDump](https://github.com/AdminTest0/SharpWxDump)
-##### [kn007/silk-v3-decoder](https://github.com/kn007/silk-v3-decoder)
-##### [吾爱破解chenhahacjl/微信 DAT 图片解密 (C#)](https://www.52pojie.cn/forum.php?mod=viewthread&tid=1507922)
-##### [huiyadanli/RevokeMsgPatcher](https://github.com/huiyadanli/RevokeMsgPatcher)
+项目在开发过程中参考了以下项目或资料,有引用相关代码,如有需要,推荐您可以去参考下相关资料:
+
+1. C#使用OpenSSL解密微信数据库,这里注意一下64位适配问题,注意dll引用: [Mr0x01/WXDBDecrypt.NET](https://github.com/Mr0x01/WXDBDecrypt.NET)<br/>
+2. C#使用地址获取微信Key: [AdminTest0/SharpWxDump](https://github.com/AdminTest0/SharpWxDump)
+3. 解密微信语音,我是直接调用解密,反正都要ffmpeg,多一个也是多,多两个也是多,懒得头铁实现: [kn007/silk-v3-decoder](https://github.com/kn007/silk-v3-decoder)
+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)
+
+### 其他声明
+[1] 理论支持所有64位版本指用户名推断获取Key模式,地址直接获取方式需要version.json支持,更新不是很及时。

+ 0 - 27
SelectWechat.xaml

@@ -1,27 +0,0 @@
-<Window x:Class="WechatPCMsgBakTool.SelectWechat"
-        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:WechatPCMsgBakTool"
-        mc:Ignorable="d"
-        WindowStartupLocation="CenterScreen"
-        Title="选择微信" Height="310" Width="600">
-    <Grid>
-        <Label Content="请选择您要打开的微信:" HorizontalAlignment="Left" Margin="29,27,0,0" VerticalAlignment="Top"/>
-        <ListView Name="list_process" Margin="32,55,32,110" SelectionChanged="list_process_SelectionChanged" >
-            <ListView.View>
-                <GridView>
-                    <GridViewColumn Header="进程名" Width="80" DisplayMemberBinding="{Binding ProcessName}" />
-                    <GridViewColumn Header="PID" Width="50" DisplayMemberBinding="{Binding ProcessId}" />
-                    <GridViewColumn Header="路径" Width="300" DisplayMemberBinding="{Binding DBPath}" />
-                </GridView>
-            </ListView.View>
-        </ListView>
-        <Button Name="btn_close" Content="确定并返回" HorizontalAlignment="Left" Margin="241,245,0,0" VerticalAlignment="Top" Width="97" Click="btn_close_Click"/>
-        <Label Content="用户名:" HorizontalAlignment="Left" Margin="34,190,0,0" VerticalAlignment="Top"/>
-        <TextBox Name="txt_username" HorizontalAlignment="Left" Margin="95,195,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="297"/>
-        <Label Content="注意确认用户名是否正确,如修改过用户名,请自行填写!如果需要使用推定方式获取Key必须正确!" HorizontalAlignment="Left" Margin="35,215,0,0" VerticalAlignment="Top" FontWeight="Bold"/>
-
-    </Grid>
-</Window>

+ 0 - 144
SelectWechat.xaml.cs

@@ -1,144 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Shapes;
-using System.Xml.Linq;
-using WechatPCMsgBakTool.Helpers;
-using WechatPCMsgBakTool.Model;
-
-namespace WechatPCMsgBakTool
-{
-    /// <summary>
-    /// SelectWechat.xaml 的交互逻辑
-    /// </summary>
-    public partial class SelectWechat : Window
-    {
-        List<ProcessInfo> processInfos = new List<ProcessInfo>();
-        public ProcessInfo? SelectProcess { get; set; } = null;
-        public SelectWechat()
-        {
-            InitializeComponent();
-            //GetWechatProcess();
-            GetWechatProcessInfos();
-            list_process.ItemsSource = processInfos;
-        }
-
-        private void GetWechatProcessInfos()
-        {
-            processInfos.Clear();
-            Process[] processes = Process.GetProcessesByName("wechat");
-            foreach (Process p in processes)
-            {
-                File.AppendAllText("debug.log", "wechat=>" + p.Id + "\r\n");
-                var lHandles = NativeAPIHelper.GetHandleInfoForPID((uint)p.Id);
-                
-                foreach (var h in lHandles)
-                {
-                    string name = NativeAPIHelper.FindHandleName(h, p);
-                    if (name != "")
-                    {
-                        // 预留handle log
-                        if (File.Exists("handle.log"))
-                        {
-                            File.AppendAllText("handle.log", string.Format("{0}|{1}|{2}|{3}\n", p.Id, h.ObjectTypeIndex, h.HandleValue, name));
-                        }
-                        if (name.Contains("\\MicroMsg.db") && name.Substring(name.Length - 3, 3) == ".db")
-                        {
-                            ProcessInfo info = new ProcessInfo();
-                            info.ProcessId = p.Id.ToString();
-                            info.ProcessName = p.ProcessName;
-                            info.DBPath = DevicePathMapper.FromDevicePath(name);
-                            processInfos.Add(info);
-                        }
-                    }
-                }
-            }
-        }
-
-        public void GetWechatProcess()
-        {
-            Process p = new Process();
-            p.StartInfo.FileName = "tools/handle64.exe";
-            p.StartInfo.Arguments = "-p wechat.exe";
-            p.StartInfo.UseShellExecute = false;
-            p.StartInfo.CreateNoWindow = true;
-            p.StartInfo.RedirectStandardOutput = true;
-            p.Start();
-
-            string i = p.StandardOutput.ReadToEnd();
-            if (i.Contains("SYSINTERNALS SOFTWARE LICENSE TERMS"))
-            {
-                MessageBox.Show("请先同意Handle64的使用协议,同意后关闭弹窗重新打开新增工作区即可");
-                Process p1 = new Process();
-                p1.StartInfo.FileName = "tools/handle64.exe";
-                p1.StartInfo.Arguments = "-p wechat.exe";
-                p1.Start();
-            }
-
-            string[] lines = i.Split(new string[] { "\r\n" }, StringSplitOptions.None);
-            bool hitFind = false;
-            ProcessInfo processInfo = new ProcessInfo();
-            foreach (string line in lines)
-            {
-                if (line.Length < 6)
-                    continue;
-
-                if (line.Substring(0, 6).ToLower() == "wechat")
-                {
-                    hitFind = true;
-                    processInfo = new ProcessInfo();
-                    string[] lineInfo = line.Split(' ');
-                    processInfo.ProcessName = lineInfo[0];
-                    processInfo.ProcessId = lineInfo[2];
-                }
-                if (hitFind)
-                {
-                    if (line.Substring(line.Length - 11, 11) == "MicroMsg.db")
-                    {
-                        Regex regex = new Regex("[a-zA-Z]:\\\\([a-zA-Z0-9() ]*\\\\)*\\w*.*\\w*");
-                        string path = regex.Match(line).Value;
-                        processInfo.DBPath = path;
-                        processInfos.Add(processInfo);
-                        hitFind = false;
-                    }
-                }
-            }
-
-            list_process.ItemsSource = processInfos;
-        }
-
-        private void list_process_SelectionChanged(object sender, SelectionChangedEventArgs e)
-        {
-            SelectProcess = list_process.SelectedItem as ProcessInfo;
-            if(SelectProcess != null)
-            {
-                string[] name_raw = SelectProcess.DBPath.Split("\\");
-                txt_username.Text = name_raw[name_raw.Length - 3];
-                
-            }
-            
-        }
-
-        private void btn_close_Click(object sender, RoutedEventArgs e)
-        {
-            if (SelectProcess != null)
-            {
-                SelectProcess.Account = txt_username.Text;
-            }
-            Close();
-        }
-    }
-}

+ 2 - 2
Tools.xaml

@@ -1,9 +1,9 @@
-<Window x:Class="WechatPCMsgBakTool.Tools"
+<Window x:Class="WechatBakTool.Tools"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:WechatPCMsgBakTool"
+        xmlns:local="clr-namespace:WechatBakTool"
         mc:Ignorable="d"
         Title="资源回退工具" Height="450" Width="800" WindowStartupLocation="CenterScreen">
     <Grid>

+ 2 - 2
Tools.xaml.cs

@@ -12,9 +12,9 @@ using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Model;
 
-namespace WechatPCMsgBakTool
+namespace WechatBakTool
 {
     /// <summary>
     /// Tools.xaml 的交互逻辑

+ 39 - 0
ViewModel/CreateWorkViewModel.cs

@@ -0,0 +1,39 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using WechatBakTool.Model;
+
+namespace WechatBakTool.ViewModel
+{
+    partial class CreateWorkViewModel : ObservableObject
+    {
+        [ObservableProperty]
+        private List<ProcessInfo> processInfos = new List<ProcessInfo>();
+
+        [ObservableProperty]
+        private ProcessInfo? selectProcess;
+
+        [ObservableProperty]
+        private string userName = "";
+
+        [ObservableProperty]
+        private int keyType = -1;
+    }
+
+    public class GetKeyConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+        {
+            return (int.Parse(parameter.ToString()!) == (int)value);
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+        {
+            return (bool)value ? parameter : Binding.DoNothing;
+        }
+    }
+}

+ 23 - 5
ViewModel/WorkspaceViewModel.cs

@@ -1,20 +1,38 @@
 using CommunityToolkit.Mvvm.ComponentModel;
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Model;
 
-namespace WechatPCMsgBakTool.ViewModel
+namespace WechatBakTool.ViewModel
 {
     partial class WorkspaceViewModel : ObservableObject
     {
-        [ObservableProperty]
-        public WXContact? wXContact;
+        private WXContact? wXContact = null;
+        public WXContact? WXContact {
+            get { return wXContact; }
+            set {  
+                wXContact = value;
+                OnPropertyChanged("WXContact");
+                OnPropertyChanged("SelectContact");
+            }
+        }
 
+        public bool SelectContact
+        {
+            get
+            {
+                if (WXContact == null)
+                    return false;
+                else
+                    return true;
+            }
+        }
         [ObservableProperty]
-        public List<WXContact>? contacts;
+        private ObservableCollection<WXContact>? contacts;
 
         private string searchString = "";
         public string SearchString

+ 165 - 9
WXUserReader.cs

@@ -1,27 +1,37 @@
-using SQLite;
+using K4os.Compression.LZ4.Encoders;
+using K4os.Compression.LZ4;
+using SQLite;
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.IO;
 using System.Linq;
+using System.Net.Mime;
 using System.Security.Cryptography;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows.Interop;
+using System.Windows.Media.Imaging;
+using System.Xml;
 using System.Xml.Linq;
-using WechatPCMsgBakTool.Helpers;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Helpers;
+using WechatBakTool.Model;
+using System.Windows;
 
-namespace WechatPCMsgBakTool
+namespace WechatBakTool
 {
     public class WXUserReader
     {
         private Dictionary<string, SQLiteConnection> DBInfo = new Dictionary<string, SQLiteConnection>();
         private UserBakConfig? UserBakConfig = null;
+        private Hashtable HeadImgCache = new Hashtable();
+        private Hashtable UserNameCache = new Hashtable();
         public WXUserReader(UserBakConfig userBakConfig) {
             string path = Path.Combine(userBakConfig.UserWorkspacePath, "DecDB");
             UserBakConfig = userBakConfig;
             LoadDB(path);
+            InitCache();
         }
 
         public void LoadDB(string path)
@@ -38,27 +48,117 @@ namespace WechatPCMsgBakTool
             }
         }
 
-        public List<WXContact>? GetWXContacts(string? name = null)
+        public void InitCache()
+        {
+            SQLiteConnection con = DBInfo["Misc"];
+            if (con == null)
+                return;
+
+            string query = @"SELECT * FROM ContactHeadImg1";
+            List<ContactHeadImg> imgs = con.Query<ContactHeadImg>(query);
+            foreach(ContactHeadImg item in imgs)
+            {
+                if (!HeadImgCache.ContainsKey(item.usrName))
+                {
+                    HeadImgCache.Add(item.usrName, item);
+                }
+            }
+
+            List<WXContact> contacts = GetWXContacts(null, true).ToList();
+            foreach(WXContact contact in contacts)
+            {
+                if (!UserNameCache.ContainsKey(contact.UserName))
+                    UserNameCache.Add(contact.UserName, contact);
+            }
+        }
+
+        public byte[]? GetHeadImgCahce(string username)
+        {
+            if (HeadImgCache.ContainsKey(username))
+            {
+                ContactHeadImg? img = HeadImgCache[username] as ContactHeadImg;
+                if (img == null)
+                    return null;
+                else
+                    return img.smallHeadBuf;
+            }
+            return null;
+        }
+
+        public int[] GetWXCount()
         {
             SQLiteConnection con = DBInfo["MicroMsg"];
             if (con == null)
-                return null;
+                return new int[] { 0, 0 };
+
+            string query = @"select count(*) as count from contact where type != 4";
+            int userCount = con.Query<WXCount>(query)[0].Count;
+
+            int msgCount = 0;
+            for (int i = 0; i <= 99; i++)
+            {
+                if (DBInfo.ContainsKey("MSG" + i.ToString()))
+                {
+                    con = DBInfo["MSG" + i.ToString()];
+                    if (con == null)
+                        return new int[] { userCount, 0 };
+
+                    query = "select count(*) as count from MSG";
+                    msgCount += con.Query<WXCount>(query)[0].Count;
+                }
+            }
+            return new int[] { userCount, msgCount };
+        }
+
+        public ObservableCollection<WXContact> GetWXContacts(string? name = null,bool all = false)
+        {
+            SQLiteConnection con = DBInfo["MicroMsg"];
+            if (con == null)
+                return new ObservableCollection<WXContact>();
             string query = @"select contact.*,session.strContent,contactHeadImgUrl.smallHeadImgUrl,contactHeadImgUrl.bigHeadImgUrl from contact 
             left join session on session.strUsrName = contact.username
             left join contactHeadImgUrl on contactHeadImgUrl.usrName = contact.username
             where type != 4 {searchName}
             order by nOrder desc";
-            
+
+            if (all)
+            {
+                query = query.Replace("where type != 4 ", "");
+            }
+
+            List<WXContact>? contacts = null;
             if (name != null)
             {
                 query = query.Replace("{searchName}", " and (username like ? or alias like ? or nickname like ? or remark like ?)");
-                return con.Query<WXContact>(query, $"%{name}%", $"%{name}%", $"%{name}%", $"%{name}%");
+                contacts = con.Query<WXContact>(query, $"%{name}%", $"%{name}%", $"%{name}%", $"%{name}%");
             }
             else
             {
                 query = query.Replace("{searchName}", "");
-                return con.Query<WXContact>(query);
+                contacts = con.Query<WXContact>(query);
             }
+
+            foreach (WXContact contact in contacts)
+            {
+                if(contact.Remark != "")
+                    contact.NickName = contact.Remark;
+
+                byte[]? imgBytes = GetHeadImgCahce(contact.UserName);
+                if (imgBytes != null)
+                {
+                    MemoryStream stream = new MemoryStream(imgBytes);
+                    BitmapImage bitmapImage = new BitmapImage();
+                    bitmapImage.BeginInit();
+                    bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
+                    bitmapImage.StreamSource = stream;
+                    bitmapImage.EndInit();
+                    contact.Avatar = bitmapImage;
+                }
+                else
+                    continue;
+            }
+
+            return new ObservableCollection<WXContact>(contacts);
         }
 
         public List<WXUserImg>? GetUserImgs()
@@ -108,6 +208,62 @@ namespace WechatPCMsgBakTool
 
                     foreach (WXMsg w in wXMsgs)
                     {
+                        if (UserNameCache.ContainsKey(w.StrTalker))
+                        {
+                            WXContact? contact = UserNameCache[w.StrTalker] as WXContact;
+                            if (contact != null)
+                            {
+                                if (contact.Remark != "")
+                                    w.NickName = contact.Remark;
+                                else
+                                    w.NickName = contact.NickName;
+                            }
+                        }
+
+                        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 (_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;
+                                }
+                            }
+                            else
+                            {
+                                w.NickName = "我";
+                            }
+                        }
+                        
                         tmp.Add(w);
                     }
                 }

+ 40 - 3
WXWorkspace.cs

@@ -1,15 +1,18 @@
 using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Security.Cryptography;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
-using WechatPCMsgBakTool.Model;
+using WechatBakTool.Helpers;
+using WechatBakTool.Model;
+using WechatBakTool.Pages;
 
-namespace WechatPCMsgBakTool
+namespace WechatBakTool
 {
     public class WXWorkspace
     {
@@ -24,6 +27,37 @@ namespace WechatPCMsgBakTool
         {
             UserBakConfig = userBakConfig;
         }
+
+        public void DecryptDB(string pid,int type)
+        {
+            if (UserBakConfig == null)
+            {
+                throw new Exception("没有工作区文件,无法解密");
+            }
+
+            if (!UserBakConfig.Decrypt)
+            {
+                byte[]? key = null;
+                key = DecryptionHelper.GetWechatKey(pid, type == 2, UserBakConfig.Account);
+                if (key == null)
+                {
+                    throw new Exception("获取到的密钥为空,获取失败");
+                }
+                string key_string = BitConverter.ToString(key, 0).Replace("-", string.Empty).ToLower().ToUpper();
+                string source = Path.Combine(UserBakConfig.UserWorkspacePath, "OriginalDB");
+                string to = Path.Combine(UserBakConfig.UserWorkspacePath, "DecDB");
+
+                DecryptionHelper.DecryUserData(key, source, to);
+                UserBakConfig.Decrypt = true;
+
+                WXUserReader reader = new WXUserReader(UserBakConfig);
+                int[] count = reader.GetWXCount();
+                UserBakConfig.Friends_Number = count[0].ToString();
+                UserBakConfig.Msg_Number = count[1].ToString();
+                SaveConfig(UserBakConfig);
+            }
+        }
+
         public void MoveDB()
         {
             string sourceBase = Path.Combine(UserBakConfig.UserResPath, "Msg");
@@ -50,7 +84,10 @@ namespace WechatPCMsgBakTool
                 }
             }
         }
-
+        public UserBakConfig ReturnConfig()
+        {
+            return UserBakConfig;
+        }
         public static void SaveConfig(UserBakConfig userBakConfig)
         {
             if(userBakConfig.UserWorkspacePath != "")

+ 4 - 3
WechatPCMsgBakTool.csproj → WechatBakTool.csproj

@@ -6,15 +6,16 @@
     <Nullable>enable</Nullable>
     <UseWPF>true</UseWPF>
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
-    <AssemblyVersion>0.6.0.2</AssemblyVersion>
-    <FileVersion>0.6.0.2</FileVersion>
-    <Version>0.6.0.2</Version>
+    <AssemblyVersion>0.9.0.0</AssemblyVersion>
+    <FileVersion>0.9.0.0</FileVersion>
+    <Version>0.9.0.0</Version>
   </PropertyGroup>
 
   <ItemGroup>
     <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
     <PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.3.6" />
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
+    <PackageReference Include="protobuf-net" Version="3.2.30" />
     <PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
   </ItemGroup>
 

+ 1 - 1
WechatPCMsgBakTool.sln → WechatBakTool.sln

@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 17
 VisualStudioVersion = 17.5.33530.505
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WechatPCMsgBakTool", "WechatPCMsgBakTool.csproj", "{2F385240-6FD0-47C5-9B5E-CC8D9AA55B25}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WechatBakTool", "WechatBakTool.csproj", "{2F385240-6FD0-47C5-9B5E-CC8D9AA55B25}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution