fzipw 2025-05-17 15:43 采纳率: 0%
浏览 25
已结题

如何给WPF应用注入dll并执行初始化?

如何给WPF应用注入dll并执行初始化?
有一个工具软件A,它是用WPF Framework4.7.2编写的。它有一个接口能执行自定义方法。
但我不清楚怎么给这个应用进行注入C#类库。
我现在在使用Vanara.PInvoke库。还有网上说的DllExport库编写的代码。但是没法用DLL Export Viewer看到我定义的初始化函数。
在获取SayHello函数地址获取的值是0

下面是注入的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

namespace OmronWLXImport
{
    public class Class1
    {
        [DllExport("SayHello")]
        public static void SayHello()
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => 
            { 
                MessageBox.Show("注入成功!"); 
            }));
        }
    }
}

这个是触发注入的按钮

        private void Btn_8_Click(object sender, RoutedEventArgs e)
        {
            List<Action> actions = new List<Action>();
            try
            {
                // 1. 查找目标进程
                Process[] targetProcess_Array = Process.GetProcessesByName("SysmacStudio");
                Process targetProcess = null;
                if (targetProcess_Array.Length == 0)
                {
                    Trace.WriteLine("SysmacStudio 未启动");
                    return;
                }
                targetProcess = targetProcess_Array[0];
                var PROCESS_ALL_ACCESS = ACCESS_MASK.STANDARD_RIGHTS_REQUIRED | ACCESS_MASK.SYNCHRONIZE | ACCESS_MASK.SPECIFIC_RIGHTS_ALL;
                var hProcess = Kernel32.OpenProcess(PROCESS_ALL_ACCESS, false, (uint)targetProcess.Id);
                
                if (hProcess.IsInvalid)
                {
                    Trace.WriteLine("句柄无效, 应用权限可能不足");
                    return;
                }
                actions.Add(() =>
                {
                    hProcess.Dispose();
                    Trace.WriteLine("句柄释放成功!");
                });

                // 3. 获取LoadLibraryW函数地址(使用Vanara的Kernel32.GetProcAddress)
                var k = Kernel32.GetModuleHandle("kernel32.dll");
                IntPtr loadLibraryAddr = Kernel32.GetProcAddress(Kernel32.GetModuleHandle("kernel32.dll"), "LoadLibraryW");
                if (loadLibraryAddr == IntPtr.Zero)
                {
                    Trace.WriteLine("无法获取LoadLibraryW地址!");
                    return;
                }

                // 4. 在目标进程中分配内存(使用Vanara的Kernel32.VirtualAllocEx)
                string dllPath = OmronWLXImportPath;
                if (!File.Exists(dllPath))
                {
                    Trace.WriteLine("dll文件不存在");
                    return;
                }

                byte[] dllPathBytes = Encoding.Unicode.GetBytes(dllPath+'\0');
                IntPtr allocMemAddress = Kernel32.VirtualAllocEx(
                    hProcess,
                    IntPtr.Zero,
                    (uint)dllPathBytes.Length,
                    Kernel32.MEM_ALLOCATION_TYPE.MEM_COMMIT | Kernel32.MEM_ALLOCATION_TYPE.MEM_RESERVE,
                    Kernel32.MEM_PROTECTION.PAGE_READWRITE);
                if (allocMemAddress == IntPtr.Zero)
                {
                    Trace.WriteLine("内存分配失败!");
                    return;
                }
                actions.Add(()=>
                {
                    VirtualFreeEx(hProcess, allocMemAddress, 0, MEM_ALLOCATION_TYPE.MEM_RELEASE);
                    Trace.WriteLine("路径内存释放成功!");
                });


                // 5. 写入 DLL路径 到目标进程(使用Vanara的Kernel32.WriteProcessMemory)
                if (!Kernel32.WriteProcessMemory(
                    hProcess,
                    allocMemAddress,
                    dllPathBytes,
                    (uint)dllPathBytes.Length,
                    out _))
                {
                    Trace.WriteLine("写入内存失败!");
                    return;
                }

                // 6. 创建远程线程执行LoadLibraryW(使用Vanara的Kernel32.CreateRemoteThread)
                using var hThread = Kernel32.CreateRemoteThread(
                                                                hProcess,
                                                                lpThreadAttributes: null,
                                                                dwStackSize: 0,
                                                                lpStartAddress: loadLibraryAddr,
                                                                lpParameter: allocMemAddress,
                                                                dwCreationFlags: 0,
                                                                out _);
                if (hThread.IsInvalid)
                {
                    Trace.WriteLine("远程线程创建失败!");
                    return;
                }
                // 等待线程执行完成
                Kernel32.WaitForSingleObject(hThread, 3000);
                Trace.WriteLine("DLL注入成功!");
                targetProcess_Array = Process.GetProcessesByName("SysmacStudio");
                targetProcess = targetProcess_Array[0];
                ;

                // 获取 SayHello 函数地址
                // 首先需要确保 DLL 已经在目标进程中加载
                IntPtr hModule = IntPtr.Zero;
                foreach (ProcessModule module in targetProcess.Modules)
                {
                    if (module.FileName == dllPath)
                    {
                        hModule = module.BaseAddress;
                        break;
                    }
                }
                if (hModule == IntPtr.Zero)
                {
                    Trace.WriteLine("DLL 在目标进程中未加载!");
                    return;
                }

                //在这里获取不到接口
                // 获取SayHello函数地址
                IntPtr sayHelloAddr = Kernel32.GetProcAddress(hModule, "SayHello");
                if (sayHelloAddr == IntPtr.Zero)
                {
                    Trace.WriteLine("无法获取SayHello地址!");
                    return;
                }
                ;

                // 创建远程线程执行 SayHello 方法
                using var hSayHelloThread = Kernel32.CreateRemoteThread(
                    hProcess,
                    lpThreadAttributes: null,
                    dwStackSize: 0,
                    lpStartAddress: sayHelloAddr,
                    lpParameter: IntPtr.Zero,
                    dwCreationFlags: 0,
                    out _);
                if (hSayHelloThread.IsInvalid)
                {
                    Trace.WriteLine("远程线程创建失败!");
                    return;
                }

                // 等待 SayHello 方法执行完成
                Kernel32.WaitForSingleObject(hSayHelloThread, 3000);
                Trace.WriteLine("DLL注入成功,SayHello方法执行完成!");
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.ToString());
            }
            finally
            {
                actions.Reverse();
                //资源释放
                foreach (var action in actions)
                {
                    action.Invoke();
                }
            }
        }

  • 写回答

5条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-05-17 15:45
    关注

    让【宝码香车】来帮你解答,本回答参考DeepSeek编写,并整理提供。
    如果还有疑问可以点击头像关注私信或评论。如果答案让您满意,请采纳、点赞、关注,非常感谢!

    WPF应用DLL注入及初始化执行解决方案

    问题分析

    您遇到的主要问题是:

    1. DLL成功注入但无法找到导出的SayHello函数
    2. 使用DllExport库导出的函数在DLL Export Viewer中不可见
    3. 获取函数地址返回0

    解决方案

    1. 修正DLL导出方式

    使用DllExport时需要注意以下几点:

    // 修正后的DLL导出代码
    using System;
    using System.Windows;
    using System.Windows.Threading;
    using RGiesecke.DllExport;
    using System.Runtime.InteropServices;
    
    
    namespace OmronWLXImport
    {
        public class Class1
        {
            [DllExport("SayHello", CallingConvention = CallingConvention.StdCall)]
            public static void SayHello()
            {
                // 确保在主线程执行UI操作
                if (Application.Current != null)
                {
                    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => 
                    { 
                        MessageBox.Show("注入成功!"); 
                    }));
                }
            }
        }
    }
    

    关键修改:

    • 显式指定调用约定为StdCall
    • 添加Application.Current空检查
    • 确保DllExport属性正确应用

    2. 项目配置修正

    1. 确保项目属性中:
      • 平台目标设置为x86x64(与目标进程一致)
      • 使用.NET Framework 4.7.2
      • 启用非托管代码支持
    1. DllExport配置中:
      • 使用RGiesecke.DllExport NuGet包
      • 在项目文件中添加:
    <PropertyGroup>
      <DllExportPlatformTarget>x86</DllExportPlatformTarget>
      <DllExportCallingConvention>StdCall</DllExportCallingConvention>
    </PropertyGroup>
    

    3. 修正注入代码

    private void Btn_8_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            // 1. 查找目标进程
            var targetProcess = Process.GetProcessesByName("SysmacStudio").FirstOrDefault();
            if (targetProcess == null)
            {
                Trace.WriteLine("SysmacStudio 未启动");
                return;
            }
    
    
            // 2. 打开进程
            using var hProcess = Kernel32.OpenProcess(
                ACCESS_MASK.PROCESS_CREATE_THREAD | ACCESS_MASK.PROCESS_QUERY_INFORMATION | 
                ACCESS_MASK.PROCESS_VM_OPERATION | ACCESS_MASK.PROCESS_VM_WRITE | ACCESS_MASK.PROCESS_VM_READ,
                false, (uint)targetProcess.Id);
            
            if (hProcess.IsInvalid)
            {
                Trace.WriteLine("句柄无效, 应用权限可能不足");
                return;
            }
    
    
            // 3. 注入DLL
            string dllPath = OmronWLXImportPath;
            if (!File.Exists(dllPath))
            {
                Trace.WriteLine("dll文件不存在");
                return;
            }
    
    
            // 4. 使用更可靠的注入方法
            IntPtr hModule = InjectDll(hProcess, dllPath);
            if (hModule == IntPtr.Zero)
            {
                Trace.WriteLine("DLL注入失败");
                return;
            }
    
    
            // 5. 获取函数地址(延迟执行确保DLL完全加载)
            IntPtr sayHelloAddr = GetRemoteProcAddress(hProcess, hModule, "SayHello");
            if (sayHelloAddr == IntPtr.Zero)
            {
                Trace.WriteLine("无法获取SayHello地址");
                return;
            }
    
    
            // 6. 执行函数
            ExecuteRemoteFunction(hProcess, sayHelloAddr);
        }
        catch (Exception ex)
        {
            Trace.WriteLine(ex.ToString());
        }
    }
    
    
    private IntPtr InjectDll(SafeHandle hProcess, string dllPath)
    {
        // 分配内存
        byte[] dllPathBytes = Encoding.Unicode.GetBytes(dllPath + '\0');
        IntPtr allocMemAddress = Kernel32.VirtualAllocEx(
            hProcess,
            IntPtr.Zero,
            (uint)dllPathBytes.Length,
            Kernel32.MEM_ALLOCATION_TYPE.MEM_COMMIT | Kernel32.MEM_ALLOCATION_TYPE.MEM_RESERVE,
            Kernel32.MEM_PROTECTION.PAGE_READWRITE);
        
        if (allocMemAddress == IntPtr.Zero) return IntPtr.Zero;
    
    
        try
        {
            // 写入路径
            if (!Kernel32.WriteProcessMemory(hProcess, allocMemAddress, dllPathBytes, 
                (uint)dllPathBytes.Length, out _))
            {
                return IntPtr.Zero;
            }
    
    
            // 创建远程线程
            IntPtr loadLibraryAddr = Kernel32.GetProcAddress(
                Kernel32.GetModuleHandle("kernel32.dll"), "LoadLibraryW");
            
            using var hThread = Kernel32.CreateRemoteThread(
                hProcess, null, 0, loadLibraryAddr, allocMemAddress, 0, out _);
            
            if (hThread.IsInvalid) return IntPtr.Zero;
    
    
            // 等待DLL加载完成
            Kernel32.WaitForSingleObject(hThread, 5000);
    
    
            // 获取模块句柄
            Kernel32.GetExitCodeThread(hThread, out IntPtr hModule);
            return hModule;
        }
        finally
        {
            Kernel32.VirtualFreeEx(hProcess, allocMemAddress, 0, 
                Kernel32.MEM_ALLOCATION_TYPE.MEM_RELEASE);
        }
    }
    

    常见问题排查

    1. DLL导出不可见
      • 确保使用DllExport 1.7+版本
      • 检查构建后事件是否执行了DllExport配置
      • 使用dumpbin /exports YourDll.dll验证导出
    1. 函数地址为0
      • 确认DLL已成功加载到目标进程
      • 检查函数名是否完全匹配(包括大小写)
      • 尝试使用GetLastError获取更多错误信息
    1. WPF特定问题
      • 确保UI操作在主线程执行
      • 检查Application.Current是否可用

    替代方案

    如果仍然无法解决,可以考虑:

    1. 使用EasyHook等成熟的注入库
    2. 改用进程间通信(IPC)方式如:
      • 命名管道
      • 内存映射文件
      • WCF
    1. 使用ManagedInjector等托管代码注入工具

    调试建议

    1. 在DLL中添加日志记录:
    [DllExport("SayHello")]
    public static void SayHello()
    {
        try
        {
            File.AppendAllText("C:\\temp\\injection.log", "SayHello called\n");
            // ...原有代码
        }
        catch(Exception ex)
        {
            File.AppendAllText("C:\\temp\\injection.log", $"Error: {ex}\n");
        }
    }
    
    1. 使用Process Monitor监控DLL加载过程
    1. 在Visual Studio中附加到目标进程进行调试
    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 5月19日
  • 创建了问题 5月17日