首页 > 系统相关 >QT程序监控不到拖拽事件如dragEnterEvent - Windows权限问题的解决方案

QT程序监控不到拖拽事件如dragEnterEvent - Windows权限问题的解决方案

时间:2024-12-31 17:51:35浏览次数:1  
标签:QT Windows token 完整性 SID 进程 dragEnterEvent tml 等级

问题:当客户端已高完整性启动(例如启动客户端的进程是Bypass UAC启动的高完整性的进程,导致客户端继承了其高完整性),由于explorer.exe资源管理器是以中等Medium权限启动,客户端的权限较高,导致设置了qt编写的客户端设置了的setAcceptDrops(true)后依然无法触发dropEvent,导致无法接受其它程序或者资源管理器拖拽过来的文件。

解决方法与代码在文章的末尾。


完整性等级概念

强制完整性控制(Mandatory Integrity Control,MIC),它是对 discretionary access control list 的补充,并且是在 DACL 之前检查的
这是从 Windows Vista 新增的安全机制,在 Windows XP 中几乎所有的进程都是运行在管理员权限下的。
在官方文档中描述为 Windows 为其划分了四个完整性级别:低、中、高、系统;但从实际上看到的对应表中,有五个等级,多了一个不受信任等级。

具有低完整性级别的主体无法写入具有中等完整性级别的对象,这就相当于提供了一种在同一用户下,根据可信程度来限制不同进程之间的交互,低完整性等级的进程都无法对高完整性进程进行注入

虽然是限制了进程间的交互,但是高低完整性的进程还是可以通过其他的进程间通信的方式来进行交互:共享内存、Sockets、RPC、Windows 消息机制、命名管道,这些是不受限制的。

微软文档:https://learn.microsoft.com/zh-cn/previous-versions/dotnet/articles/bb625963(v=msdn.10)


完整性等级

Windows 直接使用了 SID 来定义完整性等级,这样就非常容易的将此机制集成到现有的结构当中,还不用修改代码
完整性等级所使用的 SID 格式是:S-1-16-xxxx(请与Session ID会话窗口ID区分开,并不是一个概念)

16 就是强制完整性的标识,后面的 xxxx,就是所对应的 RID,用来表示完整性等级的,这个值就是上面所提到的那几个十六进制值,它们以 0x1000 为间隔,也是为了将来能够再定义其他的等级
所以组合到一起以后完整性等级所对应的 SID 就变成了

  • System
    这是最高的完整性级别,由在本地服务、网络服务和系统账户下运行的进程和服务使用。此级别的目的是在管理员和系统之间提供一个安全层,即使以完全管理员身份运行的进程也无法与系统完整性级别进程交互
    唯一的例外情况是,如果管理员账户被授予 SE_DEBUG_NAME 权限,那么他就可以在 token 中启用这个权限,来进行交互
  • High
    分配给在管理员账户下运行的进程的默认完整性级别,如果启用了 UAC,则此级别将仅提供给提升过 UAC 权限的用户
  • Medium
    授予在非管理员用户账户下运行的进程或启用 UAC 的管理员账户上的进程。
    此完整性级别的进程只能修改 HKEY_CURRENT_USER、非受保护文件夹中的文件以及具有相同或更低完整性的进程。请注意explorer.exe是Medium等级
  • Low
    最低完整性级别默认不分配给进程,它要么通过继承,要么由父进程设置。
    以低完整性级别运行的进程只能在 HKEY_CURRENT_USER\Software\AppDataLow 下操作,或者将文件写入 %USERPROFILE%\AppData\LocalLow 目录下
    低完整性进程实际上不可能对系统进行任何更改,但仍然可以读取大部分的数据。
    在 Process Explorer 中可以查看到进程的完整性等级
    可以看到 Chrome 默认启动的是 Medium 等级的,其中还有 Low 等级的,这个可能就是沙盒用到的,给它们足够低的等级,能够最大限度的减少在出现问题时所带来的影响;而大量的不被信任的进程,有可能就是各个标签页所在的处理进程

其他注意:

1.进程是无法更改自己的完整性等级的
2.进程一旦运行,完整性等级是无法再修改了,即使是更高完整性等级的进程
3.进程只能够创建具有相同或者更低完整性等级的进程
4.进程不能修改或者写入具有更高完整性等级的进程或者文件

完整性等级的限制还有几个例外的情况
1.被授予 SE_DEBUG_NAME 权限的高完整性等级的进程可以修改更高完整性等级的进程
2.中等完整性的进程可以通过一些操作提升到高完整性等级,这就是平时的 Bypass UAC 的操作
3.进程可以请求从中等完整性提升到高完整性等级,这个只能在执行的时候发生,会弹出 UAC 的提示让用户来选择


使用 Process Explorer 查看进程完整性的等级:

点击View -> Select Columns -> Process Image -> 选择Integrity Level


解决方法:

方法1:启动客户端时,使用 CreateProcessAsUser 函数启动,复制父进程的Token,调用 SetTokenInformation 修改其中的完整性等级为Medium,再付给要启动的客户端进程,这样启动的客户端就具有Medium等级了。

修改方式也有两种,一种是使用 ConvertStringSidToSid 将文本转换成对应的SID,一种是调用 AllocateAndInitializeSid 直接创建初始化对应等级的SID

知名安全标识符 (SID)对应文本

S-1-16-0      不受信任的强制等级
S-1-16-4096    低强制级别
S-1-16-8192    中强制级别
S-1-16-12288    高强制级别
S-1-16-16384    系统强制级别

(1)使用ConvertStringSidToSid

#include <Windows.h>
#include <sddl.h>
  
static void launch_notepad_as_user(HANDLE token) {
  PROCESS_INFORMATION pi;
  STARTUPINFO si;
  
  ZeroMemory(&si, sizeof(si));
  si.cb = sizeof( si );
  if (CreateProcessAsUser(token, TEXT("C:\\Windows\\Notepad.exe"), NULL, NULL,
                          NULL, FALSE, 0, NULL, NULL, &si, &pi )) {
    /* Process has been created; work with the process and wait for it to
       terminate. */
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
  }
}
  
static BOOL adjust_token_integrity_level(HANDLE &token, const char *sid) {
  /* Convert the string SID to a SID *, then adjust the token's
     privileges. */
  BOOL ret;
  PSID psd = NULL;
  if (ConvertStringSidToSidA(sid, &psd)) {    // 将"S-1-16-8192"换算成SID
    TOKEN_MANDATORY_LABEL tml;
     
    ZeroMemory(&tml, sizeof(tml));
    tml.Label.Attributes = SE_GROUP_INTEGRITY;
    tml.Label.Sid = psd;
  
    ret = SetTokenInformation(token, TokenIntegrityLevel, &tml,
                              sizeof(tml) + GetLengthSid(psd));    // 修改Token的完整性等级
     
    LocalFree(psd);
  }
  return ret;
}
  
void launch_notepad(void) {
  /* Low level; see table for integrity level string names */
  const char *requested_sid = "S-1-16-8192";        // 中强制等级
  HANDLE token_cur, token_dup;
  /* Get the current process' security token as a starting point, then modify
     a duplicate so that it runs with a fixed integrity level. */
  if (OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE |
                                            TOKEN_ADJUST_DEFAULT |
                                            TOKEN_QUERY |
                                            TOKEN_ASSIGN_PRIMARY,
                                            &token_cur)) {
    if (DuplicateTokenEx(token_cur, 0, NULL, SecurityImpersonation,  // 复制父进程token
                         TokenPrimary, &token_dup)) {
      if (adjust_token_integrity_level(token_dup, requested_sid))    // 修改token的完整性等级
        launch_notepad_as_user(token_dup);
      CloseHandle(token_dup);
    }
    CloseHandle(token_cur);
  }
}


(2)使用AllocateAndInitializeSid

// 其他如上函数和调用方式,这里只写主要流程函数
static BOOL adjust_token_integrity_level(HANDLE &hToken) 
{
    SID_IDENTIFIER_AUTHORITY MLAuthority = SECURITY_MANDATORY_LABEL_AUTHORITY; PSID pIntegritySid = NULL;
    
    // 直接创建初始化对应等级的SID
    if (!AllocateAndInitializeSid(&MLAuthority, 1, SECURITY_MANDATORY_MEDIUM_RID, 0, 0, 0, 0, 0, 0, 0, &pIntegritySid))
    {
        return FALSE;
    }
    
    TOKEN_MANDATORY_LABEL tml = {0};
    tml.Label.Attributes = SE_GROUP_INTEGRITY;
    tml.Label.Sid = pIntegritySid;
    
    // 修改Token的完整性等级
    const BOOL bRet = SetTokenInformation(hToken, TokenIntegrityLevel, &tml, (sizeof(tml) + GetLengthSid(pIntegritySid)));
    
    if (pIntegritySid) 
    { 
        FreeSid(pIntegritySid); 
    } 
    
    return bRet;
}

 

方法2:通过windows原生的事件过滤器来解决,屏蔽掉qt自带的拖拽事件过滤器,创建主窗口后启动原生事件过滤
(没试过不适合博主的项目,不知道好不好用,但应该是没问题的)

void EnableDrag(QMainWindow& w) {
    ChangeWindowMessageFilter(WM_DROPFILES, 1);
    
    w.winId() << w.effectiveWinId();
    ChangeWindowMessageFilterEx((HWND)w.effectiveWinId(), WM_DROPFILES, MSGFLT_ALLOW, NULL);
    ChangeWindowMessageFilterEx((HWND)w.effectiveWinId(), WM_COPYDATA, MSGFLT_ALLOW, NULL);
    ChangeWindowMessageFilterEx((HWND)w.effectiveWinId(), 0x0049, MSGFLT_ALLOW, NULL);

    DragAcceptFiles((HWND)w.effectiveWinId(), true);
    RevokeDragDrop((HWND)w.winId());
}

{
    QMainWindow w;
    EnableDrag(w);    // 将窗口的qt拖拽过滤器用windows原生事件过滤器替换掉
    w.setAcceptDrops(true);    // 设置窗口接收拖拽
    w.show();
    a.exec();
}
// 此时窗口已经可以接受拖拽了,重载主窗口的nativeEvent即可
// 或者如果是控件,重写控件的dragEnterEvent等函数即可

bool QMainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result) {
    if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG") {
        MSG* pMsg = reinterpret_cast<MSG*>(message);
        if (pMsg->message == WM_DROPFILES) {
            HDROP hDropInfo = (HDROP)pMsg->wParam;
            wchar_t szFilePathName[_MAX_PATH] = { 0 };
            const UINT nNumOfFiles = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
            if (nNumOfFiles > 0) {
                // DragQueryFile第二个参数为拖入文件的索引
                DragQueryFile(hDropInfo, 0, szFilePathName, _MAX_PATH); //直接取第一个 入参UINT iFile  = 0
                const QString currentfile = QString::fromWCharArray(szFilePathName);
                // currentfile 为当前拖拽文件
                // OnDragFinished(currentfile);
            }
            DragFinish(hDropInfo);
        }
    }
    return false;
}

 


参考文章链接:

https://www.cnblogs.com/SecSource/p/15949135.html

https://learn.microsoft.com/zh-cn/previous-versions/dotnet/articles/bb625963(v=msdn.10)

https://wiki.sei.cmu.edu/confluence/display/c/WIN02-C.+Restrict+privileges+when+spawning+child+processes

https://towriting.com/blog/2013/08/06/process-can-drag-drop/

 

标签:QT,Windows,token,完整性,SID,进程,dragEnterEvent,tml,等级
From: https://www.cnblogs.com/renleiguanchashi/p/18644559

相关文章

  • 一文带你轻松学会Windows版Stable Diffusion本地安装方法,快学起来!
    朋友们,大家好,正在学习研究StableDiffusion,致力于用最为朴素直白的语言为你介绍AI绘画的相关知识。StableDiffusion的门槛过高、不会安装?没关系,这篇文章教会你如何安装!(文末扫码可直接获取免费安装包~)StableDiffusion的安装部署其实并不困难,只需简单点击几下,几分钟就能......
  • 关于 qt qml 报错 QtQuick.Controls 2 没有被注册
    qml报错无法加载QtQuick.Controls背景一个简单的qtcreaterdemo,qml文件如下importQtQuick2.15importQtQuick.Window2.15Window{visible:truewidth:640height:480title:qsTr("HelloWorld")//创建一个红色的矩形Rectangle{......
  • Windows安装anaconda并使用虚拟环境
    在清华源下载Anacondahttps://mirrors.tuna.tsinghua.edu.cn/help/anaconda/安装默认安装即可测试打开安装好的AnacondaPrompt,输入conda-V打印版本即可创建虚拟环境condacreate-nmyenvpython=3.12查看虚拟环境列表condaenvlist激活虚拟环境condaactivatem......
  • WindowsServer2025官方正式版24年12月版(微软原版ISO镜像)
    MicrosoftWindowsServer202524H2官方简体中文正式版2024年12月更新版(微软原版ISO镜像)简体中文零售版2024年12月版(含标准版/标准桌面体验版/数据中心版/数据中心桌面体验版)仅64位版:WindowsServer2025Standard、WindowsServer2025Standard(DesktopExperience)......
  • windows访问不了github解决
    问题在使用腾讯云服务器时发现访问不了github网站解决在https://www.whatsmydns.net/#A/www.github.com中找出一个离国内近的ip命令行中使用如下命令测试连接pinggithub.com打开文件C:\Windows\System32\drivers\etc\hosts,在后面添加如下信息,此IP是我查到的,可......
  • 【pyqt】(一)搭建开发环境
    pyqt安装PyQt是一套用于创建图形用户界面(GUI)应用程序的Python绑定,它封装了Qt库的功能。Qt是一个用C++编写的跨平台应用程序开发框架,广泛用于开发GUI程序和触摸式应用。通过PyQt,Python开发者可以利用Qt强大的功能来构建桌面应用程序。发行版本pyqt的发行版......
  • 在Windows 10系统上编译OpenSSL3.4.0
    在Windows10系统上编译OpenSSL3.4.0现在是2024年12月30日,星期一.下载openssl源码这里我下载的是现在的时间的最新版的openssl,版本是3.4.0.https://github.com/openssl/openssl/archive/refs/tags/openssl-3.4.0.zip安装vsvs全称:VisualStudio,注意不是vscode,vscode全称......
  • 直接调用文件设置qt可执行程序的图标,运行时的图标,exe本身的图标,以及固定到任务栏时的
    //设置应用程序图标(窗口图标和任务栏图标)this->setWindowIcon(QIcon("./Icon/ReadADtool.ico"));//从资源文件中加载图标  固定到任务栏上时的图标:在pro文件添加如下指令:    设置rc文件内容:IDI_ICON1ICONDISCARDABLE"ReadADtoo......
  • Qt5.14.2 编译QtCipherSqlitePlugin ,_mm_aesimc_si128 (__m128i __X)报错解决
    1.在相同的cpu,相同版本的Qt5.14.2不同电脑下,编译由devbean/QtCipherSqlitePlugin作者处下载的源码。未修改任何东西的情况下,同事电脑一键即可编译成功!而本人则是尝试了很多次都无法编译成功!总是报错内联失败,目标特定选项不匹配等问题! 2.尝试多次后,在sqlitecipher.pro文件添......
  • [Windows] 数据恢复软件R-Studio 8.14.179623
    R-Studio是一个功能强大、节省成本的反删除和数据恢复软件系列。它采用独特的数据恢复新技术,为恢复FAT12/16/32、NTFS、NTFS5(由Windows2000/XP/2003/Vista/Windows8/Windows10创建或更新)、Ext2FS/Ext3FS(OSXLINUX文件系统)以及UFS1/UFS2(FreeBSD/OpenBSD/NetBSD文件系统)分区的......