Linuxカーネルのバイナリフッククラフト - 概要

最後の数日間にカーネルバイナリフックを再生し、泥棒中毒手作り考えることができます。

このシリーズの最後の記事では、私は実用的な例を実証し、INPUTチェーンのiptablesでのカウント数の統計は、パケットDROPを支配:
Linuxカーネルのバイナリフッククラフト-実用的な例は、iptablesのDROPを数えます

知覚に詳細を逃し記事は、最初に構成し、スタートとして合計します。

のは、それがどのようなものであったかになった後のip_local_deliverフックを見てみましょう。比較として、我々はそれが始まった道を見て:

crash> dis ip_local_deliver
...
0xffffffff81561eb5 <ip_local_deliver+165>:      movq   $0xffffffff81561ad0,-0x18(%rbp)
0xffffffff81561ebd <ip_local_deliver+173>:      callq  0xffffffff815586a0 <nf_hook_slow>
0xffffffff81561ec2 <ip_local_deliver+178>:      cmp    $0x1,%eax
0xffffffff81561ec5 <ip_local_deliver+181>:      jne    0xffffffff81561e69 <ip_local_deliver+89>
0xffffffff81561ec7 <ip_local_deliver+183>:      jmp    0xffffffff81561e5f <ip_local_deliver+79>
0xffffffff81561ec9 <ip_local_deliver+185>:      nopl   0x0(%rax)
...

私たちは、それがフックされた後、次のようになります。

crash> dis ip_local_deliver
...
0xffffffff81561eb5 <ip_local_deliver+165>:      movq   $0xffffffff81561ad0,-0x18(%rbp)
0xffffffff81561ebd <ip_local_deliver+173>:      callq  0xffffffffa0392000 <test_stub1>
0xffffffff81561ec2 <ip_local_deliver+178>:      cmp    $0x1,%eax
0xffffffff81561ec5 <ip_local_deliver+181>:      jne    0xffffffff81561e69 <ip_local_deliver+89>
0xffffffff81561ec7 <ip_local_deliver+183>:      jmp    0xffffffff81561e5f <ip_local_deliver+79>
0xffffffff81561ec9 <ip_local_deliver+185>:      nopl   0x0(%rax)

目に見えるが、ip_local_deliverの代わりにnf_hook_slow呼び出すことになって、フックがコールtest_stub1となりました。

OK、我々はtest_stub1を見て:

crash> dis test_stub1
0xffffffffa0392000 <test_stub1>:        callq  0xffffffff815586a0 <nf_hook_slow>
0xffffffffa0392005 <test_stub1+5>:      cmp    $0x1,%eax
0xffffffffa0392008 <test_stub1+8>:      je     0xffffffffa0392011 <test_stub1+17>
0xffffffffa039200a <test_stub1+10>:     incl   0xffffffffa0394280
0xffffffffa0392011 <test_stub1+17>:     retq

ああ、非常にクリーンでシンプルなロジックは、最初の命令が突く本来の機能をコピーし、余分なロジックを追加します。

彼は2つの点を強調しました。

  • ここで連れ去られtest_stub1スプライシングアセンブリ言語、レジスタの本来の機能レジスタの利用価値を知らないコンパイラの恐怖のために、Cで書かれていない理由は、保存/手動アセンブリを復元するために必要でなければなりません操作。Cで書かれていないことができ、また、残念です!
  • モジュールtest_stub1は実装定義関数のスタブではなく、メモリ使用量のkmallocの/ vmalloc分布、なぜですか?kmallocの/ vmalloc範囲が割り当てられているので、メモリを指定することができない、と我々は、この上にメモリモジュール上にメモリ0xffffffff00000000を割り当てるように、有効性を確保するために、オフセットが32ビットと元の関数スタブです。
// arch/x86/include/asm/pgtable_64_types.h
#define MODULES_VADDR    _AC(0xffffffffa0000000, UL)
#define MODULES_END      _AC(0xffffffffff000000, UL)
// arch/x86/kernel/module.c
void *module_alloc(unsigned long size)
{
   if (PAGE_ALIGN(size) > MODULES_LEN)
       return NULL;
   return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
               GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,
               -1, __builtin_return_address(0));
}

当社独自のコールのalloc APIはこれを保証するものではありません。

注⚠️、test_stub1は、余分なカウントロジックを追加し、機能命令ip_local_deliver全体をコピーしていないが、唯一の新しい追加のカウントロジックが含まれています!これが本当であるオンラインLivePatch

これは根本的に異なるkpatchの標準的な仕組みです。

しかし、それができるだけで、私は明確なフックポイントを見つけることができなかった、あまりにもケースを複雑フック関数命令の指示は非常に複雑なシーンのために、このマニュアルをプレイしていない、またはあまりにも多くの命令は、フックポイントを突くする必要があります:関数全体をコピーして、これはkpatch練習です、もちろん、私は記事を、次の午前のプログラムの手動アプローチについて説明し
https://blog.csdn.net/dog250/article/details/105093969
HTTPS://blog.csdnを。 NET / dog250 / Articleこの記事は、105 129 254 / /詳細ました
https://blog.csdn.net/dog250/article/details/105135219

操作が突く原子ではなく、すべてのスレッドが一連の命令を突くから引き出されていることを保証することはできませんので、あまりにも多くのことができない命令の数の機能を突きます。

典型的なアプローチは、すべての命令が完了した後、これにINT3リリース、参照:.突くとき、前方に入り、事前の指示をブロックし、最初に書かれた説明書に最初の原子INT3を突くことです

Https://blog.csdn.net/dog250 / Articleこの記事だった/詳細/ 84201114
https://blog.csdn.net/dog250/article/details/84258601
https://blog.csdn.net/dog250/article/details/84572893

私の意見では、全体の機能命令の完全なコピーでは、安価なメモリを少し無駄になり、これは大きな問題ではありません、もちろん、関係する純粋な工芸品のため、当然のことながら、より良いです。

以下のために職人として、ここに私の本番環境ジャグリング停止に、あなたは今では標準的な生産環境kpatchを言うことができない人。

kpatchは、標準次にftraceメカニズムを使用して、より標準化の実践を、使用しています。

私たちは、コア次にftraceがオープンで、各機能の5つのバイトはNOPされ始めていることを知って、そして操作は、関数のスタックフレームは、次のとおりです。

crash> dis ip_rcv
0xffffffff81561ee0 <ip_rcv>:    nopl   0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffff81561ee5 <ip_rcv+5>:  push   %rbp
0xffffffff81561ee6 <ip_rcv+6>:  mov    %rsp,%rbp

今、私たちはアップパッチを適用するkpatchの関数であるならば、何が起こりますか?我々は、機能set_next_buddyにkpatchを果たしたとしましょう。

私たちは、DISこれに試してみました:

crash> dis set_next_buddy
dis: set_next_buddy: duplicate text symbols found:
ffffffff810b9450 (t) set_next_buddy /usr/src/debug/kernel-3.10.0/linux-3.10.0.x86_64/kernel/sched/fair.c: 4536
ffffffffa0240410 (t) set_next_buddy [kpatch_y9h83dum]

今、私たちは2つのset_next_buddyシンボルがシステムにあった、カーネルが固有であることがわかり、当然のことながら、別のkpatch修正され、私たちはそれぞれを見て:

crash> dis ffffffff810b9450
0xffffffff810b9450 <set_next_buddy>:    callq  0xffffffff81646df0 <ftrace_regs_caller>
0xffffffff810b9455 <set_next_buddy+5>:  push   %rbp
0xffffffff810b9456 <set_next_buddy+6>:  cmpq   $0x0,0x150(%rdi)
0xffffffff810b945e <set_next_buddy+14>: mov    %rsp,%rbp
0xffffffff810b9461 <set_next_buddy+17>: jne    0xffffffff810b947a <set_next_buddy+42>
0xffffffff810b9463 <set_next_buddy+19>: jmp    0xffffffff810b9481 <set_next_buddy+49>
0xffffffff810b9465 <set_next_buddy+21>: nopl   (%rax)
0xffffffff810b9468 <set_next_buddy+24>: mov    0x148(%rdi),%rax
0xffffffff810b946f <set_next_buddy+31>: mov    %rdi,0x40(%rax)
0xffffffff810b9473 <set_next_buddy+35>: mov    0x140(%rdi),%rdi
0xffffffff810b947a <set_next_buddy+42>: test   %rdi,%rdi
0xffffffff810b947d <set_next_buddy+45>: jne    0xffffffff810b9468 <set_next_buddy+24>
0xffffffff810b947f <set_next_buddy+47>: pop    %rbp
0xffffffff810b9480 <set_next_buddy+48>: retq
0xffffffff810b9481 <set_next_buddy+49>: cmpl   $0x5,0x1f8(%rdi)
0xffffffff810b9488 <set_next_buddy+56>: jne    0xffffffff810b947a <set_next_buddy+42>
0xffffffff810b948a <set_next_buddy+58>: pop    %rbp
0xffffffff810b948b <set_next_buddy+59>: retq
// 再看另一个kpatch中的同名符号
crash> dis ffffffffa0240410
0xffffffffa0240410 <set_next_buddy>:    nopl   0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffffa0240415 <set_next_buddy+5>:  push   %rbp
0xffffffffa0240416 <set_next_buddy+6>:  cmpq   $0x0,0x150(%rdi)
0xffffffffa024041e <set_next_buddy+14>: mov    %rsp,%rbp
0xffffffffa0240421 <set_next_buddy+17>: jne    0xffffffffa0240441 <set_next_buddy+49>
0xffffffffa0240423 <set_next_buddy+19>: jmp    0xffffffffa0240448 <set_next_buddy+56>
0xffffffffa0240425 <set_next_buddy+21>: nopl   (%rax)
0xffffffffa0240428 <set_next_buddy+24>: mov    0x38(%rdi),%eax
0xffffffffa024042b <set_next_buddy+27>: test   %eax,%eax
0xffffffffa024042d <set_next_buddy+29>: je     0xffffffffa0240446 <set_next_buddy+54>
0xffffffffa024042f <set_next_buddy+31>: mov    0x148(%rdi),%rax
0xffffffffa0240436 <set_next_buddy+38>: mov    %rdi,0x40(%rax)
0xffffffffa024043a <set_next_buddy+42>: mov    0x140(%rdi),%rdi
0xffffffffa0240441 <set_next_buddy+49>: test   %rdi,%rdi
0xffffffffa0240444 <set_next_buddy+52>: jne    0xffffffffa0240428 <set_next_buddy+24>
0xffffffffa0240446 <set_next_buddy+54>: pop    %rbp
0xffffffffa0240447 <set_next_buddy+55>: retq
0xffffffffa0240448 <set_next_buddy+56>: cmpl   $0x5,0x1f8(%rdi)
0xffffffffa024044f <set_next_buddy+63>: jne    0xffffffffa0240441 <set_next_buddy+49>
0xffffffffa0240451 <set_next_buddy+65>: pop    %rbp
0xffffffffa0240452 <set_next_buddy+66>: retq

私たちはそこにゾンビ空の殻にset_next_buddy機能の元のコールftrace_regs_caller実際には、それのように見えますが、役割はkpatchアンロードにあること...少しぎこちない参照してください。

私はここで解析機能しないftrace_regs_caller、それは実際には非常に簡単です、次にftraceの操作に内部レジスタの関数を呼び出し、中間パッケージです。

ここでの私のポイントは、ということです、カーネルのバイナリをベースにkpatch修正の次にftraceは、本番環境でこのメカニズムが、それは違い、私の純粋な手を欠いたが、それはまた、メモリの無駄かもしれませんが、管理者がそう言いました。

最後に、協会はそれをすべてとkprobe下に述べています。

標準はkprobeの実施形態であるが、同じことがライン上にありませんが、それは、以下のことを言及する価値があります。管理者は名前から判断すると信じているから、むしろ非常に興味深いです機能、より、デバッグ、単にプローブの支援をkprobe。

kpatch名は特に良い撮影し、それのコアはkpatch ftarceで、感謝の管理者は、管理者は、基礎となるkpatchは、ビューの点から次にftrace、次にftrace名を知っているならば、あなたは管理者が行いますどう思いますかこれ、正確に知りませんまだ決定?

私はまだ先週STAPリアルタイム統計システムのTCP接続がそれのキューの長さをデモすることを覚えておいてください:
https://blog.csdn.net/dog250/article/details/105022347
紙が、これは本番環境である理由は、行にすることはできません、と述べました逆運用管理者、STAP / kprobe INT3秋というデバッグ技術を使用しているため。

STAP / kprobeプローブは、命令の実行は、落下が発生したときに交換INT3命令ブレークポイントの機能/最初の文で、その後、非常に有害な操作で対応するハンドラを、実行します。我々は試すことができます。

stap -e 'probe kernel.statement("reqsk_queue_removed@include/net/request_sock.h:232") {printf("hit\n"}'

reqsk_queue_removedがインライン関数であるので、我々はinet_csk_reqsk_queue_prune、明確なDIS機能を呼び出します。

crash> dis inet_csk_reqsk_queue_prune
...
0xffffffff8156e25b <inet_csk_reqsk_queue_prune+347>:    lock incl 0x4(%r15)
// 下面的int3就是触发stap的指令
0xffffffff8156e260 <inet_csk_reqsk_queue_prune+352>:    int3  // 这就是
0xffffffff8156e261 <inet_csk_reqsk_queue_prune+353>:    testb  $0xfe,0x7b(%rsi)
0xffffffff8156e265 <inet_csk_reqsk_queue_prune+357>:    mov    0x418(%r12),%rax
...

私たちは再び、DIS同じ機能をSTAPを停止した場合は、命令が復元されるでしょう。

crash> dis inet_csk_reqsk_queue_prune
...
0xffffffff8156e25b <inet_csk_reqsk_queue_prune+347>:    lock incl 0x4(%r15)
0xffffffff8156e260 <inet_csk_reqsk_queue_prune+352>:    testb  $0xfe,0x7b(%r14)
0xffffffff8156e265 <inet_csk_reqsk_queue_prune+357>:    mov    0x418(%r12),%rax

しかし、このバイナリフックINT3は、それが何を問題ではありませんか?

あなたは慎重に観察した場合、私はその手計数統計iptablesのDROPオンラインLiveKpatchを実現し、これはそれのようにINT3ではありません。

  • 機能実行処理では、独自のロジックを挿入します。
    • INT3出力イベントを使用してSTAP
    • マイkpatch追加のロジックを実行するには、呼び出しスタブに挿入され、

はい、実際には、これらの効果は同じである、kprobeのために、また、代わりにオンライン・パッチに次にftraceの方法を使用するのでは、道にもはや使用INT3ある最適化のメカニズムがあります!

この点で、我々は明らかarm_kprobeから見ることができます:

/* Arm a kprobe with text_mutex */
static void __kprobes arm_kprobe(struct kprobe *kp)
{
    if (unlikely(kprobe_ftrace(kp))) {
        arm_kprobe_ftrace(kp);  // ftrace二进制hook的方式!
        return;
    }
    /*
     * Here, since __arm_kprobe() doesn't use stop_machine(),
     * this doesn't cause deadlock on text_mutex. So, we don't
     * need get_online_cpus().
     */
    mutex_lock(&text_mutex);
    __arm_kprobe(kp); //int3陷入的方式
    mutex_unlock(&text_mutex);
}

kpatch次にftraceは、なぜそれを使用しないkprobe次にftrace、本番環境を使用することができますので、まあ、私は言わなければならない、kprobeはINT3、それはまた、使用次にftraceすることができないのですか?管理者はいますか?


温州の靴は、雨水が脂肪ではありません、濡れました。

リリース1580元の記事 ウォンの賞賛5111 ビュー1113万+

おすすめ

転載: blog.csdn.net/dog250/article/details/105223447