Under the hood

互联网上新生活
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

OpenThread种种

Posted on 2008-10-16 11:14  sting feng  阅读(7014)  评论(20编辑  收藏  举报
我在Windows NT系统(包括2K和XP)和Windows CE系统各写有一个工具,专门用来研究系统的运行时状态,比如进程、线程的各种信息,哪些DLL被哪些进程加载了,等等。如果某天突然想到一些好玩的东 西,我也会把它加到工具里,比如,下图的这个,在系统登陆、锁定界面上运行了一个计算器。

一 般情况下,我比较喜欢做的事是观察线程的Call Stack。这就要用到一个关键的API:OpenThread。基本上,想要获得跟线程有关的信息或者控制线程的运行(比如说你可以用 SuspendThread挂起一个线程甚至整个进程),都得先通过它来拿到线程的句柄。不过在Windows系统上,除了Windows 2K/XP/ME等平台,其他平台并没有一个现成的WIN32 API可供使用。这种情况下,我们必须以某种形式模拟OpenThread。

  • Windows NT 4.0。在NT 4.0的时代,可能是微软担心它过于危险,OpenThread还没成为一个标准的Win32 API。但是在NTDLL里提供了一个Native API:NtOpenThread。如果你反汇编跟踪过NtOpenProcess和NtOpenThread,会发现除了ClientId的初始化, NtOpenThread和NtOpenProcess基本上完全一样。利用NtOpenThread,OpenThread的NT4可实现如下:
HANDLE OpenThreadNT(DWORD dwDesiredAccess,
                    BOOL  bInheritHandle,
                    DWORD dwThreadId)
{
    OBJECT_ATTRIBUTES   ObjectAttributes;
    CLIENT_ID           ClientId;
    HANDLE              hThread;
    NTSTATUS            Status;

    InitializeObjectAttributes(
&ObjectAttributes, NULL, 0, NULL, NULL);

    
if (bInheritHandle)
        ObjectAttributes.Attributes 
= OBJ_INHERIT;

    ClientId.UniqueProcess 
= NULL;
    ClientId.UniqueThread 
= (HANDLE)dwThreadId;

    Status 
= NtOpenThread(&hThread,             // Thread handle
                          dwDesiredAccess,      // Access to thread object
                          &ObjectAttributes,    // Object attributes
                          &ClientId);           // Client Id

    
if (!NT_SUCCESS(Status))
    
{
        SetLastError(RtlNtStatusToDosError(Status));
        
return NULL;
    }


    
return hThread;
}

  • Windows ME/2K/XP。可能是MS觉得OpenThread其实是一个比较有用的API,也可能MS觉得开放OpenThread其实没那么危险,在这些操作系统上就直接提供了WIN32 API。因此如果是这些系统,直接用就是了。
  • Windows 95/98。在Windows 9x中,无论是公开还是未公开函数,都没有现成的可用。不过你可以搞点儿诡计,把TDB伪装成PDB,然后利用OpenProcess实现OpenThread的功能:
HANDLE OpenThread9x(DWORD dwDesiredAccess,
                    BOOL  bInheritHandle,
                    DWORD dwThreadId)
{
    
// Thread Database pointer
    PTDB pTDB = dwThreadId ^ dwObsfucator;

    pTDB
->Type = K32OBJ_PROCESS;
    HANDLE hThread 
= OpenProcess(dwDesiredAccess, bInheritHandle, dwThreadId);
    pTDB
->Type = K32OBJ_THREAD;

    
return hThread;
}


DWORD GetObsfucator()
{
    DWORD PID, PDB;

    PID 
= GetCurrentProcessId();

    __asm mov eax,fs:[
0x30];  // PDB
    __asm mov PDB,eax

    
return PDB ^ PID;
}
dwObsfucator这个东西,看过Matt Pietrek的《Windows 95 System Programming SECRETS》应该知道是怎么回事-在最初的Windows 95版本中GetCurrentProcessId和GetCurrentThreadId都是直接返回PDB和TDB指针,后来,MS搞了个小花样,用 了一个开机时自动生成的随机值做了个异或加密。Windows桌面操作系统的OpenThread实现,其实在CodeProject上这篇文章里有详细 的解释。

  • Windows CE。Windows CE也没有现成的公开或未公开的API可用。不过和Windows 9x平台类似(看Windows CE的内核源代码,我有时会怀疑很多代码是不是从Windows 9x抄过来的),你可以用OpenProcess来实现:
HANDLE WINAPI OpenThread(DWORD fdwAccess, BOOL fInherit, DWORD IDThread)
{
    HANDLE hThread
=NULL;
    BOOL fOldMode 
= SetKMode(TRUE);
   
    PTHREAD pTh 
= HandleToThread((HANDLE)IDThread);
    
if (pTh)
    
{
        HANDLE hProcess 
= pTh->pOwnerProc->hProc;
        
if (hProcess)
        
{
            HANDLE h 
= (HANDLE)IDThread;
            PHDATA phd;
            h2p(h, phd);
            
if (phd && phd->pci->type == SH_CURTHREAD)
            
{
                const_cast
<CINFO*>(phd->pci)->type = SH_CURPROC;
                hThread 
= ::OpenProcess(fdwAccess, fInherit, IDThread);
                const_cast
<CINFO*>(phd->pci)->type = SH_CURTHREAD;
            }

        }

    }

    
else
    
{
        SetLastError(ERROR_INVALID_PARAMETER);
    }

   
    SetKMode(fOldMode);
    
return hThread;
}