C/C++ 獲取線程入口地址模塊等


大多數惡意代碼為了隱藏自己的行蹤都會附加到某個進程中,在這個進程內申請一塊內存區域來存放它的代碼,畢竟隱藏的再好,代碼也要有的,不然你讓 CPU 運行什么 …

今天檢測的特征是向 YY語音 里插入了一段自己的代碼(創建了新的線程),而這個新的線程不在原有的模塊內,所以思路就是遍歷 YY.exe 這個進程中的所有線程,如果這個線程沒有對應的模塊,那么就說明這個線程是可疑的。

准備工作

#pragma region 依賴
typedef enum _THREADINFOCLASS{
    ThreadBasicInformation,
    ThreadTimes,
    ThreadPriority,
    ThreadBasePriority,
    ThreadAffinityMask,
    ThreadImpersonationToken,
    ThreadDescriptorTableEntry,
    ThreadEnableAlignmentFaultFixup,
    ThreadEventPair_Reusable,
    ThreadQuerySetWin32StartAddress,
    ThreadZeroTlsCell,
    ThreadPerformanceCount,
    ThreadAmILastThread,
    ThreadIdealProcessor,
    ThreadPriorityBoost,
    ThreadSetTlsArrayAddress,
    ThreadIsIoPending,
    ThreadHideFromDebugger,
    ThreadBreakOnTermination,
    MaxThreadInfoClass
}THREADINFOCLASS;
typedef struct _CLIENT_ID{
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
}CLIENT_ID;
typedef struct _THREAD_BASIC_INFORMATION{
    LONG ExitStatus;
    PVOID TebBaseAddress;
    CLIENT_ID ClientId;
    LONG AffinityMask;
    LONG Priority;
    LONG BasePriority;
}THREAD_BASIC_INFORMATION,*PTHREAD_BASIC_INFORMATION;
extern "C" LONG (__stdcall *ZwQueryInformationThread)(
    IN HANDLE ThreadHandle,
    IN THREADINFOCLASS ThreadInformationClass,
    OUT PVOID ThreadInformation,
    IN ULONG ThreadInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    ) = NULL;
#pragma endregion

功能實現

	HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);	// 進程快照句柄
	PROCESSENTRY32 process = {sizeof(PROCESSENTRY32)};						// 進程快照信息

	// 遍歷進程,找到 YY.exe
	while (Process32Next(hProcessSnap,&process)){
		string s_szExeFile = process.szExeFile; // char* 轉 string
		if(s_szExeFile == "YY.exe"){
			HANDLE hThreadSnap = INVALID_HANDLE_VALUE;			// 線程快照句柄 
			THREADENTRY32 te32;									// 線程快照信息

			// 創建線程快照
			hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
			if (hThreadSnap == INVALID_HANDLE_VALUE){cout << "創建線程快照失敗" << endl;}

			// 為快照分派內存空間
			te32.dwSize = sizeof(THREADENTRY32);

			// 獲取第一個線程的信息
			if (!Thread32First(hThreadSnap, &te32)){cout << "線程信息獲取失敗" << endl;}

			// 遍歷線程
			while (Thread32Next(hThreadSnap, &te32)){
				// 線程屬於 YY.exe
				if(te32.th32OwnerProcessID == process.th32ProcessID){
					// 打開線程
					HANDLE hThread = ::OpenThread (
						THREAD_ALL_ACCESS,		// 訪問權限,THREAD_ALL_ACCESS :所有權限
						FALSE,					// 由此線程創建的進程不繼承線程的句柄
						te32.th32ThreadID		// 線程 ID
						);
					if(hThread == NULL){cout << "線程打開失敗" << endl;}

					// 將區域設置設置為從操作系統獲取的ANSI代碼頁
					setlocale(LC_ALL,".ACP");

					// 獲取 ntdll.dll 的模塊句柄
					HINSTANCE hNTDLL = ::GetModuleHandle("ntdll");	

					// 從 ntdll.dll 中取出 ZwQueryInformationThread
					(FARPROC&)ZwQueryInformationThread  = ::GetProcAddress(hNTDLL,"ZwQueryInformationThread");
					
					// 獲取線程入口地址
					PVOID startaddr;						// 用來接收線程入口地址
					ZwQueryInformationThread(
						hThread,							// 線程句柄
						ThreadQuerySetWin32StartAddress,	// 線程信息類型,ThreadQuerySetWin32StartAddress :線程入口地址
						&startaddr,							// 指向緩沖區的指針
						sizeof(startaddr),					// 緩沖區的大小
						NULL								
						);

					// 獲取線程所在模塊
					THREAD_BASIC_INFORMATION tbi;			// _THREAD_BASIC_INFORMATION 結構體對象
					TCHAR modname[MAX_PATH];				// 用來接收模塊全路徑
					ZwQueryInformationThread(
						hThread,							// 線程句柄
						ThreadBasicInformation,				// 線程信息類型,ThreadBasicInformation :線程基本信息
						&tbi,								// 指向緩沖區的指針
						sizeof(tbi),						// 緩沖區的大小
						NULL
						);

					// 檢查入口地址是否位於某模塊中
					GetMappedFileName(
						::OpenProcess(						// 進程句柄
							PROCESS_ALL_ACCESS,									// 訪問權限,THREAD_ALL_ACCESS :所有權限
							FALSE,												// 由此線程創建的進程不繼承線程的句柄
							(DWORD)tbi.ClientId.UniqueProcess					// 唯一進程 ID
							), 
						startaddr,							// 要檢查的地址
						modname,							// 用來接收模塊名的指針
						MAX_PATH							// 緩沖區大小
						);
					
					// 判斷線程是否在模塊中
					if(modname[0] == '?'){cout << "線程不在模塊中" << endl;}
				}
			}			
		}
	}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM