1. 程式人生 > >程序內執行緒入口的探究

程序內執行緒入口的探究

由於要寫個檢測功能,對於這塊進行了下探究。

執行緒的常規啟動有2種:

1、Createthread方式 ,純win api的方法

2、_beginthreadex方式,這是VC中安全使用執行緒的方法

下面給出DEMO程式碼:

#include <Windows.h>
#include <process.h>

unsigned int __stdcall DemoThread1(PVOID p){
	while (1)
	{
		printf("Thread1\r\n");
		Sleep(1000);
	}
}

unsigned int __stdcall DemoThread2(PVOID p){
	while (1)
	{
		printf("Thread2\r\n");
		Sleep(1000);
	}
}

unsigned int __stdcall DemoThread3(PVOID p){
	while (1)
	{
		printf("Thread3\r\n");
		Sleep(1000);
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	//方式1
	printf("Thread1 Address = 0x%X\r\n",(DWORD)DemoThread1);
	CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)DemoThread1,NULL,0,NULL);
	//方式2
	printf("Thread2 Address = 0x%X\r\n",(DWORD)DemoThread2);
	_beginthreadex(NULL,0,DemoThread2,NULL,0,NULL);

	printf("Thread3 Address = 0x%X\r\n",(DWORD)DemoThread3);
	_beginthreadex(NULL,0,DemoThread3,NULL,0,NULL);
	system("pause");
	return 0;
}

程式碼比較簡單,有3個執行緒函式,1使用API的方式啟動,2和3使用_beginthreadex方式啟動。

程式碼執行後可以得到:


這裡打印出了3個函式的地址,然後我們通過XT觀察下執行緒入口


不難發現,Thread1的執行緒入口是可以對應上的(因為使用了API直接啟動),但是另外2根執行緒後面顯示的模組卻是MSVCR80.DLL,並且執行緒入口也不對,這是因為使用了封裝過的執行緒類啟動而導致的,這裡我們要把這個真實的入口枚舉出來。

通過對_beginthreadex進行逆向分析:


可以看到將真實的執行緒入口和引數放入結構後,最終還是使用了Createthread但是啟動的函式卻是 0x6F8B29E1,比對下剛才的執行緒入口就可以對應上了,接下來分析下這個函式,我們只要知道這個函式從哪裡讀這個執行緒引數結構就行了。


這裡可以知道引數結構是從 fls_getvalue內獲取,翻閱了下這個call內執行。可以確定是跟執行緒內TLS有關,所以分析後最終得到結果就是

_get_tlsindex獲取序號 --->TLS內獲取Flsgetvalue(KERNEL32.DLL的匯出函式)回撥,執行Flsgetvalue後可以獲取到引數結構,分析了下Flsgetvalue


可以看到有個 FS:[18]這個是取TEB,所以可以得到   [TEB + 0xFB4] + 4 * TLSINDEX + 8,這裡的TLSINDEX就是_get_tlsindex得到的。

結構拿到後 +0x54就是入口,+0x58就是引數

好了分析完了,總結下:

1、先要獲取執行緒的TEB

2、使用_get_tlsindex獲取到TLSINDEX

3、使用[TEB + 0xFB4] + 4 * TLSINDEX + 8

4、得到真實執行緒入口

ps:作者系統是win7環境,後來切換XP發現不存在 flsgetvalue這個匯出函式,所以得知這個匯出函式是win7才有的。但是不要緊,xp下這個結構是放在tls的陣列內因此第3步在xp下是 TEB + 0xE10 + 4 * TLSINDEX

這裡給出一段最終封裝的程式碼,缺少的函式自己封裝了,僅供參考:

DWORD GetThreadEntryByThreadId(DWORD dwThreadId)
{
	THREAD_BASIC_INFORMATION ThreadBasicInfo = {0};
	if (PCHunter::Instance().QueryThreadBaseInfo(dwThreadId,&ThreadBasicInfo)){
		if (ThreadBasicInfo.TebBaseAddress != NULL){
			DWORD *pTlsSlots = (DWORD*)((DWORD)ThreadBasicInfo.TebBaseAddress + TEB_TLSSLOTS);
				PVOID pEntry = PCHunter::Instance().QueryThreadEntryByThreadId(dwThreadId);
				if (pEntry != NULL){
					char szModuleName[MAX_PATH] = {0};
					GetModuleNameByMemoryAddress((DWORD)pEntry,szModuleName);

					//判斷是不是庫模組;
					if (StrStrI(szModuleName,"msvcr")){

						HMODULE hModule = GetModuleHandle(szModuleName);
						if (hModule != NULL){
							FGetTlsIndex pGetTlsIndex = (FGetTlsIndex)GetProcAddress(hModule,"__get_tlsindex");
							if (pGetTlsIndex!=NULL){

								DWORD dwIdx = pGetTlsIndex();
								//AdminPrintf("TlsIndex:%d\r\n",pGetTlsIndex());
								DWORD dwAddress = 0;
								DWORD SafeThunkCall = 0;

								//區分下是否存在 FlsGetValue 函式;
								bool IsNewKernel32 = false;
								HMODULE hKer = GetModuleHandle("kernel32.dll");
								if (hKer != NULL){
									if (GetProcAddress(hKer,"FlsGetValue") != NULL){
										IsNewKernel32 = true;
									}
								}

								if (IsNewKernel32){
									SafeThunkCall = *(DWORD*)((DWORD)ThreadBasicInfo.TebBaseAddress + TEB_SAFE_THUNK_CALL);
									dwAddress = *(DWORD*)(SafeThunkCall + dwIdx * 4 + 8);
								}else{
									SafeThunkCall = (DWORD)ThreadBasicInfo.TebBaseAddress + TEB_TLSSLOTS;
									dwAddress = *(DWORD*)(SafeThunkCall + dwIdx * 4);
								}

								if ((dwAddress != 0) && (dwAddress != 0xFFFFFFFF)) {
									pEntry = (PVOID)*(DWORD*)(dwAddress + 0x54);
									if (pEntry!=NULL){
										GetModuleNameByMemoryAddress((DWORD)pEntry,szModuleName);
									}
								}
							}
						}
					}
					return (DWORD)pEntry;
				}									
		}
	}
	return 0;
}

這裡說明1個TEB的知識

TEB +0xE10 是執行緒 TLS的地方,TLS是個32位指標陣列長度為 64

TEB +0xFB4 是執行緒 SafeThunkCall 型別為UCHAR