Hacking Windows CE: 如何从线程ID获取线程名称

本文介绍了如何在不同操作系统上获取线程的入口地址,包括利用Windows NT系列操作系统的NtQueryInformationThread API以及Windows CE上的特定内核数据结构。
在一个线程出现异常行为时,比如说CPU占用率过高,抛出异常等,你一定想知道这个线程是由哪个模块创建的。因此无论在哪个操作系统上,获取线程名称是诊断线程相关问题的重要一步。

从线程ID获取线程名称通常的方法是,先获取该线程的入口地址,然后枚举进程内所有已加载模块,最后判断线程入口地址落在哪个加载模块范围内。枚举进程内已加载模块可用Win32标准的CreateToolhelp32Snapshot/Module32First/Module32Next系列ToolHelp API得到。获取线程入口地址则没有线程的Win32 API可用。不过在Windows NT based操作系统上(包括Windows NT 4.0/2000/XP/2003,等),有一个未公开的Native API可用:NtQueryInformationThread。其声明如下:

DWORDWINAPINtQueryInformationThread(
HANDLEThreadHandle,
THREAD_INFORMATION_CLASSThreadInformationClass,
PVOIDThreadInformation,
ULONGThreadInformationLength,
PULONGReturnLength
);

获取线程入口地址可用:

DWORDGetThreadStartAddress(DWORDdwThreadId)
... {
HANDLEhThread
=OpenThread(THREAD_ALL_ACCESS,FALSE,dwThreadId);
DWORDretaddr,len,error;
retaddr
=len=0;
error
=NtQueryInformationThread(hThread,9,&retaddr,sizeof(retaddr),&len);
CloseHandle(hThread);
if(error!=0)
retaddr
=0;
returnretaddr;
}

在Windows CE上就没这么幸运了,没有任何现成的API可用。官方Windows CE Base Team的 blog这个问题的回答是可以用Remote Kernel Tracker,不过这需要你build一个特殊的kernel image,enable一些profiler功能-这在显示的问题诊断中显然是不实际的。那么有没有办法不需要什么特殊的配置就可像Windows桌面操作系统那样获得入口地址呢?有是有的,不过需要一些hack手段。仔细研究CE下的Thread内核数据结构,就会发现Thread结构中有一项是记录线程入口地址的。
typedef struct Thread ... {
DWORD_1[
3];
PPROCESSpProc;
/**//*0C:pointertocurrentprocess*/
PPROCESSpOwnerProc;
/**//*10:pointertoownerprocess*/
DWORD_2[
18];
DWORDdwStartAddr;
/**//*5c:threadPCatcreation,usedtogetthreadname*/
DWORD_3[
10];
}
THREAD, * PTHREAD; /**/ /*Thread*/

因此要做的就是想办法根据线程ID或handle得到这个数据。再研究,发现线程的Thread内核数据结构可通过句柄得到:

PTHREADpTh = HandleToThread(ThreadHandle);

而且,在Windows CE下,线程ID和其handle的值是一样的!!因此我们可以写一个这样的函数从线程ID拿到入口地址:

DWORDGetThreadStartAddress(DWORDdwThreadId)
... {
DWORDdwStartAddress
=0;
BOOLfOldMode
=SetKMode(TRUE);
PTHREADpTh
=HandleToThread((HANDLE)dwThreadId);
if(pTh)
...{
dwStartAddress
=(DWORD)MapPtrToProcess((LPVOID)pTh->dwStartAddr,pTh->pOwnerProc->hProc);
}

returndwStartAddress;
}

为了使用这些内核数据结构,我们还需要另外一些辅助结构和函数,比较完整的代码如下。当然,官方肯定是不建议这么做的,但是重要的是解决问题,你说呢。
typedef struct Process ... {
DWORD_1[
2];
HANDLEhProc;
/**//*08:handleforthisprocess,neededonlyforSC_GetProcFromPtr*/
}
PROCESS, * PPROCESS;
typedef
struct Thread ... {
DWORD_1[
3];
PPROCESSpProc;
/**//*0C:pointertocurrentprocess*/
PPROCESSpOwnerProc;
/**//*10:pointertoownerprocess*/
DWORD_2[
18];
DWORDdwStartAddr;
/**//*5c:threadPCatcreation,usedtogetthreadname*/
DWORD_3[
10];
}
THREAD, * PTHREAD; /**/ /*Thread*/

typedef
struct cinfo ... {
characName[4];/**//*00:objecttypeIDstring*/
uchardisp;
/**//*04:typeofdispatch*/
uchartype;
/**//*05:apihandletype*/
ushortcMethods;/**//*06:#ofmethodsindispatchtable*/
constPFNVOID*ppfnMethods;/**//*08:ptrtoarrayofmethods(inserveraddressspace)*/
constDWORD*pdwSig;/**//*0C:ptrtoarrayofmethodsignatures*/
PPROCESSpServer;
/**//*10:ptrtoserverprocess*/
}
CINFO; /**/ /*cinfo*/
typedefCINFO
* PCINFO;

typedef
struct _HDATAHDATA, * PHDATA;
struct _HDATA ... {
DWORD_1[
2];/**//*00:linksforactivehandlelist*/
HANDLEhValue;
/**//*08:Currentvalueofhandle(nonce)*/
DWORD
lock;/**//*0C:accessinformation*/
DWORD
ref;/**//*10:referenceinformation*/
constCINFO*pci;/**//*14:ptrtoobjectclassdescriptionstructure*/
PVOIDpvObj;
/**//*18:ptrtoobject*/
DWORDdwInfo;
/**//*1C:extrahandleinfo*/
}
; /**/ /*20:sizeof(HDATA)*/

#ifdefx86
struct KDataStruct ... {
LPDWORDlpvTls;
/**//*0x000Currentthreadlocalstoragepointer*/
HANDLEahSys[NUM_SYS_HANDLES];
/**//*0x004Ifthismoves,changekapi.h*/
DWORD_1[
4];
ulonghandleBase;/**//*0x094baseaddressofhandletable*/
}
; /**/ /*KDataStruct*/
#endif
#ifdefARM
struct KDataStruct ... {
LPDWORDlpvTls;
/**//*0x000Currentthreadlocalstoragepointer*/
HANDLEahSys[NUM_SYS_HANDLES];
/**//*0x004Ifthismoves,changekapi.h*/
DWORD_1[
6];
ulonghandleBase;/**//*0x09chandletablebaseaddress*/
}
; /**/ /*KDataStruct*/
#endif

#define HandleToThread(h)((THREAD*)GetObjectPtrByType((h),SH_CURTHREAD))
#define HANDLE_ADDRESS_MASK0x1ffffffc

void h2p(HANDLEh,PHDATA & phdRet)
... {
if((ulong)h<NUM_SYS_HANDLES+SYS_HANDLE_BASE&&(ulong)h>=SYS_HANDLE_BASE)
h
=((KDataStruct*)PUserKData)->ahSys[(uint)h-SYS_HANDLE_BASE];
if(h)
...{
phdRet
=(PHDATA)(((ulong)h&HANDLE_ADDRESS_MASK)+((KDataStruct*)PUserKData)->handleBase);
if(phdRet->hValue!=h)
phdRet
=0;
}

else
phdRet
=0;
}


PVOIDGetObjectPtrByType(HANDLEh,
int type)
... {
PHDATAphd;
h2p(h,phd);
return(phd&&phd->pci&&phd->pci->type==type)?phd->pvObj:0;
}


extern " C " LPVOIDWINAPIMapPtrToProcess(LPVOIDlpv,HANDLEhProc);
extern " C " BOOLWINAPISetKMode(BOOLfMode);

DWORDGetThreadStartAddress(DWORDdwThreadId)
... {
DWORDdwStartAddress
=0;
BOOLfOldMode
=SetKMode(TRUE);
PTHREADpTh
=HandleToThread((HANDLE)dwThreadId);
if(pTh)
...{
dwStartAddress
=(DWORD)MapPtrToProcess((LPVOID)pTh->dwStartAddr,pTh->pOwnerProc->hProc);
}

returndwStartAddress;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值