大多數惡意代碼為了隱藏自己的行蹤都會附加到某個進程中,在這個進程內申請一塊內存區域來存放它的代碼,畢竟隱藏的再好,代碼也要有的,不然你讓 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;}
}
}
}
}