#include "stdafx.h" #include "CWxObject.h" #include #define WX_MAIN_WND_NAME _T("微信") #define WX_MAIN_WND_CLASS_NAME _T("WeChatMainWndForPC") #define WX_LOGIN_WND_NAME _T("登录") #define WX_LOGIN_WND_CLASS_NAME _T("WeChatLoginWndForPC") HHOOK CWxObject::m_hook = NULL; BOOL ASCII2UNICODE(IN LPCCH lpASCIIStr, OUT PWCH pUNICODEStr, IN CONST INT& nUNICODEStrLen) { if (lpASCIIStr == NULL) return FALSE; // 获取宽字符字节数; int cchWideChar = MultiByteToWideChar(CP_ACP, 0, lpASCIIStr, -1, NULL, 0); if (cchWideChar == 0 || cchWideChar >= nUNICODEStrLen) return FALSE; // 转换成宽字符串; memset(pUNICODEStr, 0, sizeof(WCHAR)*nUNICODEStrLen); int nWriteNum = MultiByteToWideChar(CP_ACP, 0, lpASCIIStr, -1, pUNICODEStr, cchWideChar); if (nWriteNum != cchWideChar) return FALSE; return TRUE; } CWxObject::CWxObject() :m_dwWxProcId(0) , m_hWxProcess(NULL) , m_hWxMainWnd(NULL) , m_hWxLoginWnd(NULL) , m_lpInjectData(NULL) , m_lpEjectData(NULL) , m_hInjectThread(NULL) , m_hEjectThread(NULL) , m_dwPathLen(0) , m_bAttached(FALSE) //, m_hook(NULL) { memset(&m_WxMainWndInfo, 0, sizeof(WNDINFO)); } CWxObject::CWxObject(DWORD dwProcId, LPCTSTR lpDynamicLibraryPath) :m_dwWxProcId(dwProcId) , m_hWxProcess(NULL) , m_hWxMainWnd(NULL) , m_hWxLoginWnd(NULL) , m_lpInjectData(NULL) , m_lpEjectData(NULL) , m_hInjectThread(NULL) , m_hEjectThread(NULL) , m_dwPathLen(0) , m_bAttached(FALSE) //, m_hook(NULL) { memset(&m_WxMainWndInfo, 0, sizeof(WNDINFO)); setInjectionObj(dwProcId, lpDynamicLibraryPath); } CWxObject::~CWxObject() { // 卸载dll; EjectDynamicLibrary(); // 释放所有资源; if (m_hInjectThread) CloseHandle(m_hInjectThread); m_hInjectThread = NULL; if (m_hEjectThread) CloseHandle(m_hEjectThread); m_hEjectThread = NULL; if (m_lpInjectData) VirtualFreeEx(m_hWxProcess, m_lpInjectData, m_dwPathLen, MEM_RELEASE); m_lpInjectData = NULL; if (m_lpEjectData) VirtualFreeEx(m_hWxProcess, m_lpEjectData, m_dwPathLen, MEM_RELEASE); m_lpEjectData = NULL; if (m_hWxProcess) CloseHandle(m_hWxProcess); m_hWxProcess = NULL; // 退出主窗口; // 注:必须在主窗口销毁前分离; if (!m_bAttached) DetachWxWnd(); if (m_hook) UnhookWindowsHookEx(m_hook); } void CWxObject::setInjectionObj(DWORD dwProcId, LPCTSTR lpDynamicLibraryPath) { ASSERT(dwProcId != 0); ASSERT(lpDynamicLibraryPath != NULL); m_dwWxProcId = dwProcId; memset(m_szDllPath, 0, sizeof(m_szDllPath)); memset(m_wszDllPath, 0, sizeof(m_wszDllPath)); #ifdef UNICODE _tcscpy_s(m_szDllPath, lpDynamicLibraryPath); #else _tcscpy_s(m_szDllPath, lpDynamicLibraryPath); ASCII2UNICODE(lpDynamicLibraryPath, m_wszDllPath, MAX_PATH); #endif //m_hWxProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_dwWxProcId); m_hWxProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, m_dwWxProcId); if (m_hWxProcess == NULL) { WriteTextLog(_T("打开WeChat.exe进程失败")); } } BOOL CWxObject::InjectDynamicLibrary() { ASSERT(m_hWxProcess != NULL); m_dwPathLen = wcslen(m_wszDllPath) * sizeof(WCHAR) + 1; m_lpInjectData = VirtualAllocEx(m_hWxProcess, NULL, m_dwPathLen, MEM_COMMIT, PAGE_READWRITE); if (NULL == m_lpInjectData) { WriteTextLog(_T("创建WeChat.exe进程虚拟内存失败")); return FALSE; } if (WriteProcessMemory(m_hWxProcess, m_lpInjectData, m_wszDllPath, m_dwPathLen, NULL) == 0) { // 注意:MEM_RELEASE释放时第三参数一定要为0,请查看MSDN; VirtualFreeEx(m_hWxProcess, m_lpInjectData, 0, MEM_RELEASE); return FALSE; } HMODULE hk32 = GetModuleHandle(_T("kernel32.dll")); // 注意:微信使用的是W版本; LPVOID lpAddr = GetProcAddress(hk32, "LoadLibraryW"); m_hInjectThread = CreateRemoteThread(m_hWxProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpAddr, m_lpInjectData, 0, NULL); if (NULL == m_hInjectThread) { // 注意:MEM_RELEASE释放时第三参数一定要为0,请查看MSDN; VirtualFreeEx(m_hWxProcess, m_lpInjectData, 0, MEM_RELEASE); return FALSE; } WaitForSingleObject(m_hInjectThread, INFINITE); if (m_hInjectThread) CloseHandle(m_hInjectThread); m_hInjectThread = NULL; /* 注入成功后,不能释放内存否则微信会挂; if (m_lpInjectData != NULL) VirtualFreeEx(m_hWxProcess, m_lpInjectData, 0, MEM_RELEASE); */ return TRUE; } BOOL CWxObject::EjectDynamicLibrary() { if (m_hWxProcess == NULL) return TRUE; // 获取模块句柄; HANDLE hModule = FindModuleEx(m_szDllPath, m_dwWxProcId); if (hModule == NULL) { WriteTextLog(_T("获取WeChat.exe进程模块hook.dll失败")); return FALSE; } LPVOID lpAddr = GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "FreeLibraryAndExitThread");//FreeLibraryAndExitThread//FreeLibrary if (lpAddr == NULL) { WriteTextLog(_T("获取kernel32.dll中的FreeLibraryAndExitThread失败")); return FALSE; } m_hEjectThread = CreateRemoteThread(m_hWxProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpAddr, hModule, 0, NULL); if (m_hEjectThread == NULL) { WriteTextLog(_T("创建WeChat.exe远程线程(FreeLibraryAndExitThread)失败")); return FALSE; } WaitForSingleObject(m_hEjectThread, INFINITE); if (m_hEjectThread) CloseHandle(m_hEjectThread); m_hEjectThread = NULL; return TRUE; } BOOL CWxObject::FindWxMainWnd() { m_WxMainWndInfo.hWxWnd = NULL; m_WxMainWndInfo.dwWxProcId = m_dwWxProcId; _stprintf_s(m_WxMainWndInfo.szWndName, WX_MAIN_WND_NAME); _stprintf_s(m_WxMainWndInfo.szClassName, WX_MAIN_WND_CLASS_NAME); if (::EnumWindows(&EnumWindowsProc, (LPARAM)& m_WxMainWndInfo) == FALSE) { m_hWxMainWnd = m_WxMainWndInfo.hWxWnd; m_rcWxWnd = m_WxMainWndInfo.rcWnd; return TRUE; } return FALSE; } BOOL CWxObject::FindWxLoginWnd() { m_WxLoginWndInfo.hWxWnd = NULL; m_WxLoginWndInfo.dwWxProcId = m_dwWxProcId; _stprintf_s(m_WxLoginWndInfo.szWndName, WX_LOGIN_WND_NAME); _stprintf_s(m_WxLoginWndInfo.szClassName, WX_LOGIN_WND_CLASS_NAME); if (::EnumWindows(&EnumWindowsProc, (LPARAM)& m_WxLoginWndInfo) == FALSE) { m_hWxLoginWnd = m_WxLoginWndInfo.hWxWnd; m_rcWxWnd = m_WxLoginWndInfo.rcWnd; return TRUE; } return FALSE; } BOOL CWxObject::Attach2MainWnd(CWnd *pMainWnd, BOOL bLoginWnd) { HWND hWxWnd = bLoginWnd ? m_hWxLoginWnd : m_hWxMainWnd; if (hWxWnd != NULL) { // 获取微信窗口的样式; DWORD dwStyle = ::GetWindowLong(hWxWnd, GWL_STYLE); // WS_CLIPSIBLINGS告诉父窗口不要绘制子窗口出现的区域; //dwStyle |= WS_CLIPSIBLINGS; // 如果窗口隐藏的,显示出来; dwStyle |= WS_VISIBLE; // 重新设置窗口样式 ; ::SetWindowLong(hWxWnd, GWL_STYLE, dwStyle); CRect rect; pMainWnd->GetWindowRect(&rect); // 设置父窗口; ::SetParent(hWxWnd, pMainWnd->m_hWnd);//set parent of ms paint to our dialog. // 擦除背景; //SetWindowLong(hWxWnd, GWL_STYLE, WS_VISIBLE);//eraze title of ms paint window. //Positioning ms paint. // 将屏幕坐标转在窗口坐标; pMainWnd->ScreenToClient(&rect); // 居中显示; CRect rcDisplay = rect; if (bLoginWnd) {// 居中显示; if (m_rcWxWnd.Width() >= rect.Width()) { rcDisplay.left = 0; rcDisplay.right = rect.right; } else { rcDisplay.left = (rect.Width() - m_rcWxWnd.Width()) / 2; rcDisplay.right = rcDisplay.left + m_rcWxWnd.Width(); } if (m_rcWxWnd.Height() >= rect.Height()) { rcDisplay.top = 0; rcDisplay.bottom = rect.bottom; } else { rcDisplay.top = (rect.Height() - m_rcWxWnd.Height()) / 2; rcDisplay.bottom = rcDisplay.top + m_rcWxWnd.Height(); } // 注意:MoveWindow/SetWindowPos使用的是父窗口的坐标,如果父窗口为NULL,则使用的是屏幕坐标; ::MoveWindow(hWxWnd, rcDisplay.left, rcDisplay.top, rcDisplay.Width(), rcDisplay.Height(), true); //::SetWindowPos(hWxWnd, NULL, rcDisplay.left, rcDisplay.top, rcDisplay.Width(), rcDisplay.Height(), WM_WINDOWPOSCHANGING|SWP_SHOWWINDOW | SWP_HIDEWINDOW); } else {//最大化显示并固定; // 注意:MoveWindow/SetWindowPos使用的是父窗口的坐标,如果父窗口为NULL,则使用的是屏幕坐标; ::MoveWindow(hWxWnd, rcDisplay.left, rcDisplay.top, rcDisplay.Width(), rcDisplay.Height(), true); if (!bLoginWnd) ::PostMessage(hWxWnd, WM_SYSCOMMAND, SC_MAXIMIZE, NULL); //::SetWindowPos(hWxWnd, NULL, rcDisplay.left, rcDisplay.top, rcDisplay.Width(), rcDisplay.Height(), WM_MOVE| WM_SIZE| WM_WINDOWPOSCHANGING| WM_NCCALCSIZE | SWP_SHOWWINDOW); } //窗口重绘,(因创建exe时,设置为SW_HIDE,导致exe窗口会被父窗口覆盖一部分) pMainWnd->Invalidate(); ::UpdateWindow(hWxWnd); ::ShowWindow(hWxWnd, SW_SHOW); m_bAttached = TRUE; } return 0; } BOOL CWxObject::DetachWxWnd() { if (m_bAttached) { //if (m_hWxMainWnd && GetParent(m_hWxMainWnd)) if (m_hWxMainWnd) // 使用GetParent可能返回NULL ::SetParent(m_hWxMainWnd, NULL); if (m_hWxLoginWnd) ::SetParent(m_hWxLoginWnd, NULL); m_bAttached = FALSE; #ifdef _DEBUG WriteTextLog(_T("DetachWxWnd:分离微信窗口")); #endif } return 0; } BOOL CWxObject::SetHook() { if (m_hook == NULL) m_hook = SetWindowsHookEx(WH_CBT, HookProc, NULL, ::GetCurrentThreadId()); return 0; } BOOL CWxObject::EnumWindowsProc(HWND hwnd, LPARAM lParam) { DWORD dwProcId = 0, dwThreadId; TCHAR szWndName[MAX_PATH] = { 0 }; TCHAR szClassName[MAX_PATH] = { 0 }; WNDINFO* pWndInfo = (WNDINFO*)lParam; dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcId); if (dwProcId == pWndInfo->dwWxProcId) { pWndInfo->hWxWnd = hwnd; pWndInfo->dwThreadId = dwThreadId; ::GetWindowText(hwnd, szWndName, MAX_PATH); ::GetClassName(hwnd, szClassName, MAX_PATH); #ifdef _DEBUG WriteTextLog(_T("窗口名称:%s, 窗口类名:%s, 线程ID:%d,句柄:%p"), szWndName, szClassName, dwThreadId, hwnd); #endif if (_tcscmp(szWndName, pWndInfo->szWndName) == 0 && _tcscmp(szClassName, pWndInfo->szClassName) == 0) { ::GetWindowRect(hwnd, &pWndInfo->rcWnd); return FALSE; } } return TRUE; } LRESULT CWxObject::HookProc(int nCode, WPARAM wParam, LPARAM lParam) { WriteTextLog(_T("HookProc被调用")); if (nCode == HCBT_MINMAX) { WriteTextLog(_T("窗口HCBT_MINMAX")); return 1; } return CallNextHookEx(m_hook, nCode, wParam, lParam); } BOOL CWxObject::OpenWeChat() { return 0; }