CWxObject.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #include "stdafx.h"
  2. #include "CWxObject.h"
  3. #include <math.h>
  4. #define WX_MAIN_WND_NAME _T("微信")
  5. #define WX_MAIN_WND_CLASS_NAME _T("WeChatMainWndForPC")
  6. #define WX_LOGIN_WND_NAME _T("登录")
  7. #define WX_LOGIN_WND_CLASS_NAME _T("WeChatLoginWndForPC")
  8. HHOOK CWxObject::m_hook = NULL;
  9. BOOL ASCII2UNICODE(IN LPCCH lpASCIIStr, OUT PWCH pUNICODEStr, IN CONST INT& nUNICODEStrLen)
  10. {
  11. if (lpASCIIStr == NULL)
  12. return FALSE;
  13. // 获取宽字符字节数;
  14. int cchWideChar = MultiByteToWideChar(CP_ACP, 0, lpASCIIStr, -1, NULL, 0);
  15. if (cchWideChar == 0 || cchWideChar >= nUNICODEStrLen)
  16. return FALSE;
  17. // 转换成宽字符串;
  18. memset(pUNICODEStr, 0, sizeof(WCHAR)*nUNICODEStrLen);
  19. int nWriteNum = MultiByteToWideChar(CP_ACP, 0, lpASCIIStr, -1, pUNICODEStr, cchWideChar);
  20. if (nWriteNum != cchWideChar)
  21. return FALSE;
  22. return TRUE;
  23. }
  24. CWxObject::CWxObject()
  25. :m_dwWxProcId(0)
  26. , m_hWxProcess(NULL)
  27. , m_hWxMainWnd(NULL)
  28. , m_hWxLoginWnd(NULL)
  29. , m_lpInjectData(NULL)
  30. , m_lpEjectData(NULL)
  31. , m_hInjectThread(NULL)
  32. , m_hEjectThread(NULL)
  33. , m_dwPathLen(0)
  34. , m_bAttached(FALSE)
  35. //, m_hook(NULL)
  36. {
  37. memset(&m_WxMainWndInfo, 0, sizeof(WNDINFO));
  38. }
  39. CWxObject::CWxObject(DWORD dwProcId, LPCTSTR lpDynamicLibraryPath)
  40. :m_dwWxProcId(dwProcId)
  41. , m_hWxProcess(NULL)
  42. , m_hWxMainWnd(NULL)
  43. , m_hWxLoginWnd(NULL)
  44. , m_lpInjectData(NULL)
  45. , m_lpEjectData(NULL)
  46. , m_hInjectThread(NULL)
  47. , m_hEjectThread(NULL)
  48. , m_dwPathLen(0)
  49. , m_bAttached(FALSE)
  50. //, m_hook(NULL)
  51. {
  52. memset(&m_WxMainWndInfo, 0, sizeof(WNDINFO));
  53. setInjectionObj(dwProcId, lpDynamicLibraryPath);
  54. }
  55. CWxObject::~CWxObject()
  56. {
  57. // 卸载dll;
  58. EjectDynamicLibrary();
  59. // 释放所有资源;
  60. if (m_hInjectThread)
  61. CloseHandle(m_hInjectThread);
  62. m_hInjectThread = NULL;
  63. if (m_hEjectThread)
  64. CloseHandle(m_hEjectThread);
  65. m_hEjectThread = NULL;
  66. if (m_lpInjectData)
  67. VirtualFreeEx(m_hWxProcess, m_lpInjectData, m_dwPathLen, MEM_RELEASE);
  68. m_lpInjectData = NULL;
  69. if (m_lpEjectData)
  70. VirtualFreeEx(m_hWxProcess, m_lpEjectData, m_dwPathLen, MEM_RELEASE);
  71. m_lpEjectData = NULL;
  72. if (m_hWxProcess)
  73. CloseHandle(m_hWxProcess);
  74. m_hWxProcess = NULL;
  75. // 退出主窗口;
  76. // 注:必须在主窗口销毁前分离;
  77. if (!m_bAttached)
  78. DetachWxWnd();
  79. if (m_hook)
  80. UnhookWindowsHookEx(m_hook);
  81. }
  82. void CWxObject::setInjectionObj(DWORD dwProcId, LPCTSTR lpDynamicLibraryPath)
  83. {
  84. ASSERT(dwProcId != 0);
  85. ASSERT(lpDynamicLibraryPath != NULL);
  86. m_dwWxProcId = dwProcId;
  87. memset(m_szDllPath, 0, sizeof(m_szDllPath));
  88. memset(m_wszDllPath, 0, sizeof(m_wszDllPath));
  89. #ifdef UNICODE
  90. _tcscpy_s(m_szDllPath, lpDynamicLibraryPath);
  91. #else
  92. _tcscpy_s(m_szDllPath, lpDynamicLibraryPath);
  93. ASCII2UNICODE(lpDynamicLibraryPath, m_wszDllPath, MAX_PATH);
  94. #endif
  95. //m_hWxProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_dwWxProcId);
  96. m_hWxProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, m_dwWxProcId);
  97. if (m_hWxProcess == NULL)
  98. {
  99. WriteTextLog(_T("打开WeChat.exe进程失败"));
  100. }
  101. }
  102. BOOL CWxObject::InjectDynamicLibrary()
  103. {
  104. ASSERT(m_hWxProcess != NULL);
  105. m_dwPathLen = wcslen(m_wszDllPath) * sizeof(WCHAR) + 1;
  106. m_lpInjectData = VirtualAllocEx(m_hWxProcess, NULL, m_dwPathLen, MEM_COMMIT, PAGE_READWRITE);
  107. if (NULL == m_lpInjectData)
  108. {
  109. WriteTextLog(_T("创建WeChat.exe进程虚拟内存失败"));
  110. return FALSE;
  111. }
  112. if (WriteProcessMemory(m_hWxProcess, m_lpInjectData, m_wszDllPath, m_dwPathLen, NULL) == 0)
  113. {
  114. // 注意:MEM_RELEASE释放时第三参数一定要为0,请查看MSDN;
  115. VirtualFreeEx(m_hWxProcess, m_lpInjectData, 0, MEM_RELEASE);
  116. return FALSE;
  117. }
  118. HMODULE hk32 = GetModuleHandle(_T("kernel32.dll"));
  119. // 注意:微信使用的是W版本;
  120. LPVOID lpAddr = GetProcAddress(hk32, "LoadLibraryW");
  121. m_hInjectThread = CreateRemoteThread(m_hWxProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpAddr, m_lpInjectData, 0, NULL);
  122. if (NULL == m_hInjectThread)
  123. {
  124. // 注意:MEM_RELEASE释放时第三参数一定要为0,请查看MSDN;
  125. VirtualFreeEx(m_hWxProcess, m_lpInjectData, 0, MEM_RELEASE);
  126. return FALSE;
  127. }
  128. WaitForSingleObject(m_hInjectThread, INFINITE);
  129. if (m_hInjectThread)
  130. CloseHandle(m_hInjectThread);
  131. m_hInjectThread = NULL;
  132. /* 注入成功后,不能释放内存否则微信会挂;
  133. if (m_lpInjectData != NULL)
  134. VirtualFreeEx(m_hWxProcess, m_lpInjectData, 0, MEM_RELEASE);
  135. */
  136. return TRUE;
  137. }
  138. BOOL CWxObject::EjectDynamicLibrary()
  139. {
  140. if (m_hWxProcess == NULL)
  141. return TRUE;
  142. // 获取模块句柄;
  143. HANDLE hModule = FindModuleEx(m_szDllPath, m_dwWxProcId);
  144. if (hModule == NULL)
  145. {
  146. WriteTextLog(_T("获取WeChat.exe进程模块hook.dll失败"));
  147. return FALSE;
  148. }
  149. LPVOID lpAddr = GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "FreeLibraryAndExitThread");//FreeLibraryAndExitThread//FreeLibrary
  150. if (lpAddr == NULL)
  151. {
  152. WriteTextLog(_T("获取kernel32.dll中的FreeLibraryAndExitThread失败"));
  153. return FALSE;
  154. }
  155. m_hEjectThread = CreateRemoteThread(m_hWxProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpAddr, hModule, 0, NULL);
  156. if (m_hEjectThread == NULL)
  157. {
  158. WriteTextLog(_T("创建WeChat.exe远程线程(FreeLibraryAndExitThread)失败"));
  159. return FALSE;
  160. }
  161. WaitForSingleObject(m_hEjectThread, INFINITE);
  162. if (m_hEjectThread)
  163. CloseHandle(m_hEjectThread);
  164. m_hEjectThread = NULL;
  165. return TRUE;
  166. }
  167. BOOL CWxObject::FindWxMainWnd()
  168. {
  169. m_WxMainWndInfo.hWxWnd = NULL;
  170. m_WxMainWndInfo.dwWxProcId = m_dwWxProcId;
  171. _stprintf_s(m_WxMainWndInfo.szWndName, WX_MAIN_WND_NAME);
  172. _stprintf_s(m_WxMainWndInfo.szClassName, WX_MAIN_WND_CLASS_NAME);
  173. if (::EnumWindows(&EnumWindowsProc, (LPARAM)& m_WxMainWndInfo) == FALSE)
  174. {
  175. m_hWxMainWnd = m_WxMainWndInfo.hWxWnd;
  176. m_rcWxWnd = m_WxMainWndInfo.rcWnd;
  177. return TRUE;
  178. }
  179. return FALSE;
  180. }
  181. BOOL CWxObject::FindWxLoginWnd()
  182. {
  183. m_WxLoginWndInfo.hWxWnd = NULL;
  184. m_WxLoginWndInfo.dwWxProcId = m_dwWxProcId;
  185. _stprintf_s(m_WxLoginWndInfo.szWndName, WX_LOGIN_WND_NAME);
  186. _stprintf_s(m_WxLoginWndInfo.szClassName, WX_LOGIN_WND_CLASS_NAME);
  187. if (::EnumWindows(&EnumWindowsProc, (LPARAM)& m_WxLoginWndInfo) == FALSE)
  188. {
  189. m_hWxLoginWnd = m_WxLoginWndInfo.hWxWnd;
  190. m_rcWxWnd = m_WxLoginWndInfo.rcWnd;
  191. return TRUE;
  192. }
  193. return FALSE;
  194. }
  195. BOOL CWxObject::Attach2MainWnd(CWnd *pMainWnd, BOOL bLoginWnd)
  196. {
  197. HWND hWxWnd = bLoginWnd ? m_hWxLoginWnd : m_hWxMainWnd;
  198. if (hWxWnd != NULL)
  199. {
  200. // 获取微信窗口的样式;
  201. DWORD dwStyle = ::GetWindowLong(hWxWnd, GWL_STYLE);
  202. // WS_CLIPSIBLINGS告诉父窗口不要绘制子窗口出现的区域;
  203. //dwStyle |= WS_CLIPSIBLINGS;
  204. // 如果窗口隐藏的,显示出来;
  205. dwStyle |= WS_VISIBLE;
  206. // 重新设置窗口样式 ;
  207. ::SetWindowLong(hWxWnd, GWL_STYLE, dwStyle);
  208. CRect rect;
  209. pMainWnd->GetWindowRect(&rect);
  210. // 设置父窗口;
  211. ::SetParent(hWxWnd, pMainWnd->m_hWnd);//set parent of ms paint to our dialog.
  212. // 擦除背景;
  213. //SetWindowLong(hWxWnd, GWL_STYLE, WS_VISIBLE);//eraze title of ms paint window.
  214. //Positioning ms paint.
  215. // 将屏幕坐标转在窗口坐标;
  216. pMainWnd->ScreenToClient(&rect);
  217. // 居中显示;
  218. CRect rcDisplay = rect;
  219. if (bLoginWnd)
  220. {// 居中显示;
  221. if (m_rcWxWnd.Width() >= rect.Width())
  222. {
  223. rcDisplay.left = 0;
  224. rcDisplay.right = rect.right;
  225. }
  226. else
  227. {
  228. rcDisplay.left = (rect.Width() - m_rcWxWnd.Width()) / 2;
  229. rcDisplay.right = rcDisplay.left + m_rcWxWnd.Width();
  230. }
  231. if (m_rcWxWnd.Height() >= rect.Height())
  232. {
  233. rcDisplay.top = 0;
  234. rcDisplay.bottom = rect.bottom;
  235. }
  236. else
  237. {
  238. rcDisplay.top = (rect.Height() - m_rcWxWnd.Height()) / 2;
  239. rcDisplay.bottom = rcDisplay.top + m_rcWxWnd.Height();
  240. }
  241. // 注意:MoveWindow/SetWindowPos使用的是父窗口的坐标,如果父窗口为NULL,则使用的是屏幕坐标;
  242. ::MoveWindow(hWxWnd, rcDisplay.left, rcDisplay.top, rcDisplay.Width(), rcDisplay.Height(), true);
  243. //::SetWindowPos(hWxWnd, NULL, rcDisplay.left, rcDisplay.top, rcDisplay.Width(), rcDisplay.Height(), WM_WINDOWPOSCHANGING|SWP_SHOWWINDOW | SWP_HIDEWINDOW);
  244. }
  245. else
  246. {//最大化显示并固定;
  247. // 注意:MoveWindow/SetWindowPos使用的是父窗口的坐标,如果父窗口为NULL,则使用的是屏幕坐标;
  248. ::MoveWindow(hWxWnd, rcDisplay.left, rcDisplay.top, rcDisplay.Width(), rcDisplay.Height(), true);
  249. if (!bLoginWnd)
  250. ::PostMessage(hWxWnd, WM_SYSCOMMAND, SC_MAXIMIZE, NULL);
  251. //::SetWindowPos(hWxWnd, NULL, rcDisplay.left, rcDisplay.top, rcDisplay.Width(), rcDisplay.Height(), WM_MOVE| WM_SIZE| WM_WINDOWPOSCHANGING| WM_NCCALCSIZE | SWP_SHOWWINDOW);
  252. }
  253. //窗口重绘,(因创建exe时,设置为SW_HIDE,导致exe窗口会被父窗口覆盖一部分)
  254. pMainWnd->Invalidate();
  255. ::UpdateWindow(hWxWnd);
  256. ::ShowWindow(hWxWnd, SW_SHOW);
  257. m_bAttached = TRUE;
  258. }
  259. return 0;
  260. }
  261. BOOL CWxObject::DetachWxWnd()
  262. {
  263. if (m_bAttached)
  264. {
  265. //if (m_hWxMainWnd && GetParent(m_hWxMainWnd))
  266. if (m_hWxMainWnd) // 使用GetParent可能返回NULL
  267. ::SetParent(m_hWxMainWnd, NULL);
  268. if (m_hWxLoginWnd)
  269. ::SetParent(m_hWxLoginWnd, NULL);
  270. m_bAttached = FALSE;
  271. #ifdef _DEBUG
  272. WriteTextLog(_T("DetachWxWnd:分离微信窗口"));
  273. #endif
  274. }
  275. return 0;
  276. }
  277. BOOL CWxObject::SetHook()
  278. {
  279. if (m_hook == NULL)
  280. m_hook = SetWindowsHookEx(WH_CBT, HookProc, NULL, ::GetCurrentThreadId());
  281. return 0;
  282. }
  283. BOOL CWxObject::EnumWindowsProc(HWND hwnd, LPARAM lParam)
  284. {
  285. DWORD dwProcId = 0, dwThreadId;
  286. TCHAR szWndName[MAX_PATH] = { 0 };
  287. TCHAR szClassName[MAX_PATH] = { 0 };
  288. WNDINFO* pWndInfo = (WNDINFO*)lParam;
  289. dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcId);
  290. if (dwProcId == pWndInfo->dwWxProcId)
  291. {
  292. pWndInfo->hWxWnd = hwnd;
  293. pWndInfo->dwThreadId = dwThreadId;
  294. ::GetWindowText(hwnd, szWndName, MAX_PATH);
  295. ::GetClassName(hwnd, szClassName, MAX_PATH);
  296. #ifdef _DEBUG
  297. WriteTextLog(_T("窗口名称:%s, 窗口类名:%s, 线程ID:%d,句柄:%p"), szWndName, szClassName, dwThreadId, hwnd);
  298. #endif
  299. if (_tcscmp(szWndName, pWndInfo->szWndName) == 0 && _tcscmp(szClassName, pWndInfo->szClassName) == 0)
  300. {
  301. ::GetWindowRect(hwnd, &pWndInfo->rcWnd);
  302. return FALSE;
  303. }
  304. }
  305. return TRUE;
  306. }
  307. LRESULT CWxObject::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
  308. {
  309. WriteTextLog(_T("HookProc被调用"));
  310. if (nCode == HCBT_MINMAX)
  311. {
  312. WriteTextLog(_T("窗口HCBT_MINMAX"));
  313. return 1;
  314. }
  315. return CallNextHookEx(m_hook, nCode, wParam, lParam);
  316. }
  317. BOOL CWxObject::OpenWeChat()
  318. {
  319. return 0;
  320. }