从二进制文件中确定源语言?

16

回答了另一个关于在非Objective-C语言中开发iPhone的问题,我声称使用例如C#编写iPhone应用程序会引起苹果审核员的反感。 我主要是在谈论ObjC和C#库之间的UI元素差异,但评论者提出了一个有趣的观点,引发了我对这个问题的思考:

仅从二进制文件中能否确定程序所编写的语言? 如果有这样的方法,它们是什么?

为了讨论方便,我们假设:

  • 从交互角度来看(控制台行为、任何GUI外观等),两者完全相同。
  • 性能不是语言的可靠指标(不能将Java与C进行比较)。
  • 你没有解释器或其他介于你和语言之间的东西 - 只有原始可执行二进制文件。

如果您尽可能地无关语言,请获得奖励分。

8个回答

16

简短回答:

长篇回答:

如果你查看一个二进制文件,你可以找到已链接的库的名称。在TextPad中打开cmd.exe,可以轻松地在十六进制偏移量0x270处找到以下内容:msvcrt.dll、KERNEL32.dll、NTDLL.DLL、USER32.dll等。msvcrt是Microsoft'C'运行时支持函数。KERNEL32、NTDLL和USER32.dll是特定于操作系统的库,它们告诉您目标平台或构建它的平台,这取决于跨平台开发环境如何分隔两者。

撇开这些线索,任何c/c++编译器都必须将函数名称插入二进制文件中,有一个存储所有函数(或入口点)的表格。C++ "mangles"函数名来编码参数及其类型以支持重载方法。可以混淆函数名,但它们仍然存在。函数签名包括参数的数量和类型,可用于跟踪程序中使用的系统或内部调用。在偏移量0x4190处是“SetThreadUILanguage”,可以搜索以了解很多关于开发环境的信息。我在偏移量0x1ED8A处找到了入口点表格。我可以轻松地看到像printf、exit和scanf这样的名称;还有__p__fmode、__p__commode和__initenv

x86处理器的任何可执行文件都将包含数据段,其中包含在程序中包括的任何静态文本。回到cmd.exe(偏移量0x42C8),文本“S.o.f.t.w.a.r.e..P.o.l.i.c.i.e.s..M.i.c.r.o.s.o.f.t..W.i.n.d.o.w.s..S.y.s.t.e.m.”。字符串需要两倍于通常必要的字符,因为它使用双宽字符存储,可能是为了国际化。错误代码或消息是主要来源。

在偏移量B1B0处,是"p.u.s.h.d",然后是mkdir、rmdir、chdir、md、rd和cd;出于可读性考虑,我忽略了不可打印字符。这些都是传递给cmd.exe的命令参数。

对于其他程序,有时我能够找到编译程序的路径。

因此,是的,可以从二进制文件中确定源语言。


2
这完全依赖于人们链接库。如果静态完成,或者函数被复制/粘贴到源代码中会发生什么?这是一个很好的提示(我给+1),但并不总是可靠的。 - Tim
即使可执行文件是静态链接的,入口点仍然存在。它们基于定义的函数,无论这些函数来自哪个对象模块或如何链接。在运行时加载的函数不会在入口点表中具有其名称,但必须在数据段中提到,因为运行时加载程序需要它。您对源代码的复制/粘贴部分是正确的,但前提是所有代码都在主函数中,并且没有链接任何库才能避免这种情况。 - Kelly S. French

10

我不是编译器黑客(希望有一天能成为),但我想您可能能在二进制文件中找到某些迹象,表明生成它的编译器和一些编译器选项,例如指定的优化级别。

严格来说,您所问的是不可能的。也许有人坐下来拿笔纸计算出他们想编写的程序对应的二进制代码,然后在十六进制编辑器中输入那些东西。基本上,他们在没有汇编工具的情况下进行汇编语言编程。同样地,您可能永远无法确定本机二进制文件是直接用汇编语言编写的还是使用内联汇编的C语言编写的。

至于像JVM和.NET这样的虚拟机环境,我认为您应该能够通过二进制可执行文件中的字节码来识别VM。但是,除非有特定的编译器怪癖提示您,否则您可能无法确定源语言是C#还是Visual Basic。


1
可以。请参见https://dev59.com/R3I-5IYBdhLWcg3wu7FU#1704449。 - Kelly S. French
2
在理论上,我觉得这是不可能的,但在实践中却是可以的。 :) - Parappa
如果是100%汇编,您可以从检查二进制代码中看出这一点。理论上,有人可以用FORTRAN编写程序,然后通过Fortran到C应用程序运行它以获取'C'源代码。当这个代码被编译时,可能没有任何迹象表明原始语言不是'C'。这引发了一个问题,即什么才算是“编写的语言”。也许问题可以更具体地这样提出,“您能告诉我们用来创建此二进制文件的语言是什么吗?”换句话说,就是将哪种语言翻译成了二进制代码。 - Kelly S. French

3

这些工具怎么样:

PE Detective

PEiD

它们都是PE标识工具。好的,它们都是用于Windows的,但这就是我来到这里的原因。


1

如果您反汇编源代码,或者至少知道编译器,我相信您可以做到这一点。例如,不是所有的编译器都会使用相同的printf代码,因此Objective-C和gnu C在这里应该有所不同。

您已经排除了所有字节码语言,因此这个问题将比预期的要少见。


1

首先,对一些二进制文件运行what并查看输出。CVS(和SVN)标识符散布在二进制图像中。而且其中大部分来自库。

此外,通常会有一个“映射”到各种库函数。这也是一个重要的提示。

当库链接到可执行文件中时,通常会在二进制文件中包含一个带有名称和偏移量的映射。这是创建“位置无关代码”的一部分。您不能简单地将各个对象文件“硬链接”在一起。您需要一个映射,并且在将二进制文件加载到内存时必须进行一些查找。

最后,C、C++(我想象中还有C#)的启动模块是该编译器默认库的独特之处。


如果你将所有可用的库静态链接在一起,会怎样呢? - James Black
@James Black:没有改变任何事情。.o文件只是与一些指令一起连接到可执行文件中,以告诉加载器如何在内存中填充材料。 - S.Lott

0

不,字节码是与语言无关的。不同的编译器甚至可以采取相同的代码源并生成不同的二进制文件。这就是为什么你看不到通用的反编译器可以用于二进制文件。


0

命令“strings”可用于获取某些提示,以了解使用了哪种语言(例如,我刚才运行了它用于一个我编写的 C 应用程序的去除符号的二进制文件上,它找到的第一个条目是可执行文件链接的库)。


-1

嗯,C语言最初是被转换成汇编语言的,所以你可以用汇编语言编写所有的C代码。


好的,虽然并非所有C编译器都是这种方式,但是你可以通过使用gcc-S选项生成汇编代码,因此我认为这并不需要降低投票。 - alternative
1
这是一条评论,而不是答案。但因为是好的评论,所以被点赞回到了零。 - Todd Main
@Todd Main 强烈反对。我的答案是“不行”,因为我已经提供了一个反例。一般来说,答案是“通常你可以知道”,但在严格意义上,只需要一个反例就可以得出结论:这是不可能的。 - alternative
@别无选择,只能说单个反例表明不可能是不完全有帮助的,除非问题是“您是否总是可以从二进制中检测到源语言”。更准确的答案是,“通常情况下是可以的,但并非总是如此,有时可能是不可能的”。说它是不可能的意味着它永远不可能,这是不正确的。抱歉,有时我会卡在词汇选择上,我并不完全不同意,只是程度上的区别。如果您找不到源代码的证据,您可以说它必须是100%的汇编代码或作者故意混淆。 - Kelly S. French

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接