使用 DLL 注入扩展任务管理器






4.62/5 (23投票s)
2005年5月19日
4分钟阅读

168986

3019
如何使用DLL注入来扩展Windows任务管理器的功能。
引言
本文介绍如何通过DLL注入来扩展Windows任务管理器应用程序。市面上已有许多文章介绍如何使用DLL注入,但本文旨在向您展示DLL注入的实际用途。它还将为您提供任务管理器的一些实用功能。
本应用程序包含三个独立项目
- TaskExApp
这是一个在后台运行的应用程序,它会等待Windows任务管理器实例弹出。它每隔1秒钟循环检查顶层窗口,寻找任务管理器的实例。一旦检测到实例,它就会将
InstallTaskHook
DLL注入到该进程中。该应用程序有一个系统托盘图标,双击即可显示或隐藏应用程序对话框。要结束进程,只需点击“退出”按钮即可。 - InstallTaskHook
这是一个被注入到Windows任务管理器应用程序中的DLL。注入后,它会在应用程序的主菜单上创建一个名为“扩展”的额外菜单。在该菜单下有一个子菜单“获取扩展信息”,点击后将显示一个对话框,其中包含所选进程的扩展数据。您必须在进程列表中选择一个项目,并且PID字段必须可见才能使其正常工作。
- TaskExHook
这个DLL被任务管理器挂钩应用程序注入,用于获取扩展信息。目前,它获取的唯一额外信息是应用程序启动时使用的命令行参数。
显示对话框中提供的扩展信息如下:
- 命令行
应用程序启动时使用的命令行参数。
- 文件
可执行文件的完整路径。
- 模块
应用程序加载的所有模块。
请注意,在构建此应用程序时,DLL会被复制到System32目录,以便任务管理器应用程序能够找到它们。如果它们没有被复制到那里,应用程序将无法正常工作。因此,如果应用程序不起作用,请首先检查生成过程是否已正确复制了这些DLL。
背景
我过去在许多项目上都进行了广泛的Windows钩子研究,这也是我开始这个项目的初衷。然后我发现了使用CreateRemoteThread
将DLL注入进程的方法。我决定采用这种方法,既是为了学习,也因为该应用程序仅设计用于Windows 2000和XP。创建此应用程序的主要原因是,我经常需要知道应用程序的命令行参数,特别是java.exe。市面上有一些应用程序可以显示此信息,但我希望它能直接在Windows任务管理器内工作。
屏幕截图
这是主应用程序窗口,非常简单。您可以双击系统托盘图标来隐藏它而无需退出。
以下是添加到Windows任务管理器应用程序的菜单项。
这是用于显示扩展信息的对话框。
代码解析
代码相当轻量级,以下是一些更值得关注的代码方面。
从TaskExApp来看,这是它如何将InstallTaskHook
注入Windows任务管理器。
void TaskExDlg::Install(HWND hWnd, DWORD pid) { m_taskManagers.insert(pid); HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid); if (hProcess != NULL) { HANDLE hThread; char szLibPath [_MAX_PATH]; void* pLibRemote = 0; DWORD hLibModule = 0; HMODULE hKernel32 = ::GetModuleHandle("Kernel32"); if( !::GetSystemDirectory(szLibPath, _MAX_PATH)) return; strcat(szLibPath, "\\InstallTaskHook.dll"); pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE ); if( pLibRemote == NULL ) return; ::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath,sizeof(szLibPath),NULL); hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)::GetProcAddress(hKernel32, "LoadLibraryA"), pLibRemote, 0, NULL ); if( hThread != NULL ) { ::WaitForSingleObject( hThread, INFINITE ); ::GetExitCodeThread( hThread, &hLibModule ); ::CloseHandle( hThread ); } } }
InstallTaskHook
注入TaskExHook
的代码也相当相似,只是它还负责卸载DLL。DLL仅在加载过程中存在。
std::string GetCmdLineData(DWORD pid) { HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid); if (hProcess != NULL) { HANDLE hThread; char szLibPath [_MAX_PATH]; void* pLibRemote = 0; DWORD hLibModule = 0; HMODULE hKernel32 = ::GetModuleHandle("Kernel32"); ::GetSystemDirectory(szLibPath, _MAX_PATH); strcat(szLibPath, "\\TaskExHook.dll"); pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE ); if( pLibRemote == NULL ) return "Failed to get command line information...\r\n\r\n"; ::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath,sizeof(szLibPath),NULL); hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32, "LoadLibraryA"), pLibRemote, 0, NULL ); if( hThread != NULL ) { ::WaitForSingleObject( hThread, INFINITE ); ::GetExitCodeThread( hThread, &hLibModule ); ::CloseHandle( hThread ); //Now uninject the DLL using FreeLibrary... ::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE ); if( hLibModule != NULL ) { hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32, "FreeLibrary"), (void*)hLibModule, 0, NULL ); if( hThread != NULL ) { ::WaitForSingleObject( hThread, INFINITE ); ::GetExitCodeThread( hThread, &hLibModule ); ::CloseHandle( hThread ); } } } CloseHandle(hProcess); return "Command Line:\r\n\t" + std::string(g_szCmdLine); } return "Failed to get command line information...\r\n\r\n"; }
这段代码设置了调试权限,以便应用程序可以注入所有其他应用程序。
void GetDebugPrivs() { HANDLE hToken; LUID sedebugnameValue; TOKEN_PRIVILEGES tp; if (::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { if ( !::LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) ) { ::CloseHandle( hToken ); } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = sedebugnameValue; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if ( !::AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(tp), NULL, NULL ) ) { ::CloseHandle( hToken ); } ::CloseHandle( hToken ); } }
这里是InstallTaskHook
DLL实际创建菜单项并子类化主窗口以捕获菜单事件的地方。
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if( (ul_reason_for_call == DLL_PROCESS_ATTACH) ) { EnumWindows(EnumProc, GetCurrentProcessId()); if (g_hWnd) { char sz[256]; SetWindowText(g_hWnd, "Extended Task Manager"); HMENU hMenu = GetMenu(g_hWnd); int numMenus = GetMenuItemCount(hMenu); HMENU hCheck = GetSubMenu(hMenu, numMenus - 1); GetMenuString(hMenu, numMenus - 1, sz, sizeof(sz), MF_BYPOSITION); if (strcmp(sz, "Extensions")) { HMENU hPopup = CreatePopupMenu(); AppendMenu(hPopup, MF_STRING, 2112, "Get Extended Info"); AppendMenu(hMenu, MF_STRING | MF_ENABLED | MF_POPUP, (UINT_PTR)hPopup, "Extensions"); //Subclass the window with our own window procedure. wndProcOriginal = (WNDPROC)SetWindowLong(g_hWnd, GWL_WNDPROC, (LONG)(WNDPROC)FilterProc); DrawMenuBar(g_hWnd); GetDebugPrivs(); } } } return TRUE; }
问题
有时,Windows任务管理器应用程序会在注入过程中冻结。这种情况并不经常发生,所以很难调试。当发生这种情况时,只需打开另一个任务管理器实例并终止之前的实例即可。如果有人能找出原因,请告诉我。另外,该应用程序仅在Windows XP上进行了测试,但应该可以在2000/XP/2003上工作。
结论
我希望这篇文章能让您对如何利用DLL注入的优势有一个很好的了解。能够控制其他应用程序可以为您提供强大的系统控制能力,并允许您在不获取源代码的情况下对大型应用程序进行简单的修改。
我还要感谢Winspy文章,它向我介绍了CreateRemoteThread
方法进行DLL注入。