·汉化新世纪 ·汉化新世纪论坛 ·百家争鸣 ·论坛集萃 ·汉化问答 ·软件介绍
文章首页 >> 汉化教学 >> 一种简单的CreateFontA、CreateFontIndirectA字体修改方法    Creative Commons License,创作共用协议(中文版)  署名 非商业性使用 禁止演绎

一种简单的CreateFontA、CreateFontIndirectA字体修改方法

作者: cao_cong 来源:汉化新世纪 时间:2006-04-17 点击:17802

字体修改对汉化来说应该是个重点,今天我也来抛块砖头,介绍一种简单的 CreateFontA、CreateFontIndirectA 字体修改方法。采用这种方法主要是基于这两个函数返回的值都是字体的句柄,所以有通用性。开始之前我们再来回顾一下这两个 API 函数的定义:

HFONT CreateFont(

int nHeight, // logical height of font
int nWidth, // logical average character width
int nEscapement, // angle of escapement
int nOrientation, // base-line orientation angle
int fnWeight, // font weight
DWORD fdwItalic, // italic attribute flag
DWORD fdwUnderline, // underline attribute flag
DWORD fdwStrikeOut, // strikeout attribute flag
DWORD fdwCharSet, // character set identifier
DWORD fdwOutputPrecision, // output precision
DWORD fdwClipPrecision, // clipping precision
DWORD fdwQuality, // output quality
DWORD fdwPitchAndFamily, // pitch and family
LPCTSTR lpszFace // pointer to typeface name string
);

CreateFont的返回值就是字体的句柄。

HFONT CreateFontIndirect(
CONST LOGFONT *lplf // pointer to logical font structure
);
CreateFontIndirect的返回值也是字体的句柄。

既然两个返回的都是字体的句柄,我的思路就是用 CreateFontIndirectA 来统一这两个函数调用,简化我们的字体修改。说到这可能大家还是不太明白,我再说一下详细的做法:我不管你程序调用的是 CreateFontA 还是 CreateFontIndirectA 来设置字体,我只自己在程序中找个空白的地方建一个标准的9号宋体的 LOGFONT 结构,再用 CreateFontIndirectA 来调用它,这时输出的字体肯定是标准的9号宋体。但这只是我自己建的一个东西,怎么使程序在运行中调用我建好的字体呢?答案是我把我建好的这个标准字体调用过程作为一个子程序,我只要把原来程序中调用 CreateFontA 或 CreateFontIndirectA 这两个函数的地方统统改成调用我的子程序,就一切OK了。还是举个实际的例子比较好。正愁找不到好的例子时,想起雅枫以前写了个给大家练手的靶子程序,里面各种字体调用都使用了。呵呵,就用这个程序来给大家解释吧。开始之前我们先看一下程序原来的字体:

开始修改程序前的第一步我们先要看看程序中有没有 CreateFontIndirectA 字体的调用,如果有的话那非常好,没有的话我们就要自己添加一个 CreateFontIndirectA 函数的调用。关于怎么用 LordPE 给程序添加输入函数,我在我的 OllyDBG 入门7中已经详细说明了,这里我就不再罗嗦了,大家可以自行参考。输入表中有了 CreateFontIndirectA 函数就好办了。雅枫这个靶子程序里面已经有了,不用我们操心,这里的第一步可以通过了。

第二步我们要在程序中找个空白地方写我们的标准9号宋体的 LOGFONT 结构及调用代码,这里我还是使用 PEiD 的查找全0处的功能,如何查找我也在我的 OllyDBG 入门7中详细说明了,这里略过。只看一下我们得到的结果:

一般我们最好能用第一个 .text 段,因为这里是代码段,写在这里比较好一点。如果空间不够的话再考虑别的区段。通过上面的RVA我们知道要写数据的虚拟地址(VA)是004128CE(基址00400000+RVA),文件中的偏移是000128CE。标准9号宋体的 LOGFONT 是

F4FFFFFF000000000000000000000000900100000000008600000000CBCECCE5

这不需要我多说,很多讲修改字体的文章都提过了。我们把上面的标准字体结构复制下来,现在用一个16进制工具打开我们要修改的程序,我这里用 WinHEX。在 WinHEX 中我们按 ALT+G 转到偏移 128CE 处,右键选择如下操作(或按 CTR+B 组合键):

选择好菜单后会有一个对话框,点确定,因为我们这里复制的是16进制数据,把剪贴板格式选成如下图中的格式后点确定:

改好后保存(忘了说一句,改之前别忘了备份啊)。到这第二步就完成了。

第三步我们要看一下 CreateFontIndirectA 的 ThunkRVA 值,以便我们调用。这个我在我前面的的文章中说过了,详细操作略过,只是大家在用 LordPE 查看的时候注意把“总是查看 FirstThunk”的选项选上:

从上面我们可以知道 CreateFontIndirectA 的 ThunkRVA 是 00013024,虚拟地址(VA)就是 00413024。至此第三步完成。

第四步我们用 OllyDBG 载入程序,使用我在 OllyDBG 入门系列3中说的函数参考功能把 CreateFontA 和 CreateFontIndirectA 统统设上断点(如果函数参考在有些程序中设置无效的话,可以在命令行中设置,如命令行中输入 bp CreateFontA 后回车,中断的话一般是在系统 DLL 中,可以按 ALT+F9 返回),再按 CTR+G 组合键来到 004128CE(就是我们写标准9号宋体 LOGFONT 结构的地方),把代码往下翻一翻,我在我们建的 LOGFONT 结构后面空四个字节再来写我们的补丁代码:

  004128F2      68 CE284100     PUSH hanziex.004128CE                             ;   LOGFONT 结构入栈
  004128F7      FF15 24304100   CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>]  ;  GDI32.CreateFontIndirectA
  004128FD      C2 0400         RETN 4                                            ;  修改堆栈指针
 
注意上面显示的 CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>] 我们实际在 OllyDBG 中输入汇编代码时应该这样: CALL DWORD PTR [00413024]。原因我在我 OllyDBG 入门7中说过。还有个问题这里为什么不是 RET(在 OllyDBG 中写代码时是 RET,OllyDBG 显示的是 RETN,表示是个近返回,可以设置让 OllyDBG 只显示RET)而是 RETN 4 我也解释一下,因为看上面的代码我们压入堆栈一个 LOGFONT 结构,调用完后并没有恢复堆栈。这里主要是考虑到 CreateFontIndirectA 的返回值可能别的地方要用,而返回值保存在 EAX 中,我们不能随便恢复。但这样又会导致堆栈不平衡,所以我们用 RETN 4 来修改堆栈指针,加 4 个字节,保持堆栈平衡。现在我们的子程序完成了,记住上面代码的开始地址 004128F2,这就是我们子程序的调用入口。

现在是第五步了,在 OllyDBG 中按 F9 让程序运行,点击靶子程序的菜单汉化任务->任务5:字体修改,被 OllyDBG 拦下:

  00401417      FF15 2C304100   CALL DWORD PTR DS:[<&GDI32.CreateFontA>]          ;  GDI32.CreateFontA

在这条反汇编指令上双击,或按空格键,输入汇编代码为 CALL  004128F2,按确定,继续在 OllyDBG 中按 F9 让程序运行找别的,来到这里:

  004014B9      FF15 24304100   CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>]  ;  GDI32.CreateFontIndirectA

同样,把这里改成 CALL  004128F2,现在 CreateFontA 和 CreateFontIndirectA 的字体修改都完成了。在 OllyDBG 的反汇编窗口中点右键,选菜单复制到可执行文件->所有修改,用另外一个名称保存一下,如 1.exe。现在来运行一下,呵呵,一切 OK!顺便也把那几个 GetStockObject 的字体也修改一下吧。同样在函数参考中把 GetStockObject 全部设上断点,F9 运行,拦下:

  00401520   .  8B35 1C304100   MOV ESI,DWORD PTR DS:[<&GDI32.GetStockObject>]    ;  GDI32.GetStockObject
  00401526      6A 0C           PUSH 0C
  00401528   .  C645 FC 04      MOV BYTE PTR SS:[EBP-4],4                         ; |
  0040152C   .  FFD6            CALL ESI                                          ; \GetStockObject
 
GetStockObject 改起来简单,其函数原型是这样:

HGDIOBJ GetStockObject(
int fnObject // type of stock object
);

stock object 的类型中与字体相关主要有这几个:

#define OEM_FIXED_FONT       10    (16进制A)
#define ANSI_FIXED_FONT      11    (16进制B)
#define ANSI_VAR_FONT        12    (16进制C)
#define SYSTEM_FONT          13    (16进制D)
#define DEVICE_DEFAULT_FONT  14    (16进制E)
#define DEFAULT_PALETTE      15    (16进制F)
#define SYSTEM_FIXED_FONT    16    (16进制10)

#if(WINVER >= 0x0400)
#define DEFAULT_GUI_FONT     17    (16进制11)
#endif /* WINVER >= 0x0400 */

各个字体对应的16进制值我都列出来了。我们一般只需把 fnObject 参数改成 DEFAULT_GUI_FONT (即16进制11,系统的默认界面字体)就可以了。这个函数调用的时候一般是这样:

  PUSH fnObject
  CALL GetStockObject

雅枫这里玩了点小花样。我们只要把地址 00401526 处的那句 PUSH 0C 改成 PUSH 11 就可以了。现在继续改下一个,不要在 OllyDBG 中按 F9,否则你就上当了。因为雅枫这个程序把 GetStockObject 这个函数已经送到了 ESI 中,他是通过调用 ESI 来调用 GetStockObject 的,你再按 F9,就拦不下来了。现在把代码往下翻一翻,附近还有两个地方有 CALL ESI 的:

第一处:

  00401564      6A 0D           PUSH 0D
  00401566   .  FFD6            CALL ESI

第二处:

  0040159B   .  6A 09           PUSH 9
  0040159D   .  FFD6            CALL ESI

把上面的两个 PUSH XX 统统改成 PUSH 11,保存修改就可以把 GetStockObject 的字体统统改过来了。全部修改好后的效果如下图:

说到这里如果结束的话,大家肯定还会觉得这样没简单多少。我们来总结一下,看看会不会简单一点:

先看一下我们添加的字体块和调用的16进制代码:

F4FFFFFF000000000000000000000000900100000000008600000000CBCECCE50000000068CE284100FF1524304100C204

有没有看出点门道?我再修改一下大家再看看:

F4FFFFFF000000000000000000000000900100000000008600000000CBCECCE50000000068XXXXXXXXFF15XXXXXXXXC204

以上两个我标红色的地方就是我们字体块开始的偏移地址 004128CE 和 CreateFontIndirectA 函数 VA 地址 00413024 的逆序。关于为什么会这样我也不再罗嗦,我在 OllyDBG 入门系列4中说过了。现在我们就方便了,改CreateFontA 及 CreateFontIndirectA 字体的流程就变成这样:

一、先看看程序中有没有 CreateFontIndirectA 函数,没有加一个。

二、在程序中找个空白的地方,先把上面的这段16进制代码

F4FFFFFF000000000000000000000000900100000000008600000000CBCECCE50000000068CE284100FF1524304100C204

复制进去。

三、根据我们写代码的位置及我们在 LordPE 中看到的 CreateFontIndirectA ThunkRVA 值,修改 CE284100 24304100(注意写入时是逆序的),记住 68 这个字节的偏移,转换为虚拟地址(VA)。

四、启动 OllyDBG,载入我们修改过的程序,在 CreateFontA 和 CreateFontIndirectA 函数上都设上断点,断下来后对需要修改字体的地方统统改为 CALL XXXXXXXX(这里的 XXXXXXXX 就是上面那个 68 的虚拟地址)后保存。

五、运行改过字体的程序测试,有问题再检查,直到正常为止。

六、呵呵,没了。改这个程序字体要不到3分钟,可写这篇文章花了我将近5个小时。只是希望能给大家一点帮助,如有错误,欢迎指正!

【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!

汉化新世纪 责任编辑: cao_cong .:|:. 标签(Tag): CreateFontA CreateFontIndirectA 字体修改

·上一篇: 脱壳程序资源重建后的再减肥 ·下一篇: Jres 3.0 最新版下载,快抢快下哈

· 版权申明: 本文引自《汉化新世纪》,如有版权疑问请及时联系本站,以便本站处理。

· 转载申明: 本文引自《汉化新世纪》[ 作者: cao_cong],如需转载请直接联系原始作者,并请注明原始出处。

相关文章                                                                                发表评论 打印此文 关闭窗口

| 设为首页 | 加入收藏 | 联系我们 | 友情链接
Creative Commons License,创作共用协议(中文版)  署名 非商业性使用 禁止演绎
本站内容,除转载或版权特别申明的内容外,皆遵守 创造共用协议中文版之“署名-非商业性使用-禁止演绎 2.5 中国大陆”条款
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 China License.
本网站内容源自汉化新世纪论坛的摘录和汉化新世纪成员的原创文章。
凡汉化新世纪论坛的文字皆默认为汉化新世纪与原作者共同拥有并授权发布。
如对本站发布文章有所异议请来信告知,我们将及时删除。
凡商业摘录本站文字请先与我们联系,本站将保留非授权商业发布的追究权利。
凡非商业摘录本站文字请明显注明出处和原作者,并不得改动,凡改动必先征求原作者同意。
苏ICP备05002283号