确定特定进程是32位还是64位


14

给定2.6.x或更高版本的Linux内核以及既可以运行ELF32也可以运行ELF64二进制文件的现有用户区(即,过去如何知道我的CPU在Linux下支持64位操作系统?)如何确定给定进程(通过PID)是在32位还是64位模式下运行?

天真的解决方案是运行:

file -L /proc/pid/exe | grep -o 'ELF ..-bit [LM]SB'

但是,这些信息是否在/proc不依赖的情况下直接公开了libmagic

Answers:


21

如果你想限制自己ELF检测,就可以读取ELF头/proc/$PID/exe自己。这非常琐碎:如果文件中的第5个字节为1,则为32位二进制文​​件。如果为2,则为64位。对于进一步的健全性检查:

  1. 如果前5个字节是0x7f, "ELF", 1:这是32位ELF二进制文件。
  2. 如果前5个字节是0x7f, "ELF", 2:这是64位ELF二进制文件。
  3. 否则:没有定论。

您也可以使用objdump,但这会消除您的libmagic依赖关系并将其替换为libelf一个。

另一种方式:您也可以解析/proc/$PID/auxv文件。根据proc(5)

它包含在执行时传递给进程的ELF解释器信息的内容。格式是一个无符号长ID,再加上每个条目一个无符号长值。最后一个条目包含两个零。

unsigned long键的含义在中/usr/include/linux/auxvec.h。你要的AT_PLATFORM0x00000f。不要在上面引用我,但是似乎应该将值解释为a char *以获得平台的字符串描述。

您可能会发现此StackOverflow问题很有用。

另一种方式:您可以指示动态链接器(man ld)转储有关可执行文件的信息。它打印出解码后的AUXV结构到标准输出。警告:这是一个hack,但可以。

LD_SHOW_AUXV=1 ldd /proc/$SOME_PID/exe | grep AT_PLATFORM | tail -1

这将显示如下内容:

AT_PLATFORM:     x86_64

我在32位二进制文​​件上尝试过它,却得到了i686

工作原理:LD_SHOW_AUXV=1指示动态链接器在运行可执行文件之前转储解码的AUXV结构。除非您真的想让自己的生活变得有趣,否则您要避免实际运行所述可执行文件。在不实际调用其main()功能的情况下加载并动态链接它的一种方法是ldd(1)在其上运行。缺点:LD_SHOW_AUXV由外壳程序启用,因此您将获得以下内容的AUXV结构的转储:子外壳程序ldd和目标二进制文件。因此我们grep使用AT_PLATFORM,但仅保留最后一行。

解析auxv:如果您auxv自己解析结构(不依赖于动态加载器),则有一个难题:auxv结构遵循其描述的处理规则,因此sizeof(unsigned long)对于32位进程将为4,对于64将为8位进程。我们可以为我们完成这项工作。为了使此功能在32位系统上起作用,所有键码必须等于0xffffffff或小于此值。在64位系统上,最高有效的32位将为零。英特尔机器的字节序很少,因此这32位紧随内存中的最低有效位。

因此,您需要做的是:

1. Read 16 bytes from the `auxv` file.
2. Is this the end of the file?
3.     Then it's a 64-bit process.
4.     Done.
5. Is buf[4], buf[5], buf[6] or buf[7] non-zero?
6.     Then it's a 32-bit process.
7.     Done.
8. Go to 1.

解析地图文件:这是Gilles提出的,但效果不佳。这是一个修改后的版本。它依赖于读取/proc/$PID/maps文件。如果文件列出了64位地址,则过程为64位。否则为32位。问题在于,内核将通过从4个组的十六进制地址中删除前导零来简化输出,因此长度hack不能完全起作用。awk进行救援:

if ! [ -e /proc/$pid/maps ]; then
    echo "No such process"
else
    case $(awk </proc/$pid/maps -- 'END { print substr($1, 0, 9); }') in
    *-) echo "32 bit process";;
    *[0-9A-Fa-f]) echo "64 bit process";;
    *) echo "Insufficient permissions.";;
    esac
 fi

这通过检查进程的最后一个内存映射的起始地址来工作。它们被列为12345678-deadbeef。因此,如果进程是32位的,那么该地址将是8个十六进制数字长,而第九个将是连字符。如果是64位,则最高地址将比该地址更长。第九个字符将是一个十六进制数字。

请注意:除了第一个和最后一个方法外,所有其他方法都需要Linux内核2.6.0或更高版本,因为该auxv文件以前没有。


1
嗯,我想知道ELF标头是否位于/proc/[pid]/auxv“在执行时传递给进程的ELF解释器信息。格式是一个无符号长ID加上每个条目一个无符号长值”man proc)。
goldilocks 2013年

1
标头本身不是。我hd只吃了一个,但它没有魔幻数字。那里可能有一些相关的信息,但是我认为它比ELF标头本身要更频繁地更改。它也是在2.6.0中引入的,因此它并不像普遍存在/proc/PID/exe。但是它确实具有体系结构信息。我将更新我的答案。
Alexios 2013年

auxv确实比我希望的要复杂- sizeof(unsigned long)在64位上是8位还是在32位上是4位,这意味着要正确地直接解释它,您必须知道过程是64位还是32位!
Flexo

你是绝对正确的。真烦人。快速启发式:如果文件中16x + y字节(4≤y≤7)全为零,则说明您正在查看64位可执行文件。这是一个错误:我假设使用一台小的endian机器,并且所有auxv键代码都适合32位unsigned long,因此64位设备上的最高有效32位将为零。
Alexios

6

看看/proc/$pid/maps。地址范围超过32位地址(8个十六进制数字)或64位地址(16个十六进制数字)。无论哪种格式,它都适用于任何类型的可执行文件。您只能获取有关以同一用户身份运行的进程的信息(除非您是root用户)。

if ! [ -e /proc/$pid/maps ]; then
  echo No such process
elif grep -q '^........[^-]' /proc/$pid/maps; then
  echo 64-bit
elif grep -q . /proc/$pid/maps; then
  echo 32-bit
else
  echo Insufficient permissions
fi

如果您没有访问此文件的权限,那么我认为唯一的方法是尝试分析可执行文件。(尽管您始终可以阅读/proc/$pid/stat,但以不同用户身份运行的进程所显示的任何字段都不会揭示该进程的位大小。)您可以使用来很好地猜测该进程的可执行文件ps -o comm=,并在中查找该可执行文件,PATH但请注意,该进程可能已经启动了一个不同的版本PATH,或者可能已经重写了它argv[0]。然后,您可以分析可执行文件—如果您愿意接受ELF,请查看第5个字节


我已经测试了您的食谱,但失败了。OpenSuSE 12.2,x86-64,内核3.4.63-2.44-default,/ bin / bash。二进制文件和第一个堆的/ proc / $ pid / maps行以32位样式编写,而其他所有行均以64位样式编写。它们可能使用“%08x”打印,但是无论如何都要调整此配方。
Netch

我在所有尝试过的盒子上混合了8、12和16个字节的值。在不检查源代码的情况下,我的猜测是内核将填充调整为16位的最低倍数,该最低位比打印的每一行的地址范围大,因此您必须找到最长的十六进制字符序列,然后进行检查。
Alexios

但是,由于vsyscall地图始终是最高的,因此您只需更改head为即可tail-遗憾的是,由于proc无法实现seek(2),因此将无法正常工作,因此它必须更丑陋,例如awk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alexios

@Netch确实,我愚蠢地看了看vsyscall和堆栈行,却没有注意可执行文件的映射。谢谢,我已更新以查找任何非32位行。可惜,它很丑陋,但这是最可靠的(至少在x86上肯定有效,我还没有检查过sparc和arm等其他双重体系结构)。
吉尔(Gilles)“所以,别再邪恶了”
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.