设置窗口属性的系统调用为
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
),如下图所示:
完成修补程序还需要一个额外的步骤:Windows拒绝加载PE头中不包含匹配校验和的内核模块,因此必须更正校验和以匹配我们修改的文件。这个
ARTeam CheckSum Fixer
可以为我们解决这个问题:
我们结束了。备份原始win32k。sys并将其替换为修补的副本。
据我所知,这个补丁运行得很完美。我不确定什么是等效的“生产质量”解决方案,尽管可能使用另一个驱动程序来修补内存中的函数,或者添加一个直接指向
内部设置属性
; 我当然愿意接受建议。