给定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
?
给定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:
如果你想限制自己ELF检测,就可以读取ELF头的/proc/$PID/exe
自己。这非常琐碎:如果文件中的第5个字节为1,则为32位二进制文件。如果为2,则为64位。对于进一步的健全性检查:
0x7f, "ELF", 1
:这是32位ELF二进制文件。0x7f, "ELF", 2
:这是64位ELF二进制文件。您也可以使用objdump
,但这会消除您的libmagic
依赖关系并将其替换为libelf
一个。
另一种方式:您也可以解析/proc/$PID/auxv
文件。根据proc(5)
:
它包含在执行时传递给进程的ELF解释器信息的内容。格式是一个无符号长ID,再加上每个条目一个无符号长值。最后一个条目包含两个零。
unsigned long
键的含义在中/usr/include/linux/auxvec.h
。你要的AT_PLATFORM
是0x00000f
。不要在上面引用我,但是似乎应该将值解释为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
文件以前没有。
hd
只吃了一个,但它没有魔幻数字。那里可能有一些相关的信息,但是我认为它比ELF标头本身要更频繁地更改。它也是在2.6.0中引入的,因此它并不像普遍存在/proc/PID/exe
。但是它确实具有体系结构信息。我将更新我的答案。
sizeof(unsigned long)
在64位上是8位还是在32位上是4位,这意味着要正确地直接解释它,您必须知道过程是64位还是32位!
auxv
键代码都适合32位unsigned long
,因此64位设备上的最高有效32位将为零。
看看/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个字节。
vsyscall
地图始终是最高的,因此您只需更改head
为即可tail
-遗憾的是,由于proc无法实现seek(2)
,因此将无法正常工作,因此它必须更丑陋,例如awk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
/proc/[pid]/auxv
:“在执行时传递给进程的ELF解释器信息。格式是一个无符号长ID加上每个条目一个无符号长值”(man proc
)。