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

CreateFontIndirectA 字体修改一例

作者: 风中决斗 来源:汉化新世纪 时间:2006-08-16 点击:14409

CreateFontIndirectA 字体修改一例
另一种非标字体修改方法

网友“只是过客”提供的一个小工具中 CreateFontIndirectA 函数的字体调用,根据 cao cong 大侠的文章来测试后,方法的确很好用,感谢 cao cong 兄的文章,我也来一篇根据 梁利峰 大侠提供的一段字体块代码以及说明来修改,提供一个稍微复杂点的 CreateFontA 和 CreateFontIndirectA 函数非标字体修改法吧。本例就用该网友“只是过客”的目标程序 EPProt.exe 来做一下示例和说明吧。

感谢梁利峰(以下称llf)大侠提供了一段 CreateFontA 和 CreateFontIndirectA 函数字体块的代码,可以用来自行修改时让软件调用这两个非标字体,16 进制数据如下:
(由于数据较长,影响页面浏览宽度布局,保存于文本中,请下载后使用 16 进制工具打开复制即可)
CFA_CFIA_Hex_data.zip

这段数据包含 CreateFontA 和 CreateFontIndirectA 两种字体函数以及常规字型,但这段数据是 llf 大侠默认添加于文件的 00000300 地址处,所以其中一些需要修改,先把这段数据复制到目标程序中有大片 00 数据处,为了便于记忆,以本例为目标,用 WinHex 等工具添加到 0000A700 处,一会儿对应修改方便些。备份原文件后保存文件。
图1

现在用 Ollydbg 打开这个保存的目标程序,按 Ctrl + B 打开 二进制 查找窗口,输入 F4FFFFFF 确定后找到刚才添加的字体块数据代码段。
图2

现在按向下箭头往下拉,找到不远处的 CreateFontA 和 CreateFontIndirectA 函数的数据位置。
Ollydbg 中显示的代码可能会如下:
0040B53F    00E8            ADD AL,CH
0040B541    0000            ADD BYTE PTR DS:[EAX],AL
0040B543    0000            ADD BYTE PTR DS:[EAX],AL
0040B545    58              POP EAX
........
这样看上去不太直观,那么将光标移动到 0040B53F   00E8 这一行,然后按 Ctrl+Alt+向上或向下箭头按键,即可正确显示为:
0040B540    E8 00000000     CALL EPProt.0040B545
0040B545    58              POP EAX
........

那么再看这段 CreateFontIndirectA 函数的代码,然后根据 llf 大侠这段数据所对应默认 0x300 位置的说明,我们现在开始修改这段代码中的对应地址:
首先注意:RVA 地址=虚拟地址 VA - ImageBase,二进制修改时得以倒序方式写入。
即 llf 这段代码的 00400300 - 00400000 = 00000300,倒序就是 00030000
本例上面的 Call RVA 地址就是 0040B540 - 00400000 = 0000B540,倒序就是 40B50000
本例图2找到的字体块起始 RVA 地址就是 0040B500 - 00400000 = 0000B500,倒序就是 00B50000

0040B540    E8 00000000     CALL EPProt.0040B545        //*** CreateFontIndirectA 函数调用
0040B545    58              POP EAX
0040B546    2D 45030000     SUB EAX,345      //原本对应 00400345,改为上一行的RVA地址,Ctrl+E改为2D 45B50000 倒序,即对应这里的上一行 0040B545,下同
0040B54B    BB 00030000     MOV EBX,300      //原本对应 00400300,按图2找到字块开始地址 Ctrl+E 改为 BB 00B50000,即对应这里字体块起始处:0040B500
0040B550    03D8            ADD EBX,EAX
0040B552    895C24 04       MOV DWORD PTR SS:[ESP+4],EBX
0040B556    05 0C400000     ADD EAX,400C     //LordPE 中查看 CreateFontIndirectA 的 ThunkRVA 值,本例改为 05 B8E10000
0040B55B    FF20            JMP DWORD PTR DS:[EAX]
0040B55D    0000            ADD BYTE PTR DS:[EAX],AL
................................
再改 CreateFontA 函数代码的地址,再往下拉屏幕,看到这里:
0040B55F    00E8            ADD AL,CH                  //*** CreateFontA 函数调用,显示方法上同
0040B561    0000            ADD BYTE PTR DS:[EAX],AL
0040B563    0000            ADD BYTE PTR DS:[EAX],AL
0040B565    58              POP EAX
0040B566    2D 65030000     SUB EAX,365    //原本对应 00400365,改为上一行 RVA 地址,Ctrl+E 改为 2D 65B50000,即对应这里上一行 0040B565
0040B56B    5B              POP EBX
0040B56C    83C4 38         ADD ESP,38
0040B56F    33C9            XOR ECX,ECX
0040B571    51              PUSH ECX
0040B572    51              PUSH ECX
0040B573    51              PUSH ECX
0040B574    51              PUSH ECX
0040B575    51              PUSH ECX
0040B576    6A 86           PUSH -7A
0040B578    51              PUSH ECX
0040B579    51              PUSH ECX
0040B57A    51              PUSH ECX
0040B57B    68 90010000     PUSH 190
0040B580    51              PUSH ECX
0040B581    51              PUSH ECX
0040B582    51              PUSH ECX
0040B583    6A F4           PUSH -0C
0040B585    53              PUSH EBX
0040B586    05 0C400000     ADD EAX,400C   //LordPE 中 CreateFontA 的 ThunkRVA 值,本例没有,若有按上述方法修改
0040B58B    FF20            JMP DWORD PTR DS:[EAX]
0040B58D    0000            ADD BYTE PTR DS:[EAX],AL
0040B58F    0040 03         ADD BYTE PTR DS:[EAX+3],AL  //
0040B592    40              INC EAX                     //
0040B593    0060 03         ADD BYTE PTR DS:[EAX+3],AH  //
0040B596    40              INC EAX                     //
0040B597    0000            ADD BYTE PTR DS:[EAX],AL    //
以上 5 行,原本对应 llf 代码段的 00400340 和 00400360,00400300 与本例的 00400B500 对应,那么 00400340 和 00400360 对应本例的 00400B540 和 00400B560,选中上面 00400B58F 开始到 00400B597 这 5 行,Ctrl+E 修改:
00 40034000 60034000 00
改为
00 40B54000 60B54000 00

如上修改完后,按右键,菜单- Copy to executable(复制到可执行文件)-All modifily(所有修改),保存为 000.exe
如果不能保存 All modifily(所有修改),只有 Selection(选定区),那么从上到下,将您 Ollydbg 中修改过的红色数据开始,全部选中,然后按 Selection(选定区) 保存为 000.exe

现在用 Ollydbg 加载 000.exe,命令行下断:bp CreateFontIndirectA,回车后 F9 运行,中断后 F2 取消断点,再按 Alt+F9 返回程序领空,返回到 004046AA 后,往上找一行就是程序调用 CreateFontIndirectA 处:
004046A5    E8 42F5FFFF     CALL <JMP.&GDI32.CreateFontIndirectA> //找到这里
004046AA    5A              POP EDX                     //*** 返回到这里,向上找一行 ***

llf 大侠提到过:
“呼叫 CreateFont 类函数还可能使用相对偏移的“E8”方式,不过比较少见,如果遇到这种方式,则应把此呼叫如果是 Call CreateFontIndirect 的话,则改成 Call 00400340,如果是 Call CreateFont 的话,则改成 Call 00400360”

那么llf 这段代码中的 00400300 对应 本例中的 0040B500,则本例的 CreateFontIndirect 对应 00400340 的就是 0040B540,就是上面我们自行添加字体块代码中的 Call CreateFontIndirectA 地址,将上面中断返回后原程序调用 CreateFontIndirect 函数的 Call 就按 空格 键修改为 Call 0040B540

如果是 CreateFontA 函数,就是上面我们自行添加字体块代码中的
0040B55F 00E8 这行,E8 的 Call 地址就是 0040B55F的下一行,就改为 Call 0040B560
右键-复制到可执行文件,保存为 111.exe,测试运行,本例的 CreateFontIndirect 非标字体修改成功。
图3

这种方法比 cao cong 兄的方法麻烦一些,利用了 llf 大侠提供的字体块代码段来添加并修改两种非标字体,上面提到的修改几个 RVA 地址是在 0x300 处没有足够 00 空间添加字体块代码时,添加到其他 00 空间位置才修改 RVA,如果 0x300 处有足够 00 空间,就直接添加到该位置,不需要修改 RVA 地址,只要最后一步将程序调用的 Call CreateFontIndirectA 和 Call CreateFontA 修改为调用对应地址即可。

除了上面的这种“ E8”方式的 Call 外还有以下两种修改方式:

本例中最后原程序的 Call CreateFontIndirectA 如果是
004046A5    FF150C404000     Call dword ptr [0040400C]
则改为
004046A5    FF1590B54000     Call dword ptr [0040B590]
如果原程序是 Call CreateFontA 则改为 Call [0040B594]

如果原程序 Call CreateFontIndirectA 是
004046A5    8B3554F24800     mov esi, dword ptr [0048F254]
则 CreateFontIndirectA 在导入表中的 RVA 为 8F254h,把 0040B557 处开始的四个字节改成 54F20800,
就是修改 CreateFontIndirectA 的 ThunkRVA 值,然后修改上面代码为
004046A5    8B3590B54000     mov esi, dword ptr [0040B590]
即字节代码的 8B3554F24800 改为 8B3590B54000

如果要更改为大字体,则可以将字体块起始处 0040B500 的 F4 改为 F1 或更大字体;也可以将 CreateFontA 函数 0040B583 处的 6AF4 改为 6AF1 或更大字体。
如果需要更改为粗体字,可以将 0040B510 处的 9001 改为 BC02 即可,也可以更改 CreateFontA 函数字体 0040B57B 处的 6890010000 改为 68BC020000 即可。即,常规字体为 9001,粗体字为 BC02 即可。
图4

附上本文方法修改的本例程序压缩包,包含原版EPProt.exe/常规字体111.exe/粗体222.exe/以及四张截图:EPProt_CFIA_Font_Fix.rar
本文方法是将 llf 大侠的字体块代码自行添加并修改 RVA 地址一点笨方法,感谢 llf 大侠,不足之处请各位指教。

转载请保持文章完整性,并注明引用出处。


CxLrb
将老兄的教程整理一下。
A_CreateFontIndirectA_Font_modification_Sample.rar


只是个过客
CC 兄的教学确实很便捷,我先是在 WinHex 中贴上 LOGFONT 结构后,使用偏移量转换器填入 F4 处的实偏移得到虚偏移,将 68 后的地址改过,再用 LordPE 查看 CreateFontIndirectA 的 ThunkRVA 修改 FF15 后的地址,接着用梁大师的偏移量转换器填入 68 处的实偏移得出虚偏移,最后 OD 加载程序 bp CreateFontIndirectA 返回修改 Call 的地址。

刚刚练习决斗兄的教学,也是依样画葫芦,底下是在 WinHex 贴上梁大师的字体块代码,然后照决斗兄的教学做的修改:
红色框表示 -- 在偏移量转换器填入实偏移得出虚偏移。
蓝色框表示 -- 要修改的地方 (红色框获得的虚偏移 - 400000 倒序填入)。
绿色框表示 -- 两个字体函数的 ThunkRVA 值。
粉红线表示 -- 各处修改的相应地址。

唯两个 E8 不需减去 400000。
全部修改后保存再用 OD 加载 bp CreateFontIndirectA,修改 Call CreateFontIndirectA 地址为下图 A700 处 E8 的虚偏移 0040B500。
不知道这样会不会反而更复杂?

在此谢谢 CxLrb 兄的整理,方便极了,也谢谢决斗兄的教学,收获很大。
另外想请教决斗兄:
关于“ E8”方式的 Call 外那两种修改方式,我的水平太菜无法理解,可否请决斗兄再深入解说一下?谢谢你。


决斗
你的图示做的挺好,如果你测试成功了,那恭喜你,其实我这个方法也不是必须按照我的步骤做,如果不是贴在默认的 0x300 位置,只要快速修正几个 RVA 即可。smile.gif

另外两种方法,我这里是指:
如果原程序的 Call CreateFontIndirectA 不是 E8 XXXXXXXX 形式,而是 FF15 XXXXXXXX,那么就不能调用我们所添加字块代码位置 0040B540 处的 E8 XXXXXXXX,而是修改其为 FF15 90B54000,就是 0040B590 位置,因为这个位置的二进制值 40B54000 就是 0040B540 E8 Call CreateFontIndirectA 的地址倒序写入。
CreateFontA 的 FF15 XXXXXXXX 相同,让其调用 0040B594 的二进制值 60B54000,该值就是 0040B560 E8 Call CreateFontA 的地址倒序写入。
(FF15 形式的 call 和 E8 形式的 call 不同,不能直接调用,具体 llf 大侠也没有说明)

原程序如果是
mov esi, dword ptr [0048F254]
这类形式,这个 0048F254 在本例中则应该是如下:
mov esi, dword ptr [0040E1B8]
就是实际这里已告诉你 CreateFontIndirectA 的 ThunkRVA 值为 0040E1B8 - 400000 = 0000E1B8,那就直接修改为上面改 FF15 形式调用的相同位置, 就是 0040B590 处的 40B54000
那么将
mov esi, dword ptr [0040E1B8]
改为
mov esi, dword ptr [0040B590]
二进制就是
8B 35 B8E14000
改为
8B 35 90B54000

以上两个 FF15 形式的 Call 形式,修改其地址时直接按 空格 键进行汇编输入 Call 虚拟地址即可;MOV 形式如果进行二进制倒序写入的是 虚拟偏移地址(VA),就是比如 0040B590 不需要减去 400000,直接二进制写为 90B54000
两者二进制输入都是VA,不需要减 400000,与 E8 Call 不同。
其实,似乎我个人也很少碰到 mov ... 形式的字体调用,倒是 E8 和 FF15 的多一些。


汉化新世纪 责任编辑: 风中决斗 .:|:. 标签(Tag): CreateFontIndirectA CreateFontA

·上一篇: 汉化作品被杀毒软件报警声明及相关说明 ·下一篇: 在 Inno Setup 中实现倒数N秒后激活按钮

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

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

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

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