轉自:黑客反病毒
DLL注入技術指的是將一個DLL文件強行加載到EXE文件中,併成爲EXE文件中的一部分,這樣做的目的在於方便我們通過這個DLL讀寫EXE文件內存數據,(例如 HOOK EXE文件中的API),或以被注入EXE的身份去執行一些操作等等。
遠線程注入原理是利用Windows 系統中CreateRemoteThread()這個API,其中第4個參數是準備運行的線程,我們可以將LoadLibrary()填入其中,這樣就可以執行遠程進程中的LoadLibrary()函數,進而將我們自己準備的DLL加載到遠程進程空間中執行。
當然除了CreateRemoteThread()和LoadLibrary()這個兩個主要的API還是遠遠不夠的,我們還需要以下表格所示的API:
OpenProcess | 打開遠程進程 |
VirtualAllocEx | 在遠程進程中申請空間 |
WriteProcessMemory | 在遠程進程中寫入數據 |
WaitForSingleObject | 等待信號量 |
VirtualFreeEx | 釋放遠程進程中申請空間 |
CloseHandle | 關閉句柄 |
主要代碼如下:
int CRemoteThreadInjectDLL::InjectDll(DWORD dwProcessId, PTCHAR szDllName)
{
if (szDllName[0] == NULL)
return -1;
//提高權限相關操作
EnablePrivilege(TRUE);
//1. 打開進程
HANDLE hProcess = ::OpenProcess( PROCESS_ALL_ACCESS, //打開進程權限
FALSE, //是否可繼承
dwProcessId); //進程ID
if (hProcess == INVALID_HANDLE_VALUE)
return -1;
//2. 在遠程進程中申請空間
LPVOID pszDllName = ::VirtualAllocEx(hProcess, //遠程進程句柄
NULL, //建議開始地址
4096, //分配空間大小
MEM_COMMIT, //空間初始化全0
PAGE_EXECUTE_READWRITE); //空間權限
if (NULL == pszDllName)
{
return -1;
}
//3. 向遠程進程中寫入數據
BOOL bRet = ::WriteProcessMemory( hProcess, pszDllName,
szDllName, MAX_PATH, NULL);
if (NULL == bRet)
{
return -1;
}
//4. 在遠程進程中創建遠程線程
m_hInjecthread = ::CreateRemoteThread(hProcess, //遠程進程句柄
NULL, //安全屬性
0, //棧大小
(LPTHREAD_START_ROUTINE)LoadLibrary, //進程處理函數
pszDllName, //傳入參數
NULL, //默認創建後的狀態
NULL); //線程ID
if (NULL == m_hInjecthread)
{
DWORD dwErr = GetLastError();
return -1;
}
//5. 等待線程結束返回
DWORD dw = WaitForSingleObject(m_hInjecthread, -1);
//6. 獲取線程退出碼,即LoadLibrary的返回值,即dll的首地址
DWORD dwExitCode;
GetExitCodeThread(m_hInjecthread, &dwExitCode);
m_hMod = (HMODULE)dwExitCode;
//7. 釋放空間
BOOL bReturn = VirtualFreeEx(hProcess, pszDllName,
4096, MEM_DECOMMIT);
if (NULL == bReturn)
{
return -1;
}
CloseHandle(hProcess);
hProcess = NULL;
//恢復權限相關操作
EnablePrivilege(FALSE);
return 0;
}
此外,我們還需要提升進程權限以便於提高注入成功率,所需API如下表所示:
OpenProcessToken | 得到令牌句柄 |
LookupPrivilegeValue | 得到權限值 |
AdjustTokenPrivileges | 提升令牌句柄權限 |
int CRemoteThreadInjectDLL::EnablePrivilege(bool isStart)
{
//1. 得到令牌句柄
HANDLE hToken = NULL; //令牌句柄
if (!OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ,
&hToken))
{
return FALSE;
}
//2. 得到特權值
LUID luid = {0}; //特權值
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
{
return FALSE;
}
//3. 提升令牌句柄權限
TOKEN_PRIVILEGES tp = {0}; //令牌新權限
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = isStart ? SE_PRIVILEGE_ENABLED : 0;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL))
{
return FALSE;
}
//4. 關閉令牌句柄
CloseHandle(hToken);
return 0;
}
當要在指定的進程中加載DLL時,我們就需要過濾指定名稱的進程,這時遍歷進程ID並進行對比,得到所指定的進程,所需API如表所示:
CreateToolhelp32Snapshot | 創建進程快照 |
Process32First | 第一個進程快照 |
Process32Next | 循環下一個進程快照 |
DWORD CRemoteThreadInjectDLL::GetProcessId(PTCHAR pszProcessName)
{
HANDLE hProcess = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcess)
{
return 0;
}
DWORD dwProcessId = 0;
PROCESSENTRY32 process32 = {0};
process32.dwSize = sizeof(PROCESSENTRY32);
BOOL bRetProcess = FALSE;
bRetProcess = ::Process32First(hProcess, &process32);
do
{
if (_tcscmp(pszProcessName, process32.szExeFile) == 0)
{
dwProcessId = process32.th32ProcessID;
break;
}
bRetProcess = ::Process32Next(hProcess, &process32);
}while (bRetProcess);
::CloseHandle(hProcess);
return dwProcessId;
}
遠線程注入API使用較多,不易實現。但是可以批量注入和卸載,這樣對於需要反覆調試的注入就非常的方便。