为何WebView2控件在UAC提权应用中支持拖放?能否复刻该行为?
WebView2的UIPI拖放绕过机制及普通控件的复刻方案
一、WebView2如何绕过UIPI限制实现拖放
WebView2的核心突破点在于其多进程架构设计:
- 当宿主应用以管理员权限(高完整性级别)运行时,WebView2的渲染进程仍保持在中等完整性级别(与普通权限应用处于同一级别)。
- 拖放操作依赖OLE消息交互,由于普通应用与WebView2渲染进程的完整性级别一致,UIPI不会拦截它们之间的拖放消息。
- 渲染进程接收到拖放数据后,通过WebView2内置的受控跨进程通信(IPC)通道将数据传递给高权限宿主进程,这一交互不受UIPI限制,属于系统允许的进程间合法通信。
二、为普通控件(如TextBox)复刻该行为
要让高权限窗口中的普通控件接受普通权限应用的拖放,需模拟WebView2的多进程思路,核心步骤如下:
1. 创建中等完整性级别的辅助进程
该进程负责直接接收拖放操作(与普通应用同级别,UIPI不限制),再将数据传递给高权限主进程。
辅助进程代码(WinForms示例)
using System; using System.Windows.Forms; using System.IO.Pipes; using System.Text; namespace DragDropHelper { public partial class HelperForm : Form { private NamedPipeClientStream _pipeClient; public HelperForm() { InitializeComponent(); AllowDrop = true; DragEnter += HelperForm_DragEnter; DragDrop += HelperForm_DragDrop; // 连接到主进程的命名管道 _pipeClient = new NamedPipeClientStream(".", "ElevatedDragDropPipe", PipeDirection.Out); _pipeClient.Connect(); } private void HelperForm_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy; } private void HelperForm_DragDrop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); string data = string.Join("|", files); // 通过管道发送数据到主进程 byte[] buffer = Encoding.UTF8.GetBytes(data); _pipeClient.Write(buffer, 0, buffer.Length); } } } static class Program { [STAThread] static void Main() { Application.Run(new HelperForm()); } } }
2. 高权限主进程实现
主进程需创建命名管道监听辅助进程的消息,并将接收到的拖放数据同步到目标控件(如TextBox),同时确保辅助进程以中等完整性级别启动。
主进程代码(WinForms示例)
using System; using System.Windows.Forms; using System.Diagnostics; using System.IO.Pipes; using System.Text; using System.Security.AccessControl; using System.Security.Principal; namespace ElevatedDragDropDemo { public partial class MainForm : Form { private NamedPipeServerStream _pipeServer; private Process _helperProcess; public MainForm() { InitializeComponent(); StartHelperProcess(); StartPipeListener(); } private void StartHelperProcess() { // 启动辅助进程,强制以中等完整性级别运行 ProcessStartInfo psi = new ProcessStartInfo(@"DragDropHelper.exe"); psi.Verb = "open"; // 避免继承主进程的管理员权限 psi.UseShellExecute = true; _helperProcess = Process.Start(psi); } private void StartPipeListener() { // 配置管道权限,允许中等完整性进程访问 PipeSecurity pipeSecurity = new PipeSecurity(); pipeSecurity.AddAccessRule(new PipeAccessRule( new SecurityIdentifier(WellKnownSidType.AuthenticatedUsersSid, null), PipeAccessRights.ReadWrite, AccessControlType.Allow )); _pipeServer = new NamedPipeServerStream( "ElevatedDragDropPipe", PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 4096, 4096, pipeSecurity ); _pipeServer.BeginWaitForConnection(OnPipeConnected, null); } private void OnPipeConnected(IAsyncResult ar) { try { _pipeServer.EndWaitForConnection(ar); // 读取辅助进程发送的拖放数据 byte[] buffer = new byte[4096]; int bytesRead = _pipeServer.Read(buffer, 0, buffer.Length); string data = Encoding.UTF8.GetString(buffer, 0, bytesRead); string[] files = data.Split('|'); // 跨线程更新TextBox内容 Invoke(new Action(() => { textBox1.Text = string.Join(Environment.NewLine, files); })); // 重置管道监听状态 _pipeServer.Disconnect(); _pipeServer.BeginWaitForConnection(OnPipeConnected, null); } catch (Exception) { // 处理辅助进程退出等异常情况 } } protected override void OnFormClosing(FormClosingEventArgs e) { _helperProcess?.Kill(); _pipeServer?.Dispose(); base.OnFormClosing(e); } } }
3. 关键注意事项
- 完整性级别控制:辅助进程必须运行在中等完整性级别,通过设置
UseShellExecute = true和Verb = "open"可避免继承主进程的高权限。 - 管道权限配置:必须为命名管道添加允许Authenticated Users访问的规则,否则辅助进程无法建立连接。
- UI线程安全:主进程从管道读取数据后,需通过
Invoke方法跨线程更新UI控件,避免线程异常。
内容的提问来源于stack exchange,提问作者FaniX
相关产品推荐
相关产品推荐

