可执行文件中未使用的函数
对此有多种解释。
正如Itsbriany所说,混淆,但我认为这在旧的DOS可执行文件中并不是什么大事。
不需要的库函数。例如,如果您的代码链接到数学库(那些旧处理器通常没有用于浮点的数学协处理器),您通常会获得所有数学仿真。您的程序可能sqrt用于距离计算,但从不使用sin,cos和类似的三角函数,但它们以某种方式联系在一起。
函数指针。如果您的原始 C 程序有类似的内容, int (*funcs())[]={func1, func2, func3}; int result=(func[index])();
并且这些是对,和的唯一引用,那么 ida 将不会将它们识别为正在使用。但是,您会在其中找到数据外部参照。func1func2func3
C++ 对象方法,虽然当 C++ 开始被使用时,32 位处理器已经很普遍,您可以使用 dos 扩展程序将程序编译为 32 位二进制文件。如果方法从不直接调用,只能通过类的 vtable 调用,则与函数指针相同。
原始源代码中的调试函数永远不会在完成的二进制文件中调用。如果程序员做了类似的事情
#ifdef DEBUG
some_function(parm1, parm2, another_parm);
#endif
忘了把some_function进#ifdef为好,然后完成二进制文件将仍然有some_function,但它没有呼叫。
不止一名程序员在代码的独立部分工作。一个程序员可能已经被分配到的读/写一个配置文件中的任务,他执行read_config()和write_config()。后来有人决定游戏应该只用read_config(),配置部分应该在单独的设置程序中完成。如果两个函数都在同一个源文件中,它们将被编译成 - 并从 - 一个单一的目标文件中链接。
旧 dos 程序中的叠加
我不知道有什么好的教程,但这里有关于叠加层的基本信息:
您需要识别运行时不需要彼此的代码部分。例如,您的程序可能有一组函数来导航世界地图,一组不同的函数来导航地牢,还有第三组函数来与店主互动。
你会编译这些分开,所以你得到4个文件:main_game.exe,world.ovl,dungeon.ovl,和shopkeeper.ovl。(糟糕,文件名有 8 个字符的限制。)main_game.exe将被加载到低内存中,并计算要加载的覆盖层的一个段 - 全部在同一地址。最简单的方法是制作一个只有一个变量的段,它最后加载 - 该变量的地址将是加载覆盖的地址。让我们命名它overlay_start。
为了将主 .exe 连接到覆盖层,每个覆盖层在开始时都会有一个跳转表。例如,在 world.ovl 中:
0000 jmp show_world_map
0004 jmp draw_player_on_world_map
0008 jmp draw_enemies_on_world_map
....
在 dungeon.ovl 中:
0000 jmp show_dungeon_map
....
现在你需要一堆函数来链接到你的主程序来调用这些覆盖函数,它们看起来基本相同:
extern int (*overlay_start())[];
void show_world_map() { load_overlay("world.ovl"); overlay_start[0](); }
void draw_player_on_world_map() { load_overlay("world.ovl"); overlay_start[1](); }
void draw_enemies_on_world_map() { load_overlay("world.ovl"); overlay_start[2](); }
void show_dungeon_map() { load_overlay("dungeon.ovl"); overlay_start[0](); }
....
void load_overlay(char *filename) {
/* omitting all the error checking, loops for more than 64KB, etc.) */
FILE *fp=fopen(filename, "rb");
fread(overlay_start, 1, SOME_LARGE_NUMBER, fp);
fclose(fp);
}
(为了保持简洁,我在这个例子中省略了函数参数)。
根据您的编译器,其中大部分可以自动生成。这些功能,可能包括load_overlay功能,通常是进入该stub部分的内容。这只是一堆在链接时让链接器满意的函数,main_game.exe确保加载了正确的叠加层,调用预期的函数,然后返回。