65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.62/5 (23投票s)

2005年5月19日

4分钟阅读

viewsIcon

168986

downloadIcon

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任务管理器内工作。

屏幕截图

这是主应用程序窗口,非常简单。您可以双击系统托盘图标来隐藏它而无需退出。

Sample Image

以下是添加到Windows任务管理器应用程序的菜单项。

Sample Image

这是用于显示扩展信息的对话框。

Sample Image

代码解析

代码相当轻量级,以下是一些更值得关注的代码方面。

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注入。

© . All rights reserved.