Browse Source

1.替换handle64,使用windows api直接获取文件句柄
2.新增了分享链接的支持

Suxue 1 năm trước cách đây
mục cha
commit
cd9095859e
9 tập tin đã thay đổi với 328 bổ sung17 xóa
  1. 47 0
      Helpers/DevicePathMapper.cs
  2. 172 2
      Helpers/ProcessHelper.cs
  3. 57 1
      HtmlExport.cs
  4. 4 2
      Main.xaml.cs
  5. 2 0
      Model/WXModel.cs
  6. 10 5
      README.md
  7. 31 1
      SelectWechat.xaml.cs
  8. BIN
      Tools/handle64.exe
  9. 5 6
      WechatPCMsgBakTool.csproj

+ 47 - 0
Helpers/DevicePathMapper.cs

@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WechatPCMsgBakTool.Helpers
+{
+    public static class DevicePathMapper
+    {
+        [DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
+        private static extern uint QueryDosDevice([In] string lpDeviceName, [Out] StringBuilder lpTargetPath, [In] int ucchMax);
+
+        public static string FromDevicePath(string devicePath)
+        {
+            var drive = Array.Find(DriveInfo.GetDrives(), d => devicePath.StartsWith(d.GetDevicePath(), StringComparison.InvariantCultureIgnoreCase));
+            return drive != null ?
+                devicePath.ReplaceFirst(drive.GetDevicePath(), drive.GetDriveLetter()) :
+                null;
+        }
+
+        private static string GetDevicePath(this DriveInfo driveInfo)
+        {
+            var devicePathBuilder = new StringBuilder(128);
+            return QueryDosDevice(driveInfo.GetDriveLetter(), devicePathBuilder, devicePathBuilder.Capacity + 1) != 0 ?
+                devicePathBuilder.ToString() :
+                null;
+        }
+
+        private static string GetDriveLetter(this DriveInfo driveInfo)
+        {
+            return driveInfo.Name.Substring(0, 2);
+        }
+
+        private static string ReplaceFirst(this string text, string search, string replace)
+        {
+            int pos = text.IndexOf(search);
+            if (pos < 0)
+            {
+                return text;
+            }
+            return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
+        }
+    }
+}

+ 172 - 2
Helpers/ProcessHelper.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
+using System.Net.NetworkInformation;
 using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading.Tasks;
@@ -11,7 +12,14 @@ namespace WechatPCMsgBakTool.Helpers
 {
     public class ProcessHelper
     {
-        public static Process? GetProcess(string ProcessName)
+        private const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
+        private const int DUPLICATE_CLOSE_SOURCE = 0x1;
+        private const int DUPLICATE_SAME_ACCESS = 0x2;
+
+        private const int CNST_SYSTEM_HANDLE_INFORMATION = 0x10;
+        private const int OBJECT_TYPE_MUTANT = 17;
+
+        public static Process GetProcess(string ProcessName)
         {
             Process[] processes = Process.GetProcessesByName(ProcessName);
             if (processes.Length == 0)
@@ -31,7 +39,6 @@ namespace WechatPCMsgBakTool.Helpers
             else
                 return processes[0];
         }
-
         public static ProcessModule? FindProcessModule(int ProcessId, string ModuleName)
         {
             Process process = Process.GetProcessById(ProcessId);
@@ -43,6 +50,100 @@ namespace WechatPCMsgBakTool.Helpers
             return null;
         }
 
+        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)
         {
             byte[] array = new byte[nSize];
@@ -54,5 +155,74 @@ namespace WechatPCMsgBakTool.Helpers
 
         [DllImport("kernel32.dll")]
         public static extern int ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, 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);
+
+        [DllImport("kernel32.dll")]
+        private static extern bool GetHandleInformation(IntPtr hObject, out uint lpdwFlags);
+
+
+        [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
+        }
     }
+
 }

+ 57 - 1
HtmlExport.cs

@@ -1,4 +1,6 @@
-using System;
+using K4os.Compression.LZ4.Encoders;
+using K4os.Compression.LZ4;
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
@@ -6,6 +8,7 @@ using System.Text;
 using System.Threading.Tasks;
 using WechatPCMsgBakTool.Interface;
 using WechatPCMsgBakTool.Model;
+using System.Xml;
 
 namespace WechatPCMsgBakTool
 {
@@ -86,6 +89,59 @@ namespace WechatPCMsgBakTool
                     }
                     HtmlBody += string.Format("<p class=\"content\"><video controls style=\"max-height:300px;max-width:300px;\"><source src=\"{0}\" type=\"video/mp4\" /></video></p></div>", path);
                 }
+                else if(msg.Type== 49)
+                {
+                    using (var decoder = LZ4Decoder.Create(true, 64))
+                    {
+                        byte[] target = new byte[10240];
+                        int res = 0;
+                        if(msg.CompressContent != null)
+                            res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
+
+                        byte[] data = target.Skip(0).Take(res).ToArray();
+                        string xml = Encoding.UTF8.GetString(data);
+                        if (!string.IsNullOrEmpty(xml))
+                        {
+                            xml = xml.Replace("\n", "");
+                            XmlDocument xmlObj = new XmlDocument();
+                            xmlObj.LoadXml(xml);
+                            if(xmlObj.DocumentElement != null)
+                            {
+                                string title = "";
+                                string appName = "";
+                                string url = "";
+                                XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
+                                if(findNode != null)
+                                {
+                                    if(findNode.Count > 0)
+                                    {
+                                        title = findNode[0]!.InnerText;
+                                    }
+                                }
+                                findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
+                                if (findNode != null)
+                                {
+                                    if (findNode.Count > 0)
+                                    {
+                                        appName = findNode[0]!.InnerText;
+                                    }
+                                }
+                                findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
+                                if (findNode != null)
+                                {
+                                    if (findNode.Count > 0)
+                                    {
+                                        url = findNode[0]!.InnerText;
+                                    }
+                                }
+                                HtmlBody += string.Format("<p class=\"content\">{0}|{1}</p><p><a href=\"{2}\">点击访问</a></p></div>", appName, title, url);
+
+                            }
+                        }
+
+                    }
+
+                }
                 else if (msg.Type == 34)
                 {
                     string? path = reader.GetAttachment(WXMsgType.Audio, msg);

+ 4 - 2
Main.xaml.cs

@@ -1,4 +1,7 @@
-using Newtonsoft.Json;
+using K4os.Compression.LZ4;
+using K4os.Compression.LZ4.Encoders;
+using K4os.Compression.LZ4.Streams;
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -173,7 +176,6 @@ namespace WechatPCMsgBakTool
                 return;
             }
 
-
             IExport export = new HtmlExport();
             export.InitTemplate(wXContact);
             export.SetMsg(UserReader, wXContact);

+ 2 - 0
Model/WXModel.cs

@@ -110,6 +110,8 @@ namespace WechatPCMsgBakTool.Model
         public string StrTalker { get; set; } = "";
         [Column("StrContent")]
         public string StrContent { get; set; } = "";
+        [Column("CompressContent")]
+        public byte[]? CompressContent { get; set; }
     }
 
     [Table("Media")]

+ 10 - 5
README.md

@@ -2,19 +2,23 @@
 微信PC聊天记录备份工具,仅支持Windows
 
 - 支持3.9.6.33版本后,若版本更新可在version.json添加版本号和地址即可完成新版本支持
-- 导出图片、视频、音频
+- 导出图片、视频、音频、分享链接
 - 导出Html文件
 - 支持聊天频率分析,全消息库内容搜索
 
-本项目仅做学习使用,主要供个人备份自己的微信记录,请勿用于非法用途。
-本项目严禁商用。
+** 本项目仅做学习使用,主要供个人备份自己的微信记录,请勿用于非法用途。 **
+** 本项目严禁商用 **
+
 如果有什么好的建议或意见,或者遇到什么问题,欢迎提issue,看到会回。
+> [!NOTE]
+> 反馈群:815054692
+> 如果觉得不错,欢迎右上角点个star!这是对作者的鼓励,谢谢!
 
 #### 使用
 <p>1.打开微信,并登录。</p>
 <p>2.在工作区上方点击新增,选择要创建的工作区微信</p>
-<p>3.点新建会弹出Handle64的协议说明,同意即可。如没有内容显示,重新点一下新建即可</p>
-<p>4.选中刚刚创建的工作区,点击解密。(如当前多开微信,请选择对应的微信进行解)</p>
+<p>3.如同时运行多个微信,请选择微信,请注意通过路径进行区别</p>
+<p>4.选中刚刚创建的工作区,点击解密。(如当前多开微信,请选择对应的微信进行解)</p>
 <p>5.选中刚刚创建的工作区,点击读取</p>
 <p><b>尽情使用吧!</b></p>
 
@@ -30,3 +34,4 @@
 ##### [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)

+ 31 - 1
SelectWechat.xaml.cs

@@ -1,7 +1,9 @@
 using System;
 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;
@@ -13,6 +15,7 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Shapes;
+using WechatPCMsgBakTool.Helpers;
 using WechatPCMsgBakTool.Model;
 
 namespace WechatPCMsgBakTool
@@ -27,7 +30,34 @@ namespace WechatPCMsgBakTool
         public SelectWechat()
         {
             InitializeComponent();
-            GetWechatProcess();
+            //GetWechatProcess();
+            GetWechatProcessInfos();
+            list_process.ItemsSource = processInfos;
+        }
+
+        private void GetWechatProcessInfos()
+        {
+            processInfos.Clear();
+            Process[] processes = Process.GetProcessesByName("wechat");
+            foreach(Process p in processes)
+            {
+                var h_list = ProcessHelper.GetHandles(p);
+                foreach(var h in h_list)
+                {
+                    if(h.ObjectType == 40)
+                    {
+                        string name = ProcessHelper.FindHandleName(h, p);
+                        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()

BIN
Tools/handle64.exe


+ 5 - 6
WechatPCMsgBakTool.csproj

@@ -6,14 +6,16 @@
     <Nullable>enable</Nullable>
     <UseWPF>true</UseWPF>
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
-    <AssemblyVersion>0.4.1.0</AssemblyVersion>
-    <FileVersion>0.4.1.0</FileVersion>
-    <Version>0.4.1.0</Version>
+    <AssemblyVersion>0.5.0.0</AssemblyVersion>
+    <FileVersion>0.5.0.0</FileVersion>
+    <Version>0.5.0.0</Version>
   </PropertyGroup>
 
   <ItemGroup>
+    <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>
@@ -26,9 +28,6 @@
     <None Update="Tools\ffmpeg.exe">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
-    <None Update="Tools\handle64.exe">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
     <None Update="Tools\silk_v3_decoder.exe">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>