这篇博客的背景是:如果很多进程都恶意程序通过远进程注入了线程,那么应该怎么清除呢?
下面给出两种方法
PLAN A
来自 加号
通过遍历线程后,根据获取到的线程信息,对线程地址和入口代码进行检查,这种方式适合对注入代码偏移位置固定或则入口代码固定,能准确查杀,推荐使用
void Killing::KillMalRemoteThread()
{
(FARPROC&)ZwQueryInformationThread = GetProcAddress(m_hNtdll, "ZwQueryInformationThread");
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 te32;
te32.dwSize = sizeof(THREADENTRY32);
Thread32First(hThreadSnap, &te32);
HANDLE hThread;
do {
hThread = OpenThread(THREAD_ALL_ACCESS, false, te32.th32ThreadID);
setlocale(LC_ALL, ".ACP");
DWORD startAddr;
ZwQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &startAddr, sizeof(PVOID), NULL);
printf("进程 %d, 线程 %d, 入口地址 0x%x\n", te32.th32OwnerProcessID, te32.th32ThreadID, startAddr);
if (startAddr > 0x400000 && startAddr < 0x80000000)
{
if ((startAddr & 0xFFFF) == 0x3A72 || (startAddr & 0xFFFF) == 0x36F4)//判断进程入口地址2字节
{
PBYTE pbMapBase = (PBYTE)((startAddr & 0xFFFF) == 0x3A72 ? startAddr - 0x3A72 : startAddr - 0x36F4);
HANDLE hOwnerProc = OpenProcess(PROCESS_VM_READ, false, te32.th32OwnerProcessID);
BYTE sectionOff_0[4] = { 0 };
BYTE sectionOff_2F8[5] = { 0 };
ReadProcessMemory(hOwnerProc, pbMapBase, sectionOff_0, 4, NULL);
ReadProcessMemory(hOwnerProc, pbMapBase + 0x2F8, sectionOff_2F8, 5, NULL);
if (*(DWORD*)& sectionOff_0 == 0xFF243C83 &&
*(DWORD*)& sectionOff_2F8 == 0x00012DE9 &&
sectionOff_2F8[4] == 0x00)//进一步检查入口代码是否为特定值
{
printf("########\n恶意线程,在进程 %d, 线程 %d, 入口地址 0x%x\n", te32.th32OwnerProcessID, te32.th32ThreadID, startAddr);
if (TerminateThread(hThread, -1))
{
printf("已终止该线程\n");
}
printf("########\n");
}
}
}
CloseHandle(hThread);
} while (Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap);
}
PLAN B
而这个方案就比较猛一些,可以直接干掉所有不在模块中的线程,要小心一点
因为普通线程创建都会在进程的已知模块中,而恶意代码创建的就是不属于任何模块,也可以用过指定模块名来关闭线程
void WINAPI ClearThread()
{
int Flag = 3;
while (Flag)
{
Sleep(1000);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 进程快照句柄
PROCESSENTRY32 process = { sizeof(PROCESSENTRY32) }; // 进程快照信息
// 遍历进程
while (Process32Next(hProcessSnap, &process))
th32ProcessID{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE; // 线程快照句柄
THREADENTRY32 te32; // 线程快照信息
// 创建线程快照
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE) { printf("创建线程快照失败"); }
// 为快照分派内存空间
te32.dwSize = sizeof(THREADENTRY32);
// 获取第一个线程的信息
if (!Thread32First(hThreadSnap, &te32)) { printf("线程信息获取失败"); }
// 遍历线程
while (Thread32Next(hThreadSnap, &te32))
{
if (te32.th32OwnerProcessID == process.th32ProcessID)
{
// 打开线程
//printf("PID=%d, TID=%d\n",te32.th32OwnerProcessID,te32.th32ThreadID);
HANDLE hThread = ::OpenThread(
THREAD_ALL_ACCESS, // 访问权限,THREAD_ALL_ACCESS :所有权限
FALSE, // 由此线程创建的进程不继承线程的句柄
te32.th32ThreadID // 线程 ID
);
if (hThread == NULL)
{
//printf("线程打开失败%x\n", GetLastError());
continue;
}
// 将区域设置设置为从操作系统获取的ANSI代码页
setlocale(LC_ALL, ".ACP");
// 获取 ntdll.dll 的模块句柄
HINSTANCE hNTDLL = ::GetModuleHandleA("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] == NULL)
{
//modname是模块名的指针,可以比较是否是恶意模块名
printf("线程不在模块中: th32ProcessID=%d, TID=%d \n", process.th32ProcessID, te32.th32ThreadID);
if (TerminateThread(hThread, -1))
{
printf("线程已被清理\n");
}
}
modname[0] = NULL;
}
}
}
Flag -= 1;
}
}
如果病毒还有提权操作的话,我们也需要提高权限去对抗
常见的提权方式为调整令牌 详见
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!