特定のプロセスが32ビットか64ビットかを判断する


14

2.6.x以降のLinuxカーネルと、ELF32バイナリとELF64バイナリの両方を実行できる既存のユーザーランド(つまり、LinuxでCPUが64ビットオペレーティングシステムをサポートしていることをどのように確認できますか?) PIDによる)32ビットモードまたは64ビットモードで実行されていますか?

素朴な解決策は、実行することです:

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

しかし、その情報は/proc依存せずに直接公開されていlibmagicますか?

回答:


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インタープリター情報の内容が含まれます。形式は、1つの符号なしの長いIDと各エントリの1つの符号なしの長い値です。最後のエントリには2つのゼロが含まれています。

unsigned longキーの意味はにあります/usr/include/linux/auxvec.h。あなたが欲しいAT_PLATFORM、それは0x00000f。そのことを引用しないでくださいchar *が、プラットフォームの文字列の説明を取得するために、値はとして解釈されるべきであると思われます。

このStackOverflowの質問が役立つ場合があります。

さらに別の方法man ld実行可能ファイルに関する情報をダンプするように動的リンカー()に指示できます。デコードされたAUXV構造を標準出力に出力します。警告:これはハックですが、動作します。

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()関数を実際に呼び出さずにロードして動的にリンクする1つの方法は、実行ldd(1)することです。欠点LD_SHOW_AUXVはシェルによって有効になっているため、サブシェル、、lddおよびターゲットバイナリのAUXV構造のダンプを取得できます。私たちはそうgrepAT_PLATFORMための、唯一の最後の行を保持します。

auxvの解析:(auxvダイナミックローダーに依存せずに)自分で構造を解析する場合、少し難問があります:auxv構造はそれが記述するプロセスのルールに従うsizeof(unsigned long)ため、32ビットプロセスでは4、64 では8になりますビットプロセス。私たちはこの仕事をすることができます。これが32ビットシステムで機能するには、すべてのキーコードが0xffffffff以下である必要があります。64ビットシステムでは、最上位の32ビットはゼロになります。Intelマシンはリトルエンディアンなので、これらの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つのグループの16進アドレスから先行ゼロを取り除くことで出力を単純化するため、長さのハックがうまく機能しないことです。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桁の16進数であり、9番目はハイフンになります。64ビットの場合、最高アドレスはそれより長くなります。9番目の文字は16進数です。

注意してください:最初と最後のメソッド以外はすべて、Linuxカーネル2.6.0以降が必要auxvです。ファイルは以前は存在していなかったからです。


1
うーん、ELFヘッダがであるのだろうか/proc/[pid]/auxv「ELFインタプリタ情報は、実行時にプロセスに渡されたフォーマット一つのunsigned long IDプラス各エントリに対して1つの符号なしlong値です。」man proc)。
goldilocks

1
ヘッダー自体はそうではありません。私はちょうどhd1つを編集しましたが、マジック番号がありませんでした。そこには関連する情報があるかもしれませんが、ELFヘッダー自体よりも頻繁に変更される可能性があると思います。また、2.6.0で導入されたので、ほどユビキタスではありません/proc/PID/exe。しかし、アーキテクチャ情報持っています。回答を更新します。
アレクシオス

auxvは私が期待していたよりもトリッキーであることが判明しましsizeof(unsigned long)た-64ビットで8または32ビットで4です。つまり、直接解釈するにはプロセスが64ビットか32ビットかを知る必要があります!
フレキソ

あなたは絶対に正しい。それはかなり面倒です。クイックヒューリスティック:ファイル内のバイト16x + y(4≤y≤7)がすべてゼロの場合、64ビットの実行可能ファイルを見ています。これはちょっとしたことです。リトルエンディアンマシンを想定しており、すべてのauxvキーコードが32ビットに適合するunsigned longため、64ビットボックスで最も重要な32ビットはゼロになります。
アレクシオス

6

を見てください/proc/$pid/maps。アドレス範囲は、32ビットアドレス(8桁の16進数)または64ビットアドレス(16桁の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ビットの最下位倍数にパディングを調整するので、16進文字の最長シーケンスを見つけてからチェックする必要があると思います。
アレクシオス

以来しかし、vsyscallマップは常に最高で、あなただけの変更で逃げる可能性headtail-これは、悲しいことに、procが仕事を実装していないないのでseek(2)、それは同じように、何かの醜いする必要がありますので、awk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
アレクシオス

@Netch実際、私は愚かにもvsyscallとスタック行を見て、実行可能ファイルのマッピングに注意を払いませんでした。おかげで、32ビット以外の行を探すように更新しました。残念ですが、いですが、これは最も信頼性の高いものです(少なくともx86では確実に動作します。sparcやarmなどの他のデュアルアーキテクチャでは確認していません)。
ジル「SO-悪であるのをやめる」
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.