游戏辅助工具开发教程-从入门到精通之A3_20篇

三 29th, 2010 | Filed under GameCheat

作者:Figo

终于,你们盼到了教程的延续,其实网页游戏那个也算是教程的:)为了保证教程的连续性,本节内容延续上节教程,继续讲输入法注入。我会把编写一个输入法注入的主要涉及知识介绍完毕,但这次不会放出源码。

输入法注入的原理很简单。为了便于理解,这里先打个比方,如果让你把身份证、家庭电话以及个人手机号交给某些公司的推销员,你必然不同意。那么推销员怎么得到你的这些信息呢?买!比如去银行买信息,典型的像招商银行,给钱就卖,而且丫还不承认。这里,招商银行作为跳板,把你的信息给出卖了。而输入法注入的道理和这个类似,游戏为了保护自身进程空间的内存不被非法修改,往往会监视相关API调用的合法性,如果调用方不是自己进程,涉及敏感操作的话会被拒绝,这时候谁来充当招行的角色呢?输入法。绝大多数游戏不会屏蔽用户打字的操作,打字就要涉及输入法,在Vista之前,输入法的总体架构没有什么大的变化,示意图如下:
输入法逻辑结构
图中以ime为后缀的文件表示输入法文件,本质是动态链接库。接受输入的进程总会关联一定的输入法,这些关联和调用由输入法管理器建立和管理。比如上图中,进程1需要使用输入法B,那么IMM会遍历已经安装的输入法,然后把符合要求的输入法b.ime加载到进程1的内存空间,这也是为什么输入法需要导出一些标准函数接口,因为IMM需要调用这些接口去初始化和通知ime要处理哪些消息等等。而所谓输入法注入,就是假IMM之手,把傀儡ime文件加载如目标进程空间,然后再加载需要注入的dll文件,通过进程进通信达到操作目标进程的企图。

因为ime本质属于dll,所以我们可以在编写傀儡ime时候导出标准接口和我们自己定制的接口函数[如传入进程号等]。下面先介绍ime的编写过程,请结合上次的教程一起来看。在vc环境下编写ime的话,入口函数和dll一样,而在ddk环境编写的话,可以自定义ime的入口函数。一个典型的入口函数如下:
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
我们需要在DLL_PROCESS_ATTACH的时候保存hinstDLL和初始化输入法需要的窗口类,类名最长为16个字符[包含NULL在内]:
UIWnd
UIWndProc
StatusWnd
statuswnd
CompWnd
CandWnd
CandWndProc
示例代码如下[不完整]:

//www.figoyao.com
BOOL WINAPI DLLEntry (
    HINSTANCE    hInstDLL,
    DWORD        dwFunction,
    LPVOID       lpNot)
{
switch(dwFunction)
    {
case DLL_PROCESS_ATTACH:
hInst= hInstDLL;
            wc.style          = CS_MYCLASSFLAG | CS_IME;
            wc.lpfnWndProc    = MyUIServerWndProc;
            wc.cbClsExtra     = 0;
            wc.cbWndExtra     = 2 * sizeof(LONG);
            wc.hInstance      = hInst;
            wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
            wc.hIcon          = NULL;
            wc.lpszMenuName   = (LPSTR)NULL;
wc.lpszClassName  = (LPSTR)szUIClassName;
wc.hbrBackground  = NULL;
if( !RegisterClass( (LPWNDCLASS)&wc ) )
return FALSE;
            wc.style          = CS_MYCLASSFLAG | CS_IME;
            wc.lpfnWndProc    = MyCompStringWndProc;
            wc.cbClsExtra     = 0;
            wc.cbWndExtra     = cbMyWndExtra;
            wc.hInstance      = hInst;
            wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
            wc.hIcon          = NULL;
            wc.lpszMenuName   = (LPSTR)NULL;
wc.lpszClassName  = (LPSTR)szUICompStringClassName;
wc.hbrBackground  = NULL;
if( !RegisterClass( (LPWNDCLASS)&wc ) )
return FALSE;
break;
case DLL_PROCESS_DETACH:
UnregisterClass(szUIClassName,hInst);
UnregisterClass(szUICompStringClassName,hInst);
break;
    }
return TRUE;
}

一个典型的窗口回调函数处理过程如下,以UI窗口回调过程为例,而StatusWndProc、CompWndProc和CandWndProc与此相似:

LRESULT UIWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HIMC hIMC;
HGLOBAL hMyExtra;

switch(msg){
case WM_CREATE:
// Allocate the memory block for the window instance.
hMyExtra = GlobalAlloc(GHND,size_of_MyExtra);
if (!hMyExtra)
MyError();
// Set the memory handle into IMMGWL_PRIVATE
SetWindowLong(hWnd, IMMGWL_PRIVATE, (LONG)hMyExtra);
                :
                :
break;
case WM_IME_xxxx:
// Get IMC;
hIMC = GetWindowLong(hWnd,IMMGWL_IMC);
// Get the memory handle for the window instance.
hMyExtra = GetWindowLong(hWnd, IMMGWL_PRIVATE);
lpMyExtra = GlobalLock(hMyExtra);
                :
                :
GlobalUnlock(hMyExtra);
break;
            :
            :

case WM_DESTROY:
// Get the memory handle for the window instance.
hMyExtra = GetWindowLong(hWnd, IMMGWL_PRIVATE);
// Free the memory block for the window instance.
GlobalFree(hMyExtra);
break;

default:
return DefWindowProc(hWnd, msg, wParam, lParam);
    }
}

完整的标准接口导出函数清单如下:

//www.figoyao.com
LIBRARY         WINGB

EXPORTS
                ImeConversionList
                ImeConfigure
                ImeDestroy
                ImeEscape
                ImeInquire
                ImeProcessKey
                ImeSelect
                ImeSetActiveContext
                ImeSetCompositionString
                ImeToAsciiEx
                NotifyIME

                ImeRegisterWord
                ImeUnregisterWord
                ImeGetRegisterWordStyle
                ImeEnumRegisterWord

                UIWndProc
                StatusWndProc
                CompWndProc
                CandWndProc
//[IMM调用]标准导出函数声明
BOOL ImeInquire(
    LPIMEINFO lpIMEInfo,
    LPTSTR lpszWndClass,
    DWORD dwSystemInfoFlags
	);

DWORD ImeConversionList(
				  HIMC hIMC,
				  LPCTSTR lpSrc,
				  LPCANDIDATELIST lpDst,
				  DWORD dwBufLen,
				  UINT uFlag
				  );

BOOL ImeConfigure(
			 HKL hKL,
			 HWND hWnd,
			 DWORD dwMode,
			 LPVOID lpData
			 );

BOOL ImeDestroy(
		   UINT uReserved
		   );

LRESULT ImeEscape(
		  HIMC hIMC,
		  UINT uEscape,
		  LPVOID lpData
		  );

BOOL ImeProcessKey(
			  HIMC hIMC,
			  UINT uVirKey,
			  DWORD lParam,
			  CONST LPBYTE lpbKeyState
			  );

BOOL NotifyIME(
		  HIMC hIMC,
		  DWORD dwAction,
		  DWORD dwIndex,
		  DWORD dwValue
		  );

BOOL ImeSelect(
		  HIMC hIMC,
		  BOOL fSelect
		  );

BOOL WINAPI ImeSetCompositionString(
						HIMC hIMC,
						DWORD dwIndex,
						LPCVOID lpComp,
						DWORD dwCompLen,
						LPCVOID lpRead,
						DWORD dwReadLen
						);

UINT ImeToAsciiEx(
			 UINT uVirKey,
			 UINT uScanCode,
			 CONST LPBYTE lpbKeyState,
			 LPTRANSMSGLIST lpTransMsgList,
			 UINT fuState,
			 HIMC hIMC
			 );

BOOL WINAPI ImeRegisterWord(
				LPCTSTR lpszReading,
				DWORD dwStyle,
				LPCTSTR lpszString
				);

BOOL WINAPI ImeUnregisterWord(
				  LPCTSTR lpszReading,
				  DWORD dwStyle,
				  LPCTSTR lpszString
				  );

UINT WINAPI ImeGetRegisterWordStyle(
						UINT nItem,
						LPSTYLEBUF lpStyleBuf
						);

UINT WINAPI ImeEnumRegisterWord(
					HKL hKL,
					REGISTERWORDENUMPROC lpfnEnumProc,
					LPCTSTR lpszReading,
					DWORD dwStyle,
					LPCTSTR lpszString,
					LPVOID lpData
					);

DWORD WINAPI ImeGetImeMenuItems(
				   HIMC hIMC,
				   DWORD dwFlags,
				   DWORD dwType,
				   LPIMEMENUITEMINFO lpImeParentMenu,
				   LPIMEMENUITEMINFO lpImeMenu,
				   DWORD dwSize
				   );

//窗口回调过程
LRESULT CALLBACK UIWndProc(
				   HWND   hUIWnd,
				   UINT   uMsg,
				   WPARAM wParam,
				   LPARAM lParam);

LRESULT CALLBACK StatusWndProc(
					HWND   hStatusWnd,
					UINT   uMsg,
					WPARAM wParam,
				    LPARAM lParam);

LRESULT CALLBACK CompWndProc(
					HWND   hCompWnd,
					UINT   uMsg,
					WPARAM wParam,
				    LPARAM lParam);

LRESULT CALLBACK CandWndProc(
					HWND   hCandWnd,
					UINT   uMsg,
					WPARAM wParam,
					LPARAM lParam);

在注册完窗口类后,IMM会调用标准接口函数ImeInquire,用来获取UI类名[lpszWndClass]和dwSystemInfoFlags通知ime现在的输入法上下文环境:是在用户登录窗口[IME_SYSINFO_WINLOGON]还是正常的输入环境或者是16位窗口[IME_SYSINFO_WOW16]。
下面四个函数用来提供用户向字典文件添加/删除自定义字符功能,一般可以忽略,
ImeRegisterWord
ImeUnregisterWord
ImeGetRegisterWordStyle
ImeEnumRegisterWord

一个典型的处理过程如下:

BOOL WINAPI ImeRegisterWord(
    LPCTSTR lpszReading,
    DWORD   dwStyle,
    LPCTSTR lpszString)
{
    return (FALSE);
}

BOOL WINAPI ImeUnregisterWord(
    LPCTSTR lpszReading,
    DWORD   dwStyle,
    LPCTSTR lpszString)
{
    return (FALSE);
}

UINT WINAPI ImeGetRegisterWordStyle(
    UINT       nItem,
    LPSTYLEBUF lpStyleBuf)
{
    return (FALSE);
}

UINT WINAPI ImeEnumRegisterWord(
    REGISTERWORDENUMPROC lpfnRegisterWordEnumProc,
    LPCTSTR              lpszReading,
    DWORD                dwStyle,
    LPCTSTR              lpszString,
    LPVOID               lpData)
{
    return (FALSE);
}

其它标准函数的典型示例如下:

UINT
    ImeToAsciiEx(
    uVirKey,
    uScanCode,
    lpbKeyState,
 lpTransMsgList,
    fuState,
    hIMC
   )
{
    DWORD dwMyNumMsg = 0;

. . .

    // Set the messages that the IME wants to generate.
    pTransMsgList->TransMsg[0].message =msg;
    pTransMsgList->TransMsg[0].wParam = wParam;
    pTransMsgList->TransMsg[0].lParam = lParam;

    // Count the number of the messages that the IME wants to generate.
    dwMyNumMsg++;

. . .

    return dwMyNumMsg;
}
//----------------------------
BOOL WINAPI ImeSelect(
    HIMC hIMC,
    BOOL fSelect)
{
    LPINPUTCONTEXT lpIMC;
    BOOL           fRet;

    if (!hIMC) {
        return (FALSE);
    }

    lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
    if (!lpIMC) {
        return (FALSE);
    }

    fRet = Select(hIMC, lpIMC, fSelect);

    ImmUnlockIMC(hIMC);

    return (fRet);
}
//------------------------------------
LRESULT WINAPI ImeEscape(       // escape function of IMEs
    HIMC   hIMC,
    UINT   uSubFunc,
    LPVOID lpData)
{
    LRESULT lRet;

    switch (uSubFunc) {
    case IME_ESC_QUERY_SUPPORT:

        if ( lpData == NULL )
           return FALSE;

        switch (*(LPUINT)lpData) {
        case IME_ESC_QUERY_SUPPORT:
        case IME_ESC_MAX_KEY:
        case IME_ESC_IME_NAME:
        case IME_ESC_GETHELPFILENAME:
            return (TRUE);
        case IME_ESC_SEQUENCE_TO_INTERNAL:
        case IME_ESC_GET_EUDC_DICTIONARY:
        case IME_ESC_SET_EUDC_DICTIONARY:
        case IME_INPUTKEYTOSEQUENCE:      // will not supported in next version
            return (FALSE);               // will not supported in GB IME
        default:
            return (FALSE);

        }
        break;
    case IME_ESC_SEQUENCE_TO_INTERNAL:
    case IME_ESC_GET_EUDC_DICTIONARY:
    case IME_ESC_SET_EUDC_DICTIONARY:
    case IME_INPUTKEYTOSEQUENCE:
        return (FALSE);
    case IME_ESC_MAX_KEY:
        return ((WORD) 4);
    case IME_ESC_IME_NAME:

        if ( lpData == NULL )
           return  FALSE;

        lstrcpy(lpData, szImeName);
        return (TRUE);

    case IME_ESC_GETHELPFILENAME:
        {
           TCHAR szIMEGUDHlpName[MAX_PATH];

           if (lpData == NULL )
              return FALSE;

           szIMEGUDHlpName[0] = 0;
           GetWindowsDirectory((LPTSTR)szIMEGUDHlpName, MAX_PATH);
           lstrcat((LPTSTR)szIMEGUDHlpName, TEXT("\\HELP\\WINGB.CHM"));

           lstrcpy(lpData, szIMEGUDHlpName);

           return TRUE;

        }

    default:
        return (FALSE);
    }

    return (lRet);
}
//---------------------------------------
BOOL WINAPI ImeSetActiveContext(
    HIMC   hIMC,
    BOOL   fOn)
{
    if (!fOn) {
    } else if (!hIMC) {
    } else {
        LPINPUTCONTEXT lpIMC;

        lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
        if (!lpIMC) {
            return (FALSE);
        }

        InitContext(lpIMC);

        ImmUnlockIMC(hIMC);
    }

    return (TRUE);
}
//-------------------------------------
BOOL WINAPI ImeDestroy(         // this dll is unloaded
    UINT uReserved)
{
    if (uReserved) {
        return (FALSE);
    }

    return (TRUE);
}
//-----------------------------------
BOOL WINAPI ImeConfigure(      // configurate the IME setting
    HKL     hKL,               // hKL of this IME
    HWND    hAppWnd,           // the owner window
    DWORD   dwMode,
    LPVOID  lpData)            // mode of dialog
{
    switch (dwMode) {
    case IME_CONFIG_GENERAL:
        DialogBox(hInst, TEXT("ImeSet"), (HWND)hAppWnd, (DLGPROC)ImeSetDlgProc);
        break;
    default:
        return (FALSE);
        break;
    }
    return (TRUE);
}
//-------------------------------------
BOOL WINAPI ImeSetCompositionString(
    HIMC   hIMC,
    DWORD  dwIndex,
    LPVOID lpComp,
    DWORD  dwCompLen,
    LPVOID lpRead,
    DWORD  dwReadLen)
{

    LPINPUTCONTEXT      lpIMC;
    LPCOMPOSITIONSTRING lpCompStr;
    LPPRIVCONTEXT       lpImcP;
    BOOL                fRet;

    if (!hIMC) {
        return (FALSE);
    }

    // composition string must  == reading string
    // reading is more important
    if (!dwReadLen) {
        dwReadLen = dwCompLen;
    }

    // composition string must  == reading string
    // reading is more important
    if (!lpRead) {
        lpRead = lpComp;
    }

    if (!dwReadLen) {
        lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
        if (!lpIMC) {
            return (FALSE);
        }

        CompCancel(hIMC, lpIMC);
        ImmUnlockIMC(hIMC);
        return (TRUE);
    } else if (!lpRead) {
        return (FALSE);
    } else if (!dwCompLen) {
    } else if (!lpComp) {
    } else if (dwReadLen != dwCompLen) {
        return (FALSE);
    } else if (lpRead == lpComp) {
    } else if (!lstrcmp(lpRead, lpComp)) {
        // composition string must  == reading string
    } else {
        // composition string != reading string
        return (FALSE);
    }

    if (dwIndex != SCS_SETSTR) {
        return (FALSE);
    }

    lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
    if (!lpIMC) {
        return (FALSE);
    }

    if (!lpIMC->hCompStr) {
        ImmUnlockIMC(hIMC);
        return (FALSE);
    }

    lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
    if (!lpCompStr) {
        ImmUnlockIMC(hIMC);
        return (FALSE);
    }

    lpImcP = (LPPRIVCONTEXT)ImmLockIMCC(lpIMC->hPrivate);
    if (!lpCompStr) {
        ImmUnlockIMCC(lpIMC->hCompStr);
        ImmUnlockIMC(hIMC);
        return (FALSE);
    }

    fRet = SetString(hIMC, lpIMC, lpCompStr, lpImcP, lpRead, dwReadLen);

    ImmUnlockIMCC(lpIMC->hPrivate);
    ImmUnlockIMCC(lpIMC->hCompStr);
    ImmUnlockIMC(hIMC);

    return (fRet);
}
//---------------------------------
BOOL WINAPI NotifyIME(
    HIMC        hIMC,
    DWORD       dwAction,
    DWORD       dwIndex,
    DWORD       dwValue)
{
    LPINPUTCONTEXT lpIMC;
    DWORD          fdwImeMsg;
    BOOL           fRet;

    fRet = FALSE;

    if (!hIMC) {
        return (fRet);
    }

    switch (dwAction) {
    case NI_OPENCANDIDATE:      // after a composition string is determined
                                // if an IME can open candidate, it will.
                                // if it can not, app also can not open it.
    case NI_CLOSECANDIDATE:
        return (fRet);          // not supported
    case NI_SELECTCANDIDATESTR:
    case NI_SETCANDIDATE_PAGESTART:
    case NI_SETCANDIDATE_PAGESIZE:
        break;                  // need to handle it
    case NI_CHANGECANDIDATELIST:
        break;
    case NI_CONTEXTUPDATED:
        switch (dwValue) {
        case IMC_SETCONVERSIONMODE:
        case IMC_SETOPENSTATUS:
            break;              // need to handle it
        case IMC_SETCANDIDATEPOS:
        case IMC_SETCOMPOSITIONFONT:
        case IMC_SETCOMPOSITIONWINDOW:
            return (TRUE);      // not important to the IME
        default:
            return (fRet);      // not supported
        }
        break;
    case NI_COMPOSITIONSTR:
        switch (dwIndex) {
        case CPS_COMPLETE:
            break;              // need to handle it
        case CPS_CONVERT:       // all composition string can not be convert
        case CPS_REVERT:        // any more, it maybe work for some
                                // intelligent phonetic IMEs
            return (fRet);
        case CPS_CANCEL:
            break;              // need to handle it
        default:
            return (fRet);      // not supported
        }
        break;                  // need to handle it
    default:
        return (fRet);          // not supported
    }

    lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
    if (!lpIMC) {
        return (fRet);
    }

    fRet = TRUE;

    switch (dwAction) {
    case NI_CONTEXTUPDATED:
        switch (dwValue) {
        case IMC_SETCONVERSIONMODE:
            if ((lpIMC->fdwConversion ^ dwIndex) & IME_CMODE_CHARCODE) {
                // reject CHARCODE
                lpIMC->fdwConversion &= ~IME_CMODE_CHARCODE;
                MessageBeep((UINT)-1);
                break;
            }

            fdwImeMsg = 0;

            if ((lpIMC->fdwConversion ^ dwIndex) & IME_CMODE_NOCONVERSION) {
                lpIMC->fdwConversion |= IME_CMODE_NATIVE;
                lpIMC->fdwConversion &= ~(IME_CMODE_CHARCODE|
                    IME_CMODE_EUDC|IME_CMODE_SYMBOL);
            }

            if ((lpIMC->fdwConversion ^ dwIndex) & IME_CMODE_EUDC) {
                lpIMC->fdwConversion |= IME_CMODE_NATIVE;
                lpIMC->fdwConversion &= ~(IME_CMODE_CHARCODE|
                    IME_CMODE_NOCONVERSION|IME_CMODE_SYMBOL);
            }

            if ((lpIMC->fdwConversion ^ dwIndex) & IME_CMODE_SOFTKBD) {

                fdwImeMsg |= MSG_IMN_UPDATE_SOFTKBD;

            }

            if ((lpIMC->fdwConversion ^ dwIndex) == IME_CMODE_NATIVE) {
                lpIMC->fdwConversion &= ~(IME_CMODE_CHARCODE|
                    IME_CMODE_NOCONVERSION|IME_CMODE_EUDC);
                fdwImeMsg |= MSG_IMN_UPDATE_SOFTKBD;
            }

            if (fdwImeMsg) {
                GenerateImeMessage(hIMC, lpIMC, fdwImeMsg);
            }

            if ((lpIMC->fdwConversion ^ dwIndex) & ~(IME_CMODE_FULLSHAPE|
                IME_CMODE_SOFTKBD)) {
            } else {
                break;
            }

            CompCancel(hIMC, lpIMC);
            break;
        case IMC_SETOPENSTATUS:
            if (lpIMC->fdwConversion & IME_CMODE_SOFTKBD) {
                GenerateImeMessage(hIMC, lpIMC, MSG_IMN_UPDATE_SOFTKBD);
            }
            CompCancel(hIMC, lpIMC);
            break;
        default:
            break;
        }
        break;
    case NI_SELECTCANDIDATESTR:
        if (!lpIMC->fOpen) {
            fRet = FALSE;
            break;
        } else if (lpIMC->fdwConversion & IME_CMODE_NOCONVERSION) {
            fRet = FALSE;
            break;
        } else if (lpIMC->fdwConversion & IME_CMODE_EUDC) {
            fRet = FALSE;
            break;
        } else if (!lpIMC->hCandInfo) {
            fRet = FALSE;
            break;
        } else {
            LPCANDIDATEINFO lpCandInfo;

            lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(lpIMC->hCandInfo);
            if(!lpCandInfo){
                fRet = FALSE;
                break;
            }

            NotifySelectCand(hIMC, lpIMC, lpCandInfo, dwIndex, dwValue);

            ImmUnlockIMCC(lpIMC->hCandInfo);
        }

        break;
    case NI_CHANGECANDIDATELIST:
        fdwImeMsg = 0;

        fdwImeMsg |= MSG_CHANGE_CANDIDATE;
        GenerateImeMessage(hIMC, lpIMC, fdwImeMsg);

        break;
    case NI_SETCANDIDATE_PAGESTART:
    case NI_SETCANDIDATE_PAGESIZE:
        if (dwIndex != 0) {
            fRet = FALSE;
            break;
        } else if (!lpIMC->fOpen) {
            fRet = FALSE;
            break;
        } else if (lpIMC->fdwConversion & IME_CMODE_NOCONVERSION) {
            fRet = FALSE;
            break;
        } else if (lpIMC->fdwConversion & (IME_CMODE_EUDC|IME_CMODE_SYMBOL)) {
            fRet = FALSE;
            break;
        } else if (!lpIMC->hCandInfo) {
            fRet = FALSE;
            break;
        } else {
            LPCANDIDATEINFO lpCandInfo;
            LPCANDIDATELIST lpCandList;

            lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(lpIMC->hCandInfo);
            if (!lpCandInfo) {
                fRet = FALSE;
                break;
            }

            lpCandList = (LPCANDIDATELIST)((LPBYTE)lpCandInfo +
                lpCandInfo->dwOffset[0]);

            if (dwAction == NI_SETCANDIDATE_PAGESTART) {
                if (dwValue < lpCandList->dwCount) {
                    lpCandList->dwPageStart = lpCandList->dwSelection =
                        dwValue;
                }
            } else {
                if (lpCandList->dwCount) {
                    lpCandList->dwPageSize = dwValue;
                }
            }

            ImmUnlockIMCC(lpIMC->hCandInfo);
        }

        break;
    case NI_COMPOSITIONSTR:
        switch (dwIndex) {
        case CPS_CANCEL:
            CompCancel(hIMC, lpIMC);
            break;
        case CPS_COMPLETE:
            {
                LPPRIVCONTEXT lpImcP;

                lpImcP = (LPPRIVCONTEXT)ImmLockIMCC(lpIMC->hPrivate);
                if (!lpImcP) {
                    break;
                }

                if (lpImcP->iImeState == CST_INIT) {
                    CompCancel(hIMC, lpIMC);
                    // can not do any thing
                } else if (lpImcP->iImeState == CST_CHOOSE) {
                    LPCOMPOSITIONSTRING lpCompStr;
                    LPCANDIDATEINFO     lpCandInfo;

                    lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
                    if(!lpCompStr){
                        break;
                    }

                    lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(lpIMC->hCandInfo);
                    if (lpCandInfo) {
                        LPCANDIDATELIST lpCandList;

                        lpCandList = (LPCANDIDATELIST)((LPBYTE)lpCandInfo +
                            lpCandInfo->dwOffset[0]);

                        SelectOneCand(lpIMC, lpCompStr, lpImcP, lpCandList);

                       ImmUnlockIMCC(lpIMC->hCandInfo);

                       GenerateMessage(hIMC, lpIMC, lpImcP);
                    }

                    if (lpCompStr) ImmUnlockIMCC(lpIMC->hCompStr);
                } else if ((lpIMC->fdwConversion & (IME_CMODE_NATIVE|
                    IME_CMODE_EUDC|IME_CMODE_SYMBOL)) != IME_CMODE_NATIVE) {
                    CompCancel(hIMC, lpIMC);
                } else if (lpImcP->iImeState == CST_INPUT) {
                    LPCOMPOSITIONSTRING lpCompStr;
                    LPGUIDELINE         lpGuideLine;
                    LPCANDIDATEINFO     lpCandInfo;

                    lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
                    if(!lpCompStr){
                        break;
                    }

                    lpGuideLine = (LPGUIDELINE)ImmLockIMCC(lpIMC->hGuideLine);
                    if(!lpGuideLine){
                        ImmUnlockIMCC(lpIMC->hCompStr);
                        break;
                    }

                    CompWord(' ', lpIMC, lpCompStr, lpImcP, lpGuideLine);

                    if (lpImcP->iImeState == CST_INPUT) {
                        CompCancel(hIMC, lpIMC);
                    } else if (lpImcP->iImeState != CST_CHOOSE) {
                    } else if (lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(
                        lpIMC->hCandInfo)) {
                        LPCANDIDATELIST lpCandList;

                        lpCandList = (LPCANDIDATELIST)((LPBYTE)lpCandInfo +
                            lpCandInfo->dwOffset[0]);

//                        SelectOneCand(hIMC, lpIMC, lpCompStr, lpImcP, lpCandList);
                        SelectOneCand(lpIMC, lpCompStr, lpImcP, lpCandList);

                       ImmUnlockIMCC(lpIMC->hCandInfo);
                    } else {
                    }

                    if (lpCompStr) ImmUnlockIMCC(lpIMC->hCompStr);
                    if (lpGuideLine) ImmUnlockIMCC(lpIMC->hGuideLine);

                    // don't phrase predition under this case
                    if (lpImcP->fdwImeMsg & MSG_ALREADY_OPEN) {
                        lpImcP->fdwImeMsg = (lpImcP->fdwImeMsg | MSG_CLOSE_CANDIDATE) &
                            ~(MSG_OPEN_CANDIDATE|MSG_CHANGE_CANDIDATE);
                    } else {
                        lpImcP->fdwImeMsg &= ~(MSG_CLOSE_CANDIDATE|MSG_OPEN_CANDIDATE);
                    }

                    GenerateMessage(hIMC, lpIMC, lpImcP);
                } else {
                    CompCancel(hIMC, lpIMC);
                }

                ImmUnlockIMCC(lpIMC->hPrivate);
            }
            break;
        default:
            break;
        }
        break;
    default:
        break;
    }

    ImmUnlockIMC(hIMC);
    return (fRet);
}

输入法的安装有2种方法:复杂的和简单的:)
1、所谓的简单的安装方法
首先使用GetSystemDirectory获取system文件夹的路径,我们假设系统为XP,安装在C盘,那么获取的结果为C:\WINDOWS\system32。假设我们的ime文件为IMEDemo.ime,在获取完system路径后,继续合成一个szFileName为C:\WINDOWS\system32\IMEDemo.ime的路径,然后使用CopyFile函数把文件拷贝至szFileName所至的位置,此时调用函数即可,示例代码如下:

...  ...
			//安装输入法
			char szFilePath[MAX_PATH] = "";
			GetSystemDirectory(szFilePath,MAX_PATH);
			strcat(szFilePath,"\\IMEDemo.IME");

			CopyFile("IMEDemo.IME",szFilePath,FALSE);

			HKL hkl = ImmInstallIME(szFilePath,"微软拼音输入法[Figo]");
			if (FALSE == ImmIsIME(hkl))
			{
				MessageBoxW(hwnd,L"ImmInstallIME函数失败",L"出错了",MB_OK|MB_ICONERROR);
				SendMessage(hwnd,WM_CLOSE,0,0);
			}
...  ...

2、所谓复杂的安装方法
这种方法我是参考五笔爱好者论坛的,未验证。
//——————引文—————-
★Win9x/Me 系统

步骤一:拷贝文件。
在Win9x/Me系统下, 把输入法相关文件(一般是*.IME,可能还有其它 .DLL文件)拷贝到 Win$\System$ 目录下。

步骤二:修改注册表。
①第一处:
在 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\ 下新建主键(E0xx0804, 请替换 xx 为一不重复的十六进制数),再在该主键下新建三个字串项,内容如下:
格式为:名称 = 数据
IME File = imeName.ime
Layout File = kbdus.kbd
Layout Text = 输入名名称
②第二处:
在 HKEY_CURRENT_USER\Keyboard Layout\preload 下新建一主键 x ,(数字, 比现有的主键最大数大 1)
其内容为:(默认) = E0xx0804 (默认用 “” 表示键名, E0xx0804 就是上述第一处新建的那一个主键名)

步骤三:激活输入法。(即使桌面右下角的输入法列表中出现新安装的输入法)
使用Windows API:LoadKeyoardLayout(”E0xx0804″, KLF_ACTIVATE), 激活输入法, 具体用法请查看 MSDN。

★WinNT/2000/XP 系统

步骤一:拷贝文件。
在WinNT/2000/XP 系统下, 把输入法相关文件(一般是*.IME,可能还有其它 .DLL文件)拷贝到 Win$\System$ 目录下。

步骤二:修改注册表。
①第一处:
在 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\ 下新建主键(E0xx0804, 请替换 xx 为一不重复的十六进制数),再在该主键下新建三个字串项,内容如下:
格式为:名称 = 数据
IME file = imeName.ime
Layout File = kbdus.DLL
Layout Fext = 输入名名称
②第二处:
HKEY_CURRENT_USER\Keyboard Layout\Preload 下,新建字串项 x ,名称及数据为: x = E0xx0804 (x 是数字, 比现有的最大键名值大 1, E0xx0804 是上述新建的主键名)。

步骤三:激活输入法。(即使桌面右下角的输入法列表中出现新安装的输入法)
使用Windows API:LoadKeyoardLayout(”E0xx0804″, KLF_ACTIVATE), 激活输入法, 具体用法请查看 MSDN。

说明:
①步骤三如果不做的话,则在重启计算机后,桌面右下角的输入法列表中才会出现你安装的输入法,否则需要用户手动在“控制面板”的“输入法”中添加到桌面右下角的输入法列表中去。
②Win$是指系统Windows的安装目录,Win9x/Me下一般是 x:\Windows,而 WinNT/2000 一般是 x:\Winnt,WinXP下一般是 x:\Windows。
③System$是指 Windows的系统目录,Win9x/Me下一般是 Win$ 目录下的 System 目录,WinNT/2000/XP 下一般是 Win$ 下的System32目录。
//——————引文完—————-
卸载方法如下

...  ...
HKEY hkey;
 if(RegOpenKey(HKEY_CURRENT_USER, "Keyboard Layout\\Preload", &hkey) == ERROR_SUCCESS)
 {
  BOOL  bret = UnloadKeyboardLayout((HKL)0xe0270804 );
  RegDeleteValue(hkey, "7");
//  ::SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
 }
...  ...

现在有个问题来了,输入法安装成功后,怎么才能注入到目标窗口呢,大致说来有三种方法:
1、向目标窗口发送WM_INPUTLANGCHANGEREQUEST消息
PostMessage(HWND_BROADCAST,WM_INPUTLANGCHANGEREQUEST,0,(LPARAM)hkl);
HWND_BROADCAST表示一个广播消息句柄,它会向所有窗口发送INPUTLANGCHANGEREQUEST消息,当然,也可以使用具体的窗口句柄代替HWND_BROADCAST使得针对性更强。hkl是ImmInstallIME的返回值。但是此种方法有个缺陷,每次都需要发送消息。

2、把我们的输入法设为默认输入法
使用函数
BOOL WINAPI SystemParametersInfo(
__in UINT uiAction,
__in UINT uiParam,
__inout PVOID pvParam,
__in UINT fWinIni
);
调用方法示例如下:
SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, hkl, SPIF_SENDWININICHANGE)

3、使用ActivateKeyboardLayout函数
//获取当前线程的输入法
HKL hklPre = ActivateKeyboardLayout(hkl,0);
其中hklPre为前一个输入法标识句柄,hkl为我们的傀儡输入法标识句柄。
//获取我们输入法对应的标识字符串
char szIMEStr[MAX_LINE] = “”;
GetKeyboardLayoutName(szIMEStr);
LoadKeyboardLayout(szIMEStr,KLF_REORDER);
如果执行成功的话,我们的傀儡输入法就成为了默认的输入法,这样会使得后面加载的程序都会被注入。

MSDN中说LoadLibrary函数只可以加载.exe或者.dll后缀的文件(either a .dll or .exe file),其实对于.ime后缀的executable module也可以加载,我已经写代码验证过了。

上面的信息基本足够你写出来注入的程序了。当然,我假设你懂得如何在dll中设置共享的数据段:
//–=公共数据段=–
#pragma data_seg(”IMESHARED”)
… …
#pragma data_seg()
#pragma comment(linker, “/section:IMESHARED,RWS”)
//IMESHARED是数据段名,随意起。

上面这些知识是我的认识,必然会存在种种不足,本人不对因此造成的一切坏的结果负连带责任。一切以微软的文档和实践为准,动手写代码才是王道!
关于IME的文档很少,我能找到的已经打包:点击下载。当然,记得先谢国家:)同时,聪明伶俐的你如果写出来注入的完整代码后,可以拿我写的小程序来做实验,当然,这个程序未加入任何保护:点击下载。修改血量和子弹即可。如果你不想学习这种技术,也可以等到驱动讲解的部分再来获取权限。You will be back!
crossfire

转载请注明出处:http://www.figoyao.com/blog/2010/03/29/1270

  1. simple
    三 30th, 201019:08

    老师辛苦了,这些知识足够我花好长时间消化掉了。这一系列下来给我的感觉就是讲解通俗易懂,代码的书写规范得比教科书都好。而且出发点都是以“授人以鱼不如授人以渔”为基准 。

  2. dormancy
    四 25th, 201004:31

    理解老师滴苦衷。。。但是我找了2天了。还是有些迷惑。望老师能给予指点。看完19课我滴理解。就是自己建立一个DLL然后把19个标准函数接口算是重载进来吧。因为不用实现什么功能。所以也不用写函数体啦。。。看20课一下就迷茫啦。。。
    不知道该如何理解。。。怎么下手啦

  3. zhihuizhixun
    五 2nd, 201020:13

    谢谢老师,老师辛苦了。写得很好。通俗易懂。。谢谢

  4. xxymg
    六 20th, 201013:56

    好 好 好 辛苦了 老师

  5. 迷途
    六 28th, 201013:00

    建议老师做视频教程。那样的教程才具有连贯性,立体性。让C++普及吧!!