【问题标题】:Getting a handle to the process's main thread获取进程主线程的句柄
【发布时间】:2010-12-30 11:41:01
【问题描述】:

我在一些小型测试应用程序中创建了一个附加线程,并希望从这个附加线程中挂起主线程。附加线程是通过CreateRemoteThread 从外部进程创建的。

由于SuspendThread 需要一个HANDLE 到应该暂停的线程,我想知道如何从我的附加线程中运行的代码中获取这个HANDLE

【问题讨论】:

  • 它是windows 7下的32位进程。我使用的是Visual Studio 2008,因此是Visual C++。
  • 你想只挂起“主”线程还是全部?你到底想达到什么目的?它可能有另一种方式来做到这一点......
  • 主线程就足够了。但是,暂停所有线程的解决方案也适用于我的情况。我想从我的附加线程中调用一些非线程安全函数,通常由进程主线程调用。
  • 暂停一个线程不会使你的其他线程的操作线程安全。就您所知,主线程可能在您调用 SuspendThread 时正在执行该非线程安全操作。

标签: c++ windows multithreading dll-injection


【解决方案1】:

我认为一旦进程开始,主线程与其他线程就没有什么区别了。不过,你可以enumerate all threads in the process,使用GetThreadTimes来查找创建时间最早的线程。调用 OpenThread 从线程 ID 中获取 HANDLE

【讨论】:

    【解决方案2】:

    这种类型的许多有用的 API 函数位于(当然!)Tool Help 套件中。 CreateToolhelp32Snapshot() API 将为指定进程拍摄正在运行的线程的快照。

    // Take a snapshot of all running threads  
    hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); 
    if( hThreadSnap == INVALID_HANDLE_VALUE ) 
      return( FALSE );
    

    完整示例代码here.

    返回的结构不区分主线程和其他线程。我不知道这样做的机制;虽然某些版本的 C 运行时都会在主线程结束时全部 ExitProcess(),但在所有最近的版本中,进程会继续运行,直到最后一个线程退出。

    Interjay 建议使用 GetThreadTimes 可能是最好的选择。如果你可以CreateProcess() 目标进程,PROCESS_INFORMATION 块的 hThread 成员包含主线程的 tid。欢迎其他人的任何想法。

    【讨论】:

    • 我想知道像 OllyDbg 这样的软件是如何找出“主线程”的。您还可以使用它附加到已经运行的进程。
    • DebugActiveProcess() -- msdn.microsoft.com/en-us/library/ms679295%28VS.85%29.aspxGetThreadContext() 返回“当前线程上下文”的寄存器,但我不知道主线程的区别。
    【解决方案3】:
    DWORD GetMainThreadId () {
        const std::tr1::shared_ptr<void> hThreadSnapshot(
            CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0), CloseHandle);
        if (hThreadSnapshot.get() == INVALID_HANDLE_VALUE) {
            throw std::runtime_error("GetMainThreadId failed");
        }
        THREADENTRY32 tEntry;
        tEntry.dwSize = sizeof(THREADENTRY32);
        DWORD result = 0;
        DWORD currentPID = GetCurrentProcessId();
        for (BOOL success = Thread32First(hThreadSnapshot.get(), &tEntry);
            !result && success && GetLastError() != ERROR_NO_MORE_FILES;
            success = Thread32Next(hThreadSnapshot.get(), &tEntry))
        {
            if (tEntry.th32OwnerProcessID == currentPID) {
                result = tEntry.th32ThreadID;
            }
        }
        return result;
    }
    

    【讨论】:

    • 是否保证进程的“主”线程始终是快照中的第一个?此外,问题不在于如何识别主线程。这是关于如何获得句柄,这个答案忽略了。
    • 甚至不能保证进程的“主”线程仍然存在!进程的主线程可能已经完成了ExitThread
    • 您可以通过从模块的 PE 标头中获取主线程的起始地址来进行完整性检查。如果起始地址与线程的起始地址匹配(通过调用 NtQueryInformationThread 获得),那么您就拥有了主线程。根据经验,这总是给出正确的线索,或者没有@RaymondChen 提到的那样,但至少没有误报
    • 感谢您的解决方案
    【解决方案4】:

    您为什么不直接创建一个程序范围的全局(如果必须使用extern

    HANDLE mainThread ;
    DWORD mainThreadId ;
    

    在 main 的第一行,(在创建任何线程之前)做

    mainThread = GetCurrentThread() ;
    mainThreadId = GetCurrentThreadId() ;
    

    您可以使用any form of IPC 与远程进程共享 id 或 HANDLE(尚未验证共享 HANDLE 是否有效,但应该可以!)

    【讨论】:

    【解决方案5】:

    用这个函数获取线程id:

    /* CAUTION: ONLY x86 TESTED
     * get the thread id of the main thread of a target process
     *
     * params:
     *     DWORD dwPid  process id of the target process
     *
     * return:
     *     Success      thread id
     *     Error        NULL
     */
    DWORD GetMainThreadId(DWORD dwPid)
    {
        LPVOID lpTid;
    
        _asm
        {
            mov eax, fs:[18h]
            add eax, 36
            mov [lpTid], eax
        }
    
        HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, dwPid);
        if(hProcess == NULL)
            return NULL;
    
        DWORD dwTid;
        if(ReadProcessMemory(hProcess, lpTid, &dwTid, sizeof(dwTid), NULL) == FALSE)
        {
            CloseHandle(hProcess);
            return NULL;
        }
    
        CloseHandle(hProcess);
    
        return dwTid;
    }
    

    简单打开线程获取句柄:

    /*
     * get a handle to the main thread of a target process
     * if successfull, the returned handle must be closed with CloseHandle()
     *
     * params:
     *     DWORD dwPid              process id of the target process
     *     DWORD dwDesiredAccess    desired access rights to the thread
     *
     * return:
     *     Success      thread handle with desired access rights
     *     Error        NULL
     */
    HANDLE GetMainThreadHandle(DWORD dwPid, DWORD dwDesiredAccess)
    {
        DWORD dwTid = GetMainThreadId(dwPid);
        if(dwTid == FALSE)
            return NULL;
    
        return OpenThread(dwDesiredAccess, FALSE, dwTid);
    }
    

    【讨论】:

    • 我不相信这会在各种环境中完美运行。具体来说,您假设所有机器上的每个进程中的 Tib 地址都是相等的,甚至 GetMainThreadId() 的调用者也是主线程,对吧?你应该解释它是如何工作的,因为它有太多可疑的东西要问。
    • 这很棒,但我正在尝试制作一个兼容 32 位和 64 位的函数
    猜你喜欢
    • 2023-03-29
    • 1970-01-01
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    • 2015-03-07
    • 1970-01-01
    • 2010-12-25
    相关资源
    最近更新 更多