代码之家  ›  专栏  ›  技术社区  ›  Cauterite

对于XP上的控制台窗口,SetProp()失败

  •  0
  • Cauterite  · 技术社区  · 9 年前

    我正在尝试使用 SetProp 函数,但在我的XP-32bit SP3系统上,它总是失败 ERROR_ACCESS_DENIED (5) 当被赋予控制台窗口的句柄时,而在Windows7上它似乎工作得很好。

    此C程序演示了问题:

    #include <stdio.h>
    #include <windows.h>
    void main() {
        {
            HWND Wnd = GetConsoleWindow();
            SetLastError(ERROR_SUCCESS);
            int Success = SetPropW(Wnd, L"asdf", (HANDLE) 0xdeadbeef);
            int Error = GetLastError();
    
            printf("console (%x)\n", Wnd);
            printf("\tsuccess: %u\n", Success); // 0 - failed
            printf("\terror: %u\n", Error); // 5 - ERROR_ACCESS_DENIED
            printf("\tprop: %x\n\n", (unsigned) GetPropW(Wnd, L"asdf"));
        };
    
        {
            HWND Wnd = GetDesktopWindow();
            SetLastError(ERROR_SUCCESS);
            int Success = SetPropW(Wnd, L"asdf", (HANDLE) 0xdeadbeef);
            int Error = GetLastError();
    
            printf("desktop (%x)\n", Wnd);
            printf("\tsuccess: %u\n", Success); // 1 - succeeded
            printf("\terror: %u\n", Error); // 0 - ERROR_SUCCESS
            printf("\tprop: %x\n\n", (unsigned) GetPropW(Wnd, L"asdf"));
        };
    };
    

    它似乎也会影响与当前进程无关的控制台窗口,而且我没有发现任何可见的非控制台窗口会导致这种情况。

    有什么好处?SetProp文档中唯一提到的是

    当UIPI[User Interface Privilege Isolation]阻止属性更改时,GetLastError将返回5

    但是UIPI在XP上不存在,它在有UIPI的Windows7上运行良好。

    我在Wine和ReactOS实现中检查了函数的源代码,但似乎没有任何方法可以让它们这样做。实际的Microsoft实现似乎主要只是一个系统调用(在我的操作系统上是0x1213), 所以我没有办法分析它 .(更新: win32k.sys NtUserSetProp disassembly )

    如果有人能想出一个变通办法,我很想听听。

    额外代码: http://pastebin.com/RmJHxRPF - tests every window on the current desktop


    作为参考,我面临这个问题的场景是:我正在编写一个桌面shell,并且希望将元数据存储在窗口上,只要该窗口存在(即使我的shell进程停止运行),这些元数据就会一直存在。

    1 回复  |  直到 9 年前
        1
  •  0
  •   Cauterite    9 年前

    设置窗口属性的系统调用为 NtUserSetProp 在里面 %windir%\system32\win32k.sys 。下面是它的大致功能(用假名C,基于 the disassembly ):

    NtUserSetProp(hwnd, atom, value) {
    
        WND* esi = ValidateHwnd(hwnd); // call win32k!ValidateHwnd; mov esi,eax
        if (!esi) {return;}; // test esi,esi; jz win32k!NtUserSetProp+0x95
    
        // check if the window is the current thread's desktop's main window (THREADINFO.rpdesk.pDeskInfo.spwnd)
        THREADINFO* eax = gptiCurrent; // mov eax,[win32k!gptiCurrent]
        DESKTOP* eax = eax.rpdesk; // mov eax,[eax+0x3c]
        DESKTOPINFO* eax = eax.pDeskInfo; // mov eax,[eax+0x4]
        if (eax.spwnd == esi) {// cmp [eax+0x8],esi
            // SetPropW(GetDesktopWindow(), ...) takes this path
            goto SetTheProperty; // jz SetTheProperty
        };
    
        // check if the process associated with the window (WND.pti.ppi) is NOT in the same session as the current process
        EPROCESS* eax = PsGetCurrentProcess(); // call win32k!_imp__PsGetCurrentProcess
        PROCESSINFO* eax = PsGetProcessWin32Process(eax); // call win32k!_imp__PsGetProcessWin32Process
        THREADINFO* ecx = esi.pti; // mov ecx,[esi+0x8]
        PROCESSINFO* ecx = ecx.ppi; // mov ecx,[ecx+0x2c]
        DWORD eax = eax.luidSession.LowPart; // mov eax,[eax+0x160]
        if (eax != ecx.luidSession.LowPart) {// cmp eax,[ecx+0x160]
            // SetPropW(GetConsoleWindow(), ...) takes this path
            goto Fail; // jnz win32k!NtUserSetProp+0x69;
        };
        //repeat for luidSession.HighPart
    
        SetTheProperty : {
            return InternalSetProp(esi, atom, value); // call win32k!InternalSetProp
        };
    
        Fail : {
            UserSetLastError(5); // call win32k!UserSetLastError
            return; // jmp win32k!NtUserSetProp+0x93
        };
    };
    

    所有实际工作都由 InternalSetProp . NtUserSetProp 的唯一目的是检查与窗口关联的进程是否在同一会话中运行( PROCESSINFO.luidSession )作为调用函数的进程。XP上的控制台窗口由 csrss.exe 服务,它在与用户不同的会话上运行,尽管窗口实际上在用户的会话中打开,所以 NtUserSetProp 失败。它为在用户会话的桌面窗口上设置属性设置了一个例外( GetDesktopWindow() ),它也是由csrss创建的。

    在Windows 7和更高版本上,csrss不再负责控制台管理 conhost.exe 填充此角色,该角色确实在用户的会话中运行,因此 NtUserSetProp 没有投诉。

    解决方案

    我选择简单地修补函数,这样它就不会失败,事实证明这非常简单。我加载了win32k。sys作为数据文件,并将窗口切换到“反汇编”视图。最困难的部分可能是在win32k中查找函数。sys(crc32:edb43324), NtUserSetProp 从偏移量0x282F4开始。

    然后我删除(替换为 nop s) 之后的跳跃 luidSession 比较(一个用于 luidSession.LowPart ,一个用于 luidSession.HighPart ),如下图所示: patchin' dat dwiver

    完成修补程序还需要一个额外的步骤:Windows拒绝加载PE头中不包含匹配校验和的内核模块,因此必须更正校验和以匹配我们修改的文件。这个 ARTeam CheckSum Fixer 可以为我们解决这个问题: ARTeam CheckSum Fixer

    我们结束了。备份原始win32k。sys并将其替换为修补的副本。

    据我所知,这个补丁运行得很完美。我不确定什么是等效的“生产质量”解决方案,尽管可能使用另一个驱动程序来修补内存中的函数,或者添加一个直接指向 内部设置属性 ; 我当然愿意接受建议。