首先想編這種程序需要壹些基礎知識。
會用Vc++,包括16/32位。
精通Windows API特別是GDI,KERNEL部分。
懂匯編語言,會用softice調試程序,因為這種程序最好用softice調試。
二.基本原理
在Window 3.x時代,windows系統提供的字符輸出函數只有很少的幾個。
TextOut
ExtTextOut
DrawText
......
其中DrawText最終是用ExtTextOut實現的。
所以Windows的所有字符輸出都是由調用TextOut和ExtTextOut實現的。因此,如果妳可以修改這兩個函數的入口,讓程序先調用妳自己的壹個函數再調用系統的字符輸出,妳就可以得到Windows所有輸出的字符了。
到了Windows95時代,原理基本沒變,但是95比3.x要復雜。開始的時候,壹些在windows3.x下編寫的取詞軟件仍然可以是使用。但是後來出了個IE4,結果很多詞典軟件就因為不支持IE4而被淘汰了,但同時也給壹些軟件創造了機會,如金山詞霸。其實IE4的問題並不復雜,只不過它的輸出的是unicode字符,是用TextOutW和ExtTextOutW輸出的。知道了這壹點,只要也截取就可以了。不過實現方法復雜壹點,以後會有詳細講解。現在又出了個IE5,結果詞霸也不好用了,微軟真是#^@#$%$*&^@#@..........
我研究後找到了壹種解決辦法,但還有些問題,有時會取錯,正在繼續研究,希望大家***同探討。
另外還有WindowsNT,原理也是壹樣,只是實現方法和95下完全不同。
三.技術要點
要實現取詞,主要要解決以下技術問題。
1.截取API入口,獲得API的參數。
2.安全地潛入Windows內部,良好地兼容Windows的各個版本
3.計算鼠標所在的單詞和字母。
4.如果妳在Window95下,做32位程序,還涉及Windows32/16混合編程的技術。
今天先到這裏吧!最好準備壹份softice for 95/98和金山詞霸,讓我們先來分析壹下別人是怎麽做的。
歡迎與我聯系
E-Mail:yeedong@163.net
主題 屏幕取詞技術系列講座(二)
作者 亦東
很抱歉讓大家久等了!
我看了壹些人的回帖,發現很多人對取詞的原理還是不太清楚。
首先我來解釋壹下hook問題。詞霸中的確用到了hook,而且他用了兩種hook其中壹種是Windows標準hook,通過SetWindowHook安裝壹個回調函數,它安裝了壹個鼠標hook,是為了可以及時響應鼠標的消息用的和取詞沒太大關系。
另壹種鉤子是API鉤子,這才是取詞的核心技術所在。他在TextOut等函數的開頭寫了壹個jmp語句,跳轉到自己的代碼裏。
妳用softice看不到這個跳轉語句是因為它只在取詞的壹瞬間才存在,平時是沒有的。
妳可以在TextOut開頭設壹個讀寫斷點
bpm textout
再取詞,就會找到詞霸用來寫鉤子的代碼了。
/**********************************
所以我在次強調,想學這種技術壹定要懂匯編語言和熟練使用softice.
**********************************/
至於從cjktl95中dump出來的未公開函數是和Windows32/16混合編程有關的,以後我會提到他們。
我先來講述取詞的過程,
0 判斷鼠標是否在壹個地方停留了壹段時間
1 取得鼠標當前位置
2 以鼠標位置為中心生成壹個矩形
3 掛上API鉤子
4 讓這個矩形產生重畫消息
5 在鉤子裏等輸出字符
6 計算鼠標在哪個單詞上面,把這個單詞保存下來
7 如果得到單詞則摘掉API鉤子,在壹段時間後,無論是否得到單詞都摘掉API鉤子
8 用單詞查詞庫,顯示解釋框。
很多步驟實現起來都有壹些難度,所以在中國可以做壹個完善的取詞詞典的人屈指可數。
其中0,1,2,7,8比較簡單就不提了。
先說如何掛鉤子:
所謂鉤子其實就是在WindowsAPI入口寫壹個JMP XXXX:XXXX語句,跳轉到自己的代碼裏。
步驟如下:
1.取得Windows API入口,用GetProcAddress實現
2.保存API入口的前五個字節,因為JMP是0xEA,地址是4個字節
3.寫入跳轉語句
這步最復雜
Windows的代碼段本來是不可以寫的,但是Microsoft給自己留了個後門。
有壹個未公開函數是AllocCsToDsAlias,
UINT WINAPI ALLOCCSTODSALIAS(UINT);
妳可以取到這個函數的入口,把API的代碼段的選擇符(要是不知道什麽是選擇符,就先去學學保護模式編程吧)傳給他,他會返回壹個可寫的數據段選擇符。這個選擇符用完要釋放的。用新選擇符和API入口的偏移量合成壹個指針就可以寫windows的代碼段了。
這就是取詞技術的最核心的東東,不止取詞,連外掛中文平臺全屏漢化都是使用的這種技術。現在知道為什麽這麽簡單的幾句話卻很少知道了吧?因為太多的產品使用他,太多的公司靠他賺錢了。
這些公司和產品有:中文之星,四通利方,南極星,金山詞霸,實達銘泰的東方快車,roboword,譯典通,即時漢化專家等等等等。。。。還有至少20多家小公司。他們的具體實現雖然不同,但大致原理是相同的。
我這些都是隨手寫的,也沒有提綱之類的東西,以後如果有機會我會整理壹下,大家先湊合著看吧!xixi...
主題 關於屏幕取詞的討論(三)
作者 亦東
讓大家久等,很抱歉,前些時候工作忙硬盤又壞了,太不幸了。
這回來點真格的。
咱們以截取TextOut為例。
下面是代碼:
//截取TextOut
typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT);
ALLOCCSTODSALIAS AllocCsToDsAlias;
BYTE NewValue[5];//保存新的入口代碼
BYTE OldValue[5];//API原來的入口代碼
unsigned char * Address=NULL;//可寫的API入口地址
UINT DsSelector=NULL;//指向API入口的可寫的選擇符
WORD OffSetEntry=NULL;//API的偏移量
BOOL bHookAlready = FALSE; //是否掛鉤子的標誌
BOOL InitHook()
{
HMODULE hKernel,hGdi;
hKernel = GetModuleHandle("Kernel");
if(hKernel==NULL)
return FALSE;
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//這是未公開的API所以要這樣取地址
if(AllocCsToDsAlias==NULL)
return FALSE;
hGdi = GetModuleHandle("Gdi");
if(hmGdi==NULL)
return FALSE;
FARPROC Entry = GetProcAddress(hGdi,"TextOut");
if(Entry==NULL)
return FALSE;
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代碼段的選擇符
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配壹個等同的可寫的選擇符
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址
NewValue[0]=0xEA;
*((DWORD*)(NewValue+1)) = (DWORD)MyTextOut;
OldValue[0]=Address[0];
*((DWORD*)(OldValue+1)) = *((DWORD*)(Address+1));
}
BOOL ClearHook()
{
if(bHookAlready)
HookOff();
FreeSelector(DsSelector);
}
BOOL HookOn()
{
if(!bHookAlready){
for(int i=0;i<5;i++){
Address[i]=NewValue[i];
}
bHookAlready=TRUE;
}
}
BOOL HookOff()
{
if(bHookAlready){
for(int i=0;i<5;i++){
Address[i]=OldValue[i];
}
bHookAlready=FALSE;
}
}
//鉤子函數,壹定要和API有相同的參數和聲明
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
{
BOOL ret;
HookOff();
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//調原來的TextOut
HookOn();
return ret;
}
上面的代碼是壹個最簡單的掛API鉤子的例子,我要提醒大家的是,這段代碼是我憑記憶寫的,我以前的代碼丟了,我沒有編譯測試過
因為我沒有VC++1.52.所以代碼可能會有錯。
建議使用Borland c++,按16位編譯。
如果用VC++1.52,則要改個選項
在VC++1.52的Option裏,有個內存模式的設置,選大模式,和"DS!=SS DS Load on Function entry.",切記,否則會系統崩潰。