一种在注入进程中使用WTL创建无焦点不在任务栏出现“吸附”窗口的方法和思路
最近一直在做沙箱项目,在项目快接近结尾的时候,我想给在我们沙箱中运行的程序界面打上一个标记——标识其在我们沙箱中运行的。我大致想法是:在被注入程序的顶层窗口上方显示一个“标题性”窗口,顶层窗口外框外显示一个“异形”的空心窗口。这些窗口如影子般随着其被“吸附”窗口移动而移动,大小变化而变化。(转载请指明出处)以记事本为被注入程序为例:
我用的注入和HooKApi方案是采用微软的detour库。关于如何HookApi的方法,可以参看我之前的《一种注册表沙箱的思路、实现——Hook Nt函数》。注入的方案,我采用的Detour库的DetourCreateProcessWithDll函数,该函数的W版原型是
[cpp]
BOOL WINAPI DetourCreateProcessWithDllW(LPCWSTR lpApplicationName,
__in_z LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation,
LPCSTR lpDllName,
PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
该函数从倒数第二个参数之前的全是CreateProcessW的参数,倒数第二个参数是我们要注入的DLL的路径,最后一个参数是真实的CreateProcessW的函数入口地址。该函数的实现细节是:
1 以挂起的方式启动被注入程序
2 在内存中,修改被注入程序的导入表信息,在表中增加一个我们要注入的DLL中的导出函数
3 恢复被挂起的进程
该方案通过修改程序导入表,让系统误以为该程序需要调用到我们要注入的DLL中的导出函数,于是将我们注入的DLL加载到该进程内存空间,从而实现注入。这儿有个细节要说明:该方案要求我们注入DLL要至少有一个导出函数,哪怕这个函数什么也不做。
源码中RegSandBoxMainDialog工程是个MFC工程,它用于启动我们注入的进程并实现注入。我们查看其注入代码的实现
[cpp]
UpdateData(TRUE);
char chCurrentPath[MAX_PATH] = {0};
GetModuleFileNameA( NULL, chCurrentPath, MAX_PATH );
std::string wszCurrentPath = chCurrentPath;
size_t nindex = wszCurrentPath.rfind('\\');
wszCurrentPath = wszCurrentPath.substr( 0, nindex );
wszCurrentPath += "\\";
std::string szDllPath = wszCurrentPath;
szDllPath += "HookWindow.dll"; // 拼接处注入DLL的完整路径
std::wstring wszFilePath = m_Path; // 被注入进程的路径
STARTUPINFO StartupInfo;
ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION ProcessInfo;
ZeroMemory(&ProcessInfo, sizeof(PROCESS_INFORMATION));
// FL:使用DetourCreateProcessWithDll需要注入的DLL要有一个导出函数
BOOL bSuc = DetourCreateProcessWithDll( wszFilePath.c_str(), NULL, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE ,
NULL, NULL, &StartupInfo, &ProcessInfo, szDllPath.c_str(), CreateProcessW );
HookWindow工程编译连接处HookWIndow.dll,我将在之后一步一步介绍这个DLL的编写。
HookWindow是一个Win32 dll工程,我们为其定义一个def文件HookWindow.def,其内容为:
[plain]
LIBRARY "HookWindow"
EXPORTS Notify
Notify函数是为了达到DetourCreateProcessWithDll要求:注入DLL必须要至少有一个导出函数(原因已在上面说明过)而设计的,实际这个函数什么也没做,他就是个空壳。
[plain]
VOID Notify(){
}
现在得开始考虑窗口的实现了。我们知道windows系统是消息驱动的模型,那么我们的“吸附”窗口的消息模型该是什么样的?当时我思考方案时得出以下两种方案:
1 Hook进程内窗口消息,在消息链中根据顶层窗口消息而决定我们窗口的创建、显示、隐藏和销毁。这相当于我们窗口的消息循环使用了被注入进程的顶层窗口的消息循环。
2 注入进程后,启动一个线程,该线程负责创建窗口,同时在该线程中再启动一个监视被注入进程顶层窗口的线程,该线程将根据其得到的被注入进程窗口的位置大小状态等信息告诉我们窗口应该做何种处理。
这两种方法各有其优缺点,方法1比方法2少1个线程,但是存在一种场景:当点击被注入程序顶层窗口的非客户区时,我们的窗口会被盖掉,因为这个时候还没轮到我们窗口处理该消息(SetWIndowsHookEx WH_CALLWNDPROCRET),此时我们无法让我们窗口显示在被注入进程顶层窗口前面。方法2就是比方法1多出线程数,如果我想创建两个窗口,就多出两个窗口线程,以此类推。如我设想的需求,我将创建一个管理外框异形空心窗口的线程和一个“标题”窗口,那就多出两个线程。
我觉得我这两个窗口要处理的消息非常简单,同样也想做点与众不同。于是我设计了这样的方案,方案是融合了方案1和方案2的优点:
<补充:软件开发 , C++ ,