giovedì 21 marzo 2019

Remote Thread DLL Injection

Uno dei metodi classici di iniezione del codice è quello di creare un thread da remoto all'interno del processo target per fagli eseguire una chiamata a LoadLibrary che caricherà, a sua volta, la DLL con il codice da iniettare. Si presenterà, come al solito, un esempio banale ma esplicativo. Si consideri un semplice target con il seguente sorgente

#include <Windows.h>
#include <iostream>
 
int wmain(int argcwchar_t * argv[])
{
 std::cout << "Target running!" << std::endl;
 std::cout << "Press any key to exit!" << std::endl;
 std::getchar();
 return 0;
}




Per quanto riguarda la DLL, il codice è altrettanto banale

#include <Windows.h>
 
BOOL APIENTRY DllMain(HMODULE hModule,
 DWORD  dwReason,
 LPVOID lpReserved
)
{
 switch (dwReason)
 {
 case DLL_PROCESS_ATTACH:
 {
  MessageBox(NULLL"MyDLL.dll Loaded!"L"MyDLL"MB_OK | MB_ICONWARNING);
  break;
 }
 case DLL_THREAD_ATTACH:
 case DLL_THREAD_DETACH:
 case DLL_PROCESS_DETACH:
  break;
 }
 return TRUE;
}

Non resta che il codice dell'iniettore in cui la funzione GetProcessIdByProcessName usa l'API esposta in TlHelp32.h per calcolare l'ID del processo target partendo dal suo nome (si veda [2] per maggiori info). La funzione CreateRemoteThreadInjectDll, invece, usa l'API di Windows (si veda [1] o la documentazione online di Microsoft) per ottenere l'HANDLE del processo target, allocare spazio sufficiente a contenere il percorso assoluto della DLL, scrivere tale percorso nello spazio appena allocato, ottenere un puntatore a LoadLibraryW definita in kernel32.dll ed infine chiamare CreateRemoteThread che avvierà un thread nel processo target con il compito di chiamare LoadLibraryW per caricare la DLL. Da notare che poiché la chiamata a LoadLibraryW viene eseguita nel processo target la stringa che contiene il percorso assoluto della DLL deve trovarsi nello spazio degli indirizzi di quest'ultimo. Ecco perché si alloca lo spazio nel processo target e ci si scrive dentro tale percorso.

#include <Windows.h>
#include <iostream>
#include "InjectDll.h"
 
// sopprime warning di sicurezza per alcune funzioni deprecate tipo wcscat
#pragma warning(disable : 4996)
 
void GetAppPath(wchar_t *szCurFile)
{
 GetModuleFileName(0, szCurFileMAX_PATH);
 
 for (SIZE_T i = wcslen(szCurFile) - 1; i >= 0; i--)
 {
  if (szCurFile[i] == '\\')
  {
   szCurFile[i + 1] = '\0';
   break;
  }
 }
}
 
 
int wmain(int argcwchar_t * argv[])
{
 wchar_t szDllFile[MAX_PATH] = { 0 };
 
 GetAppPath(szDllFile);
 std::wcscat(szDllFile, L"MyDLL.dll");
 
 BOOL bRet = CreateRemoteThreadInjectDll(L"MyTarget.exe", szDllFile);
 if (bRet == FALSE)
  std::cout << "Dll Injection Error." << std::endl;
 else
  std::cout << "Dll Injection OK!" << std::endl;
 
 std::cout << "Press any key to Exit!" << std::endl;
 std::getchar();
 
 return 0;
}


#include "InjectDll.h"
 
 
void ShowError(const wchar_t *pszText)
{
 wchar_t szErr[MAX_PATH] = { 0 };
 wsprintf(szErr, L"%s Error[%d]\n"pszText, GetLastError());
 MessageBox(NULL, szErr, L"ERROR"MB_OK);
}

 
DWORD GetProcessIdByProcessName(const wchar_t *pszProcessName)
{
 DWORD dwProcessId = 0;
 PROCESSENTRY32 pe32 = { 0 };
 HANDLE hSnapshot = NULL;
 BOOL bRet = FALSE;
 RtlZeroMemory(&pe32, sizeof(pe32));
 pe32.dwSize = sizeof(pe32);
 
 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 if (NULL == hSnapshot)
 {
  ShowError(L"CreateToolhelp32Snapshot");
  return dwProcessId;
 }
 
 bRet = Process32First(hSnapshot, &pe32);
 while (bRet)
 {
  if (0 == lstrcmpi(pe32.szExeFile, pszProcessName))
  {
   dwProcessId = pe32.th32ProcessID;
   break;
  }
 
  bRet = Process32Next(hSnapshot, &pe32);
 }
 
 return dwProcessId;
}

 
BOOL CreateRemoteThreadInjectDll(const wchar_t *pszProcessNamewchar_t *pszDllFileName)
{
 HANDLE hProcess = NULL;
 DWORD dwProcessId = 0;
 SIZE_T dwSize = 0;
 LPVOID pDllAddr = NULL;
 FARPROC pFuncProcAddr = NULL;
 
 dwProcessId = GetProcessIdByProcessName(pszProcessName);
 if (dwProcessId <= 0)
 {
  return FALSE;
 }
 
 hProcess = OpenProcess(PROCESS_ALL_ACCESSFALSE, dwProcessId);
 if (hProcess == NULL)
 {
  ShowError(L"OpenProcess");
  return FALSE;
 }
 
 // lstrlen restituisce il numero di caratteri (meno quello nullo finale)
 // ma a noi interessa il numero di byte da allocare
 // ricordando che ogni wchar_t è 2 byte
 dwSize = 1 + lstrlen(pszDllFileName) * 2;
 pDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMITPAGE_READWRITE);
 if (pDllAddr == NULL)
 {
  ShowError(L"VirtualAllocEx");
  return FALSE;
 }
 
 if (WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL) == FALSE)
 {
  ShowError(L"WriteProcessMemory");
  return FALSE;
 }
 
 pFuncProcAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
 if (pFuncProcAddr == NULL)
 {
  ShowError(L"GetProcAddress_LoadLibraryW");
  return FALSE;
 }
 
 HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL);
 if (hRemoteThread == NULL)
 {
  ShowError(L"CreateRemoteThread");
  return FALSE;
 }
 
 CloseHandle(hProcess);
 return TRUE;
}







Codice Sorgente:
RemoteThread_Injection




Riferimenti:
[1] Windows Via C/C++ - Richter, Nasarre
[2] Taking a Snapshot and Viewing Processes
[3] https://github.com/DemonGan/Windows-Hack-Programming

Nessun commento:

Posta un commento