烦恼一般都是想太多了。

0%

使用远程线程进行DLL的注入

每个进程都有自己的虚拟内存地址空间, Windows 提供了一些API函数来让我们可以从进程外部修改进程的内存,启动线程等。这样我们就可以通过一些非常规的手段让进程执行一些我们自己的代码了。其基本的工作原理是 利用 CreateRemoteThread 在目标进程中建立一个线程,然后调用进程中已经存在的 LoadLibrary() 函数来加载我们指定的 DLL。

CreateRemoteThread()

这个函数与 CreateThread 很像,只是多了一个参数而已。

HANDLE CreateRemoteThread(
HANDLE hProcess,
PSECURITY_ATTRIBUTES psa,
DWORD dwStackSize,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD fdwCreate,
PDWORD pdwThreadId);”

hProcess 表明了我们想要在其中建立线程的进程句柄,想要获得一个进程的句柄,我们可以用 OpenProcess() 函数来获得。

LoadLibrary()

这个方法会加载指定路径的 DLL 文件。

HINSTANCE LoadLibrary(PCTSTR pszLibFile);

其实这个函数根据我们的字符集会调用不同的函数:

HINSTANCE WINAPI LoadLibraryA(LPCSTR  pszLibFileName);
HINSTANCE WINAPI LoadLibraryW(LPCWSTR pszLibFileName);
#ifdef UNICODE
#define LoadLibrary LoadLibraryW
#else
#define LoadLibrary LoadLibraryA
#endif // !UNICODE”

同时其原型与线程的启动函数非常的相似:

DWORD WINAPI ThreadFunc(PVOID pvParam);

看起来我们只需要这样就可以启动一个线程来加载我们自己的DLL了:

HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0,
LoadLibraryA, "C:\\MyLib.dll", 0, NULL);

HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0,
LoadLibraryW, "C:\\MyLib.dll", 0, NULL);

这样我们建立的新线程就会立马的调用 LoadLibrary{A|W}() 函数了。

问题

但实际上我们是不能如同上面这样做,因为存在两个问题。

第一个问题是,对于我们想调用的 LoadLibrary{A|W}() 函数,编译后是不可以直接用此名字调用的,这样会出现莫名其妙的问题,我们只能通过查找到这个函数名称所在的内存地址进行调用。

// Get the real address of LoadLibraryA in Kernel32.dll.
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");

HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0,
pfnThreadRtn, "C:\\MyLib.dll", 0, NULL);

第二个问题是,对于我们需要传递给 LoadLibrary{A|W}() 函数的参数,我们想要加载的 DLL 路径 是位于我们调用进程的地址空间内。这也会出现问题,所以我们还需要将 DLL 路径名先写到目标进程内才行。 这里我们就要用到几个函数了

// 请求向目标进程分配内存
PVOID VirtualAllocEx(
HANDLE hProcess,
PVOID pvAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect);

// 请求目标进程释放内存
BOOL VirtualFreeEx(
HANDLE hProcess,
PVOID pvAddress,
SIZE_T dwSize,
DWORD dwFreeType);

// 读目标进程内存
BOOL ReadProcessMemory(
HANDLE hProcess,
PVOID pvAddressRemote,
PVOID pvBufferLocal,
DWORD dwSize,
PDWORD pdwNumBytesRead);

// 写目标进程内存
BOOL WriteProcessMemory(
HANDLE hProcess,
PVOID pvAddressRemote,
PVOID pvBufferLocal,
DWORD dwSize,
PDWORD pdwNumBytesWritten);

总结注入过程

  1. VirtualAllocEx() 分配一块内存,我们会用来存储我们的 DLL 路径。
  2. WriteProcessMemory() 将我们的 DLL 路径写上 第 1 步申请的内存中。
  3. GetProcAddress() 函数来获取我们的 LoadLibrary{A|W}() 函数地址。
  4. CreateRemoteThread() 来创建远程线程,执行我们找到的 LoadLibrary{A|W}() 函数地址,同时传递 第 1 步中申请的内存作为参数(现在其已经存储了我们的 DLL 路径了)。
  5. VirtualFreeEx() 用来释放我们申请存储 DLL 路径的内存。
  6. 调用 GetProcAddress() 来获取 FreeLibrary() 函数的地址。
  7. 调用 CreateRemoteThread() 建立线程执行 FreeLibrary() 来释放我们 DLL 的 HINSTANCE。

这几个步骤中我们需要获得的进程句柄可以用 OpenProcess() 来获得:

hProcess = OpenProcess(
PROCESS_CREATE_THREAD | // For CreateRemoteThread
PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx
PROCESS_VM_WRITE, // For WriteProcessMemory
FALSE, dwProcessId);

具体的例子可以查看 Windows Via C/C++ 的第 22.4.1 节。

Code

/****************************************************************************** Module: InjLib.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre ******************************************************************************/
#include "..\CommonFiles\CmnHdr.h" /* See Appendix A. */
#include <windowsx.h>
#include <stdio.h>


#include <tchar.h>
#include <malloc.h> // For alloca #include <TlHelp32.h>
#include "Resource.h"
#include <StrSafe.h>
#ifdef UNICODE
#define InjectLib InjectLibW #define EjectLib EjectLibW
#else
#define InjectLib InjectLibA #define EjectLib EjectLibA
#endif // !UNICODE


BOOL WINAPI InjectLibW(DWORD dwProcessId, PCWSTR pszLibFile) {
BOOL bOk = FALSE; // Assume that the function fails
HANDLE hProcess = NULL, hThread = NULL;
PWSTR pszLibFileRemote = NULL;
__try {
// Get a handle for the target process.
hProcess = OpenProcess(
PROCESS_QUERY_INFOMATION | // require by Alpha
PROCESS_CREATE_THREAD | // For CreateRemoteThread
PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx
PROCESS_VM_WRITE, // For WriteProcessMemory
FALSE, dwProcessId);
if (hProcess == NULL) __leave;

// Calculate the number of bytes needed for the DLL's pathname
int cch = 1 + lstrlenW(pszLibFile); // numbers of wchars;
int cb = cch * sizeof(wchar_t); // so, wchar is not one byte per char.

// Allocate space in the remote process for the pathname
pszLibFileRemote = (PWSTR)
VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);
if (pszLibFileRemote == NULL) __leave;

// Copy the DLL's pathname to the remote process' address space
if (!WriteProcessMemory(hProcess, pszLibFileRemote,
(PVOID) pszLibFile, cb, NULL)) __leave;

// Get the real address of LoadLibraryW in Kernel32.dll
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
if (pfnThreadRtn == NULL) __leave;

// Create a remote thread that calls LoadLibraryW(DLLPathname)
hThread = CreateRemoteThread(hProcess, NULL, 0,
pfnThreadRtn, pszLibFileRemote, 0, NULL);
if (hThread == NULL) __leave;
// Wait for the remote thread to terminate
WaitForSingleObject(hThread, INFINITE);
bOk = TRUE; // Everything executed successfully }
__finally { // Now, we can clean everything up
// Free the remote memory that contained the DLL's pathname
if (pszLibFileRemote != NULL)
VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);
if (hThread != NULL)
CloseHandle(hThread);
if (hProcess != NULL)
CloseHandle(hProcess);
}
return(bOk);
}


BOOL WINAPI InjectLibA(DWORD dwProcessId, PCSTR pszLibFile) {
// Allocate a (stack) buffer for the Unicode version of the pathname
SIZE_T cchSize = lstrlenA(pszLibFile) + 1;
PWSTR pszLibFileW = (PWSTR)
_alloca(cchSize * sizeof(wchar_t));
// Convert the ANSI pathname to its Unicode equivalent
StringCchPrintfW(pszLibFileW, cchSize, L"%S", pszLibFile);
// Call the Unicode version of the function to actually do the work.
return(InjectLibW(dwProcessId, pszLibFileW));
}

BOOL WINAPI EjectLibW(DWORD dwProcessId, PCWSTR pszLibFile) {
BOOL bOk = FALSE; // Assume that the function fails
HANDLE hthSnapshot = NULL;
HANDLE hProcess = NULL, hThread = NULL;

__try {
// Grab a new snapshot of the process
hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
if (hthSnapshot == INVALID_HANDLE_VALUE) __leave;

// Get the HMODULE of the desired library MODULEENTRY32W me = { sizeof(me) };
BOOL bFound = FALSE;
BOOL bMoreMods = Module32FirstW(hthSnapshot, &me);
for (; bMoreMods; bMoreMods = Module32NextW(hthSnapshot, &me)) {
bFound = (_wcsicmp(me.szModule, pszLibFile) == 0) || (_wcsicmp(me.szExePath, pszLibFile) == 0);
if (bFound) break;
}
if (!bFound) __leave;
// Get a handle for the target process.
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION |
PROCESS_CREATE_THREAD | // For CreateRemoteThread
PROCESS_VM_OPERATION, FALSE, dwProcessId);
if (hProcess == NULL) __leave;

// Get the real address of FreeLibrary in Kernel32.dll
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");
if (pfnThreadRtn == NULL) __leave;

// Create a remote thread that calls FreeLibrary()
hThread = CreateRemoteThread(hProcess, NULL, 0,
pfnThreadRtn, me.modBaseAddr, 0, NULL);
if (hThread == NULL) __leave;
// Wait for the remote thread to terminate
WaitForSingleObject(hThread, INFINITE);
bOk = TRUE; // Everything executed successfully }
__finally { // Now we can clean everything up
if (hthSnapshot != NULL)
CloseHandle(hthSnapshot);
if (hThread != NULL)
CloseHandle(hThread);
if (hProcess != NULL)
CloseHandle(hProcess);
}
return(bOk);
}

BOOL WINAPI EjectLibA(DWORD dwProcessId, PCSTR pszLibFile) {
// Allocate a (stack) buffer for the Unicode version of the pathname
SIZE_T cchSize = lstrlenA(pszLibFile) + 1;
PWSTR pszLibFileW = (PWSTR)
_alloca(cchSize * sizeof(wchar_t));
// Convert the ANSI pathname to its Unicode equivalent
StringCchPrintfW(pszLibFileW, cchSize, L"%S", pszLibFile);
// Call the Unicode version of the function to actually do the work.
return(EjectLibW(dwProcessId, pszLibFileW));
}

BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam) {
chSETDLGICONS(hWnd, IDI_INJLIB);
return(TRUE);
}

void Dlg_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) {
switch (id) {
case IDCANCEL:
EndDialog(hWnd, id);
break;
case IDC_INJECT:
DWORD dwProcessId = GetDlgItemInt(hWnd, IDC_PROCESSID, NULL, FALSE);
if (dwProcessId == 0) {
// A process ID of 0 causes everything to take place in the // local process; this makes things easier for debugging.
dwProcessId = GetCurrentProcessId();
}
TCHAR szLibFile[MAX_PATH];
GetModuleFileName(NULL, szLibFile, _countof(szLibFile));
PTSTR pFilename = _tcsrchr(szLibFile, TEXT('\\')) + 1;
_tcscpy_s(pFilename, _countof(szLibFile) - (szLibFile - szLibFile),
TEXT("22-ImgWalk.DLL"));
if (InjectLib(dwProcessId, szLibFile)) {
chVERIFY(EjectLib(dwProcessId, szLibFile));
chMB("DLL Injection/Ejection successful.");
} else {
chMB("DLL Injection/Ejection failed.");
}
break;
}
}

NT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
chHANDLE_DLGMSG(hWnd, WM_INITDIALOG, Dlg_OnInitDialog);
chHANDLE_DLGMSG(hWnd, WM_COMMAND, Dlg_OnCommand); }
return(FALSE);
}

int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int) {
DialogBox(hInstExe, MAKEINTRESOURCE(IDD_INJLIB), NULL, Dlg_Proc);
return(0);
}
/****************************************************************************** Module: ImgWalk.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
******************************************************************************/
#include "..\CommonFiles\CmnHdr.h" /* See Appendix A. */
#include <tchar.h>
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad) {
if (fdwReason == DLL_PROCESS_ATTACH) {
char szBuf[MAX_PATH * 100] = { 0 };
PBYTE pb = NULL;
MEMORY_BASIC_INFORMATION mbi;
while (VirtualQuery(pb, &mbi, sizeof(mbi)) == sizeof(mbi)) {
int nLen;
char szModName[MAX_PATH];
if (mbi.State == MEM_FREE)
mbi.AllocationBase = mbi.BaseAddress;
if ((mbi.AllocationBase == hInstDll) ||
(mbi.AllocationBase != mbi.BaseAddress) ||
(mbi.AllocationBase == NULL)) {
// Do not add the module name to the list
// if any of the following is true:
// 1. If this region contains this DLL
// 2. If this block is NOT the beginning of a region // 3. If the address is NULL
nLen = 0;
} else {
nLen = GetModuleFileNameA((HINSTANCE) mbi.AllocationBase,
szModName, _countof(szModName));
}
if (nLen > 0) {
wsprintfA(strchr(szBuf, 0), "\n%p-%s",
mbi.AllocationBase, szModName);
}
pb += mbi.RegionSize;
}
// NOTE: Normally, you should not display a message box in DllMain // due to the loader lock described in Chapter 20. However, to keep


// this sample application simple, I am violating this rule.
chMB(&szBuf[1]);
}
return(TRUE);
}