我目前正在进行一个项目,该项目依赖于检测可执行文件属于哪个操作系统。
我只在ELF可执行格式工作,所以我试图使用e_ident[EI_OSABI]
值,但不给予健康的结果。
- Linux和OpenBSD可执行文件被视为sysV可执行文件
- 该值因编译器而异
此外,PT_INTERP
部分不适合作为解决方案,因为它不提供共享库(.so
)的信息,有时链接器的名称不包括内核的名称(例如:/lib/ld-musl-x86_64.so.1
。它不包含像/lib64/ld-linux-x86-64.so.2
这样的内核名称。)
我认为第三种方法可以找到只存在于一个内核上并且总是添加到可执行文件中的系统调用。
如果我们给予一个例子来使它可以理解:
- 只有Linux具有名为
foo
的系统调用,并且此系统调用始终用于每个Linux可执行文件和Linux共享库 - 只有FreeBSD有名为
bar
的系统调用,并且这个系统调用总是用于每个FreeBSD可执行文件和FreeBSD共享库 - 只有OpenBSD有名为
baz
的系统调用,并且这个系统调用总是在每个OpenBSD可执行文件和OpenBSD共享库中使用
如果我在可执行文件中找到foo
,bar
,baz
系统调用之一,我可以检测到它的操作系统。
我的问题是:
- BSD和Linux是否有这种系统调用?
- 它们在可执行文件中的位置是否固定。(例如
foo
始终存在于.dsym
部分)
1条答案
按热度按时间a5g8bdjr1#
不幸的是,系统调用没有命名,它们有编号。例如,在Linux x86-64上,要调用
read(2)
,您使用syscall 0,要调用write(2)
,您使用syscall 1。然而,在FreeBSD上,它们分别是系统调用3和4。通常有一个header,sys/syscall.h
,提供值。此外,您还可以高兴地看到,系统调用在不同的体系结构中可能会有所不同,至少在Linux上是这样,有些体系结构具有系统调用,而有些体系结构则没有。(它只能处理最多2^31-1字节的文件)已经过时了,没有用了,x86-64没有费心去实现这样的东西,
stat
系统调用在x86-64上总是64位的。此外,系统调用通常位于libc中,因为通常调用C函数,然后libc具有关于什么系统调用编号对应于什么的所有知识。某些操作系统,如Linux,允许用户从其二进制文件直接进行系统调用;然而,其他的,像OpenBSD,不这样做,如果你尝试,内核会谋杀你的进程。因此,在大多数情况下,可执行文件本身不包含任何实际的系统调用。
如果你的目标是检测二进制文件,你需要多管齐下的方法,因为没有一种方法是足够的。首先,当ELF头中的OS ABI不是SysV时,它通常是正确的。例如,这是检测FreeBSD的好方法。(但是,你也应该使用
PT_INTERP
,它也会提供合适的上下文。musl的ld.so在其名称中确实不包含linux
,但你知道如果它是musl,它就是Linux。如果是可执行文件,您可能还需要查看libc值。(例如,在FreeBSD上,它是非常有用的
/libexec/ld-elf.so.1
),但您可能能够将操作系统与其libc版本区分开来。某些系统具有版本符号,因此查看版本符号可能会有所帮助。许多系统还具有特殊的注解部分(例如,MirBSD有.note.miros.ident
)。如果二进制文件是静态的,你就不会有libc或
PT_INTERP
,所以你可能需要多看一些。静态Go二进制文件有一个.go.buildinfo
部分,其中包含一个GOOS=
值(例如,GOOS=linux
),你可以使用。然而,幸运的是,在 * 大多数 * 情况下,
file
可以通过在二进制文件上运行它来为您提供这些信息。然而,并不是在所有情况下(MirBSD是一个很好的例子),所以如果您想处理 * 所有 * ELF二进制文件,您真的必须退回到一些更复杂的spelunking。