Win7甚至在它之前开始,想要hook进程创建已经不能简单的HookCreateProcessInternalW这一个函数了,因为Vista开始引入了UAC机制。如果一个进程想要通过UAC弹窗创建管理员进程,hook这个进程的CreateProcessInternalW已经不起作用,我在几年前的博文《Win7/VistaHook CreateProcess 的特别之处》(http://blog.csdn.net/myjisgreat/article/details/46478247)已经提到了这点变化,并且给出了解决方案,Hook SHCreateProcess。然而据我最近的研究,Win10已经没有SHCreateProcess这个函数了。所以本文以Win10 x64版本的Explorer为蓝本,研究Win10下创建进程的接口,并给出Hook进程创建的解决方案。
我首先hook了KERNELBASE!CreateProcessInternalW,通过回溯,可以找到explorer的创建进程调用栈如下:
KERNELBASE!CreateProcessInternalW
KERNELBASE!CreateProcessW+0x66
KERNEL32!CreateProcessWStub+0x53
windows_storage!CInvokeCreateProcessVerb::_CallCreateProcess+0x11f
windows_storage!CInvokeCreateProcessVerb::_PrepareAndCallCreateProcess+0x203
windows_storage!CInvokeCreateProcessVerb::_TryCreateProcess+0x36
windows_storage!CInvokeCreateProcessVerb::Launch+0x58
windows_storage!CInvokeCreateProcessVerb::Execute+0x3e
windows_storage!CBindAndInvokeStaticVerb::_DoCommand+0x123
windows_storage!CBindAndInvokeStaticVerb::_TryCreateProcessDdeHandler+0x68
windows_storage!CBindAndInvokeStaticVerb::Execute+0x1b0
windows_storage!CRegDataDrivenCommand::_TryInvokeAssociation+0xaf
如果我右键->管理员运行一个程序,KERNELBASE!CreateProcessInternalW并没有hook到这个消息,这与Win7是一致的。可以看到windows_storage!CInvokeCreateProcessVerb::_CallCreateProcess是一个关键函数。简单看了下windows.storage.dll的导出函数,发现好多shell32的函数实现都分到了这个dll里。
继续研究CInvokeCreateProcessVerb::_CallCreateProcess,通过反汇编,发现其调用了一个函数windows_storage!AicLaunchAdminProcess, 看来就是它实际创建管理员进程。我们windbg bp这个函数,可以看到只有explorer创建管理员进程时,windows_storage!AicLaunchAdminProcess才会被调用,否则创建普通进程调用的是KERNELBASE!CreateProcessInternalW
通过网上查询这个函数,可以看到http://www.codeproject.com/Articles/19165/Vista-UAC-The-Definitive-Guide, AicLaunchAdminProcess这个函数在vista和win7时会被SHCreateProcess调用,这也验证了AicLaunchAdminProcess就是我们要找的调用UAC窗口创建管理员进程的关键函数。我在AicLaunchAdminProcess函数下了断点,同样可以拦截ShellExecuteEx的runas verb创建管理员进程的操作,说明Hook AicLaunchAdminProcess函数具有通用性。
我们需要hook的就是在windows.storage.dll里的AicLaunchAdminProcess。接下来是如何得到这个函数的原型,以便我们进行hook。通过IDA,得到初步的函数原型:
int __fastcall AicLaunchAdminProcess (void*a1, void *a2, int a3, int a4, char *a5, HWND a6, int a7, int a8, _DWORD *a9)
我们仍然不知道各个参数是什么含义,不过我们继续反编译调用它的上级函数CInvokeCreateProcessVerb::_CallCreateProcess:
看到调用AicLaunchAdminProcess的代码为:
v15 = AicLaunchAdminProcess(
(void *)lpApplicationName,
*(void **)(v1 + 312),
v14,
dwCreationFlags,
*(char **)(v1 + 56),
*(HWND *)(v1 + 96),
v1 + 148,
v1 + 240,
&v44);
}
同一个函数内还有调用CreateProcess和CreateProcessAsUser
v8 = CreateProcessW(
lpApplicationName,
*(LPWSTR *)(v1 + 312),
0,
0,
*(_DWORD *)(v1 + 292),
dwCreationFlags,
*(LPVOID *)(v1 + 268),
*(LPCWSTR *)(v1 + 56),
(LPSTARTUPINFOW)(v1 + 148),
(LPPROCESS_INFORMATION)(v1 + 240));
}
v8 = CreateProcessAsUserW(
*(HANDLE *)(v1 + 256),
lpApplicationName,
*(LPWSTR *)(v1 + 312),
0,
0,
*(_DWORD *)(v1 + 292),
dwCreationFlags | 0x2000000,
*(LPVOID *)(v1 + 268),
*(LPCWSTR *)(v1 + 56),
(LPSTARTUPINFOW)(v1 + 148),
(LPPROCESS_INFORMATION)(v1 + 240));
}
而这两个函数的参数含义是明确的,通过对比参数,我们可以得到AicLaunchAdminProcess的原型为:
int __fastcall AicLaunchAdminProcess(WCHAR*lpApplicationName, WCHAR *lpCommandLine,void*a3, DWORD dwCreationFlags, WCHAR *lpCurrentDirectory, HWND a6, LPSTARTUPINFOWlpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, DWORD *a9)
接下来是如何确定AicLaunchAdminProcess的地址,它不是一个导出函数,无法方便的获取地址。与我的另一篇博文http://blog.csdn.net/myjisgreat/article/details/46478303类似,我们先hook AicLaunchAdminProcess将会调用的函数GetMonitorInfoW,然后通过栈中的返回地址得到AicLaunchAdminProcess 的大致地址。反汇编可以看到AicLaunchAdminProcess的函数头之前为一堆int 3指令,而int 3指令在内存中用0xCC表示。所以我们从大致地址向前搜索内存,找到存储值0xCCCCCCCC的地址,把地址+4就是AicLaunchAdminProcess的地址了。
Hook AicLaunchAdminProcess的代码已经包含在我的Windows Hook平台FishHook中了。地址为https://github.com/Menooker/FishHook
顺便安利下我的Windows Hook平台FishHook,如果这篇文章对你有用,请star下我的项目,顺便试用下FishHook吧~