进程内线程入口的探究

由于要写个检测功能,对于这块进行了下探究。

线程的常规启动有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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值