您的位置:首页 > 其它

远程线程的注入和注出

2016-03-29 21:24 387 查看
前言

昨天做远程线程的注入注出试验,出现了2个问题

* 注出时崩溃

* 注出时窗口属性变了(e.g. Unicode版变成了Ascii版)

现在问题已经解决, 可以稳定的连续注入和注出. 如果以后要用到远程线程, 下面的代码就直接能用了

试验环境: win7X64 + vs2013 + 控制台版测试程序 + SDK版DLL

试验对象: x86版的calc.exe和x64版的calc.exe

试验操作: VK_HOME键弹框, VK_END键弹框并卸载注入DLL

现在编码熟练度确实好很多, 只需要IDE和本地MSDN就可以完成工程了.

工程下载点

src_calcInject.zip

注入管理程序

// calcInjectManager.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
#include <conio.h>
#include <ctype.h>

#include <psapi.h>
#pragma comment(lib, "psapi.lib")

#define OBJ_CLASS_NAME _T("CalcFrame")
#define INJECT_DLL_NAME _T("calcInjectDll.dll")
#define MEMORY_MAP_NAME _T("for Inject dll")

#pragma pack(push)
#pragma pack(1)
typedef struct _tag_obj_exe_info {
DWORD dwPID;
TCHAR szClassName[MAX_PATH];

_tag_obj_exe_info() {
dwPID = 0;
ZeroMemory(szClassName, sizeof(szClassName));
}
}TAG_OBJ_EXE_INFO;
#pragma pack(pop)

/// 注入DLL到计算器, x86/x64均可,
/// 管理程序和注入用DLL和目标程序要编译成相同的x86 or x64
void fnInjectDll();

int _tmain(int argc, _TCHAR* argv[])
{
do
{
fnInjectDll();
_tprintf(_T("if need inject dll, press c, other key to quit\n"));
} while ('c' == _getch());

_tsystem(_T("pause"));
return 0;
}

void fnInjectDll() {
HWND hWnd = NULL;
DWORD dwPID = 0;
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
DWORD dwThreadID = 0;
TCHAR szBuf[MAX_PATH] = { _T('\0') };
TCHAR* pTmp = NULL;
FILE * pFile = NULL;
LPVOID lpAddress = NULL;
SIZE_T  NumberOfBytesWritten = 0;
DWORD dwRc = 0;
HANDLE hFileMap = NULL;
LPVOID lpMapBuf = NULL;
TAG_OBJ_EXE_INFO ObjExeInfo;

_tprintf(_T("inject to calc\n"));

do
{
hWnd = FindWindow(OBJ_CLASS_NAME, NULL);
if (NULL == hWnd) {
_tprintf(_T("can't find calc\n"));
break;
}

_tcscpy_s(ObjExeInfo.szClassName, OBJ_CLASS_NAME);
GetWindowThreadProcessId(hWnd, &dwPID);
ObjExeInfo.dwPID = dwPID;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
if (NULL == hProcess) {
_tprintf(_T("can't open process\n"));
break;
}

/// inject.dll on my dir
GetModuleFileName(NULL, szBuf, sizeof(szBuf) / sizeof(TCHAR));
pTmp = _tcsrchr(szBuf, _T('\\'));
if (NULL == pTmp) {
_tprintf(_T("can't make inject dll' name\n"));
break;
}

_tcscpy_s(pTmp + 1, MAX_PATH - ((DWORD)pTmp + 1 - (DWORD)szBuf), INJECT_DLL_NAME);
if ((0 != _tfopen_s(&pFile, szBuf, _T("rb"))) || (NULL == pFile)) {
_tprintf(_T("can't find inject dll [%s]\n"), szBuf);
break;
}

fclose(pFile);

lpAddress = VirtualAllocEx(hProcess, 0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == lpAddress) {
_tprintf(_T("VirtualAllocEx failed\n"));
}

if (!WriteProcessMemory(hProcess, lpAddress,
szBuf, (_tcslen(szBuf) + 1) * sizeof(TCHAR), &NumberOfBytesWritten)) {
_tprintf(_T("can't WriteProcessMemory for dll name\n"));
break;
}

/// write ObjExeInfo to MemoryMap
hFileMap = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0x0, 01000, MEMORY_MAP_NAME);
if (NULL == hFileMap) {
_tprintf(_T("CreateFileMapping failed\n"));
break;
}

lpMapBuf = MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
MAX_PATH);
if (NULL == lpMapBuf) {
_tprintf(_T("MapViewOfFile failed\n"));
break;
}

memcpy(lpMapBuf, &ObjExeInfo, sizeof(ObjExeInfo));
UnmapViewOfFile(lpMapBuf);

hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)LoadLibraryW, lpAddress, 0, &dwThreadID);
if (NULL == hThread) {
_tprintf(_T("CreateRemoteThread failed\n"));
break;
}

if ((WAIT_OBJECT_0 != WaitForSingleObject(hThread, INFINITE))
|| (0 == GetExitCodeThread(hThread, &dwRc))) {
_tprintf(_T("thread execute failed\n"));
break;
}

_tprintf(_T("dll inject ok\n"));
} while (0);

if (NULL != hFileMap) {
CloseHandle(hFileMap);
}

if (NULL != hThread) {
CloseHandle(hThread);
}

if (NULL != hProcess) {
if (NULL != lpAddress) {
VirtualFreeEx(hProcess, lpAddress, 0x0, MEM_RELEASE);
}

CloseHandle(hProcess);
}
}


注入用DLL

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
#include <assert.h>
#include <process.h>

#define MEMORY_MAP_NAME _T("for Inject dll")

#pragma pack(push)
#pragma pack(1)
typedef struct _tag_obj_exe_info {
DWORD dwPID;
TCHAR szClassName[MAX_PATH];

_tag_obj_exe_info() {
dwPID = 0;
ZeroMemory(szClassName, sizeof(szClassName));
}
}TAG_OBJ_EXE_INFO;

typedef struct _tag_find_obj_exe {
TAG_OBJ_EXE_INFO Obj;
BOOL bFindObj;
HWND hWnd;

_tag_find_obj_exe() {
bFindObj = FALSE;
hWnd = NULL;
}
}TAG_FIND_OBJ_EXE;
#pragma pack(pop)

TAG_FIND_OBJ_EXE g_FindObjExe;
WNDPROC g_pWndProcOrg = NULL; ///< 用GetWindowLong得到的原始窗口过程处理函数地址
WNDPROC g_pWndProcPrev = NULL; ///< 用SetWindowLong返回的原始窗口过程处理函数地址
/// assert(g_pWndProcOrg == g_pWndProcPrev); ///< 必定成立

HMODULE g_hDllModule = NULL;

/// 必须在EXE传来目标窗口时,确定是Unicode版还是Ascii版的目标程序
/// 等恢复原始窗口过程时, 再判断, 会得到错误的结果
BOOL g_bObjWindowIsUnicode = FALSE;

void OnDllAttach(); ///< Dll被加载
void OnDllDetach(); ///< Dll被卸载
void fnSubClass(); ///< 子类化
void fnUnSubClass(); ///< 反子类化

/// 注入后的新窗口处理过程函数
LRESULT CALLBACK WndProcNew(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

/// 枚举回调, 用来验证EXE传来的参数是否正确
BOOL CALLBACK EnumWindowsProc(_In_ HWND   hwnd, _In_ LPARAM lParam);

/// 卸载DLL用的线程
void __cdecl ThreadProc(void *);

BOOL APIENTRY DllMain(HMODULE hModule,
DWORD  ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
g_hDllModule = hModule; ///< 保存Dll句柄
OnDllAttach();
}
break;

case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
{
}
break;

case DLL_PROCESS_DETACH:
{
OnDllDetach();
}
break;
}
return TRUE;
}

void OnDllAttach() {
HANDLE hFileMap = NULL; ///< 注入管理程序EXE创建的参数内存映射
LPVOID lpMapBuf = NULL; ///< 打开内存映射后的缓冲区指针
TCHAR szBuf[MAX_PATH] = { _T('\0') }; ///< 临时缓冲区

OutputDebugString(_T("load calcInjectDll\n"));

do {
/// read paramter from memory map
hFileMap = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, MEMORY_MAP_NAME);
if (NULL == hFileMap) {
OutputDebugString(_T("OpenFileMapping failed"));
break;
}

if (NULL != hFileMap) {
lpMapBuf = MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
MAX_PATH);
if (NULL == lpMapBuf) {
OutputDebugString(_T("MapViewOfFile failed\n"));
break;
}

memcpy(&g_FindObjExe.Obj, lpMapBuf, sizeof(g_FindObjExe.Obj));
UnmapViewOfFile(lpMapBuf);

/// find obj exe by param, the parameter is right ?
EnumWindows(EnumWindowsProc, (LPARAM)&g_FindObjExe);
if (!g_FindObjExe.bFindObj) {
OutputDebugString(_T("can't find obj exe\n"));
break;
}

/// 第一时间判断目标窗口是W版还是A版 !
g_bObjWindowIsUnicode = ::IsWindowUnicode(g_FindObjExe.hWnd);

/// @todo is my host is the g_FindObjExe ?

/// 接收键盘消息的是子窗口
/// 计算器
/// - CalcFrame
/// -- Static
/// -- #32770
/// --- Static
/// --- Static
/// --- Static => find it!

/// No.1子窗口A
assert(NULL != g_FindObjExe.hWnd);
g_FindObjExe.hWnd = GetWindow(g_FindObjExe.hWnd, GW_CHILD); ///< CalcFrame
/// A的No.2子窗口B
assert(NULL != g_FindObjExe.hWnd);
g_FindObjExe.hWnd = GetWindow(g_FindObjExe.hWnd, GW_CHILD); ///< Static
g_FindObjExe.hWnd = GetWindow(g_FindObjExe.hWnd, GW_HWNDNEXT); ///< #32770
/// B的No.3子窗口C
g_FindObjExe.hWnd = GetWindow(g_FindObjExe.hWnd, GW_CHILD); ///< Static
assert(NULL != g_FindObjExe.hWnd);
g_FindObjExe.hWnd = GetWindow(g_FindObjExe.hWnd, GW_HWNDNEXT); ///< Static
g_FindObjExe.hWnd = GetWindow(g_FindObjExe.hWnd, GW_HWNDNEXT); ///< Static, find!

/// 计算器输入区输入几个数字, 看看是否找的对?
GetClassName(g_FindObjExe.hWnd, szBuf, sizeof(szBuf) / sizeof(TCHAR));
GetWindowText(g_FindObjExe.hWnd, szBuf, sizeof(szBuf) / sizeof(TCHAR));

fnSubClass();
_stprintf_s(szBuf, sizeof(szBuf) / sizeof(TCHAR),
_T(">> g_FindObjExe.hWnd = 0x%p, g_pWndProcOrg = 0x%p"), g_FindObjExe.hWnd, g_pWndProcOrg);
OutputDebugString(szBuf);
}
} while (0);

if (NULL != hFileMap) {
CloseHandle(hFileMap);
}
}

void OnDllDetach() {
OutputDebugString(_T("unload calcInjectDll\n"));
fnUnSubClass();
}

LRESULT CALLBACK WndProcNew(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
TCHAR szBuf[MAX_PATH] = { _T('\0') };
LRESULT lRc = 0;
assert(NULL != g_pWndProcOrg);

/// lParam bit 30 is last key status, bit30 = 0 is last key was up
if (WM_KEYDOWN == uMsg) {
if (0 == (lParam & 0x40000000)) {
if (VK_HOME == wParam) {
OutputDebugString(_T("WM_KEYDOWN : VK_HOME"));
/// show a dialog
::MessageBox(hWnd, _T("VK_HOME press down"), _T("calcInjectDll"), MB_OK);
}
else if (VK_END == wParam) {
OutputDebugString(_T("WM_KEYDOWN : VK_END"));
/// unload inject dll
::MessageBox(hWnd, _T("VK_END press down, will be unload dll"), _T("calcInjectDll"), MB_OK);
_stprintf_s(szBuf, sizeof(szBuf) / sizeof(TCHAR),
_T("<< g_FindObjExe.hWnd = 0x%p, g_pWndProcOrg = 0x%p"), g_FindObjExe.hWnd, g_pWndProcOrg);
OutputDebugString(szBuf);

/// 使DLL卸载稳定的做法
/// * 先执行原始的消息处理
lRc = g_pWndProcOrg(hWnd, uMsg, wParam, lParam);

/// * 还原窗口过程
if (NULL != g_pWndProcOrg) {
(WNDPROC)::SetWindowLongPtrA(
g_FindObjExe.hWnd, GWLP_WNDPROC, (LONG_PTR)g_pWndProcOrg);
}

/// * 起线程卸载DLL, 不返回 !
_beginthread(ThreadProc, 0, NULL);

/// 返回原始窗口过程处理函数的结果, 下一次就不会进这里了:)
return lRc;
}
}
}

return g_pWndProcOrg(hWnd, uMsg, wParam, lParam);
}

void __cdecl ThreadProc(void *) {
Sleep(1);

/// ! 必须使用FreeLibraryAndExitThread
FreeLibraryAndExitThread(g_hDllModule, 0x1000);
}

BOOL CALLBACK EnumWindowsProc(
_In_ HWND   hwnd,
_In_ LPARAM lParam
) {
BOOL bNeedFindNext = TRUE;
TCHAR szBuf[MAX_PATH] = { _T('\0') };
TAG_FIND_OBJ_EXE* pFindObjExe = (TAG_FIND_OBJ_EXE*)lParam;

do
{
if (NULL == pFindObjExe) {
OutputDebugString(_T("paramter error"));
bNeedFindNext = FALSE;
break;
}

if (0 == GetClassName(hwnd, szBuf, sizeof(szBuf) / sizeof(TCHAR))) {
break;
}

if (0 == _tcscmp(szBuf, pFindObjExe->Obj.szClassName)) {
pFindObjExe->bFindObj = TRUE;
pFindObjExe->hWnd = hwnd;
bNeedFindNext = FALSE;
}
} while (0);

return bNeedFindNext;
}

void fnSubClass() {
/// subclass, 必须按照被注入的目标程序来确定子类化API的W或A版 !
/// 否则子类化失败
/// 注入管理程序,注入用DLL,目标程序必须配套, all x86 or all x64 !
/// x86程序只能注入x86目标程序, x64程序只能注入x64程序
if (::IsWindowUnicode(g_FindObjExe.hWnd)) {
OutputDebugString(_T("fnSubClass by [Unicode]"));
g_pWndProcOrg = (WNDPROC)::GetWindowLongPtrW(g_FindObjExe.hWnd, GWLP_WNDPROC);
g_pWndProcPrev = (WNDPROC)::SetWindowLongPtrW(
g_FindObjExe.hWnd, GWLP_WNDPROC, (LONG_PTR)WndProcNew);
}
else {
OutputDebugString(_T("fnSubClass by [Ascii]"));
g_pWndProcOrg = (WNDPROC)::GetWindowLongPtrA(g_FindObjExe.hWnd, GWLP_WNDPROC);
g_pWndProcPrev = (WNDPROC)::SetWindowLongPtrA(
g_FindObjExe.hWnd, GWLP_WNDPROC, (LONG_PTR)WndProcNew);
}

assert(g_pWndProcOrg == g_pWndProcPrev); ///< 必定相等
}

void fnUnSubClass() {
/// subclass, 必须按照被注入的目标程序来确定子类化API的W或A版 !
/// 否则还原原始窗口过程后,按键的反应完全不对了:)

/// 不能现判断,必须使用子类化之前就判断好的标志 g_bObjWindowIsUnicode
/// 防止恢复窗口过程时再判断,得出错误的结果
if (g_bObjWindowIsUnicode) {
OutputDebugString(_T("fnUnSubClass by [Unicode]"));
::SetWindowLongPtrW(
g_FindObjExe.hWnd, GWLP_WNDPROC, (LONG_PTR)g_pWndProcOrg);
}
else {
OutputDebugString(_T("fnUnSubClass by [Ascii]"));
::SetWindowLongPtrA(
g_FindObjExe.hWnd, GWLP_WNDPROC, (LONG_PTR)g_pWndProcOrg);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: