GNU/Linux >> Linux の 問題 >  >> Linux

Linux –共有ライブラリのメモリページが複数のプロセスで共有されるようにするにはどうすればよいですか?

最初に私がやりたいこと、次に何とかできること、そして最後に私の問題について説明しましょう。

目標:Cでフラッシュ+フラッシュキャッシュ攻撃を実装する

私はCでフラッシュ+フラッシュキャッシュ攻撃(https://gruss.cc/files/flushflush.pdf)を実装しようとしています。基本的に、共有ライブラリを使用する場合、2つの異なるプロセスが同じメモリページを共有する可能性があるという事実を利用します。これにより、キャッシュの「共有使用」が発生します。

犠牲プロセスがあり、継続的に実行され、場合によっては関数 funcを実行していると仮定します。 共有ライブラリからインポートしました。

並行して、被害者が func を呼び出したときにスパイすることを目的とした、被害者と同じコンピューター上で実行されているスパイプロセスがあると想定します。 。スパイは同じ共有ライブラリにもアクセスできます。スパイプロセスの擬似コードは次のとおりです。

i=0;
for (i = 0; i < trace_length ; i++)
{
    trace[i] = flush_time( address of function "func");
    i++;
}

ここで、 flush_time(

addressが指すメモリをCPUがフラッシュするのにかかる時間を返す関数です。 すべてのキャッシュレベルから。 Intelプロセッサでは、これはアセンブリ命令 clflushを介して実現できます。 。 clflushの実行を観察できます アドレスがキャッシュに存在しない場合は、より高速になります。その結果、メモリアドレスをフラッシュするために必要なタイミングは、キャッシュ内に存在する場合(または存在しない場合)に直接変換できます。

スパイプロセスは、時間の経過に伴うflush_timeの結果を含むトレースベクトルを返します。以前の観察から、被害者が関数 func も呼び出すと、このトレースはより高いタイミングを示します。 。したがって、被害者が func を呼び出しているときに、スパイは差し引きます。 。

何とかして:GSL共有ライブラリに対して攻撃を機能させる

共有ライブラリがGSLである前述の攻撃を実装しました。恣意的に、私は gsl_stats_meanを選択しました ( gsl_statistics_doubleで定義されています )関数 funcとして機能します スパイしたいです。

その場合、被害者のプログラムが gsl_stats_mean を呼び出すとタイミングの違いがはっきりとわかるため、スパイは完全に機能します。

私の問題:攻撃は自家製の共有ライブラリでは機能しません

自分の共有ライブラリを作成して、スパイ/被害者のテストに使用したいと思います。 と仮定しましょう spy.cが置かれているフォルダを示します およびvictim.c ファイルはです。 2つのファイルmyl.cを作成しました およびmyl.h フォルダ内./src / myl 、それぞれ funcの説明が含まれています そしてそれは宣言です。以前のように、私のスパイの目標は funcの使用を検出することです 犠牲者から。

両方のspy.c およびvictim.c インクルード行を含める:

 #include "src/myl/myl.h"

共有ライブラリの作成は、次のコマンドを使用して行われます。

gcc -c -fPIC src/myl/myl.c -o bin/shared/myl.o  #creation of object in ./bin/shared
gcc -shared bin/shared/myl.o -o bin/shared/libmyl.so #creation of the shared library in ./bin/shared
gcc -c spy.c -o spy.o #creation of spy's process object file
gcc -c victim.c -o victim.o #creation of victim's process object file
gcc spy.o -Lbin/shared -lmyl -o spy #creation of spy's executable
gcc victim.o -Lbin/shared -lmyl -o victim #creation of victim's executable

次に、次の行を使用して犠牲者とスパイを起動します。

LD_LIBRARY_PATH=$(pwd)/bin/shared ./victim
LD_LIBRARY_PATH=$(pwd)/bin/shared ./spy

ただし、GSL関数を使用していた場合とは対照的に、キャッシュ上のアクティビティは表示されなくなりました。これは、スパイプロセスと被害者プロセスが共有ライブラリの同じメモリページを共有していないことを意味していると思います(ただし、GSLを使用している場合はそうでした)。この方法でコンパイルする場合、GSL関数をターゲットにするときにスパイは引き続き機能することに注意してください。

私の主な質問は次のとおりです。自家製のコンパイル済み共有ライブラリが、複数のプロセスによって同時に実行されるときにメモリページングが共有されるようにするにはどうすればよいですか? GSL、gmp、ネイティブライブラリなど、私がインストールした「適切な」ライブラリの場合のようです。しかし、私が自分で作ったものではありません。

よろしくお願いします。答えが簡単な場合はお詫び申し上げます。

編集: LD_DEBUG =libsの出力 およびファイル スパイと被害者の両方。
注:被害者は pg2と呼ばれます スパイはpg1と呼ばれます (ごめんなさい)

最初に被害者用のライブラリ、次に被害者用のファイル( pg2 )。次に、スパイのlibs、スパイのファイル( pg1 ):

LD_DEBUG=libs LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg2
 31714: find library=libmyl.so [0]; searching
 31714:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared/tls:/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared      (LD_LIBRARY_PATH)
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64/libmyl.so
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/libmyl.so
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64/libmyl.so
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31714: 
 31714: find library=libc.so.6 [0]; searching
 31714:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31714:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libc.so.6
 31714:  search cache=/etc/ld.so.cache
 31714:   trying file=/lib/x86_64-linux-gnu/libc.so.6
 31714: 
 31714: 
 31714: calling init: /lib/x86_64-linux-gnu/libc.so.6
 31714: 
 31714: 
 31714: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31714: 
 31714: 
 31714: initialize program: ./pg2
 31714: 
 31714: 
 31714: transferring control: ./pg2
 31714: 



LD_DEBUG=files LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg2
 31901: 
 31901: file=libmyl.so [0];  needed by ./pg2 [0]
 31901: file=libmyl.so [0];  generating link map
 31901:   dynamic: 0x00007f5a3b34be48  base: 0x00007f5a3b14b000   size: 0x0000000000201028
 31901:     entry: 0x00007f5a3b14b580  phdr: 0x00007f5a3b14b040  phnum:                  7
 31901: 
 31901: 
 31901: file=libc.so.6 [0];  needed by ./pg2 [0]
 31901: file=libc.so.6 [0];  generating link map
 31901:   dynamic: 0x00007f5a3b144ba0  base: 0x00007f5a3ad81000   size: 0x00000000003c99a0
 31901:     entry: 0x00007f5a3ada1950  phdr: 0x00007f5a3ad81040  phnum:                 10
 31901: 
 31901: 
 31901: calling init: /lib/x86_64-linux-gnu/libc.so.6
 31901: 
 31901: 
 31901: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31901: 
 31901: 
 31901: initialize program: ./pg2
 31901: 
 31901: 
 31901: transferring control: ./pg2
 31901: 


LD_DEBUG=libs LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg1
 31938: find library=libmyl.so [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared/tls:/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared      (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64/libmyl.so
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/libmyl.so
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64/libmyl.so
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31938: 
 31938: find library=libgsl.so.23 [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libgsl.so.23
 31938:  search cache=/etc/ld.so.cache
 31938:   trying file=/usr/local/lib/libgsl.so.23
 31938: 
 31938: find library=libgslcblas.so.0 [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libgslcblas.so.0
 31938:  search cache=/etc/ld.so.cache
 31938:   trying file=/usr/local/lib/libgslcblas.so.0
 31938: 
 31938: find library=libc.so.6 [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libc.so.6
 31938:  search cache=/etc/ld.so.cache
 31938:   trying file=/lib/x86_64-linux-gnu/libc.so.6
 31938: 
 31938: find library=libm.so.6 [0]; searching
 31938:  search path=/home/romain/Documents/work/test/page_sharing/bin/shared       (LD_LIBRARY_PATH)
 31938:   trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libm.so.6
 31938:  search cache=/etc/ld.so.cache
 31938:   trying file=/lib/x86_64-linux-gnu/libm.so.6
 31938: 
 31938: 
 31938: calling init: /lib/x86_64-linux-gnu/libc.so.6
 31938: 
 31938: 
 31938: calling init: /lib/x86_64-linux-gnu/libm.so.6
 31938: 
 31938: 
 31938: calling init: /usr/local/lib/libgslcblas.so.0
 31938: 
 31938: 
 31938: calling init: /usr/local/lib/libgsl.so.23
 31938: 
 31938: 
 31938: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31938: 
 31938: 
 31938: initialize program: ./pg1
 31938: 
 31938: 
 31938: transferring control: ./pg1
 31938: 
0: 322 # just some output of my spying program
1: 323 # just some output of my spying program
 31938: 
 31938: calling fini: ./pg1 [0]
 31938: 
 31938: 
 31938: calling fini: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so [0]
 31938: 
 31938: 
 31938: calling fini: /usr/local/lib/libgsl.so.23 [0]
 31938: 
 31938: 
 31938: calling fini: /usr/local/lib/libgslcblas.so.0 [0]
 31938: 
 31938: 
 31938: calling fini: /lib/x86_64-linux-gnu/libm.so.6 [0]
 31938: 




LD_DEBUG=files LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg1
 31940: 
 31940: file=libmyl.so [0];  needed by ./pg1 [0]
 31940: file=libmyl.so [0];  generating link map
 31940:   dynamic: 0x00007fb3d8794e48  base: 0x00007fb3d8594000   size: 0x0000000000201028
 31940:     entry: 0x00007fb3d8594580  phdr: 0x00007fb3d8594040  phnum:                  7
 31940: 
 31940: 
 31940: file=libgsl.so.23 [0];  needed by ./pg1 [0]
 31940: file=libgsl.so.23 [0];  generating link map
 31940:   dynamic: 0x00007fb3d8582ac8  base: 0x00007fb3d8126000   size: 0x000000000046da60
 31940:     entry: 0x00007fb3d8180e30  phdr: 0x00007fb3d8126040  phnum:                  7
 31940: 
 31940: 
 31940: file=libgslcblas.so.0 [0];  needed by ./pg1 [0]
 31940: file=libgslcblas.so.0 [0];  generating link map
 31940:   dynamic: 0x00007fb3d8124df0  base: 0x00007fb3d7ee8000   size: 0x000000000023d050
 31940:     entry: 0x00007fb3d7eea120  phdr: 0x00007fb3d7ee8040  phnum:                  7
 31940: 
 31940: 
 31940: file=libc.so.6 [0];  needed by ./pg1 [0]
 31940: file=libc.so.6 [0];  generating link map
 31940:   dynamic: 0x00007fb3d7ee1ba0  base: 0x00007fb3d7b1e000   size: 0x00000000003c99a0
 31940:     entry: 0x00007fb3d7b3e950  phdr: 0x00007fb3d7b1e040  phnum:                 10
 31940: 
 31940: 
 31940: file=libm.so.6 [0];  needed by /usr/local/lib/libgsl.so.23 [0]
 31940: file=libm.so.6 [0];  generating link map
 31940:   dynamic: 0x00007fb3d7b1cd88  base: 0x00007fb3d7815000   size: 0x00000000003080f8
 31940:     entry: 0x00007fb3d781a600  phdr: 0x00007fb3d7815040  phnum:                  7
 31940: 
 31940: 
 31940: calling init: /lib/x86_64-linux-gnu/libc.so.6
 31940: 
 31940: 
 31940: calling init: /lib/x86_64-linux-gnu/libm.so.6
 31940: 
 31940: 
 31940: calling init: /usr/local/lib/libgslcblas.so.0
 31940: 
 31940: 
 31940: calling init: /usr/local/lib/libgsl.so.23
 31940: 
 31940: 
 31940: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
 31940: 
 31940: 
 31940: initialize program: ./pg1
 31940: 
 31940: 
 31940: transferring control: ./pg1
 31940: 
0: 325 # just some output of my spying program
1: 327 # just some output of my spying program
 31940: 
 31940: calling fini: ./pg1 [0]
 31940: 
 31940: 
 31940: calling fini: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so [0]
 31940: 
 31940: 
 31940: calling fini: /usr/local/lib/libgsl.so.23 [0]
 31940: 
 31940: 
 31940: calling fini: /usr/local/lib/libgslcblas.so.0 [0]
 31940: 
 31940: 
 31940: calling fini: /lib/x86_64-linux-gnu/libm.so.6 [0]
 31940: 

承認された回答:

ldからのデバッグ出力以降 ダイナミックリンカー/ローダーは、両方の victim およびspy プログラムが正しい入力ファイルをロードする場合、次のステップは、カーネルが実際に libmyl.soの物理ページを設定したかどうかを確認することです。 犠牲者間で共有されるメモリにロードされます およびspy

Linuxでは、カーネルバージョン2.6.25以降、 pagemapを介してこれを確認できます。 ユーザースペースプログラムが/proc 内のファイルを読み取ることにより、ページテーブルと関連情報を調べることができるカーネルのインターフェイス 。

ページマップを使用して2つのプロセスがメモリを共有しているかどうかを確認する一般的な手順は、次のようになります。

  1. 読み取り/proc //マップ 両方のプロセスで、メモリ空間のどの部分がどのオブジェクトにマップされているかを判断します。
  2. 関心のあるマップを選択します。この場合は、 libmyl.soのあるページを選択します。 マップされます。
  3. / proc / / pagemapを開きます 。 ページマップ ページごとに1つずつ、64ビットのページマップ記述子で構成されます。 pagemap内のページのアドレスとその記述子アドレスの間のマッピング ページアドレス/ページサイズ*記述子サイズ 。調べたいページの記述子を探します。
  4. pagemapから各ページの符号なし整数として64ビット記述子を読み取ります 。
  5. ページ記述子のビット0〜54のページフレーム番号(PFN)を libmyl.so間で比較します。 犠牲者のページ およびspy 。 PFNが一致する場合、2つのプロセスは同じ物理ページを共有しています。
関連:Windows –パーティションスキームを台無しにしたときにWindowsがLinuxファイルシステムを破壊しないようにするにはどうすればよいですか?

次のサンプルコードは、 pagemap プロセス内からアクセスして印刷できます。 dl_iterate_phdr()を使用します プロセスのメモリスペースにロードされた各共有ライブラリの仮想アドレスを決定し、対応するページマップを検索して出力します。 / proc / / pagemapから 。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <link.h>
#include <errno.h>
#include <error.h>

#define E_CANNOT_OPEN_PAGEMAP 1
#define E_CANNOT_READ_PAGEMAP 2

typedef struct __attribute__ ((__packed__)) {
    union {
        uint64_t pmd;
        uint64_t page_frame_number : 55;
        struct {
            uint64_t swap_type: 5;
            uint64_t swap_offset: 50;
            uint64_t soft_dirty: 1;
            uint64_t exclusive: 1;
            uint64_t zero: 4;
            uint64_t file_page: 1;
            uint64_t swapped: 1;
            uint64_t present: 1;
        };
    };
} pmd_t;


static int print_pagemap_for_phdr(struct dl_phdr_info *info,
                                  size_t size, void *data)
{
    struct stat statbuf;

    size_t pagesize = sysconf(_SC_PAGESIZE);

    char pagemap_path[BUFSIZ];
    int pagemap;

    uint64_t start_addr, end_addr;

    if (!strcmp(info->dlpi_name, "")) {
        return 0;
    }

    stat(info->dlpi_name, &statbuf);

    start_addr = info->dlpi_addr;
    end_addr = (info->dlpi_addr + statbuf.st_size + pagesize) & ~(pagesize-1);

     printf("n%10p-%10p %snn",
            (void *)start_addr,
            (void *)end_addr,
            info->dlpi_name);

     snprintf(pagemap_path, sizeof pagemap_path, "/proc/%d/pagemap", getpid());

     if ((pagemap = open(pagemap_path, O_RDONLY)) < 0) {
         error(E_CANNOT_OPEN_PAGEMAP, errno, 
               "cannot open pagemap: %s", pagemap_path);
     }

     printf("%10s %8s %7s %5s %8s %7s %7sn",
            "", "", "soft-", "", "file /", "", "");
     printf("%10s %8s %7s %5s %11s %7s %7sn",
            "address", "pfn", "dirty", "excl.",
            "shared anon", "swapped", "present");

     for (unsigned long i = start_addr; i < end_addr; i += pagesize) {
          pmd_t pmd;

          if (pread(pagemap, &pmd.pmd, sizeof pmd.pmd, (i / pagesize) * sizeof pmd) != sizeof pmd) {
              error(E_CANNOT_READ_PAGEMAP, errno,
                    "cannot read pagemap: %s", pagemap_path);
          }

          if (pmd.pmd != 0) {
              printf("0x%10" PRIx64 " %06" PRIx64 " %3d %5d %8d %9d %7dn", i,
                     (unsigned long)pmd.page_frame_number,
                     pmd.soft_dirty,
                     pmd.exclusive,
                     pmd.file_page,
                     pmd.swapped,
                     pmd.present);
          }
    }

    close(pagemap);

    return 0;
}

int main()
{
    dl_iterate_phdr(print_pagemap_for_phdr, NULL);

    exit(EXIT_SUCCESS);
}

プログラムの出力は次のようになります。

$ sudo ./a.out

0x7f935408d000-0x7f9354256000 /lib/x86_64-linux-gnu/libc.so.6

                      soft-         file /                
   address      pfn   dirty excl. shared anon swapped present
0x7f935408d000 424416   1     0        1         0       1
0x7f935408e000 424417   1     0        1         0       1
0x7f935408f000 422878   1     0        1         0       1
0x7f9354090000 422879   1     0        1         0       1
0x7f9354091000 43e879   1     0        1         0       1
0x7f9354092000 43e87a   1     0        1         0       1
0x7f9354093000 424790   1     0        1         0       1
...

ここで:

  • アドレス ページの仮想アドレスです
  • pfn ページのページフレーム番号です
  • ソフトダーティ ページテーブルエントリ(PTE)でsoft-dirtyビットが設定されているかどうかを示します。
  • 除外 ページが排他的にマップされているかどうかを示します(つまり、ページはこのプロセスに対してのみマップされます)。
  • ファイル/共有anon ページがファイルページなのか共有匿名ページなのかを示します。
  • スワップ ページが現在スワップされているかどうかを示します( present を意味します) ゼロです。
  • 現在 ページが現在プロセス常駐セットに存在するかどうかを示します( swapped を意味します) ゼロです。
関連:基本的なLinux(1)チートシート

(注:サンプルプログラムは sudoで実行します Linux 4.0以降、 CAP_SYS_ADMINを持つユーザーのみ 機能は、 / proc / / pagemapからPFNを取得できます 。 Linux 4.2以降、ユーザーが CAP_SYS_ADMIN を持っていない場合、PFNフィールドはゼロになります。 。この変更の理由は、PFNによって公開された仮想から物理へのマッピングに関する情報を使用して、別のメモリ関連の脆弱性であるRowhammer攻撃を悪用することをより困難にするためです。)

サンプルプログラムを数回実行すると、ページの仮想アドレスが変更されるはずですが(ASLRのため)、他のプロセスで使用されている共有ライブラリのPFNは同じままである必要があります。

libmyl.soのPFNの場合 犠牲者間の一致 およびspy プログラムでは、攻撃コード自体で攻撃が失敗する理由を探し始めます。 PFNが一致しない場合、追加のビットは、ページが共有されるように設定されていない理由を示すヒントを与える可能性があります。 ページマップ ビットは次のことを示します:

present file exclusive state:
   0      0     0      non-present
   1      1     0      file page mapped somewhere else
   1      1     1      file page mapped only here
   1      0     0      anonymous non-copy-on-write page (shared with parent/child)
   1      0     1      anonymous copy-on-write page (or never forked)

(MAP_FILE | MAP_PRIVATE)のコピーオンライトページ このコンテキストでは、領域は匿名です。

ボーナス: 回数を取得するには ページがマップされている場合、PFNを使用して / proc / kpagecountでページを検索できます。 。このファイルには、各ページがマップされ、PFNによってインデックスが付けられた回数の64ビットカウントが含まれています。


Linux
  1. Linuxでメモリキャッシュをクリアする方法

  2. Linuxで実行中のプロセスを強制終了する方法

  3. Linux 共有ライブラリがエクスポートしている関数のリストを表示するにはどうすればよいですか?

  1. Linuxで共有メモリセグメントに接続されているプロセスを一覧表示する方法は?

  2. Linux in C で共有メモリを使用する方法

  3. Linux で共有ライブラリのバージョン管理を行う方法は?

  1. 共有ライブラリ (.so) は、ローディング プログラムに実装されている関数をどのように呼び出すことができますか?

  2. Linux の man ページの読み方は?

  3. Linux は、メモリが不足したかどうかを確認せずにプロセスを強制終了し始めますか?