社区应用 最新帖子 精华区 社区服务 会员列表 统计排行 社区论坛任务 迷你宠物
  • 2649阅读
  • 0回复

动态汉化Windows技术的分析

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
"陷阱"技术探秘──动态汉化Windows技术的分析 `h_,I R<  
]K0<DO9  
四通利方(RichWin)、中文之星(CStar)是大家广为熟知的汉化Windows产品,"陷阱"技术即动态修改Windows代码,一直是其对外宣称的过人技术。本文从Windows的模块调用机制与重定位概念着手,介绍了"陷阱"技术的实现,并给出了采用"陷阱"技术动态修改Windows代码的示例源程序。 E"bYl3  
WM NcPHcj  
一、发现了什么? lz@fXaZM  
笔者多年来一直从事Windows下的软件开发工作,经历了Windows 2.0 、 3.0 、3.1 ,直至Windows 95、NT的成长过程,也遍历了长青窗口、长城窗口、DBWin、CStar、RichWin等多个Windows汉化产品。从现在看来,影响最大也最为成功的,当推四通利方的RichWin;此外,中文之星CStar与RichWin师出一门,其核心技术自然也差不多。其对外宣传采用独特的"陷阱" 技术即动态修改Windows代码,一直是笔者感兴趣的地方。 ZO{uG(u  
EXEHDR是Microsoft Visual C++开发工具中很有用的一个程序,它可以检查NE(New-Exe cutable)格式文件,用它来分析RichWin的WSENGINE.DLL或CStar的CHINESE.DLL,就会发现与众不同的两点(以CStar 1.20为例): zx'G0Z9]  
.MMFN }1O  
C:\CSTAR>exehdr chinese.dll /v 64>E|w  
.................................. jDI O,XuF  
[Rw0']i`4  
6 type   offset target  Ek(. ["  
BASE   060a seg   2 offset 0000 :\L{S  
PTR   047e imp GDI.GETCHARABCWIDTHS VdQ}G!d  
PTR   059b imp GDI.ENUMFONTFAMILIES ii0AhQ  
PTR   0451 imp DISPLAY.14 ( EXTTEXTOUT ) q$e2x=?  
PTR   0415 imp KEYBOARD.4 ( TOASCII ) EcrM`E#kaZ  
PTR   04ba imp KEYBOARD.5 ( ANSITOOEM ) V"(S<o  
PTR   04c9 imp KEYBOARD.6 ( OEMTOANSI ) $q]((@i.  
PTR   04d8 imp KEYBOARD.134( ANSITOOEMBUFF ) aB"W6[  
PTR   05f5 imp USER.430   ( LSTRCMP ) 9r@r\-  
PTR   04e7 imp KEYBOARD.135( OEMTOANSIBUFF ) :pcKww|V  
PTR   0514 imp USER.431   ( ANSIUPPER ) /E$"\md  
PTR   0523 imp USER.432   ( ANSILOWER ) jFpXTy[>  
PTR   05aa imp GDI.56     ( CREATEFONT ) -X5rGp++  
PTR   056e imp USER.433   ( ISCHARALPHA ) dG}fpQ3&  
PTR   05b9 imp GDI.57     ( CREATEFONTINDIRECT ) X{\>TOk   
PTR   057d imp USER.434   ( ISCHARALPHANUMERIC ) +[8s9{1{C  
PTR   049c imp USER.179   ( GETSYSTEMMETRICS ) Sx+.<]t2A  
PTR   0550 imp USER.435   ( ISCHARUPPER ) F=yrqRS=  
PTR   055f imp USER.436   ( ISCHARLOWER ) *DObtS_ 6  
PTR   0532 imp USER.437   ( ANSIUPPERBUFF ) 5:E7nqsNhq  
PTR   0541 imp USER.438   ( ANSILOWERBUFF ) kM|akG  
PTR   05c8 imp GDI.69     ( DELETEOBJECT ) G*uy@s:  
PTR   058c imp GDI.70     ( ENUMFONTS ) e*jt(p[Ge  
PTR   04ab imp KERNEL.ISDBCSLEADBYTE L)&?$V  
PTR   05d7 imp GDI.82     ( GETOBJECT ) CUfD[un2D  
PTR   048d imp KERNEL.74   ( OPENFILE ) q3JoU/Sf  
PTR   0460 imp GDI.91     ( GETTEXTEXTENT ) EC$wi|i  
PTR   05e6 imp GDI.92     ( GETTEXTFACE ) p}_bu@;.Z  
PTR   046f imp GDI.350   ( GETCHARWIDTH ) {^>m3  
PTR   0442 imp GDI.351   ( EXTTEXTOUT ) JYOyz+wNd  
PTR   0604 imp USER.471   ( LSTRCMPI ) ) Yz` 6  
PTR   04f6 imp USER.472   ( ANSINEXT ) S*Un$ngAh  
PTR   0505 imp USER.473   ( ANSIPREV ) yd[}?  
PTR   0424 imp USER.108   ( GETMESSAGE ) D{I^_~-\5  
PTR   0433 imp USER.109   ( PEEKMESSAGE ) lidzs<W-fW  
RxU6.5N  
35 relocations 1BwCJ7?8  
_C~e(/=z  
(括号内为笔者加上的对应Windows API函数。) 2;r(?ebw  
第一,在数据段中,发现了重定位信息。 n?_!gqK  
第二,这些重定位信息提示的函数,全都与文字显示输出和键盘、字符串有关。也就是说汉化Windows,必须修改这些函数。 hL~@Ah5&t  
在这非常特殊的地方,隐藏着什么呢?毋庸置疑,这与众不同的两点,对打开"陷阱"技术之门而言,不是金钥匙,也是敲门砖。 nzE4P3 C+  
v' .:?9  
二、Windows的模块调用机制与重定位概念 _%w-y(Sqn  
为了深入探究"陷阱"技术,我们先来介绍Windows的模块调用机制。 Xg?hh 0s  
Windows的运行分实模式、标准模式和增强模式三种,虽然这几种模式各不相同,但其核心模块的调用关系却是完全一致的。 .9+"rK}u  
主要的三个模块,有如下的关系: k-xh-&  
·KERNEL是Windows系统内核,它不依赖其它模块。 RoSh|$JF  
·GDI是Windows图形设备接口模块,它依赖于KERNEL模块。 o1YX^-<[F  
·USER是Windows用户接口服务模块,它依赖于KERNEL、GDI模块及设备驱动程序等所有模块。 'x{g P?.  
这三个模块,实际上就是Windows的三个动态链接库。KERNEL有三种系统存在形式:Kern el.exe(实模式)、Krnl286.exe(标准模式)、Krnl386.exe(386增强模式);GDI模块是Gdi.ex e;USER模块是User.exe。虽然文件名都以EXE为扩展名,但它们实际都是动态链接库。同时,几乎所有的API函数都隐藏在这三个模块中。用EXEHDR对这三个模块分析,就可列出一大堆大家所熟悉的Windows API函数。 <iunDL0  
以GDI模块为例,运行结果如下: i%+cPQ^o  
C:\WINDOWS\SYSTEM>exehdr gdi.exe 9V`/zq?  
SLpB$puS  
Exports: ~'KymarPU  
LOpn PH`  
rd seg offset name qEPvV  
............ yjvzA|(YC  
351 1 923e EXTTEXTOUT exported, shared data 6 /gh_'&  
56 3 19e1 CREATEFONT exported, shared data ]]`hnzJX  
............ ]?S\So+  
&H$ 3`"p5u  
至此,读者已能从Windows纷繁复杂的系统中理出一些头续来。下面,再引入一个重要概念——重定位。 c-3AzB#[  
一个Windows执行程序对调用API函数或对其它动态库的调用,在程序装入内存前,都是一些不能定位的动态链接;当程序调入内存时,这些远调用都需要重新定位,重新定位的依据就是重定位表。在Windows执行程序(包括动态库)的每个段后面,通常都跟有这样一个重定位表。重定位包含调用函数所在模块、函数序列号以及定位在模块中的位置。 KRQKL`}}  
例如,用EXEHDR /v 分析CHINESE.DLL得到: 4\4onCzuT  
6 type offset target =:n>yZ3T  
z:-a7_   
.......... W_9-JM(r  
vt<r_&+ pJ  
PTR 0442 imp GDI.351 W,5A|Q~  
u$$@Hw  
.......... 5:/ zbt\C  
I!&|L0Qq  
就表明,在本段的0442H偏移处,调用了GDI的第351号函数。如果在0442H处是0000:FFFF ,表示本段内仅此一处调用了GDI.351函数;否则,表明了本段内还有一处调用此函数,调用的位置就是0442H处所指向的内容,实际上重定位表只含有引用位置的链表的链头。那么,GDI. 351是一个什么函数呢?用EXEHDR对GDI.EXE作一分析,就可得出,在GDI的出口(Export)函数中,第351号是ExtTextOut。 )9MmL-7K  
这样,我们在EXEHDR这一简单而非常有用的工具帮助下,已经在Windows的浩瀚海洋中畅游了一会,下面让我们继续深入下去。 T^g2N`w2  
I-oI,c%+  
三、动态汉化Windows原理 >(S4h}^I  
我们知道,传统的汉化Windows的方法,是要直接修改Windows的显示、输入、打印等模块代码,或用DDK直接开发"中文设备"驱动模块。这样不仅工作量大,而且,系统的完备性很难保证,性能上也有很多限制(早期的长青窗口就是如此),所以只有从内核上修改Windows核心代码才是最彻底的办法。 <#<4A0:  
从Windows的模块调用机制,我们可以看到,Windows实际上是由包括在KERNEL、GDI、US ER等几个模块中的众多函数支撑的。那么,修改其中涉及语言文字处理的函数,使之能适应中文需要,不就能达到汉化目的了吗? QCQku\GLV  
因而,我们可以得出这样的结论:在自己的模块中重新编写涉及文字显示、输入的多个函数,然后,将Windows中对这些函数的引用,改向到自己的这些模块中来。修改哪些函数才能完成汉化,这需要深入分析Windows的内部结构,但CHINESE.DLL已明确无误地告诉了我们,在其数据段的重定位表中列出的引用函数,正是CStar修改了的Windows函数!为了验证这一思路, 我们利用RichWin作一核实。 IlG)=?8XZ  
用EXEHDR分析GDI.EXE,得出ExtTextOut函数在GDI的第一代码段6139H偏移处(不同版本的Windows其所在代码段和偏移可能不一样)。然后,用HelpWalk(也是Microsoft Visual C+ +开发工具中的一个)检查GDI的Code1段,6139H处前5个字节是 B8 FF 05 45 55,经过运行Ri chWin 4.3 for Internet后,再查看同样的地方,已改为 EA 08 08 8F 3D。其实反汇编就知道,这5个字节就是 Jmp 3D8F:0808,而句柄为0x3D8F的模块,用HelpWalk能观察正是RichWin 的WSENGINE.DLL的第一代码段( 模块名为TEXTMAN)。而偏移0808H处 B8 B7 3D 45 55 8B E C 1E,正是一个函数起始的地方,这实际上就是RichWin所重改写的ExtTextOut函数。退出Ri chWin后,再用HelpWalk观察GDI的Code1代码段,一切又恢复正常!这与前面的分析结论完全吻合!那么,下一个关键点就是如何动态修改Windows的函数代码,也就是汉化Windows的核心——"陷阱"技术。 /~NsHStn  
i`)bn 1Xm  
四、"陷阱"技术 eU 'DQp*  
讨论"陷阱"技术,还要回到前面的两个发现。发现之二,已能解释为修改的Windows函数,而发现之一却仍是一个迷。 `G&W%CHB  
数据段存放的是变量及常量等内容,如果这里面包含有重定位信息,那么,必定要在变量说明中将函数指针赋给一个FARPROC类型的变量,于是,在变量说明中写下: Er^ijh,  
FARPROC FarProcFunc=ExtTextOut; r/'9@oM  
果然,在自己程序的数据段中也有了重定位信息。这样,当程序调入内存时,变量FarPro cFunc已是函数ExtTextOut的地址了。 cP%mkh_ri  
要直接修改代码段的内容,还遇到一个难题,就是代码段是不可改写的。这时,需要用到一个未公开的Windows函数AllocCStoDSAlias,取得与代码段有相同基址的可写数据段别名, 其函数声明为: Kj,C 9  
WORD FAR PASCAL AllocCStoDSAlias(WORD code_sel); h!ZEZ|{  
参数是代码段的句柄,返回值是可写数据段别名句柄。 EGL1[7It`  
Windows中函数地址是32位,高字节是其模块的内存句柄,低字节是函数在模块内的偏移。将得到的可写数据段别名句柄锁定,再将函数偏移处的5个字节保留下来,然后将其改为转向替代函数(用 EA Jmp): Da*=uW9  
*(lpStr+wOffset) =0xEA; /2pf*\u  
四通利方(RichWin)、中文之星(CStar)是大家广为熟知的汉化Windows产品,"陷阱"技术即动态修改Windows代码,一直是其对外宣称的过人技术。本文从Windows的模块调用机制与重定位概念着手,介绍了"陷阱"技术的实现,并给出了采用"陷阱"技术动态修改Windows代码的示例源程序。 E</Um M+ R  
//源程序 relocate.c (m80isl  
|>@Gbgw^M  
#include <WINDOWS.H> CwZ+P n0  
#include <dos.h> =)vmX0vL  
BOOL WINAPI MyExtTextOut(HDC hDC, int x, int y, UINT nInt1, const RECTFAR*lpRect,LPCSTR lpStr, UINT nInt2, int FAR* lpInt); /fbI4&SB!  
WORD FAR PASCAL AllocCStoDSAlias(WORD code_sel); $7eO33Bm  
typedef struct tagFUNC i71 ,  
{ u[9i>7}9  
FARPROC lpFarProcReplace; //替代函数地址 MEMD8:['  
FARPROC lpFarProcWindows; //Windows函数地址 IXNcn@tN  
BYTE   bOld;     //保存原函数第一字节 < gB>j\:  
LONG   lOld;     //保存原函数接后的四字节长值 =G3O7\KmH  
}FUNC; S453oG"  
FUNC Func={MyExtTextOut,ExtTextOut}; l?v`kAMR  
//Windows主函数 &cztUM(  
int PASCAL WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) ,}2yxo;i  
{ {Ak{ ct\t  
HANDLE hMemCode; //代码段句柄 t=syo->  
WORD hMemData; //相同基址的可写数据段别名 [T#5$J  
WORD   wOffset;   //函数偏移 H_KE^1  
LPSTR lpStr; R}njFQvS)  
LPLONG lpLong; Qg;A (\z  
char   lpNotice[96]; Wc [@,  
hMemCode=HIWORD((LONG) Func.lpFarProcWindows ); Q3> 3!FAO  
wOffset=LOWORD((LONG) Func.lpFarProcWindows ); }ozlED`E  
wsprintf(lpNotice,"函数所在模块句柄 0x%4xH,偏移 0x%4xH",hMemCode,wOffset); ;> **+ezF  
MessageBox(NULL,lpNotice,"提示",MB_OK); 6wC|/J^  
//取与代码段有相同基址的可写数据段别名 u}Vc2a,WV  
hMemData=AllocCStoDSAlias(hMemCode); 3&'ll51t  
lpStr=GlobalLock(hMemData); l G12Su/  
lpLong=(lpStr+wOffset+1 ); /3->TS  
//保存原函数要替换的头几个字节 _yY(&(]#  
Func.bOld=*(lpStr+wOffset); $~vy,^  
Func.lOld=*lpLong; p>4$&-  
*(lpStr+wOffset)=0xEA; JF!?i6V  
*lpLong=Func.lpFarProcReplace; ~6m-2-14q  
GlobalUnlock(hMemData); z1[2.&9D-  
MessageBox(NULL,"改为自己的函数","提示",MB_OK); zJJ KLr;  
//将保留的内容改回来 rOQhS]TP*  
hMemData=AllocCStoDSAlias(hMemCode); Bf!i(gM  
lpStr=GlobalLock(hMemData); v9R#=m/=  
lpLong=(lpStr+wOffset+1 ); Fq/?0B8  
*(lpStr+wOffset)=Func.bOld; jV Yt=j*"V  
*lpLong=Func.lOld; +^tq?PfE  
GlobalUnlock(hMemData); YY-{&+,  
MessageBox(NULL,"改回原Windows函数","提示",MB_OK); `l,=iy$  
return 1; 6}^0/ 76^,  
} !]1X0wo\  
k_%2Ok   
//自己的替代函数 #R$d6N[H  
|d^r"wbs3  
BOOL WINAPI MyExtTextOut(HDC hDC, int x, int y, UINT nInt1, const RECT FAR* TJFxo? gC"  
lpRect, LPCSTR lpStr, UINT nInt2, int FAR* lpInt) _h>S7-X  
{ le*mr0a  
BYTE NameDot[96]= #k2&2W=x  
{ j~,7JJ (y  
0x09, 0x00, 0xfd, 0x08, 0x09, 0x08, 0x09, 0x10, 0x09, 0x20, CqX2R:#  
0x79, 0x40, 0x41, 0x04, 0x47, 0xfe, 0x41, 0x40, 0x79, 0x40, Li~(kw3  
0x09, 0x20, 0x09, 0x20, 0x09, 0x10, 0x09, 0x4e, 0x51, 0x84, lxoc.KDtR  
0x21, 0x00, 0x02, 0x00, 0x01, 0x04, 0xff, 0xfe, 0x00, 0x00, fTiqY72h  
0x1f, 0xf0, 0x10, 0x10, 0x10, 0x10, 0x1f, 0xf0, 0x00, 0x00, 2GOQ|Z  
0x7f, 0xfc, 0x40, 0x04, 0x4f, 0xe4, 0x48, 0x24, 0x48, 0x24, &09z`* ,  
0x4f, 0xe4, 0x40, 0x0c, 0x10, 0x80, 0x10, 0xfc, 0x10, 0x88, u4TU"r("A  
0x11, 0x50, 0x56, 0x20, 0x54, 0xd8, 0x57, 0x06, 0x54, 0x20, Q!K@  
0x55, 0xfc, 0x54, 0x20, 0x55, 0xfc, 0x5c, 0x20, 0x67, 0xfe, YSwAu,$jf  
0x00, 0x20, 0x00, 0x20, 0x00, 0x20 & V :q}Q  
}; 1~:7W  
[^xLK  
HBITMAP hBitmap,hOldBitmap; xcdy/J&  
HDC   hMemDC; #- $?2?2  
BYTE far *lpDot; nN" Y~W^k  
int   i; ppr95 Y]^  
for ( i=0;i<3;i++ ) 2KVMQH`B9  
{ 9,|{N(N<!  
lpDot=(LPSTR)NameDot+i*32; ?95^&4Oh0  
hMemDC=CreateCompatibleDC(hDC); qS<a5`EA  
hBitmap=CreateBitmap(16,16,1,1,lpDot); m qgA  
SetBitmapBits(hBitmap,32L,lpDot); 0VC8'6S_k  
hOldBitmap=SelectObject(hMemDC,hBitmap); owL>w  
BitBlt(hDC,x+i*16,y,16,16,hMemDC,0,0,SRCCOPY); yoa"21E$  
DeleteDC(hMemDC); xLX<. z!r  
DeleteObject(hBitmap); (dD+?ZOO  
} #(& ! ^X3  
return TRUE; )\!_`ob  
} '9^+J7iO(+  
W^; wr#  
//模块定义文件 relocate.def -=BQVJ_dK{  
 jL8[;*^G  
NAME     RELOCATE )W9W8>Cc5_  
EXETYPE   WINDOWS @Ee{ GH^-  
CODE     PRELOAD MOVEABLE DISCARDABLE USfpCRj9  
DATA     PRELOAD MOVEABLE MULTIPLE @igGfYy  
HEAPSIZE 1024 [of{~  
EXPORTS \Z9+U:n  
GJz d4kj  
五、结束语 Z$!>hiz2  
本文从原理上分析了称为"陷阱"技术的动态汉化Windows方法,介绍了将任一Windows函数调用改向到自己指定函数处的通用方法,这种方法可以拓展到其它应用中,如多语种显示、不同内码制式的切换显示等。
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
批量上传需要先选择文件,再选择上传
认证码:
验证问题:
10+5=?,请输入中文答案:十五