LoginSignup
0
1

More than 1 year has passed since last update.

RPM ファイルのバイナリを読んで構造を知る

Last updated at Posted at 2021-06-12

概要

この記事は、以下を目標としています。

  • RPM ファイルには何が書かれているのかを調べます。バイナリエディタで *.rpm を開いて、どの辺りに何が書かれているのかを探ります
  • 簡単な Python スクリプトでヘッダをダンプしてみます
  • RPM パッケージの依存関係で provides name が使われているのを見ます

とりあえず、以下のダンプを見るだけで雰囲気はわかると思います。

参照したドキュメント

自分が見つけられた中では、Linux Standard Base の以下のドキュメントが一番参考になりました。

RPM 公式の以下のドキュメントは図が乗っていてわかりやすいのですが、書きかけで止まっている感じです。

古いドキュメントのようですが、上よりは以下のほうが詳しそうでした。

RPM ファイル形式の解説記事です。

使用するサンプルファイル

Remi リポジトリより、以下の rpm ファイルを例として使用します。

このパッケージを選んだのはたまたまです。たまたま、このパッケージの依存関係を調べていた時に、とったメモをベースにして、本記事を作成しました。

以下のコマンドでパッケージの情報が得られます。

$ rpm -qip php70-php-pecl-imagick-3.4.4-17.el7.remi.x86_64.rpm
Name        : php70-php-pecl-imagick
Version     : 3.4.4
Release     : 17.el7.remi
Architecture: x86_64
Install Date: (not installed)
Group       : Unspecified
Size        : 522499
License     : PHP
Signature   : DSA/SHA1, Mon 22 Feb 2021 06:07:07 PM JST, Key ID 004e6f4700f97f56
Source RPM  : php70-php-pecl-imagick-3.4.4-17.el7.remi.src.rpm
Build Date  : Mon 22 Feb 2021 06:06:05 PM JST
Build Host  : builder.remirepo.net
Relocations : (not relocatable)
Packager    : Remi Collet
Vendor      : Remi's RPM repository <https://rpms.remirepo.net/>
URL         : https://pecl.php.net/package/imagick
Bug URL     : https://forum.remirepo.net/
Summary     : Extension to create and modify images using ImageMagick
Description :
Imagick is a native php extension to create and modify images
using the ImageMagick API.

Package built for PHP 7.0 as Software Collection (php70 by remi).

4つのセクション

rpm ファイルは、4つのセクションに分かれています。

  1. Lead セクション - 基本情報
  2. Signature セクション - 署名
  3. Header セクション - パッケージの詳細情報
  4. Payload セクション - パッケージに含まれるファイル

2番めの Signature セクションと 3番目の Header セクションは、同じ形式 (Header Structure) で書かれています。

最初の Lead Section から見ていきます。

Lead Section - 基本情報

                     +---+---+---+---+---+---+---+---+---+---+
                     |M1 |M2 |M3 |M4 |MAJ|MIN| TYPE  | ARCH  | (more ->)
                     +---+---+---+---+---+---+---+---+---+---+
                     +~~~~~~~~~~~~~~~~~~+---+---+
                     | 66 bytes of NAME |OS |SIG| (more ->)
                     +~~~~~~~~~~~~~~~~~~+---+---+
                     +~~~~~~~~~~~~~~~~~~~~~~+
                     | 16 bytes of RESERVED |
                     +~~~~~~~~~~~~~~~~~~~~~~+

例:

00000000: edab eedb 0300 0000 0001 7068 7037 302d  ..........php70-
00000010: 7068 702d 7065 636c 2d69 6d61 6769 636b  php-pecl-imagick
00000020: 2d33 2e34 2e34 2d31 372e 656c 372e 7265  -3.4.4-17.el7.re
00000030: 6d69 0000 0000 0000 0000 0000 0000 0000  mi.............. 
00000040: 0000 0000 0000 0000 0000 0000 0001 0005  ................ # name ここまで + 0001 + 0005
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 
  • Magic Number ed ab ee db で始まる
  • Version 0300 - RPM ファイルフォーマットのバージョン 3.0
  • TYPE 0000 - バイナリが 0、ソースが 1
  • ARCH 0001 - x86 architecture は 1
  • NAME (66bytes): php70-php-pecl-imagick-3.4.4-17.el7.remi + 00*
  • OS 0001 - Linux は 1
  • SIG 0005 - signature type 5 (現在使われているのは 5)
  • RESERVED 00 * 16 バイト

OS と SIG は図だと 1 バイトのように書かれていますが、2バイトずつあります。

Signature セクション - 署名

この Signature セクションと、次の Header セクションは、両方とも Header Structure という形式で書かれています。

Header Structure 形式は、

  • ヘッダ - インデックスの個数とデータのサイズが書かれています
  • インデックス (* N個) - データストア内の位置、内容(タグ名)、形式、サイズが書かれています
  • データ (* N個) - インデックスから参照されるデータが格納されています

という3部構成になっています (ヘッダがいくつも出てきてわかりにくいですね……)。

Signature セクションのヘッダ

                     +---+---+---+---+---+---+---+---+---+---+---+---+
                     |HM1|HM2|HM3|VER|   RESERVED    |  INDEXCOUNT   | (more ->)
                     +---+---+---+---+---+---+---+---+---+---+---+---+
                     +---+---+---+---+===============+============+
                     |   STORESIZE   | Index Entries | Data Store |
                     +---+---+---+---+===============+============+

例:

00000060: 8ead e801 0000 0000 0000 0007 0000 0140  ...............@
  • Magic Number 8e ad e8 で始まる
  • VER 1 - バージョン1
  • RESERVED 4バイト - 0 で埋める
  • INDEXCOUNT 4 バイト - 0000 0007 Index エントリが 7 個ある
  • STORESIZE 4 バイト - 0000 0140 Data Store の長さは 320 バイト

Signature セクションのインデックスとデータ

1つのインデックスは固定長で 16 バイトあります。上の INDEXCOUNT にある通り、7 個のインデックスが含まれています。

                     +---+---+---+---+---+---+---+---+---+---+---+---+
                     |      TAG      |     TYPE      |     OFFSET    | (more ->)
                     +---+---+---+---+---+---+---+---+---+---+---+---+

                     +---+---+---+---+
                     |     COUNT     |
                     +---+---+---+---+

例:

00000070: 0000 003e 0000 0007 0000 0130 0000 0010  ...>.......0.... # 1つ目のインデックス
00000080: 0000 010b 0000 0007 0000 0000 0000 0077  ...............w # 2つ目のインデックス
00000090: 0000 010d 0000 0006 0000 0077 0000 0001  ...........w.... # 3つ目のインデックス
000000a0: 0000 03e8 0000 0004 0000 00a0 0000 0001  ................ # 4つ目のインデックス
000000b0: 0000 03ec 0000 0007 0000 00a4 0000 0010  ................ # 5つ目のインデックス
000000c0: 0000 03ed 0000 0007 0000 00b4 0000 0077  ...............w # 6つ目のインデックス
000000d0: 0000 03ef 0000 0004 0000 012c 0000 0001  ...........,.... # 7つ目のインデックス
000000e0:

それぞれのタグの定義は、このドキュメント にある程度リストアップされています。(全てではありません)

1 つ目のインデックスとデータ

00000070: 0000 003e 0000 0007 0000 0130 0000 0010  ...>.......0.... # 1つ目のインデックス

1つ目のインデックスの内容は以下の通り。

  • TAG 3e - 62: RPMTAG_HEADERSIGNATURES
  • TYPE 7 - RPM_BIN_TYPE バイナリ
  • OFFSET 130 - 304 バイト目 (データは一番最後の位置にある)
  • COUNT 10 - 16 バイト

各インデックスは、後続のデータストア内のデータを指しています。

インデックスとデータは、同じ順序で並んでいるとは限りません。この 1 つ目のインデックスは、データストアの最後の項目を指しています。

OFFSET 304 バイト + COUNT 16 バイトで、合計 320 バイト。ヘッダに書かれていたデータストアのサイズ STORESIZE 320 バイトと一致しています。

TAGTYPE により、データストアの中身が RPMTAG_HEADERSIGNATURES というバイナリであることがわかります。

インデックスが指しているデータの中身は以下の通り。

# 開始位置 e0 + オフセット 130 = 210
00000210: 0000 003e 0000 0007 ffff ff90 0000 0010  ...>............

RPMTAG_HEADERSIGNATURES の内容は、ドキュメントによると、

The signature tag differentiates a signature header from a metadata header, and identifies the original contents of the signature header.

とのことですが、いまいちよくわかりませんでした。。
ffff ff90 の部分以外はインデックスと一致するので、Signature セクションの終わりを示しているのかもしれません。

2 つ目のインデックスとデータ

00000080: 0000 010b 0000 0007 0000 0000 0000 0077  ...............w # 2つ目のインデックス
  • TAG 10b - 267: RPMSIGTAG_DSA
  • TYPE 7 - RPM_BIN_TYPE バイナリ
  • OFFSET 0 - 0 バイト目
  • COUNT 77 - 119 バイト

2つ目のインデックスに相当するデータは以下の通り。これがデータストアの最初のデータです。

# 開始位置 e0 + オフセット 0 = e0
000000e0: 8875 0400 1102 0035 1621 041e e04c ce88  .u.....5.!...L..
000000f0: a4ae 4aa2 9a5d f500 4e6f 4700 f97f 5605  ..J..]..NoG...V.
00000100: 0260 3374 3b17 1c72 706d 7340 6661 6d69  .`3t;..rpms@fami
00000110: 6c6c 6563 6f6c 6c65 742e 636f 6d00 0a09  llecollet.com...
00000120: 1000 4e6f 4700 f97f 5609 f300 9f63 1f04  ..NoG...V....c..
00000130: 49ca bfee 1ff7 2281 a07c b90b 342a c8c0  I....."..|..4*..
00000140: bf00 9f4d 91aa e114 dc79 6ba9 8650 e7f4  ...M.....yk..P..
00000150: 8854 89e1 cdfb 4163 3530 3863 3966 6364  .T....Ac508c9fcd # 41 まで

中身は、Header セクションの DSA 署名とのこと。

3 つ目のインデックスとデータ

00000090: 0000 010d 0000 0006 0000 0077 0000 0001  ...........w.... # 3つ目のインデックス
  • TAG 10d - 269: RPMSIGTAG_SHA1
  • TYPE 6 - RPM_STRING_TYPE 文字列
  • OFFSET 77 - 119 バイト目
  • COUNT 1 - 文字列は NULL で終端する可変長の値

COUNT が 1 となっています。文字列 (RPM_STRING_TYPE) は NULL で終端し、サイズは 1 とだけ書かれるようです。データを読まないと、文字列の長さはわかりません。

# 開始位置 e0 + オフセット 77 = 157
00000150: 8854 89e1 cdfb 4163 3530 3863 3966 6364  .T....Ac508c9fcd
00000160: 3439 6530 6332 3735 6334 3532 6163 3531  49e0c275c452ac51
00000170: 3132 6334 3932 3735 6537 3832 3839 3600  12c49275e782896. # SHA1 文字列 + NULL

中身は Header セクションの SHA1 チェックサムです。値は文字列で c508c9fcd49e0c275c452ac5112c49275e782896 となっています。

4、5 つ目のインデックスとデータ

000000a0: 0000 03e8 0000 0004 0000 00a0 0000 0001  ................ # 4つ目のインデックス
000000b0: 0000 03ec 0000 0007 0000 00a4 0000 0010  ................ # 5つ目のインデックス
  • 4つ目
    • TAG 3e8 - 1000: RPMSIGTAG_SIZE
    • TYPE 4 - RPM_INT32_TYPE
    • OFFSET a0 - 160 バイト目
    • COUNT 1 - 1 個
  • 5つ目
    • TAG 3ec - 1004: RPMSIGTAG_MD5
    • TYPE 7 - RPM_BIN_TYPE バイナリ
    • OFFSET a4 - 164 バイト目
    • COUNT 10 - 16 バイト

RPMSIGTAG_SIZE は、この後に続く、Header セクションと Payload セクションの合計サイズで、RPMSIGTAG_MD5 は、その MD5 チェックサムです。

4つ目、5つ目のインデックスに相当するデータは以下の通り。

# 開始位置 e0 + オフセット a0 = 180
00000180: 0001 fe90 64f3 99f8 9d9d 524c 34f0 bca8  ....d.....RL4... # SIZE \x1fe90
00000190: 27f0 99df # MD5 64f399f89d9d524c34f0bca827f099df

Header + Payload の合計サイズは \x1fe90 (130,704 バイト) あることがわかります。

これに \x220 (544 バイト) を足すと、rpm ファイルのサイズ 131,248 バイトに一致することが確認できます。544 バイトは Lead + Signature セクションのサイズです。\x220 から Header セクションが開始します。

Header + Payload セクションの MD5 は以下のコマンドで確認できます。冒頭の 544 バイトを飛ばして、以降の md5 を計算します。

# Mac で実行
$ dd if=php70-php-pecl-imagick-3.4.4-17.el7.remi.x86_64.rpm bs=1 skip=544 | md5
130704+0 records in
130704+0 records out
130704 bytes transferred in 0.235784 secs (554338 bytes/sec)
64f399f89d9d524c34f0bca827f099df

6 つ目のインデックスとデータ

  • TAG 3ed - 1005: RPMSIGTAG_GPG
  • TYPE 7 - RPM_BIN_TYPE バイナリ
  • OFFSET b4 - 180 バイト目
  • COUNT 77 - 119 バイト

データは以下の通り。

# 開始位置 e0 + オフセット b4 = 194
00000190: 27f0 99df 8875 0400 1102 0035 1621 041e  '....u.....5.!.. # \x88 から
000001a0: e04c ce88 a4ae 4aa2 9a5d f500 4e6f 4700  .L....J..]..NoG.
000001b0: f97f 5605 0260 3374 3b17 1c72 706d 7340  ..V..`3t;..rpms@
000001c0: 6661 6d69 6c6c 6563 6f6c 6c65 742e 636f  famillecollet.co
000001d0: 6d00 0a09 1000 4e6f 4700 f97f 56c7 3500  m.....NoG...V.5.
000001e0: 9f65 7edd 2d28 d66f e9ec bbb7 d11f e72e  .e~.-(.o........
000001f0: 808e 0e6c cd00 9e2f ad0e d80d fd50 e2d0  ...l.../.....P..
00000200: 656b a17f 0c58 acc8 d86a 5800 0008 025c  ek...X...jX....\ #\x58 まで

中身は Header + Payload セクションの DSA 署名とのこと。

7 つ目のインデックスとデータ

000000d0: 0000 03ef 0000 0004 0000 012c 0000 0001  ...........,.... # 7つ目のインデックス
  • TAG 3ef - 1007: RPMSIGTAG_PAYLOADSIZE
  • TYPE 4 - RPM_INT32_TYPE
  • OFFSET 12c - 300 バイト目 (上の OFFSET + COUNT は 299。アライメントで1バイト空いてる?)
  • COUNT 1 - 1 個

内容は Payload セクションの、非圧縮状態のサイズです。

# 開始位置 e0 + オフセット 12c = 20c
00000200: 656b a17f 0c58 acc8 d86a 5800 0008 025c  ek...X...jX....\ # \x0008025c

Payload を展開したサイズ \x0008025c (524,892 バイト) は、rpm2cpio コマンドで確認できます。

$ rpm2cpio php70-php-pecl-imagick-3.4.4-17.el7.remi.x86_64.rpm > tmp.cpio
$ ls -l tmp.cpio
-rw-r--r-- 1 koseki koseki 524892 May  3 22:16 tmp.cpio

Header セクション - パッケージの詳細情報

Signature セクションと同様、Header セクションも Header Structure 形式で記述されます。
8e ad e8 で始まるヘッダがあり、インデックスが複数個あり、インデックスから参照されるデータストアが続きます。

Header セクションには、パッケージの各種情報が書かれています。

  • パッケージ名
  • バージョン
  • ライセンス
  • 依存関係

スクリプトでダンプする

例で使用している rpm パッケージには、Header セクションに 70 個のデータが含まれていました。さすがに手動で読むには多すぎたので、Python スクリプトを書いてダンプしました。

Python の struct モジュール を使うことで、固定長のヘッダやインデックスは簡単に読むことができました。可変長のデータストアを読むには、もう少し面倒なコーディングが必要でした。

依存関係

Header セクションの中で、依存関係がどのように表現されているかを見てみます。

この RPM パッケージが提供する機能 (capability) が、

  • RPMTAG_PROVIDENAME
  • RPMTAG_PROVIDEFLAGS
  • RPMTAG_PROVIDEVERSION

に書かれています。

[ 29] RPMTAG_PROVIDENAME
  b'config(php70-php-pecl-imagick)'
  b'php70-php-imagick'
  b'php70-php-imagick(x86-64)'
  b'php70-php-pecl(imagick)'
  b'php70-php-pecl(imagick)(x86-64)'
  b'php70-php-pecl-imagick'
  b'php70-php-pecl-imagick(x86-64)'
  b'scl-package(php70)'

[ 50] RPMTAG_PROVIDEFLAGS
  268435464
  8
  8
  8
  8
  8
  8
  32768

[ 51] RPMTAG_PROVIDEVERSION
  b'3.4.4-17.el7.remi'
  b'3.4.4'
  b'3.4.4'
  b'3.4.4'
  b'3.4.4'
  b'3.4.4-17.el7.remi'
  b'3.4.4-17.el7.remi'
  b''

この RPM パッケージが何に依存しているかが、

  • RPMTAG_REQUIRENAME
  • RPMTAG_REQUIREFLAGS
  • RPMTAG_REQUIREVERSION

に書かれています。

[ 31] RPMTAG_REQUIRENAME
  b'/bin/sh'
  b'/bin/sh'
  b'/bin/sh'
  b'config(php70-php-pecl-imagick)'
  b'libMagickCore-6.Q16.so.7()(64bit)'
  b'libMagickWand-6.Q16.so.7()(64bit)'
  b'libc.so.6()(64bit)'
  b'libc.so.6(GLIBC_2.14)(64bit)'
  b'libc.so.6(GLIBC_2.2.5)(64bit)'
  b'libc.so.6(GLIBC_2.3.4)(64bit)'
  b'libc.so.6(GLIBC_2.4)(64bit)'
  b'php70-php(api)'
  b'php70-php(zend-abi)'
  b'php70-runtime'
  b'php70-runtime(remi)(x86-64)'
  b'rpmlib(CompressedFileNames)'
  b'rpmlib(FileDigests)'
  b'rpmlib(PayloadFilesHavePrefix)'
  b'rtld(GNU_HASH)'
  b'rpmlib(PayloadIsXz)'

[ 30] RPMTAG_REQUIREFLAGS
  256
  288
  4352
  268435464
  16384
  16384
  16384
  16384
  16384
  16384
  16384
  8
  8
  16384
  0
  16777226
  16777226
  16777226
  16384
  16777226

[ 32] RPMTAG_REQUIREVERSION
  b''
  b''
  b''
  b'3.4.4-17.el7.remi'
  b''
  b''
  b''
  b''
  b''
  b''
  b''
  b'20151012-64'
  b'20151012-64'
  b''
  b''
  b'3.0.4-1'
  b'4.6.0-1'
  b'4.0-1'
  b''
  b'5.2-1'

フラグはバージョンの大小比較 (>=, <=, ==, etc.) を表しています。 (25.2.4.4.2. Package Dependencies Attributes)

RPM には、「ファイル名」や「パッケージ名」の他に、Provides Name と呼ばれる名前があることがわかります。これは、そのパッケージが提供する機能 (capability) に名前を付けた「仮想パッケージ名」です。

  • ファイル名: php70-php-pecl-imagick-3.4.4-17.el7.remi.x86_64.rpm
  • パッケージ名:
    • php70-php-pecl-imagick-3.4.4-17.el7.remi (Lead セクション name)
    • php70-php-pecl-imagick (RPMTAG_NAME)
  • 仮想パッケージ名 (Provides Name):
    • config(php70-php-pecl-imagick)
    • php70-php-imagick
    • php70-php-imagick(x86-64)
    • php70-php-pecl(imagick)
    • php70-php-pecl(imagick)(x86-64)
    • php70-php-pecl-imagick
    • php70-php-pecl-imagick(x86-64)
    • scl-package(php70)

Provides Name は /bin/sh のような名前がついている場合があります。

Requires Name に、パッケージ名や Provides Name を列挙することで、RPM の依存関係が成り立っています。

依存関係を調べるコマンド

RPM ファイルに含まれる Provides Name は以下のコマンドで調べることができます。

$ rpm -q --provides -p php70-php-pecl-imagick-3.4.4-17.el7.remi.x86_64.rpm
config(php70-php-pecl-imagick) = 3.4.4-17.el7.remi
php70-php-imagick = 3.4.4
php70-php-imagick(x86-64) = 3.4.4
php70-php-pecl(imagick) = 3.4.4
php70-php-pecl(imagick)(x86-64) = 3.4.4
php70-php-pecl-imagick = 3.4.4-17.el7.remi
php70-php-pecl-imagick(x86-64) = 3.4.4-17.el7.remi
scl-package(php70)

Requires Name は、以下のコマンドで調べられます。

$ rpm -q --requires -p php70-php-pecl-imagick-3.4.4-17.el7.remi.x86_64.rpm
/bin/sh
/bin/sh
/bin/sh
config(php70-php-pecl-imagick) = 3.4.4-17.el7.remi
libMagickCore-6.Q16.so.7()(64bit)
libMagickWand-6.Q16.so.7()(64bit)
libc.so.6()(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.3.4)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
php70-php(api) = 20151012-64
php70-php(zend-abi) = 20151012-64
php70-runtime
php70-runtime(remi)(x86-64)
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rtld(GNU_HASH)
rpmlib(PayloadIsXz) <= 5.2-1

yum search コマンドは Provides Name にはヒットしないようです。代わりに、yum provides コマンドを使います。

$ yum provides 'config(php70-php-pecl-imagick)'
:
php70-php-pecl-imagick-3.4.4-10.el7.remi.x86_64 : Extension to create and modify images using ImageMagick
Repo        : remi-safe
Matched from:
Provides    : config(php70-php-pecl-imagick) = 3.4.4-10.el7.remi
:

RPM ファイルの URL は以下のようにして検索できます。

$ yumdownloader --urls 'config(php70-php-pecl-imagick)'
:
http://rpms.remirepo.net/enterprise/7/safe/x86_64/php70-php-pecl-imagick-3.4.4-17.el7.remi.x86_64.rpm

Payload セクション - パッケージに含まれるファイル

最後の Payload セクションには、パッケージに含まれるファイルが、cpio アーカイブ形式 gzip 圧縮で格納されています。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1