Kprobesを使用すると、カーネル内のコードに動的にフックできます。必要な情報を提供するプロセスの作成と破棄に関連する機能の中から、適切な機能を見つける必要があります。たとえば、作成されたタスクの場合、fork.c の do_fork() が開始するのに適しています。破棄されたタスクの場合、do_exit. retprobe を作成することをお勧めします。これは kprobe の一種であり、関数の実行の最後に、関数が戻る前に制御を追加で提供します。関数が戻る前に制御が必要な理由は、戻り値を確認してプロセスの作成に成功したかどうかを確認するためです。エラーが発生した場合、関数は負の値を返すか、場合によっては 0 を返します。
これを行うには、kretprobe 構造体を作成します。
static struct kretprobe do_fork_probe = {
.entry_handler = (kprobe_opcode_t *) my_do_fork_entry,
.handler = (kprobe_opcode_t *) my_do_fork_ret,
.maxactive = 20,
.data_size = sizeof(struct do_fork_ctx)
};
my_do_fork_entry は制御がフックされた関数に入ると実行され、 my_do_fork_ret はそれが戻る直前に実行されます。次のようにフックします。
do_fork_probe.kp.addr =
(kprobe_opcode_t *) kallsyms_lookup_name("do_fork");
if ((ret = register_kretprobe(&do_fork_probe)) <0) {
// handle error
}
フックの実装では、引数と戻り値を取得するのは少し扱いにくいです。これらは、保存されたレジスタ pt_regs データ構造を介して取得します。return フックを見てみましょう。x86 では、regs->ax を介して戻り値を取得します。
static int my_do_fork_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct do_fork_ctx *ctx = (struct do_fork_ctx *) ri->data;
int ret = regs->ax; // This is on x86
if (ret > 0) {
// It's not an error, probably a valid process
}
}
エントリ ポイントでは、レジスタを介して引数にアクセスできます。たとえば、x86 では、regs->di が最初の引数、regs->si が 2 番目の引数などです。Google で完全なリストを取得できます。レジスタは他の計算のために上書きされている可能性があるため、return フックの引数としてこれらのレジスタに依存しないでください。
これを機能させるには、確かに多くのフープをジャンプする必要がありますが、うまくいけば、このメモが正しい方向に進むはずです.