该程序使用 PharLap DOS 扩展程序,如其 MZ 标头中所示。32 位可执行程序从 offset 开始18A0
,根据“重定位表标头内的偏移量”(参见http://www.program-transformation.org/Transform/PcExeFormat),在该位置您可以看到正确的签名P3
。根据头信息,可执行文件的长度是0x95851
,这是另一个提示这是正确的。在这部分的末尾,从 开始18A0
,您可以看到一个文本字符串“Hello EA”,在接下来的 32 字节“页面”中MZ
,表明嵌入了另一个可执行文件的签名。所以这大部分必须包含主要的可执行文件。
使用简单的十六进制编辑器以我喜欢的 16 个十六进制字符的宽度浏览文件时,我注意到在执行向下翻页时重复出现的模式(这是一种“了解”文件包含的数据类型的好方法)。我看到图案每 2 行重复一次,当我将显示宽度设置为 32 时,图案很明显。可执行格式总是以一个固定的头开始,通常后面跟着很多用于填充的零,所以我怀疑重复模式可能是 XOR 键。一个简单的 C 程序证实了这一点;我不知道从哪里开始解码,但 32 的第一个非全零倍数似乎是一个很好的猜测: offset 0x1AA0
。
从那里解码证明预感是正确的:
00000 : Y...r9..n3.>..-.A@I.7P........h4..a"1.P(s.......x. rG..f...X.+..
00040 : ..a|D.P(.b..A...x......f3..F..h4....a.P(...........o7..f3..F2...
00080 : .@@@@@@...@@BLASTER=@ULTRASND=@GOLD=@mvsound.sys@DEVICEdevice@@@
000C0 : @@@@@@@@@@@.......ULTRAMID@@@@@@@@@@@@@@@ ..@.@@@@@@.........@.@
00100 : .@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@@.@@@@..@@...@@@..@@......&...
00140 : ./....8....C....N....X...@c.@@ m....y...................C.......
00180 : .PCSPKR.ADV@MT32MPU.ADV@ADLIB.ADV@ADLIBG.ADV@SBFM.ADV@SBFM.ADV@S
001C0 : BP1FM.ADV@SBP2FM.ADV@PASFM.ADV@PASOPL.ADV@TANDY.ADV@GF1MIDI.ADV@
00200 : CUST_MID.ADV@SBP2FM.ADV@SBAWE32.ADV@@@@ALGDIG.ADV@SBDIG.ADV@SBDI
00240 : G.ADV@SBPDIG.ADV@SBPDIG.ADV@PASDIG.ADV@PASDIG.ADV@@GF1DIGI.ADV@C
(etc.)
所以下一步是向下滚动到这部分的末尾,看看那里有什么。灾难!我看到的不是可读文本,而是随机数据——但仍然具有清晰的模式。
但是“可执行文件”不是一个连续的长数据块。通常看到它被分为“可执行代码”、“初始化数据”、“未初始化数据”、“重定位”等单独的部分。当加载到内存中时,这些部分都从对齐的地址开始,但不一定在文件本身中,或者具有相同的“内存页面”大小。因此,XOR 加密可能会在新部分的开头重新开始。PharLap 标头应包含有关每个部分开始和结束位置的信息(如果您要尝试调整程序,则应查看此内容),但要确认 XOR 键是否相同,我只需调整起始位置。进一步开始一个位置,没有成功,
890C0 : B.@.L.@^W.@.a.@^l.@.v.@^..@...@ @ @ @ @ @ @ @~FIFA International
89100 : Soccer@ @PC Version by@~The Creative Assembly@ @~Lead Programme
89140 : r@ @Tim Ansell@ @ @ @~Programmers@ @Adrian Panton@Clive Gratton@
89180 : @ @~Lead Artist@ @Will Hallsworth@ @ @ @~Additional Artwork@ @A
891C0 : lan Ansell@ @ @ @~Original Music@Composed, Produced@and Performe
89200 : d by@Ray Deefholts@for ~HFC Music@ @Additional Drum@Programming
89240 : and@Assistance@ @Tim Ansell@ @~Sound Effects@ @Bill Lusty@ @ @ @
89280 : ~Producer@ @Kevin Buckner@ @ @ @~Associate Producer@ @Nick Golds
(etc.)
这就是我需要的证据:数据部分确实使用了相同的 XOR 密钥。下一步:测试从 0 到 31 的所有可能性,看看是否会出现一些情况。只有在 +30 结果才奏效,就像我要放弃一样:
782C0 : ..@...@,..@..Algeria@Ali Mehdaoui Igail@Mohammed Said@Abdel Dahb
78300 : i@Hamid Ahkmar@Nagar Baltuni@Omar Mahjabi@Ali Cherif@Hamar Mahbo
78340 : ud@Khered Adjali@Imahd Tasfarouk@Alamar Sahid@Mahmar Ahboud@Akha
78380 : r Binnet@Mouhrad Dahlib@Mahied Amruk@Lakhar Diziri@Amaar Azir@Mu
783C0 : stafa Farai@Akmar Bahoud@Ahmad Said@Taraki Aziz@Argentina@Alfio
(etc.)
因此,可执行文件中的每个单独部分都使用 32 字节的 XOR 密钥进行加密;这个 XOR 键对于所有部分都是相同的;它从每个部分开始一个新的。
下面的 C 程序将解密整个文件,您必须手动调整起始位置。要编辑文件,您必须:
- 阅读 PharLap 的部分。
- 单独解密每个部分。
- 全部写入一个新文件。
- 调整你想要的。
- 再次加密这些部分(它是一个 XOR 密钥,所以它使用完全相同的算法)。
- 将加密文件复制回主可执行文件。
关于#4 的注释:你提到改变球员的名字。由于它的名字以零结尾的列表,你可以假设有指针的列表,以这些名称在其他地方。这意味着您只能更改名称的单个字符——而不能使其更长。如果您想自由调整所有名称,则必须找到指针列表并进行调整。
(初步更新)
XOR 编码不使用节。相反,似乎每个块都以一个确定其长度的单词开头,可能还有 1 或 2 个下一个单词(可能(再次)设置 XOR 键的起始位置)。目前还没有定论。
可执行文件有很多零。如果您计算每个 32 字节块中零的数量,对所有 32 个可能的位置进行异或,并打印出具有最高数量的异或位置,您可以看到相同“最佳”猜测的连续列表。这表明有更长和更短的部分与相同的密钥异或,可能有助于确定长度算法。
#include <stdio.h>
#include <stdlib.h>
unsigned char encrypt[32] = {
0x23, 0x91, 0xC8, 0xE4, 0x72, 0x39, 0x9C, 0xCE,
0x67, 0x33, 0x99, 0xCC, 0xE6, 0x73, 0xB9, 0x5C,
0x2E, 0x17, 0x8B, 0x45, 0xA2, 0x51, 0xA8, 0x54,
0x2A, 0x95, 0xCA, 0x65, 0x32, 0x19, 0x8C, 0x46
};
int main(int argc, char *argv[])
{
FILE *f;
int i, c, d = 0;
f = fopen ("../Downloads/fifa/fifa.exe", "rb");
if (!f)
{
printf ("yeah no such file\n");
return 0;
}
/* reasonable assumption for start: */
fseek (f, 0x1aa0, SEEK_SET);
/* adjust per section! this position is valid for the names only */
fseek (f, 30, SEEK_CUR);
c = 0;
printf ("%05X : ", d);
do
{
d++;
i = fgetc (f);
if (i == EOF) break;
i ^= encrypt[c & 31];
if (i >= ' ' && i <= '~') putchar (i); else if (i) putchar ('.'); else putchar ('@');
if (++c >= 64)
{
c = 0;
printf ("\n");
printf ("%05X : ", d);
}
} while (d < 0x95851);
fclose (f);
return 0;
}