Inline hook ImmGetCompositionStringW获取中文输入

HOOK截获中文输入会出现响应多次的情况。 当在Word中输入“你好” 记录下来的结果变成了"你好你好你好你好"实在不解。。代码如下 case WM_IME_COMPOSITION: //MessageBox(NULL, "ImmGetContext3", "ImmGetContext3", MB_OK); //if (m_hEditWnd == pmsg->hwnd 阅读详情
// dllmain.cpp : 定义 DLL 应用程序的入口点。
// 本程序通过hook ImmGetCompositionStringW获取中文输入记录
//
#include "stdafx.h"

#define		OPCODESIZE		6
#define INFO_BUFFER_SIZE 32767

typedef BOOL(WINAPI *lpImmGetCompositionString)(HIMC hIMC,
												DWORD dwIndex,
												LPVOID lpBuf,
												DWORD dwBufLen);

BOOL WINAPI myImmGetCompositionString( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen);

VOID WINAPI BeginRedirect(LPVOID);

lpImmGetCompositionString lpOrigImmGetCompositionString = NULL;

BYTE oldBytes[OPCODESIZE] = {0};

BYTE JMP[OPCODESIZE] = {0};

DWORD oldProtect, myProject = PAGE_EXECUTE_READWRITE;

WCHAR wzDbg[512] = {NULL};

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	WCHAR wzProcName[MAX_PATH] = {NULL};
	WCHAR wzUserName[INFO_BUFFER_SIZE] = {NULL};
	DWORD wzUserNameLen = INFO_BUFFER_SIZE;
	HMODULE hIMM32 = 0;

	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		GetModuleFileNameW( NULL, wzProcName, MAX_PATH);

		if ( GetUserNameW( wzUserName, &wzUserNameLen))
		{
			if ( (lstrcmpiW( wzUserName, L"SYSTEM") == 0)			||
				 (lstrcmpiW( wzUserName, L"LOCAL SERVICE") == 0)	||
				 (lstrcmpiW( wzUserName, L"NETWORK SERVICE") == 0)	)
			{
				break;
			}
		}

		OutputDebugStringW( wzProcName);
		
		//获取初始函数的首地址
		//hIMM32 = GetModuleHandleW( L"IMM32.dll");
		
		hIMM32 = LoadLibraryW( L"IMM32.dll");
		if ( hIMM32 == NULL)
		{
#ifdef _DEBUG
			OutputDebugStringW( L"load IMM32.dll failed!\r\n");
#endif
			break;
		}
		else
		{
#ifdef _DEBUG
			wsprintfW( wzDbg, L"IMM32.dll address is %u\r\n", hIMM32);
			OutputDebugStringW( wzDbg);
#endif
		}
		
		lpOrigImmGetCompositionString = (lpImmGetCompositionString)GetProcAddress( hIMM32, "ImmGetCompositionStringW");
		
		if ( lpOrigImmGetCompositionString == NULL)
		{
#ifdef _DEBUG
			wsprintfW(wzDbg, L"get ImmGetCompositionStringW failed!error = %d\r\n", GetLastError());
			OutputDebugStringW( wzDbg);
#endif
		}
		else
		{
#ifdef _DEBUG
			OutputDebugStringW( L"get ImmGetCompositionStringW successed!\r\n");
			wsprintfW( wzDbg, L"myImmGetCompositionString = %d, lpOrigImmGetCompositionString = %d\r\n", (DWORD)myImmGetCompositionString, (DWORD)lpOrigImmGetCompositionString);
			OutputDebugStringW( wzDbg);
#endif
			BeginRedirect(myImmGetCompositionString);
		}
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}


VOID WINAPI BeginRedirect(LPVOID lpNewFunAddr)
{
	DWORD dwJmpSize;
	BYTE tempJMP[OPCODESIZE] = {0xE9,0x90,0x90,0x90,0x90,0xC3};

	//检查参数是否合法
	if ( lpNewFunAddr == 0)
	{
#ifdef _DEBUG
		OutputDebugStringW( L"非法参数!");
#endif
		return;
	}
	memcpy(JMP, tempJMP, OPCODESIZE);
	
	dwJmpSize = ((DWORD)lpNewFunAddr - (DWORD)lpOrigImmGetCompositionString - 5);
	//计算跳转距离
#ifdef _DEBUG
	wsprintfW( wzDbg, L"lpNewFunAddr = %d , lpOrigImmGetCompositionString = %d\r\n", (DWORD)lpNewFunAddr, (DWORD)lpOrigImmGetCompositionString);
	OutputDebugStringW( wzDbg);
	wsprintfW( wzDbg, L"jmp %d, distance is %d\r\n", lpNewFunAddr, dwJmpSize);
	OutputDebugStringW( wzDbg);
#endif
	
	//改变函数前6个字节页属性
	VirtualProtect((LPVOID)lpOrigImmGetCompositionString, OPCODESIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
	
	//保存原函数前6个字节的机器码
	memcpy(oldBytes, lpOrigImmGetCompositionString, OPCODESIZE);
	
	//补齐跳转距离
	memcpy(&JMP[1], &dwJmpSize, 4);
	
	//把跳转到自定义函数的机器码复制到原函数前5个字节
	memcpy(lpOrigImmGetCompositionString, JMP, OPCODESIZE);
	
	//还原初始页属性
	VirtualProtect((LPVOID)lpOrigImmGetCompositionString, OPCODESIZE, oldProtect, NULL);
}


BOOL WINAPI myImmGetCompositionString( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
{
	//进入自定义的函数中首先需要还原原始函数的前6字节的操作码
	VirtualProtect( (LPVOID)lpOrigImmGetCompositionString, OPCODESIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
	
	memcpy(lpOrigImmGetCompositionString, oldBytes, OPCODESIZE);
	
	//还原完之后调用原函数
	BOOL bRetValue = lpOrigImmGetCompositionString( hIMC, dwIndex, lpBuf, dwBufLen);
	
	//还原为hook后的操作码
	memcpy(lpOrigImmGetCompositionString, JMP, OPCODESIZE);
	
	//还原页属性
	VirtualProtect( (LPVOID)lpOrigImmGetCompositionString, OPCODESIZE, oldProtect, NULL);
	
	OutputDebugStringW((LPWCH)lpBuf);
	
	return bRetValue;
}

输入法编辑器(IME)程序设计(3) Composition StringThe composition string is the current text in the composition window. This is the text that the IME converts to final characters. Each composition string consists of one or more clau 阅读详情

相关推荐

C#中实现输入法控制的完整解决方案

在现代软件开发中,尤其是在涉及多语言输入支持的应用场景下,对输入法(IME, Input Method Editor)的精确控制成为提升用户体验的关键环节。C#作为.NET平台的核心编程语言,提供了丰富的底层接口与扩展能力,使得开发者能够通过调用Windows API实现对输入法行为的精细化管理。本章将系统介绍C#环境下进行输入法控制的基本概念、应用场景以及技术挑战,涵盖输入法切换、上下文管理、窗口句柄绑定等核心功能的目标设定。// 示例:使用 P/Invoke 声明 ImmGetContext 函数。

weixin_42577735的博客 1795

[Imm]Imm API学习笔记——GCS常数

GCS常数是什么东西?当你在输入法打开状态时敲键盘,输入法会尝试将你输入的内容转为中文字符(这个过程称为Composition——组字),然后向窗口发送WM_IME_COMPOSITION通知当前的组字状态(lParam参数),然后用户可以通过调用ImmGetCompositionString消息得到详细的组字信息。   注意ImmGetCompositionString的dwBufLen参数的

fishmai的专栏 1408

通过HOOK来截获中文输入

通过HOOK来截获中文输入 用VC代码编写

易语言输入法候选字列表

易语言输入法候选字列表源码,输入法候选字列表,Api_ImmGetContext,Api_ImmGetCandidateList,Api_ImmGetCompositionString,Api_SetWindowLong,Api_CallWindowProc,Api_RtlMoveMemory

CCC数字钥匙3.0标准解读(22)

介绍设备制造商服务器和车辆制造商服务器之间的通信协议.

weixin_41738640的博客 1748

Windows IME(二)

Composition String The composition string is the current text in the composition window. This is the text that the IME converts to final characters. Each composition string consists of one or more

沧海一粟 3182

SystemVue 链路预算、仿真分析步骤

使用SystemVue进行链路预算、频谱分析等,相对ADS更全面。

BeCourageable的博客 3833

利用IME完成接收输入法输入功能.

利用IME完成接收输入法输入功能. 调用windows的imm32.dll完成窗体接收输入法功能. 代码如下    /// <summary> /// 输入法组件 /// </summary> class ImeComponent { #region Ev...

weixin_30632899的博客 441

如何入门强化学习

很多同学在入门强化学习的时候都会遇到困难,那我这里就简单介绍一下应该如何入门强化学习,并以开源代码为例详解强化学习实战。

seeksister的博客 3302

【Spreadsheet】香港规范结构设计电子表格库开发方案

深入理解香港本地规范特点和要求结合国际先进标准(Eurocode) 的技术内容考虑香港地理环境特殊性 (台风、地质等)建立完善的质量控制体系保持持续更新维护的能力。

hmywillstronger的博客 1361

如何捕捉键盘中英文输入

      最近在完成一个项目,需要捕捉用户的输入,传统的Keylogger网上资料较多,但捕捉的是按键输入,对于大量的中文输入,单纯键盘按键输入没有什么意义。本文说明如何实现中英文的输入捕捉。     一、实现思路             通过Windows钩子,截获每个应用程序的输入消息,获取输入内容。英文输入捕捉WM_CHAR消息,中文输入则铺捉WM_IME_ENDCOMPOSITIO...

Cougars的博客 4709

一、数学建模层次分析法(AHP)【清风数学建模个人笔记】

层次分析法: 层次分析法主要用于解决评价类问题(例如:选择哪种方案更好、哪位员工、运动员等表现得更优秀) 评价类问题: 分析问题得出评判问题的子标,分别给不同子标赋予权重(权重和要为1) ...

m0_58882109的博客 1万+

键盘记录工具(支持中文)

看到网上有很多键盘记录工具,技痒,于是自己动手写了一个。        记录键盘,很显然需要使用钩子。如果只是记录所按下的按键,使用WH_KEYDOWN_LL即可,此钩子可以拦截所有按下键盘的动作(除极少数键外)。但是如果要记录通过输入法输入的中文字符,那么WH_KEYDOWN_LL是不够的,这里需要使用WH_GETMESSAGE钩子。这个钩子拦截所有从消息队列中取出的消息。而windows上所

zlwzlwzlw的专栏 8271

【Docker】一招解决超时镜像超时报错:`ERROR: Get https://registry-1.docker.io/v2/`

Docker 镜像拉取超时或失败并不是 Docker 本身的缺陷,而是网络环境和默认配置在特定场景下的限制所导致的结果。通过本文的分析可以看到,问题的核心往往集中在三个方面:网络访问延迟、DNS 解析速度以及客户端默认超时时间。针对这些问题,最直接、有效的做法是使用国内镜像加速器,这不仅能够显著提升拉取速度,还能保证在企业内网或家庭网络中更加稳定

博客 1027

万字长文告诉新手如何学习Python图像处理(上篇完结 四十四) | 「Python」有奖征文

这篇文章是图像处理的最后一篇文章,后面我们将进入新的章节。图像处理文章主要讲解的图像处理方法包括图像几何运算、图像量化采样、图像点运算、图像形态学处理、图像增强、图像平滑、图像锐化、图像特效、图像分割、傅里叶变换与霍夫变换、图像分类等。个人感觉如果你是编程初学者、Python初学者或图像处理爱好者,这个系列真心适合你学习,并且这篇文章算是Python图像处理的学习路线,希望您喜欢。

杨秀璋的专栏 2万+

关于HOOK截入中文输入

关于HOOK截入中文输入

小发猫 1449

如何通过 GitHub Actions 使用 SSH 自动化部署到阿里云 ECS 实例

通过 GitHub Actions 和 SSH 自动化部署,可以显著提高你的开发效率和部署的可靠性。本文介绍了如何生成 SSH 密钥、将公钥添加到阿里云 ECS 实例、将私钥添加到 GitHub Secrets,以及编写 GitHub Actions 工作流来实现自动化部署。希望这篇文章对你有所帮助!

吃面不喝汤的博客 1511

Hook技术:通过Dll注入Hook IAT让计算器显示中文数字(附源码)

计算机的计算器是我们常用的一个小工具(小时候,总是计算机计算器分不开来,哈哈)。 众所周知,计算器在计算的过程中,显示的都是阿拉伯数字,因为阿拉伯数字是一个全世界通用的表示方法,那么如何让计算器显示中文数字"一、二、三......"呢? 这里我们就可以使用Dll注入去hook关键API实现**计算器显示中文数字**。

weixin_43742894的博客 3419

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

输入法编辑的一些资料

windows系统提供的管理函数(Input Method Manager,简称IMM)。IMM函数可以被IME函数使用,也可用于应用软件直接管理IME。相关术语:(1)input method context 简称IMC--输入法相关部分,在这里解释为:相关连的应用程序(进程)(2)component of the input context 简称IMCC--IMC的部件,是IN

gglike的专栏 1237

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

IME消息、函数、命令、需要的数据结构、常量

IME消息  以下列出IME中用到的消息。WM_IME_CHAR(IME得到了转换结果中的一个字符)WM_IME_COMPOSITION(IME根据用户击键的情况更改了按键组合状态)WM_IME_COMPOSITIONFULL(IME检测到按键组合窗口的区域无法继续扩展)WM_IME_CONTROL(由应用程序直接向IME发出控制请求)WM_IME_ENDCOMPOSITION(IME完成了对用户...

weixin_30583563的博客 721

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

输入法简介

这个又是很早的东西了,呵呵,也许有人会用的着吧!输入法编辑器篇输入法编辑器(IME)是允许用户使用标准键盘输入复杂字母与符号,如日文汉字的程式。本文描述创建及管理IME(input method editor)视窗的方法。1、输入法编辑器简介IME 不是检索用户可能用到的所字符值,而是监控用户的按键,来预料用户可能需要的字符,并提供一张备择字符列表以供用户从中选出所需字符。  默认情况下,IM

kathywp 3150

截取中文输入法输入的字符串,更改编码为ANSI

在全英文的XP中,在非unicode的程序中输入的中文会转为“??”。 通过截获WM_IME_COMPOSITION消息得到输入的字符串 ImmGetCompositionStringW得到Unicode WideCharToMultiByte转换为ANSI [code="c++"] BOOL CchartestDlg::PreTranslateMessage(MSG* pMsg...

JasonRight 727
上一篇: 断点工作原理
下一篇: WAV文件格式
CoderAldrich
博客等级 码龄12年 431粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值