Suxue 1 year ago
parent
commit
33089f8fcf

+ 212 - 0
Helpers/NativeAPI.cs

@@ -0,0 +1,212 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace WechatPCMsgBakTool.Helpers
+{
+    public class NativeAPI
+    {
+        // Constants
+        //=================================================
+
+        internal static uint NTSTATUS_STATUS_SUCCESS = 0x0;
+        internal static uint NTSTATUS_STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
+        internal static uint NTSTATUS_STATUS_ACCESS_DENIED = 0xC0000022;
+
+        // API Constants
+        internal static uint SystemExtendedHandleInformation = 0x40;
+        internal static uint DUPLICATE_SAME_ACCESS = 0x2;
+
+
+        // Structs
+        //=================================================
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct OBJECT_NAME_INFORMATION
+        {
+            public UNICODE_STRING Name;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct OSVERSIONINFOEX
+        {
+            public uint OSVersionInfoSize;
+            public uint MajorVersion;
+            public uint MinorVersion;
+            public uint BuildNumber;
+            public uint PlatformId;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+            public string CSDVersion;
+            public ushort ServicePackMajor;
+            public ushort ServicePackMinor;
+            public ushort SuiteMask;
+            public byte ProductType;
+            public byte Reserved;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct UNICODE_STRING
+        {
+            public ushort Length;
+            public ushort MaximumLength;
+            public IntPtr Buffer;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct GENERIC_MAPPING
+        {
+            public uint GenericRead;
+            public uint GenericWrite;
+            public uint GenericExecute;
+            public uint GenericAll;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct OBJECT_TYPE_INFORMATION
+        {
+            public UNICODE_STRING TypeName;
+            public uint TotalNumberOfObjects;
+            public uint TotalNumberOfHandles;
+            public uint TotalPagedPoolUsage;
+            public uint TotalNonPagedPoolUsage;
+            public uint TotalNamePoolUsage;
+            public uint TotalHandleTableUsage;
+            public uint HighWaterNumberOfObjects;
+            public uint HighWaterNumberOfHandles;
+            public uint HighWaterPagedPoolUsage;
+            public uint HighWaterNonPagedPoolUsage;
+            public uint HighWaterNamePoolUsage;
+            public uint HighWaterHandleTableUsage;
+            public uint InvalidAttributes;
+            public GENERIC_MAPPING GenericMapping;
+            public uint ValidAccessMask;
+            public byte SecurityRequired;
+            public byte MaintainHandleCount;
+            public byte TypeIndex;
+            public byte ReservedByte;
+            public uint PoolType;
+            public uint DefaultPagedPoolCharge;
+            public uint DefaultNonPagedPoolCharge;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct OBJECT_ALL_TYPES_INFORMATION
+        {
+            public uint NumberOfObjectTypes;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct SYSTEM_HANDLE_INFORMATION_EX
+        {
+            public IntPtr NumberOfHandles;
+            public IntPtr Reserved;
+            public SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX[] Handles;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
+        {
+            public IntPtr Object;
+            public IntPtr UniqueProcessId;
+            public IntPtr HandleValue;
+            public uint GrantedAccess;
+            public ushort CreatorBackTraceIndex;
+            public ushort ObjectTypeIndex;
+            public uint HandleAttributes;
+            public uint Reserved;
+        }
+
+        // Enums
+        //=================================================
+
+        internal enum OBJECT_INFORMATION_CLASS
+        {
+            ObjectBasicInformation = 0,
+            ObjectNameInformation = 1,
+            ObjectTypeInformation = 2,
+            ObjectAllTypesInformation = 3,
+            ObjectHandleInformation = 4
+        }
+
+        internal enum POOL_TYPE
+        {
+            NonPagedPool,
+            NonPagedPoolExecute = NonPagedPool,
+            PagedPool,
+            NonPagedPoolMustSucceed = NonPagedPool + 2,
+            DontUseThisType,
+            NonPagedPoolCacheAligned = NonPagedPool + 4,
+            PagedPoolCacheAligned,
+            NonPagedPoolCacheAlignedMustS = NonPagedPool + 6,
+            MaxPoolType,
+            NonPagedPoolBase = 0,
+            NonPagedPoolBaseMustSucceed = NonPagedPoolBase + 2,
+            NonPagedPoolBaseCacheAligned = NonPagedPoolBase + 4,
+            NonPagedPoolBaseCacheAlignedMustS = NonPagedPoolBase + 6,
+            NonPagedPoolSession = 32,
+            PagedPoolSession = NonPagedPoolSession + 1,
+            NonPagedPoolMustSucceedSession = PagedPoolSession + 1,
+            DontUseThisTypeSession = NonPagedPoolMustSucceedSession + 1,
+            NonPagedPoolCacheAlignedSession = DontUseThisTypeSession + 1,
+            PagedPoolCacheAlignedSession = NonPagedPoolCacheAlignedSession + 1,
+            NonPagedPoolCacheAlignedMustSSession = PagedPoolCacheAlignedSession + 1,
+            NonPagedPoolNx = 512,
+            NonPagedPoolNxCacheAligned = NonPagedPoolNx + 4,
+            NonPagedPoolSessionNx = NonPagedPoolNx + 32,
+        }
+
+        internal enum PROCESS_ACCESS_FLAGS : uint
+        {
+            All = 0x001F0FFF,
+            Terminate = 0x00000001,
+            CreateThread = 0x00000002,
+            VMOperation = 0x00000008,
+            VMRead = 0x00000010,
+            VMWrite = 0x00000020,
+            DupHandle = 0x00000040,
+            SetInformation = 0x00000200,
+            QueryInformation = 0x00000400,
+            Synchronize = 0x00100000
+        }
+
+        // API
+        //=================================================
+
+        [DllImport("kernel32.dll")]
+        internal static extern bool CloseHandle(IntPtr hObject);
+
+        [DllImport("ntdll.dll")]
+        internal static extern uint RtlGetVersion(
+            ref OSVERSIONINFOEX VersionInformation);
+
+        [DllImport("ntdll.dll")]
+        internal static extern void RtlZeroMemory(
+            IntPtr Destination,
+            uint length);
+
+        [DllImport("ntdll.dll")]
+        internal static extern uint NtQueryObject(
+            IntPtr objectHandle,
+            OBJECT_INFORMATION_CLASS informationClass,
+            IntPtr informationPtr,
+            uint informationLength,
+            ref uint returnLength);
+
+        [DllImport("ntdll.dll")]
+        internal static extern uint NtQuerySystemInformation(
+            uint SystemInformationClass,
+            IntPtr SystemInformation,
+            uint SystemInformationLength,
+            ref uint ReturnLength);
+
+
+        [DllImport("kernel32.dll")]
+        internal static extern IntPtr OpenProcess(PROCESS_ACCESS_FLAGS dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
+
+        [DllImport("kernel32.dll", SetLastError = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        internal static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
+
+        [DllImport("kernel32.dll")]
+        internal static extern IntPtr GetCurrentProcess();
+    }
+}

+ 151 - 0
Helpers/NativeAPIHelper.cs

@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static WechatPCMsgBakTool.Helpers.NativeAPI;
+
+namespace WechatPCMsgBakTool.Helpers
+{
+    public class NativeAPIHelper
+    {
+        // Managed native buffer
+        internal static IntPtr AllocManagedMemory(uint iSize)
+        {
+            IntPtr pAlloc = Marshal.AllocHGlobal((int)iSize);
+            RtlZeroMemory(pAlloc, iSize);
+
+            return pAlloc;
+        }
+
+        // Free managed buffer
+        internal static bool FreeManagedMemory(IntPtr pAlloc)
+        {
+            Marshal.FreeHGlobal(pAlloc);
+
+            return true;
+        }
+
+        // Get an array of OBJECT_ALL_TYPES_INFORMATION, describing all object types
+        // Win8+ only
+        internal static string FindHandleName(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX systemHandleInformation, Process process)
+        {
+            IntPtr ipHandle = IntPtr.Zero;
+            IntPtr openProcessHandle = IntPtr.Zero;
+            IntPtr hObjectName = IntPtr.Zero;
+            try
+            {
+                PROCESS_ACCESS_FLAGS flags = PROCESS_ACCESS_FLAGS.DupHandle | PROCESS_ACCESS_FLAGS.VMRead;
+                openProcessHandle = OpenProcess(flags, false, process.Id);
+                // 通过 DuplicateHandle 访问句柄
+                if (!DuplicateHandle(openProcessHandle, systemHandleInformation.HandleValue, GetCurrentProcess(), out ipHandle, 0, false, DUPLICATE_SAME_ACCESS))
+                {
+                    return "";
+                }
+
+                uint nLength = 0;
+                hObjectName = AllocManagedMemory(256 * 1024);
+
+                // 查询句柄名称
+                while (NtQueryObject(ipHandle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, hObjectName, nLength, ref nLength) == NTSTATUS_STATUS_INFO_LENGTH_MISMATCH)
+                {
+                    FreeManagedMemory(hObjectName);
+                    if (nLength == 0)
+                    {
+                        Console.WriteLine("Length returned at zero!");
+                        return "";
+                    }
+                    hObjectName = AllocManagedMemory(nLength);
+                }
+                OBJECT_NAME_INFORMATION? objObjectName = new OBJECT_NAME_INFORMATION();
+                objObjectName = Marshal.PtrToStructure(hObjectName, objObjectName.GetType()) as OBJECT_NAME_INFORMATION?;
+                if (objObjectName == null)
+                    return "";
+                if (objObjectName.Value.Name.Buffer != IntPtr.Zero)
+                {
+                    string? strObjectName = Marshal.PtrToStringUni(objObjectName.Value.Name.Buffer);
+                    if (strObjectName != null)
+                        return strObjectName;
+                }
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.Message);
+            }
+            finally
+            {
+                FreeManagedMemory(hObjectName);
+                CloseHandle(ipHandle);
+                CloseHandle(openProcessHandle);
+            }
+            return "";
+        }
+
+        internal static List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetHandleInfoForPID(uint ProcId)
+        {
+            // Create return object
+            List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> ltei = new List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>();
+
+            // Create Buffer variable
+            IntPtr BuffPtr = IntPtr.Zero;
+
+            // Loop till success
+            uint LoopSize = 0;
+            while (true)
+            {
+                BuffPtr = AllocManagedMemory(LoopSize);
+                uint SystemInformationLength = 0;
+                uint CallRes = NtQuerySystemInformation(SystemExtendedHandleInformation, BuffPtr, LoopSize, ref SystemInformationLength);
+                if (CallRes == NTSTATUS_STATUS_INFO_LENGTH_MISMATCH)
+                {
+                    FreeManagedMemory(BuffPtr);
+                    LoopSize = Math.Max(LoopSize, SystemInformationLength);
+                }
+                else if (CallRes == NTSTATUS_STATUS_SUCCESS)
+                {
+                    break;
+                }
+                else if (CallRes == NTSTATUS_STATUS_ACCESS_DENIED)
+                {
+                    FreeManagedMemory(BuffPtr);
+                    throw new AccessViolationException("[!] Failed to query SystemExtendedHandleInformation: Access Denied");
+                }
+                else
+                {
+                    FreeManagedMemory(BuffPtr);
+                    throw new InvalidOperationException("[!] Failed to query SystemExtendedHandleInformation.");
+                }
+            }
+
+            // Read handle count
+            Int32 HandleCount = Marshal.ReadInt32(BuffPtr);
+
+            // Move Buff ptr
+            BuffPtr = (IntPtr)(BuffPtr.ToInt64() + (IntPtr.Size * 2));
+
+            // Loop handles
+            for (int i = 0; i < HandleCount; i++)
+            {
+                ulong iCurrProcId = (ulong)Marshal.ReadIntPtr((IntPtr)(BuffPtr.ToInt64() + IntPtr.Size));
+                if (ProcId == iCurrProcId)
+                {
+                    // Ptr -> SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
+                    SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX? tei = (SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX?)Marshal.PtrToStructure(BuffPtr, typeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX));
+
+                    if (tei == null)
+                        continue;
+                    else
+                        ltei.Add(tei.Value);
+                }
+
+                // Move Buffptr
+                BuffPtr = (IntPtr)(BuffPtr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX)));
+            }
+
+            // Return list
+            return ltei;
+        }
+    }
+}

+ 4 - 163
Helpers/ProcessHelper.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.IO;
 using System.Linq;
 using System.Net.NetworkInformation;
 using System.Runtime.InteropServices;
@@ -12,12 +13,8 @@ namespace WechatPCMsgBakTool.Helpers
 {
     public class ProcessHelper
     {
-        private const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
-        private const int DUPLICATE_SAME_ACCESS = 0x2;
-
-        private const int CNST_SYSTEM_HANDLE_INFORMATION = 0x10;
-
-        public static Process GetProcess(string ProcessName)
+        
+        public static Process? GetProcess(string ProcessName)
         {
             Process[] processes = Process.GetProcessesByName(ProcessName);
             if (processes.Length == 0)
@@ -83,99 +80,6 @@ namespace WechatPCMsgBakTool.Helpers
             return offset;
         }
 
-        public static List<SYSTEM_HANDLE_INFORMATION> GetHandles(Process process)
-        {
-            List<SYSTEM_HANDLE_INFORMATION> aHandles = new List<SYSTEM_HANDLE_INFORMATION>();
-            int handle_info_size = Marshal.SizeOf(new SYSTEM_HANDLE_INFORMATION()) * 20000;
-            IntPtr ptrHandleData = IntPtr.Zero;
-            try
-            {
-                ptrHandleData = Marshal.AllocHGlobal(handle_info_size);
-                int nLength = 0;
-
-                while (NtQuerySystemInformation(CNST_SYSTEM_HANDLE_INFORMATION, ptrHandleData, handle_info_size, ref nLength) == STATUS_INFO_LENGTH_MISMATCH)
-                {
-                    handle_info_size = nLength;
-                    Marshal.FreeHGlobal(ptrHandleData);
-                    ptrHandleData = Marshal.AllocHGlobal(nLength);
-                }
-
-                long handle_count = Marshal.ReadIntPtr(ptrHandleData).ToInt64();
-                IntPtr ptrHandleItem = ptrHandleData + Marshal.SizeOf(ptrHandleData);
-
-                for (long lIndex = 0; lIndex < handle_count; lIndex++)
-                {
-                    SYSTEM_HANDLE_INFORMATION? oSystemHandleInfo = new SYSTEM_HANDLE_INFORMATION();
-                    oSystemHandleInfo = Marshal.PtrToStructure(ptrHandleItem, oSystemHandleInfo.GetType()) as SYSTEM_HANDLE_INFORMATION?;
-                    if (oSystemHandleInfo == null)
-                        throw new Exception("获取SYSTEM_HANDLE_INFORMATION失败");
-                    ptrHandleItem += Marshal.SizeOf(new SYSTEM_HANDLE_INFORMATION());
-                    if (oSystemHandleInfo.Value.ProcessID != process.Id) { continue; }
-                    aHandles.Add(oSystemHandleInfo.Value);
-                }
-            }
-            catch (Exception)
-            {
-                throw;
-            }
-            finally
-            {
-                Marshal.FreeHGlobal(ptrHandleData);
-            }
-            return aHandles;
-        }
-        public static string FindHandleName(SYSTEM_HANDLE_INFORMATION systemHandleInformation, Process process)
-        {
-            IntPtr ipHandle = IntPtr.Zero;
-            IntPtr openProcessHandle = IntPtr.Zero;
-            IntPtr hObjectName = IntPtr.Zero;
-            try
-            {
-                PROCESS_ACCESS_FLAGS flags = PROCESS_ACCESS_FLAGS.DupHandle | PROCESS_ACCESS_FLAGS.VMRead;
-                openProcessHandle = OpenProcess(flags, false, process.Id);
-                // 通过 DuplicateHandle 访问句柄
-                if (!DuplicateHandle(openProcessHandle, new IntPtr(systemHandleInformation.Handle), GetCurrentProcess(), out ipHandle, 0, false, DUPLICATE_SAME_ACCESS))
-                {
-                    return "";
-                }
-
-                int nLength = 0;
-                hObjectName = Marshal.AllocHGlobal(256 * 1024);
-
-                // 查询句柄名称
-                while ((uint)(NtQueryObject(ipHandle, (int)OBJECT_INFORMATION_CLASS.ObjectNameInformation, hObjectName, nLength, ref nLength)) == STATUS_INFO_LENGTH_MISMATCH)
-                {
-                    Marshal.FreeHGlobal(hObjectName);
-                    if (nLength == 0)
-                    {
-                        Console.WriteLine("Length returned at zero!");
-                        return "";
-                    }
-                    hObjectName = Marshal.AllocHGlobal(nLength);
-                }
-                OBJECT_NAME_INFORMATION? objObjectName = new OBJECT_NAME_INFORMATION();
-                objObjectName = Marshal.PtrToStructure(hObjectName, objObjectName.GetType()) as OBJECT_NAME_INFORMATION?;
-                if (objObjectName == null)
-                    return "";
-                if (objObjectName.Value.Name.Buffer != IntPtr.Zero)
-                {
-                    string? strObjectName = Marshal.PtrToStringUni(objObjectName.Value.Name.Buffer);
-                    if (strObjectName != null)
-                        return strObjectName;
-                }
-            }
-            catch (Exception ex)
-            {
-                Console.WriteLine(ex.Message);
-            }
-            finally
-            {
-                Marshal.FreeHGlobal(hObjectName);
-                CloseHandle(ipHandle);
-                CloseHandle(openProcessHandle);
-            }
-            return "";
-        }
         // 这里开始下面是对Windows API引用声明
         public static byte[]? ReadMemoryDate(IntPtr hProcess, IntPtr lpBaseAddress, int nSize = 100)
         {
@@ -189,70 +93,7 @@ namespace WechatPCMsgBakTool.Helpers
 
         [DllImport("kernel32.dll", SetLastError = true)]
         public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int nSize, out int lpNumberOfBytesRead);
-        [DllImport("ntdll.dll")]
-        private static extern uint NtQuerySystemInformation(int SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength, ref int returnLength);
-
-        [DllImport("kernel32.dll")]
-        private static extern IntPtr OpenProcess(PROCESS_ACCESS_FLAGS dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
-
-        [DllImport("kernel32.dll", SetLastError = true)]
-        [return: MarshalAs(UnmanagedType.Bool)]
-        private static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
-
-        [DllImport("kernel32.dll")]
-        private static extern IntPtr GetCurrentProcess();
-
-        [DllImport("ntdll.dll")]
-        private static extern int NtQueryObject(IntPtr ObjectHandle, int ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength, ref int returnLength);
-
-        [DllImport("kernel32.dll")]
-        private static extern bool CloseHandle(IntPtr hObject);
-
-        [StructLayout(LayoutKind.Sequential, Pack = 1)]
-        public struct SYSTEM_HANDLE_INFORMATION
-        { // Information Class 16
-            public ushort ProcessID;
-            public ushort CreatorBackTrackIndex;
-            public byte ObjectType;
-            public byte HandleAttribute;
-            public ushort Handle;
-            public IntPtr Object_Pointer;
-            public IntPtr AccessMask;
-        }
-        private enum OBJECT_INFORMATION_CLASS : int
-        {
-            ObjectBasicInformation = 0,
-            ObjectNameInformation = 1,
-            ObjectTypeInformation = 2,
-            ObjectAllTypesInformation = 3,
-            ObjectHandleInformation = 4
-        }
-        [StructLayout(LayoutKind.Sequential, Pack = 1)]
-        private struct OBJECT_NAME_INFORMATION
-        {
-            public UNICODE_STRING Name;
-        }
-        [StructLayout(LayoutKind.Sequential)]
-        private struct UNICODE_STRING
-        {
-            public ushort Length;
-            public ushort MaximumLength;
-            public IntPtr Buffer;
-        }
-        [Flags]
-        private enum PROCESS_ACCESS_FLAGS : uint
-        {
-            All = 0x001F0FFF,
-            Terminate = 0x00000001,
-            CreateThread = 0x00000002,
-            VMOperation = 0x00000008,
-            VMRead = 0x00000010,
-            VMWrite = 0x00000020,
-            DupHandle = 0x00000040,
-            SetInformation = 0x00000200,
-            QueryInformation = 0x00000400,
-            Synchronize = 0x00100000
-        }
+        
     }
 
 }

+ 0 - 2
HtmlExport.cs

@@ -44,7 +44,6 @@ namespace WechatPCMsgBakTool
                 File.AppendAllText(path, HtmlBody);
                 HtmlBody = "";
             }
-            
         }
 
         public void SetEnd()
@@ -140,7 +139,6 @@ namespace WechatPCMsgBakTool
                         }
 
                     }
-
                 }
                 else if (msg.Type == 34)
                 {

+ 32 - 0
Main.xaml.cs

@@ -3,8 +3,10 @@ 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;
@@ -17,6 +19,7 @@ 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;
@@ -267,5 +270,34 @@ namespace WechatPCMsgBakTool
                 }
             }
         }
+
+        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("不存在");
+                        }
+                    }
+                }
+            }
+        }
     }
 }

+ 33 - 3
Main2.xaml

@@ -33,9 +33,36 @@
                 </DrawingGroup>
             </DrawingImage.Drawing>
         </DrawingImage>
+        <DrawingImage x:Key="friends_nums">
+            <DrawingImage.Drawing>
+                <DrawingGroup ClipGeometry="M0,0 V1024 H1024 V0 H0 Z">
+                    <GeometryDrawing Brush="#FFF" Geometry="F1 M1024,1024z M0,0z M512.018127441406,140.7236328125C386.078552246094,140.7236328125 283.536560058594,243.226898193359 283.536560058594,369.204376220703 283.536560058594,495.106048583984 386.003570556641,597.648864746094 512.018127441406,597.648864746094 637.994781494141,597.648864746094 740.535949707031,495.106872558594 740.535949707031,369.204376220703 740.535949707031,243.226898193359 637.994781494141,140.7236328125 512.018127441406,140.7236328125z" />
+                    <GeometryDrawing Brush="#FFF" Geometry="F1 M1024,1024z M0,0z M688.11962890625,592.435577392578C639.437561035156,630.917663574219 578.812805175781,654.760192871094 512.018127441406,654.760192871094 445.222625732422,654.760192871094 384.636596679688,630.844329833984 335.842468261719,592.435577392578 243.466674804688,651.248413085937 179.148040771484,754.530334472656 170.313385009766,874.148376464844 202.656860351563,878.545104980469 260.212310791016,883.2763671875 342.347747802734,883.2763671875L681.539367675781,883.2763671875C763.712707519531,883.2763671875 821.304412841797,878.545104980469 853.686614990234,874.148376464844 844.924468994141,754.454528808594 780.606658935547,651.248413085938 688.11962890625,592.435577392578z" />
+                </DrawingGroup>
+            </DrawingImage.Drawing>
+        </DrawingImage>
+        <DrawingImage x:Key="msg_nums">
+            <DrawingImage.Drawing>
+                <DrawingGroup ClipGeometry="M0,0 V1024 H1024 V0 H0 Z">
+                    <GeometryDrawing Brush="#FFF" Geometry="F1 M1024,1024z M0,0z M512,917.333333A308.458667,308.458667,0,0,1,425.941333,908.181333C366.592,890.88 248.298666,903.808 153.365333,922.666667 153.365333,922.666667 145.173333,924.010667 144.661333,924.010667A42.282667,42.282667,0,0,1,103.253333,871.488C123.498666,779.754667,131.306666,670.741333,115.584,596.906667A405.333333,405.333333,0,1,1,512,917.333333z M339.008,640L552.341333,640A32,32,0,1,0,552.341333,576L339.008,576A32,32,0,0,0,339.008,640z M680.341333,405.333333L339.008,405.333333A32,32,0,0,0,339.008,469.333333L680.341333,469.333333A32,32,0,1,0,680.341333,405.333333z" />
+                </DrawingGroup>
+            </DrawingImage.Drawing>
+        </DrawingImage>
+        <DataTemplate x:Key="ListViewItemContentTemplate">
+            <Grid Margin="5">
+                <Label Margin="0" Content="{Binding Account}" FontSize="16" VerticalAlignment="Top" HorizontalAlignment="Left" Foreground="White" />
+                <Image Source="{StaticResource friends_nums}" Width="15" Height="15" Margin="2,30,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" />
+                <Label Margin="13,25.5,0,0" Content="{Binding Friends_Number}" VerticalAlignment="Top" HorizontalAlignment="Left" Foreground="White" />
+                <Image Source="{StaticResource msg_nums}" Width="15" Height="15" Margin="60,30,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" />
+                <Label Margin="73,25.5,0,0" Content="{Binding Msg_Number}" VerticalAlignment="Top" HorizontalAlignment="Left" Foreground="White" />
+                <Border Margin="150,25.5,0,0"  VerticalAlignment="Top" HorizontalAlignment="Left" CornerRadius="5" BorderThickness="1" Background="White" >
+                    <Label Padding="5" FontSize="8" Content="{Binding DecryptStatus}"  Foreground="#2775b6" Background="Transparent" />
+                </Border>
+            </Grid>
+        </DataTemplate>
     </Window.Resources>
     <Grid Background="White">
-        <Image Name="img_btn_min" Source="{StaticResource svg_min}" Width="20" Height="20" HorizontalAlignment="Left" Margin="860,20,0,0" VerticalAlignment="Top">
+        <Image Panel.ZIndex="100" Name="img_btn_min" Source="{StaticResource svg_min}" Width="20" Height="20" HorizontalAlignment="Left" Margin="860,20,0,0" VerticalAlignment="Top">
             <Image.RenderTransform>
                 <RotateTransform CenterX="0.5" CenterY="0.5" />
             </Image.RenderTransform>
@@ -65,7 +92,7 @@
                 </EventTrigger>
             </Image.Triggers>
         </Image>
-        <Image Name="img_btn_close" Source="{StaticResource svg_close}" Width="20" Height="20" HorizontalAlignment="Left" Margin="900,20,0,0" VerticalAlignment="Top" MouseLeftButtonDown="img_btn_close_MouseLeftButtonDown">
+        <Image Panel.ZIndex="100" Name="img_btn_close" Source="{StaticResource svg_close}" Width="20" Height="20" HorizontalAlignment="Left" Margin="900,20,0,0" VerticalAlignment="Top" MouseLeftButtonDown="img_btn_close_MouseLeftButtonDown">
             <Image.RenderTransform>
                 <RotateTransform CenterX="0.5" CenterY="0.5" />
             </Image.RenderTransform>
@@ -96,7 +123,7 @@
             </Image.Triggers>
         </Image>
         <Grid Width="230" Background="#2775b6" HorizontalAlignment="Left" IsHitTestVisible="True">
-            <ListView Background="Wheat" Margin="0,0,0,85" />
+            <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.Triggers>
@@ -134,6 +161,9 @@
             </Grid>
             <Label Name="lab_version" Content="版本:" Margin="10" VerticalAlignment="Bottom" HorizontalAlignment="Center" Foreground="White" />
         </Grid>
+        <Grid Margin="230,0,0,0" Width="720" Background="Transparent" HorizontalAlignment="Left" Panel.ZIndex="1">
+            <Frame Name="MainFrame" Source="/Pages/Welcome.xaml" NavigationUIVisibility="Hidden" />
+        </Grid>
     </Grid>
 
 </Window>

+ 56 - 2
Main2.xaml.cs

@@ -1,5 +1,8 @@
-using System;
+using Newtonsoft.Json;
+using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -10,7 +13,7 @@ using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
-using System.Windows.Shapes;
+using WechatPCMsgBakTool.Model;
 
 namespace WechatPCMsgBakTool
 {
@@ -19,15 +22,66 @@ namespace WechatPCMsgBakTool
     /// </summary>
     public partial class Main2 : Window
     {
+        public static UserBakConfig? CurrentUserBakConfig;
+        private ObservableCollection<UserBakConfig> userBakConfigs = new ObservableCollection<UserBakConfig>();
         public Main2()
         {
             InitializeComponent();
             lab_version.Content += $" {Application.ResourceAssembly.GetName().Version}";
+            // list_workspace.Items.Add(new { Name = "sxcoder", Friends_Number=23, Msg_Number=102302, Decrypt="已解密" });
+            LoadWorkspace();
         }
 
         private void img_btn_close_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
         {
             Application.Current.Shutdown();
         }
+
+        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 list_workspace_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            UserBakConfig? config = list_workspace.SelectedItem as UserBakConfig;
+            if(config != null)
+            {
+                CurrentUserBakConfig = config;
+                MainFrame.Navigate(new Uri("pack://application:,,,/Pages/Workspace.xaml"));
+            }
+            else
+            {
+                MessageBox.Show("工作区配置文件异常,请确认工作区配置是否正常", "错误", MessageBoxButton.OK);
+                return;
+            }
+        }
     }
 }

+ 38 - 0
Model/WXModel.cs

@@ -13,10 +13,16 @@ namespace WechatPCMsgBakTool.Model
         public string UserResPath { get; set; } = "";
         public string UserWorkspacePath { get; set; } = "";
         public bool Decrypt { get; set; } = false;
+        public string DecryptStatus
+        {
+            get { return Decrypt ? "已解密" : "未解密"; }
+        }
         public string Hash { get; set; } = "";
         public string NickName { get; set; } = "";
         public string UserName { get; set; } = "";
         public string Account { get; set; } = "";
+        public string Friends_Number { get; set; } = "-";
+        public string Msg_Number { get; set; } = "-";
 
         public event PropertyChangedEventHandler? PropertyChanged;
         private void OnPropertyChanged(string propertyName)
@@ -113,6 +119,22 @@ namespace WechatPCMsgBakTool.Model
         public string StrContent { get; set; } = "";
         [Column("CompressContent")]
         public byte[]? CompressContent { get; set; }
+        [Column("BytesExtra")]
+        public byte[]? BytesExtra { get; set; }
+    }
+
+    [Table("ChatRoom")]
+    public class WXChatRoom
+    {
+        [Column("ChatRoomName")]
+        public string ChatRoomName { get; set; } = "";
+        [Column("UserNameList")]
+        public string UserNameList { get; set; } = "";
+        [Column("DisplayNameList")]
+        public string DisplayNameList { get; set; } = "";
+        [Column("RoomData")]
+        public byte[]? RoomData { get; set; }
+
     }
 
     [Table("Media")]
@@ -132,5 +154,21 @@ namespace WechatPCMsgBakTool.Model
         public string Alias { get; set; } = "";
         [Column("NickName")]
         public string NickName { get; set; } = "";
+        [Column("strContent")]
+        public string LastMsg { get; set; } = "";
+        [Column("ExtraBuf")]
+        public byte[]? ExtraBuf { get; set; }
+        [Column("smallHeadImgUrl")]
+        public string Avatar { get; set; } = "";
+    }
+
+    [Table("ContactHeadImgUrl")]
+    public class WXUserImg {
+        [Column("usrName")]
+        public string UserName { get; set; } = "";
+        [Column("smallHeadImgUrl")]
+        public string SmallImg { get; set; } = "";
+        [Column("bigHeadImgUrl")]
+        public string BigImg { get; set; } = "";
     }
 }

+ 30 - 0
Pages/Welcome.xaml

@@ -0,0 +1,30 @@
+<Page x:Class="WechatPCMsgBakTool.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"
+      mc:Ignorable="d" 
+      d:DesignHeight="450" d:DesignWidth="800"
+      Title="Welcome" Background="White">
+
+    <Grid>
+        <Label FontSize="20" Margin="30,15" Content="欢迎页" HorizontalAlignment="Left" VerticalAlignment="Top" />
+        <Label Margin="30,45,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="欢迎使用溯雪微信备份工具,阅读使用说明有助于您对本工具的了解。"></Label>
+        <Label Margin="30,65,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="1、如果您是第一次使用本工具,请点击左下角新建工作区,开始进行微信备份。"></Label>
+        <Label Margin="30,85,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="2、如果您是需要对同一微信进行再次备份导出,也请点击新建工作区,会自行覆盖。"></Label>
+        <Label Margin="30,105,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="3、完成工作建立后,请点击左侧建立的工作区,进入工作台。"></Label>
+        <Label Margin="30,125,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="4、未解密备份,请先解密微信备份,请保持微信PC版是打开状态。"></Label>
+        <Label Margin="30,145,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="---关于解密方式"></Label>
+        <Label Margin="30,165,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="---版本推定"></Label>
+        <Label Margin="30,185,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="版本推定是通过version.json内的版本基址来直接获取key进行解密,不需要依赖用户名,缺点是每次微信版本更新"></Label>
+        <Label Margin="30,205,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="都需要额外维护version.json,更新不一定及时。"></Label>
+        <Label Margin="30,225,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="---用户名推定"></Label>
+        <Label Margin="30,245,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="用户名推定是通过新建工作区的时候,填写用户名,工具通过内存搜索的方式,自动推出key所在地址,并获取。"></Label>
+        <Label Margin="30,265,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="该方式的优点在于不受版本限制,推荐使用该方式进行解密。缺点就是劳烦您一定要写对微信用户名。"></Label>
+        <Label Margin="30,285,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="5、解密完成后,读取,就能愉快使用工具啦!"></Label>
+
+        <Label Margin="30,335,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="如果感觉工具好用,欢迎到github给我点个Star!点击本条去Star  >>>  SuxueCode/WechatPCMsgBakTool" Foreground="#2775b6" MouseDown="StarGithub_MouseDown"></Label>
+        
+    </Grid>
+</Page>

+ 10 - 4
WorkSpaceItem.xaml.cs → Pages/Welcome.xaml.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -13,16 +14,21 @@ using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
 using System.Windows.Shapes;
 
-namespace WechatPCMsgBakTool
+namespace WechatPCMsgBakTool.Pages
 {
     /// <summary>
-    /// WorkSpaceItem.xaml 的交互逻辑
+    /// Welcome.xaml 的交互逻辑
     /// </summary>
-    public partial class WorkSpaceItem : UserControl
+    public partial class Welcome : Page
     {
-        public WorkSpaceItem()
+        public Welcome()
         {
             InitializeComponent();
         }
+
+        private void StarGithub_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+            Process.Start("explorer.exe", "https://github.com/SuxueCode/WechatPCMsgBakTool");
+        }
     }
 }

+ 135 - 0
Pages/Workspace.xaml

@@ -0,0 +1,135 @@
+<Page x:Class="WechatPCMsgBakTool.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"
+      mc:Ignorable="d" 
+      d:DesignHeight="450" d:DesignWidth="800"
+      Title="Workspace" Background="White">
+    <Page.Resources>
+        <Style x:Key="RepeatButtonTransparent" TargetType="{x:Type RepeatButton}">
+            <Setter Property="OverridesDefaultStyle" Value="true"/>
+            <Setter Property="Background" Value="#2775b6"/>
+            <Setter Property="Focusable" Value="false"/>
+            <Setter Property="IsTabStop" Value="false"/>
+            <Setter Property="Template">
+                <Setter.Value>
+                    <ControlTemplate TargetType="{x:Type RepeatButton}">
+                        <Border Background="{TemplateBinding Background}" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" CornerRadius="4"/>
+                    </ControlTemplate>
+                </Setter.Value>
+            </Setter>
+        </Style>
+        
+        <!-- 这里是listview滚动条的滑动块部分样式-->
+        <Style x:Key="ScrollBarThumbVertical" TargetType="{x:Type Thumb}">
+            <Setter Property="OverridesDefaultStyle" Value="true"/>
+            <Setter Property="IsTabStop" Value="false"/>
+            <Setter Property="Template">
+                <Setter.Value>
+                    <ControlTemplate TargetType="{x:Type Thumb}">
+                        <Border x:Name="rectangle" Background="#BB2775b6" Height="{TemplateBinding Height}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}" CornerRadius="4"/>
+                        <ControlTemplate.Triggers>
+                            <Trigger Property="IsMouseOver" Value="true">
+                                <Setter Property="Background" TargetName="rectangle" Value="#772775b6"/>
+                            </Trigger>
+                            <Trigger Property="IsDragging" Value="true">
+                                <Setter Property="Background" TargetName="rectangle" Value="#772775b6"/>
+                            </Trigger>
+                        </ControlTemplate.Triggers>
+                    </ControlTemplate>
+                </Setter.Value>
+            </Setter>
+        </Style>
+        <DataTemplate x:Key="ListViewItemContentTemplate">
+            <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}"/>
+            </Grid>
+        </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}"/>
+            </Grid>
+        </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}"/>
+            </Grid>
+        </DataTemplate>
+        <DataTemplate x:Key="MsgAudio">
+            <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}"/>
+            </Grid>
+        </DataTemplate>
+    </Page.Resources>
+    <Grid>
+        <!--
+        <Label FontSize="20" Margin="30,15" Content="工作台" HorizontalAlignment="Left" VerticalAlignment="Top" />
+        <Label Name="lab_status" Margin="30,45,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="当前工作区还未解密,请先解密。"></Label>
+        <Button Name="btn_decrypt" Margin="35,80,0,0" Width="60" Height="30" Content="解密" Foreground="White" Background="#2775b6" BorderThickness="0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="btn_decrypt_Click">
+            <Button.Resources>
+                <Style TargetType="{x:Type Border}">
+                    <Setter Property="CornerRadius" Value="3" />
+                </Style>
+            </Button.Resources>
+        </Button>
+        <Button Name="btn_read" Margin="125,80,0,0" Width="60" Height="30" Content="读取" IsEnabled="False" Foreground="White" Background="#2775b6" BorderThickness="0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="btn_read_Click">
+            <Button.Resources>
+                <Style TargetType="{x:Type Border}">
+                    <Setter Property="CornerRadius" Value="3" />
+                </Style>
+            </Button.Resources>
+        </Button>
+        -->
+        <TextBox Name="txt_find_user" Padding="10,0" Height="30" VerticalAlignment="Top" VerticalContentAlignment="Center" FontSize="14" HorizontalAlignment="Left" Width="230" BorderThickness="0,0,10,1" BorderBrush="#2775b6" TextChanged="txt_find_user_TextChanged" GotFocus="txt_find_user_GotFocus" Text="{Binding SearchString, Mode=TwoWay}" />
+        <ListView Margin="0,30,0,0" Background="Transparent" HorizontalAlignment="Left" Width="230" Name="list_users" ItemTemplate="{DynamicResource ListViewItemContentTemplate}" BorderThickness="0,0,1,0" BorderBrush="#2775b6" SelectionChanged="list_users_SelectionChanged" ItemsSource="{Binding Contacts}">
+            <ListView.Resources>
+                <Style TargetType="{x:Type ScrollBar}">
+                    <Setter Property="Background" Value="Transparent"/>
+                    <Setter Property="BorderBrush" Value="Transparent"/>
+                    <Setter Property="BorderThickness" Value="0" />
+                    <Setter Property="Template">
+                        <Setter.Value>
+                            <ControlTemplate TargetType="{x:Type ScrollBar}">
+                                <Grid x:Name="Bg" SnapsToDevicePixels="true" Width="8" HorizontalAlignment="Right">
+                                    <Grid.RowDefinitions>
+                                        <RowDefinition Height="0"/>
+                                        <RowDefinition Height="1*"/>
+                                        <RowDefinition Height="0"/>
+                                    </Grid.RowDefinitions>
+                                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Row="1" CornerRadius="5"/>
+                                    <Track x:Name="PART_Track" IsDirectionReversed="true" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="1">
+                                        <Track.DecreaseRepeatButton>
+                                            <RepeatButton Command="{x:Static ScrollBar.PageUpCommand}" Style="{StaticResource RepeatButtonTransparent}" HorizontalAlignment="Right" Width="8"/>
+                                        </Track.DecreaseRepeatButton>
+                                        <Track.IncreaseRepeatButton>
+                                            <RepeatButton Command="{x:Static ScrollBar.PageDownCommand}" Style="{StaticResource RepeatButtonTransparent}" HorizontalAlignment="Right" Width="8"/>
+                                        </Track.IncreaseRepeatButton>
+                                        <Track.Thumb>
+                                            <Thumb Style="{StaticResource ScrollBarThumbVertical}" Width="8" />
+                                        </Track.Thumb>
+                                    </Track>
+                                </Grid>
+                            </ControlTemplate>
+                        </Setter.Value>
+                    </Setter>
+                </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>
+        </ListView>
+    </Grid>
+</Page>

+ 86 - 0
Pages/Workspace.xaml.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+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 WechatPCMsgBakTool.Model;
+using WechatPCMsgBakTool.ViewModel;
+
+namespace WechatPCMsgBakTool.Pages
+{
+    /// <summary>
+    /// Workspace.xaml 的交互逻辑
+    /// </summary>
+    public partial class Workspace : Page
+    {
+        public WXUserReader? UserReader { get; set; }
+        private WorkspaceViewModel ViewModel { get; set; } = new WorkspaceViewModel();
+        public Workspace()
+        {
+            InitializeComponent();
+            DataContext = ViewModel;
+            UserBakConfig? config = Main2.CurrentUserBakConfig;
+            if (config != null)
+            {
+                UserReader = new WXUserReader(config);
+                if (config.Decrypt)
+                {
+                    ViewModel.Contacts = UserReader.GetWXContacts();
+                }
+            }
+        }
+
+        private void btn_read_Click(object sender, RoutedEventArgs e)
+        {
+            if (Main2.CurrentUserBakConfig == null)
+            {
+                MessageBox.Show("工作区配置加载失败,请检查配置文件是否正常","错误");
+                return;
+            }
+        }
+
+        private void list_users_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            ViewModel.WXContact = list_users.SelectedItem as WXContact;
+            if(ViewModel.WXContact == null || UserReader == null)
+            {
+                return;
+            }
+            List<WXMsg>? msgs = UserReader.GetWXMsgs(ViewModel.WXContact.UserName);
+            ListViewItem i = new ListViewItem();
+        }
+
+        private void txt_find_user_TextChanged(object sender, TextChangedEventArgs e)
+        {
+            if (UserReader == null)
+                return;
+
+            string findName = txt_find_user.Text;
+            if (txt_find_user.Text == "搜索...")
+                findName = "";
+
+            ViewModel.Contacts = UserReader.GetWXContacts(findName);
+        }
+
+        private void txt_find_user_GotFocus(object sender, RoutedEventArgs e)
+        {
+            if (txt_find_user.Text == "搜索...")
+                txt_find_user.Text = "";
+
+            Debug.WriteLine(ViewModel.SearchString);
+        }
+    }
+}

+ 13 - 0
README.md

@@ -1,7 +1,13 @@
+> [!NOTE]
+> 本分支为项目开发分支,变动较为频繁且可能不可用<br/>
+> 如果你希望观察作者的开发动态,可以参考这个分支。
+<br/>
+
 # WechatPCMsgBakTool
 微信PC聊天记录备份工具,仅支持Windows
 
 - 支持3.9.6.33版本后,若版本更新可在version.json添加版本号和地址即可完成新版本支持
+- 支持用户名推定key位置,无视版本,创建工作区请正常录入微信号
 - 导出图片、视频、音频、分享链接
 - 导出Html文件
 - 支持聊天频率分析,全消息库内容搜索
@@ -12,6 +18,13 @@
 
 如果有什么好的建议或意见,或者遇到什么问题,欢迎提issue,看到会回。
 
+### 近期开发规划
+- 【进行中】UI、界面交互全面更新;
+- 【进行中】群聊支持;
+- 各种消息记录完善
+- 工作区更新逻辑完善
+- 自定义HTML模版
+- 各种数据信息统计
 
 > [!NOTE]
 > 反馈群:815054692<br/>

+ 6 - 5
SelectWechat.xaml.cs

@@ -4,7 +4,6 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
-using System.Management;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
@@ -43,16 +42,18 @@ namespace WechatPCMsgBakTool
             Process[] processes = Process.GetProcessesByName("wechat");
             foreach (Process p in processes)
             {
-                var h_list = ProcessHelper.GetHandles(p);
-                foreach (var h in h_list)
+                File.AppendAllText("debug.log", "wechat=>" + p.Id + "\r\n");
+                var lHandles = NativeAPIHelper.GetHandleInfoForPID((uint)p.Id);
+                
+                foreach (var h in lHandles)
                 {
-                    string name = ProcessHelper.FindHandleName(h, p);
+                    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.ObjectType, h.Handle, name));
+                            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")
                         {

+ 47 - 0
ViewModel/WorkspaceViewModel.cs

@@ -0,0 +1,47 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WechatPCMsgBakTool.Model;
+
+namespace WechatPCMsgBakTool.ViewModel
+{
+    partial class WorkspaceViewModel : ObservableObject
+    {
+        [ObservableProperty]
+        public WXContact? wXContact;
+
+        [ObservableProperty]
+        public List<WXContact>? contacts;
+
+        private string searchString = "";
+        public string SearchString
+        {
+            set
+            {
+                if (value == "搜索...")
+                    searchString = "";
+                else
+                    searchString = value;
+
+                OnPropertyChanged("SearchString");
+            }
+            get
+            {
+                if (searchString == "")
+                    return "搜索...";
+                return searchString;
+            }
+        }
+
+        public string SearchRealString
+        {
+            get
+            {
+                return searchString;
+            }
+        }
+    }
+}

+ 29 - 3
WXUserReader.cs

@@ -43,15 +43,41 @@ namespace WechatPCMsgBakTool
             SQLiteConnection con = DBInfo["MicroMsg"];
             if (con == null)
                 return null;
-            string query = "select * from contact";
+            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 (name != null)
             {
-                query = "select * from contact where username like ? or alias like ? or nickname like ? or remark like ?";
+                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}%");
             }
-            return con.Query<WXContact>(query);
+            else
+            {
+                query = query.Replace("{searchName}", "");
+                return con.Query<WXContact>(query);
+            }
         }
 
+        public List<WXUserImg>? GetUserImgs()
+        {
+            SQLiteConnection con = DBInfo["MicroMsg"];
+            if (con == null)
+                return null;
+            string query = "select * from contactHeadImgUrl";
+            return con.Query<WXUserImg>(query);
+        }
+
+        public List<WXChatRoom>? GetWXChatRooms()
+        {
+            SQLiteConnection con = DBInfo["MicroMsg"];
+            if (con == null)
+                return null;
+            string query = "select * from ChatRoom";
+            return con.Query<WXChatRoom>(query);
+        }
         public List<WXMsg>? GetWXMsgs(string uid,string msg = "")
         {
             List<WXMsg> tmp = new List<WXMsg>();

+ 4 - 8
WechatPCMsgBakTool.csproj

@@ -6,16 +6,16 @@
     <Nullable>enable</Nullable>
     <UseWPF>true</UseWPF>
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
-    <AssemblyVersion>0.6.0.1</AssemblyVersion>
-    <FileVersion>0.6.0.1</FileVersion>
-    <Version>0.6.0.1</Version>
+    <AssemblyVersion>0.6.0.2</AssemblyVersion>
+    <FileVersion>0.6.0.2</FileVersion>
+    <Version>0.6.0.2</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="sqlite-net-pcl" Version="1.8.116" />
-    <PackageReference Include="System.Management" Version="7.0.2" />
   </ItemGroup>
 
   <ItemGroup>
@@ -36,8 +36,4 @@
     </None>
   </ItemGroup>
 
-  <ItemGroup>
-    <Folder Include="ViewModel\" />
-  </ItemGroup>
-
 </Project>

+ 0 - 12
WorkSpaceItem.xaml

@@ -1,12 +0,0 @@
-<UserControl x:Class="WechatPCMsgBakTool.WorkSpaceItem"
-             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"
-             mc:Ignorable="d" 
-             d:DesignHeight="80" d:DesignWidth="230" Background="White">
-    <Grid>
-            
-    </Grid>
-</UserControl>