识别一个软件项目的血统往往变得复杂,因为开发者经常重命名函数、重写逻辑,或者将软件移植到完全不同的编程语言中。传统的追踪这些血统的方法通常依赖于匹配源代码文本或函数名称。然而,一项针对原始 DOOM 源代码 43 个后代作品的研究表明,这些标签可能会产生误导。例如,“Chocolate Doom”保留了原始作品 87% 的函数名称,但这些函数内部仅有 35% 的实际代码保持不变。在其他情况下,如“Doom Retro”,名称被保留,但近 100% 的内部代码都被重写了。
为了克服人类可读文本的局限性,研究人员使用了一种完全忽略名称和注释的代码测量引擎。相反,它通过为每个函数记录数十项测量值来分析程序的“形状”,包括其长度、分支模式以及它调用哪些其他函数。通过识别那些测量值过于接近以至于不可能是巧合的“相似”函数,该引擎能够以 93% 的准确率识别出副本。这种结构指纹足够强大,足以将 DOOM 与无关的 C 程序(如 Quake 或 Quake II)区分开来,后者的形状与 DOOM 的重合度仅在 0% 到 0.6% 之间。

图像展示了程序“形状”或结构指纹的概念,这种指纹可以在不同的版本和移植版本中幸存。
当一个项目完全使用新语言(如 Go 或 Scheme)重写时,单个函数的形状可能会消失,但“布线”——即哪些游戏子系统(如渲染器或物理引擎)相互调用的映射——往往能够幸存。在一项隐藏所有名称的盲测中,Go 语言的翻译版本仍能将 60% 的函数正确分类到相应的游戏部分中,显著优于 20% 的随机猜测。然而,这种结构蓝图并非普遍存在。在 Java、Kotlin 和 C# 等语言中的移植版本没有显示出可检测的血统,因为它们将代码重新组织成微小的、碎片化的部分(通常是三行或更少),有效地抹除了原始设计的特征。这凸显了一个关键的权衡:虽然结构分析可以在大多数移植版本中追踪血统,但当架构“形状”在翻译过程中被根本拆解时,这种方法就会失效。