CWxObject.cpp 8.1 KB

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