在windows下,要实现屏幕取词,可以通过拦截 ExtTextOutA,ExtTextOutW,TextOutA,TextOutW,这4个API来实现,另外4个DrawTextExA,DrawTextExW,DrawTextA,DrawTextW 貌似是调用前面4个去实现的,具体我没有去验证过。
本文示例为通过拦截 ExtTextOutW 这个API可以实现屏幕取词的部分功能。
它的函数定义为:
BOOL WINAPI ExtTextOutW(HDC hdc, int x, int y, UINT options, CONST RECT * lprect, LPCWSTR lpString, UINT c, CONST INT * lpDx);
其中lpString参数包含了字符串地址信息,我们只需要到目标进程拦截这个API,然后读取lpString里面的字符数据,然后显示到一个文本框里面,就行了。
要实现这个功能,需要做两件事:
1. 安装一个全局鼠标钩子,拦截WM_MOUSEMOVE消息。
2. 如果检查到鼠标在屏幕的某处停留时间超过1秒,则在鼠标所在的窗口进程挂上API钩子,取得所需信息后,立即摘掉API钩子。
注意:该程序必须在Release版本下运行,如果是DeBug版本的话,因为编译器没做优化和加了一些debug代码,所以会内存读写报错
===========================================
下面是生成mousehook.dll的代码:
// dllmain.cpp : 定义 DLL 的初始化例程。 // #include "stdafx.h" #include <afxwin.h> #include <afxdllx.h> #include "MyMouseHook.h" #ifdef _DEBUG #define new DEBUG_NEW #endif extern HINSTANCE hInstanceHook; static AFX_EXTENSION_MODULE MouseHookDLL = { NULL, NULL }; extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { // 如果使用 lpReserved,请将此移除 UNREFERENCED_PARAMETER(lpReserved); if (dwReason == DLL_PROCESS_ATTACH) { TRACE0("MouseHook.DLL 正在初始化!\n"); // 扩展 DLL 一次性初始化 if (!AfxInitExtensionModule(MouseHookDLL, hInstance)) return 0; // 将此 DLL 插入到资源链中 // 注意: 如果此扩展 DLL 由 // MFC 规则 DLL (如 ActiveX 控件)隐式链接到, // 而不是由 MFC 应用程序链接到,则需要 // 将此行从 DllMain 中移除并将其放置在一个 // 从此扩展 DLL 导出的单独的函数中。使用此扩展 DLL 的 // 规则 DLL 然后应显式 // 调用该函数以初始化此扩展 DLL。否则, // CDynLinkLibrary 对象不会附加到 // 规则 DLL 的资源链,并将导致严重的 // 问题。 new CDynLinkLibrary(MouseHookDLL); hInstanceHook=hInstance; // WM_MY_MOUSEMOVE=RegisterWindowMessage(WM_MY_MOUSEMOVE_MSG); } else if (dwReason == DLL_PROCESS_DETACH) { TRACE0("MouseHook.DLL 正在终止!\n"); // 在调用析构函数之前终止该库 AfxTermExtensionModule(MouseHookDLL); } return 1; // 确定 }
//MyMouseHook.h // #pragma once #define WM_MY_MOUSEMOVE (WM_USER+10) // CMyMoueHook 命令目标 class AFX_EXT_CLASS CMyMouseHook : public CObject { public: CMyMouseHook(); virtual ~CMyMouseHook(); public: BOOL StartHook(HWND hWnd); //安装钩子函数 BOOL StopHook(HWND hWnd); //卸载钩子函数 };
// MyMoueHook.cpp : 实现文件 // #include "stdafx.h" #include "MyMouseHook.h" #pragma data_seg("mydata") HWND hWnd_Mouse=NULL; //设置需要捕获鼠标消息的窗口句柄,即所有鼠标消息都会另外发送一份消息到该窗口 #pragma data_seg() #pragma comment(linker,"/SECTION:mydata,RWS") HHOOK hHook=NULL; //鼠标钩子句柄 HINSTANCE hInstanceHook=NULL; //插入保存DLL实例句柄 extern "C" LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam) { if(nCode < 0) { CallNextHookEx(hHook, nCode, wParam, lParam); return 0; } if(nCode>=0) { LPMSG msg=(LPMSG)lParam; if(msg->message==WM_MOUSEMOVE || msg->message==WM_NCMOUSEMOVE) { PostMessage(hWnd_Mouse, WM_MY_MOUSEMOVE, 0, 0); // PostMessage(hWnd_Mouse, WM_MOUSEMOVE, 0, 0); } } return CallNextHookEx(hHook,nCode,wParam,lParam); //继续传递钩子信息 } // CMyMoueHook CMyMouseHook::CMyMouseHook() { } CMyMouseHook::~CMyMouseHook() { // StopHook(); } // CMyMoueHook 成员函数 BOOL CMyMouseHook::StartHook(HWND hWnd) { if(hHook==NULL) { hHook=SetWindowsHookExW(WH_GETMESSAGE,(HOOKPROC)MouseProc,hInstanceHook,0); if(hHook!=NULL) { hWnd_Mouse=hWnd; //设置需要捕获鼠标消息的窗口句柄 return TRUE; } }else { if(hWnd_Mouse!=NULL) { if(hWnd_Mouse==hWnd) { return FALSE; //该窗口句柄已经hook了 }else { hWnd_Mouse=hWnd; //设置需要捕获鼠标消息的窗口句柄,这样将会覆盖前一个句柄 return TRUE; } } } return FALSE; //failed to set hook } BOOL CMyMouseHook::StopHook(HWND hWnd) { if(hWnd!=hWnd_Mouse || hWnd==NULL){return FALSE;} BOOL bResult=UnhookWindowsHookEx(hHook); if(bResult){hWnd_Mouse=NULL;hHook=NULL;} return bResult; }
下面是实现屏幕取词的代码:
// pingmuquciDlg.h : 头文件 // #pragma once #include "afxwin.h" #include "../mousehook/MyMouseHook.h" #define WM_HOOK_TEXTOUT (WM_USER+101) typedef struct _RemoteParam { DWORD dwTextOut; DWORD dwExtTextOut; DWORD dwPostMessage; DWORD dwSendMessage; DWORD dwGetCurrentProcess; DWORD dwWriteProcessMemory; DWORD dwMessageBox; unsigned char oldCode[10]; unsigned char newCode[10]; DWORD FunAddr; BOOL bHookAlready; //是否挂上API钩子 HWND hwnd; //安放钩子的那个窗口句柄 LPCWSTR lpString; //专门用于保存TextOut的字符串地址 wchar_t info[260]; } RemoteParam, * pRemoteParam; // CpingmuquciDlg 对话框 class CpingmuquciDlg : public CDialogEx { // 构造 public: CpingmuquciDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_PINGMUQUCI_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: CMyMouseHook m_mouse_hook; CEdit m_edit; BOOL is_quci_open; CString m_edit_str; HWND m_hwnd_hook; BOOL bHookAlready; //是否挂钩子的标志 unsigned char oldcode[10]; //用来保存API的原始代码 unsigned char newcode[10]; //用来保存API修改后的代码 DWORD dwFunAddr; DWORD dwPramaAddr; HANDLE m_hProcess; DWORD m_dwProcessId; int size_Func; int size_Pramam; RemoteParam m_RParam; public: BOOL enableDebugPriv(); //提升进程访问权限 DWORD processNameToId(LPCTSTR lpszProcessName); //根据进程名称得到进程ID,如果有多个运行实例的话,返回第一个枚举到的进程的ID BOOL ClearHook(HANDLE hProcess); //清理钩子代码 BOOL HookOn(HANDLE hProcess); //挂上API钩子 BOOL HookOff(HANDLE hProcess); //摘掉API钩子 BOOL InitHook(HANDLE hProcess); //初始化API钩子 public: afx_msg void OnBnClickedButton1(); //开启屏幕取词 afx_msg void OnBnClickedButton2(); //关闭屏幕取词 afx_msg void OnTimer(UINT_PTR nIDEvent); protected: afx_msg LRESULT OnHookTextout(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnMyMousemove(WPARAM wParam, LPARAM lParam); };
// //拦截 ExtTextOut(...),并实现一个粗糙的屏幕取词功能 // //作者:过客 //邮箱:[email protected] //日期:2014.04.27 // // pingmuquciDlg.cpp : 实现文件 // #include "stdafx.h" #include "pingmuquci.h" #include "pingmuquciDlg.h" #include "afxdialogex.h" #include <TlHelp32.h> #ifdef _DEBUG #define new DEBUG_NEW #endif #define TIME_ID_1 1 #pragma comment(lib,"../Debug/mousehook.lib") // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx { public: CAboutDlg(); // 对话框数据 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CpingmuquciDlg 对话框 CpingmuquciDlg::CpingmuquciDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CpingmuquciDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); is_quci_open=FALSE; m_edit_str=_T(""); m_hwnd_hook=NULL; bHookAlready=FALSE; dwFunAddr=NULL; dwPramaAddr=NULL; m_hProcess=NULL; m_dwProcessId=NULL; size_Func=0; size_Pramam=0; ::memset(oldcode,0,10); ::memset(newcode,0,10); } void CpingmuquciDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_EDIT1, m_edit); } BEGIN_MESSAGE_MAP(CpingmuquciDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON1, &CpingmuquciDlg::OnBnClickedButton1) ON_BN_CLICKED(IDC_BUTTON2, &CpingmuquciDlg::OnBnClickedButton2) ON_MESSAGE(WM_HOOK_TEXTOUT, &CpingmuquciDlg::OnHookTextout) ON_WM_TIMER() ON_MESSAGE(WM_MY_MOUSEMOVE, &CpingmuquciDlg::OnMyMousemove) END_MESSAGE_MAP() // CpingmuquciDlg 消息处理程序 BOOL CpingmuquciDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CpingmuquciDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CpingmuquciDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CpingmuquciDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } //========================================================================== //typedef HANDLE (__stdcall * PFN_CREATEFILE)(LPCWSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE); typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCWSTR, LPCWSTR, DWORD); typedef BOOL (__stdcall * PFN_WRITEPROCESSMEMORY)(HANDLE,LPVOID,LPCVOID,SIZE_T,SIZE_T*); typedef HANDLE (__stdcall * PFN_GETCURRENTPROCESS)(void); //typedef BOOL (__stdcall * PFN_TEXTOUT)(HDC, int, int, LPCSTR, int); typedef BOOL (__stdcall * PFN_TEXTOUT)(HDC, int, int, LPCWSTR, int); //typedef BOOL (__stdcall * PFN_EXTTEXTOUT)(HDC, int, int, UINT, CONST RECT*, LPCSTR, UINT, CONST INT*); typedef BOOL (__stdcall * PFN_EXTTEXTOUT)(HDC, int, int, UINT, CONST RECT*, LPCWSTR, UINT, CONST INT*); typedef BOOL (__stdcall * PFN_POSTMESSAGE)(HWND, UINT, WPARAM, LPARAM); typedef BOOL (__stdcall * PFN_SENDMESSAGE)(HWND, UINT, WPARAM, LPARAM); typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCWSTR, LPCWSTR, DWORD); void HookTextOut(LPVOID lParam) { RemoteParam* pRP=(RemoteParam*)lParam; DWORD dwParamaAddr=0; //自定义结构体的地址 DWORD NextIpAddr=0; //CPU下一个IP地址 HDC hdc=NULL; //设备描述表句柄,画图用的 int x=0; //字符串的开始位置 x坐标 int y=0; //字符串的开始位置 y坐标 UINT options=0; CONST RECT *lprect=NULL; LPCWSTR lpString=NULL; //字符串指针 int c=0; //字符串中字符的个数 CONST INT * lpDx=NULL; BOOL Res=FALSE; //API TextOut 的返回值 //下面的汇编代码是将TextOut(...)里面的6个参数和另外两个变量(dwParamaAddr和NextIpAddr)保存到变量中,以便后面调用 __asm { MOV EAX,[EBP+8] //注意是[EBP+8],而不是[EBP+4] MOV [dwParamaAddr], EAX MOV EAX,[EBP+12] MOV [NextIpAddr], EAX MOV EAX,[EBP+16] MOV [hdc], EAX MOV EAX,[EBP+20] MOV [x], EAX MOV EAX,[EBP+24] MOV [y], EAX MOV EAX,[EBP+28] MOV [options], EAX MOV EAX,[EBP+32] MOV [lprect], EAX MOV EAX,[EBP+36] MOV [lpString], EAX MOV EAX,[EBP+40] MOV [c], EAX MOV EAX,[EBP+44] MOV [lpDx], EAX } PFN_GETCURRENTPROCESS pfnGetCurrentProcess=(PFN_GETCURRENTPROCESS)pRP->dwGetCurrentProcess; PFN_WRITEPROCESSMEMORY pfnWriteProcessMemory=(PFN_WRITEPROCESSMEMORY)pRP->dwWriteProcessMemory; PFN_TEXTOUT pfnTextOut=(PFN_TEXTOUT)pRP->dwTextOut; PFN_EXTTEXTOUT pfnExtTextOut=(PFN_EXTTEXTOUT)pRP->dwExtTextOut; PFN_POSTMESSAGE pfnPostMessage=(PFN_POSTMESSAGE)pRP->dwPostMessage; PFN_SENDMESSAGE pfnSendMessage=(PFN_SENDMESSAGE)pRP->dwSendMessage; PFN_MESSAGEBOX pfnMessageBox=(PFN_MESSAGEBOX)pRP->dwMessageBox; //恢复API原来的样子,即摘掉API钩子 pfnWriteProcessMemory(pfnGetCurrentProcess(), (LPVOID)pfnExtTextOut, (LPCVOID)pRP->oldCode, 10, NULL); //调用正常的TextOut // Res=pfnTextOut(hdc, x, y, lpString, c); Res=pfnExtTextOut(hdc, x, y, options, lprect, lpString, c, lpDx); //下面的代码是将TextOut里面的lpString悄悄发送给我们挂钩子的windows应用程序 if(pRP->hwnd!=NULL) { pRP->lpString=lpString; // pfnPostMessage(pRP->hwnd, WM_HOOK_TEXTOUT, MAKELONG(0,0), MAKELONG(0,0)); //进程间通信 pfnSendMessage(pRP->hwnd, WM_HOOK_TEXTOUT, MAKELONG(0,0), MAKELONG(0,0)); //进程间通信 } // int allowFlag = pfnMessageBox(NULL, lpString, pRP->info, MB_ICONWARNING | MB_YESNO); //下面代码是善后工作,这个非常重要,如果没有的话,EIP将回不到调用TextOut(...)的下一条命令,这条命令在"notepad.exe"的领空 //而不是kernel32.dll的领空,要切记。 __asm { POP EDI POP ESI POP EBX MOV EDX, [NextIpAddr] //调用TextOut(...)这一命令的下一条命令地址 MOV EAX, [Res] //函数返回值要放到EAX寄存器里,这一句可省略,原因是pfnTextOut(...)会自动将结果存入EAX MOV ESP, EBP POP EBP // MOV ESP, EBP ADD ESP, 11*4 //此处为了平衡栈,可以进一步优化 PUSH EDX RET //ret指令用栈中的数据(即edx里面的地址),修改IP的内容,注意不修改CS的内容,retf才是 } } BOOL CpingmuquciDlg::enableDebugPriv() { HANDLE hToken; LUID sedebugnameValue; TOKEN_PRIVILEGES tkp; if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { return FALSE; } if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) { CloseHandle(hToken); return FALSE; } tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = sedebugnameValue; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //特权启用 if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) //启用指定访问令牌的特权 { CloseHandle(hToken); return FALSE; } return TRUE; } DWORD CpingmuquciDlg::processNameToId(LPCTSTR lpszProcessName) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 pe; pe.dwSize = sizeof(PROCESSENTRY32); if(!Process32First(hSnapshot, &pe)) { MessageBox(_T("The frist entry of the process list has not been copyied to the buffer"), _T("Notice"), MB_ICONINFORMATION | MB_OK); return 0; } while(Process32Next(hSnapshot, &pe)) //循环查找下一个进程 { if(!strcmp(lpszProcessName, pe.szExeFile)) //找到了 { return pe.th32ProcessID; } } return 0; } BOOL CpingmuquciDlg::InitHook(HANDLE hProcess) { //提升进程访问权限 if(!enableDebugPriv()) { printf("提升进程访问权限 Error!\n"); return -1; } //定义线程体的大小,实际分配的内存大小是页内存大小的整数倍 size_Func=1024*8; dwFunAddr = (DWORD)VirtualAllocEx(hProcess, NULL, size_Func, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if((LPVOID)dwFunAddr == NULL) { printf("申请线程内存失败!\n"); CloseHandle(hProcess); return FALSE; } size_Pramam=sizeof(RemoteParam); dwPramaAddr = (DWORD)VirtualAllocEx(hProcess, NULL, size_Pramam, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if((LPVOID)dwPramaAddr == NULL) { printf("申请参数内存失败!\n"); CloseHandle(hProcess); return FALSE; } // RemoteParam m_RParam; ZeroMemory(&m_RParam, sizeof(m_RParam)); HMODULE hKernel32 = LoadLibrary("kernel32.dll"); HMODULE hUser32 = LoadLibrary("user32.dll"); HMODULE hGdi32 = LoadLibrary("gdi32.dll"); m_RParam.dwTextOut = (DWORD)GetProcAddress(hGdi32, "TextOutW"); m_RParam.dwExtTextOut = (DWORD)GetProcAddress(hGdi32, "ExtTextOutW"); m_RParam.dwGetCurrentProcess = (DWORD)GetProcAddress(hKernel32, "GetCurrentProcess"); m_RParam.dwWriteProcessMemory = (DWORD)GetProcAddress(hKernel32, "WriteProcessMemory"); m_RParam.dwPostMessage = (DWORD)GetProcAddress(hUser32, "PostMessageW"); m_RParam.dwSendMessage = (DWORD)GetProcAddress(hUser32, "SendMessageW"); m_RParam.dwMessageBox = (DWORD)GetProcAddress(hUser32, "MessageBoxW"); m_RParam.bHookAlready=TRUE; m_RParam.hwnd=m_hwnd_hook; m_RParam.lpString=NULL; wchar_t str2[]={L"我拦截API成功了,哈哈^o^"}; ::wmemcpy_s(m_RParam.info,sizeof(m_RParam.info),str2,sizeof(str2)); for(int i=15; i<31; i++){m_RParam.info[i]=L'\0';} // unsigned char oldcode[10]; // unsigned char newcode[10]; int praadd = (int)dwPramaAddr; int threadadd = (int)dwFunAddr; newcode[4] = praadd>>24; newcode[3] = (praadd<<8)>>24; newcode[2] = (praadd<<16)>>24; newcode[1] = (praadd<<24)>>24; newcode[0] = 0x68; //0x68: push newcode[1..4],参数先压栈 int offsetaddr = threadadd - (int)m_RParam.dwExtTextOut - 10 ; newcode[9] = offsetaddr>>24; newcode[8] = (offsetaddr<<8)>>24; newcode[7] = (offsetaddr<<16)>>24; newcode[6] = (offsetaddr<<24)>>24; newcode[5] = 0xE8; //0xE8: call newcode[6..9],然后调用函数 for(int i = 0; i < 10; i++){m_RParam.newCode[i]=newcode[i];} CString str_oldcode; str_oldcode="m_RParam.NewCode: "; for(int i = 0; i< 10; i++){CString str;str.Format("0x%.2x ", m_RParam.newCode[i]);str_oldcode+=str;} str_oldcode+="\r\n"; DWORD dwRead = 0; if(!ReadProcessMemory(GetCurrentProcess(), (LPCVOID)m_RParam.dwExtTextOut, oldcode, 10, &dwRead)) { printf("read error"); CloseHandle(hProcess); FreeLibrary(hKernel32); FreeLibrary(hUser32); FreeLibrary(hGdi32); return FALSE; } strcat((char*)m_RParam.oldCode, (char*)oldcode); m_RParam.FunAddr = dwFunAddr; str_oldcode+="m_RParam.OldCode: "; for(int i = 0; i< 10; i++){CString str;str.Format("0x%.2x ", m_RParam.oldCode[i]);str_oldcode+=str;} str_oldcode+="\n"; DWORD dwWrite = 0; if(!WriteProcessMemory(hProcess, (LPVOID)dwFunAddr, (LPVOID)&HookTextOut, size_Func, &dwWrite)) { printf("WriteRemoteProcessesMemory Error 1 !\n"); CloseHandle(hProcess); FreeLibrary(hKernel32); FreeLibrary(hUser32); FreeLibrary(hGdi32); return FALSE; } if(!WriteProcessMemory(hProcess, (LPVOID)dwPramaAddr, (LPVOID)&m_RParam, sizeof(RemoteParam), &dwWrite)) { printf("WriteRemoteProcessesMemory Error 2 !\n"); CloseHandle(hProcess); FreeLibrary(hKernel32); FreeLibrary(hUser32); FreeLibrary(hGdi32); return FALSE; } FreeLibrary(hKernel32); FreeLibrary(hUser32); FreeLibrary(hGdi32); CString str; str.Format("m_RParam.dwExtTextOut:%.8x\r\nRParam.dwMessageBox:%.8x\r\nRParam.dwGetCurrentProcess:%.8x\r\nRParam.dwWriteProcessMemory:%.8x\r\nRParam.FunAddr:%.8x\r\ndwPramaAddr=%.8x\r\n", m_RParam.dwExtTextOut, m_RParam.dwMessageBox, m_RParam.dwGetCurrentProcess, m_RParam.dwWriteProcessMemory, m_RParam.FunAddr, dwPramaAddr); str+=str_oldcode; m_edit.SetWindowText(str); return TRUE; } BOOL CpingmuquciDlg::ClearHook(HANDLE hProcess) { HookOff(m_hProcess); VirtualFreeEx(m_hProcess, (LPVOID)dwFunAddr, 0, MEM_RELEASE); //MEM_DECOMMIT仅标示内存空间不可用,内存页还将存在。MEM_RELEASE完全回收。 VirtualFreeEx(m_hProcess, (LPVOID)dwPramaAddr, 0, MEM_RELEASE); //MEM_DECOMMIT仅标示内存空间不可用,内存页还将存在。MEM_RELEASE完全回收。 dwFunAddr=NULL; dwPramaAddr=NULL; m_hProcess=NULL; return TRUE; } BOOL CpingmuquciDlg::HookOn(HANDLE hProcess) { DWORD dwWrite=0; if(!WriteProcessMemory(hProcess, (LPVOID)m_RParam.dwExtTextOut, (LPVOID)newcode, 10, &dwWrite)) //挂上API钩子 { printf("WriteRemoteProcessesMemory Error 4 !\n"); return FALSE; } return TRUE; } BOOL CpingmuquciDlg::HookOff(HANDLE hProcess) { DWORD dwWrite=0; if(!WriteProcessMemory(hProcess, (LPVOID)m_RParam.dwExtTextOut, (LPVOID)oldcode, 10, &dwWrite)) //摘掉API钩子 { printf("WriteRemoteProcessesMemory Error 5 !\n"); return FALSE; } return TRUE; } void CpingmuquciDlg::OnBnClickedButton1() //打开API钩子 { m_hwnd_hook=this->GetSafeHwnd(); //将对话框设置为安装API钩子的元凶 m_mouse_hook.StartHook(m_hwnd_hook); //开启全局鼠标钩子 } void CpingmuquciDlg::OnBnClickedButton2() //关闭API钩子 { m_mouse_hook.StopHook(m_hwnd_hook); //关闭全局鼠标钩子 } afx_msg LRESULT CpingmuquciDlg::OnHookTextout(WPARAM wParam, LPARAM lParam) { wchar_t str[200]; ZeroMemory(str, sizeof(str)/sizeof(wchar_t)); RemoteParam RParam; ZeroMemory(&RParam, sizeof(RParam)); DWORD dwRead=0; if(!ReadProcessMemory(m_hProcess, (LPCVOID)dwPramaAddr, &RParam, sizeof(RParam), &dwRead)) { printf("read error"); CloseHandle(m_hProcess); return 1; } if(!ReadProcessMemory(m_hProcess, (LPCVOID)RParam.lpString, str, sizeof(str), &dwRead)) { printf("read error"); CloseHandle(m_hProcess); return 1; } char sss[400]; ZeroMemory(sss, sizeof(sss)/sizeof(char)); int len=WideCharToMultiByte(CP_ACP,0,str,-1,NULL,0,NULL,NULL); WideCharToMultiByte(CP_ACP,0,str,-1,sss,len,NULL,NULL); //宽字符转多字节字符 m_edit.SetWindowText(sss); //将截取来的字符串显示到对话框的文本框里面 return 0; } void CpingmuquciDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if(nIDEvent==TIME_ID_1) { this->KillTimer(TIME_ID_1); POINT pt; GetCursorPos(&pt); CWnd* pWnd=WindowFromPoint(pt); if(pWnd!=NULL) { HWND hwnd=pWnd->GetSafeHwnd(); DWORD dwProcessId=NULL; GetWindowThreadProcessId(hwnd, &dwProcessId); //获取进程ID if(dwProcessId!=NULL) { if(m_dwProcessId!=dwProcessId) { if(m_hProcess!=NULL){ClearHook(m_hProcess);} m_hProcess=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ, FALSE, dwProcessId); m_dwProcessId=dwProcessId; InitHook(m_hProcess); } HookOn(m_hProcess); //挂上API钩子 RECT rc; rc.left=pt.x-2; rc.right=pt.x+2; rc.top=pt.y; rc.bottom=pt.y+4; pWnd->ScreenToClient(&rc); pWnd->InvalidateRect(&rc); //使矩形区域失效,放入WM_PAINT消息 pWnd->UpdateWindow(); //强制更新 HookOff(m_hProcess); //摘掉API钩子 } } } CDialogEx::OnTimer(nIDEvent); } afx_msg LRESULT CpingmuquciDlg::OnMyMousemove(WPARAM wParam, LPARAM lParam) { this->KillTimer(TIME_ID_1); this->SetTimer(TIME_ID_1,1000,NULL); //设置鼠标在屏幕某处停留1秒的时钟 return 0; }
效果截图: