2009-07-28 14 views
13

私の夏の研究のために、カーネルの作業に取り掛かりました。私たちは、特定のRTT計算でTCPを変更しようとしています。私がしたいのは、tcp_input.cの関数の1つの解決を、動的にロードされたカーネルモジュールが提供する関数に置き換えることです。私はこれが修正を開発して配布するペースを改善すると思う。Linuxカーネル機能をモジュールに置き換えることはできますか?

私が興味を持っている機能は静的であると宣言されていましたが、非静的な関数でカーネルを再コンパイルし、EXPORT_SYMBOLによってエクスポートしました。これは、関数がカーネルの他のモジュール/パーツにアクセスできるようになったことを意味します。私はこれを "cat/proc/kallsyms"で確認しました。

今や、シンボルアドレスを最初から動的にロードされた関数に書き換えることができるモジュールをロードすることができます。同様に、モジュールをアンロードするときは、元のアドレスを復元します。これは実行可能なアプローチですか?どのようにしてこれをよりうまく実装できるか、皆さんに提案がありますか?

ありがとうございます!

Overriding functionality with modules in Linux kernel

編集と同じ:
これは私の最終的なアプローチでした。
は、次の関数を考えると(私は上書きしたかったし、エクスポートされません):

static void internal_function(void) 
{ 
    // do something interesting 
    return; 
} 

そうのように変更します。

static void internal_function_original(void) 
{ 
    // do something interesting 
    return; 
} 

static void (*internal_function)(void) = &internal_function_original; 
EXPORT_SYMBOL(internal_function); 

これは(期待される機能識別子としてではなく、関数ポインタを再定義しています同様の方法で呼び出すことができます)。 EXPORT_SYMBOL()はアドレスをグローバルにアクセス可能にするため、モジュール(または他のカーネルの場所)から変更することができます。

今、あなたは、次の形式でカーネルモジュールを書くことができます。

static void (*original_function_reference)(void); 
extern void (*internal_function)(void); 

static void new_function_implementation(void) 
{ 
    // do something new and interesting 
    // return 
} 

int init_module(void) 
{ 
    original_function_reference = internal_function; 
    internal_function   = &new_function_implementation; 
    return 0; 
} 

void cleanup_module(void) 
{ 
    internal_function = original_function_reference; 
} 

このモジュールは、動的にロードされたバージョンと元の実装を置き換えます。アンロードすると、元の参照(および実装)が復元されます。私の具体的なケースでは、私はRTTの新しい推定値をTCPに提供しました。モジュールを使用することで、カーネルを再コンパイルして再起動することなく、小さな調整と再テストを行うことができます。

答えて

7

私はそれがうまくいくかどうかはわかりません - あなたが置き換えたい関数への内部呼び出しのシンボル解決は、モジュールが読み込まれるまでにすでに完了していると思います。

代わりに、既存の関数の名前を変更してから、関数の元の名前でグローバル関数ポインタを作成してコードを変更できます。関数のポインタを内部関数のアドレスに初期化して、既存のコードを変更しないで動作させます。グローバル関数ポインタのシンボルをエクスポートすると、モジュールはモジュールのロードとアンロード時に代入によってその値を変更できます。

+2

グローバルフックを追加する際に提案したルートを終了しました。実装が簡単で、必要なものを正確に提供しました。 シンボル解決に関する情報をありがとう。私は、シンボルテーブルがどのようにいつアクセスされたか(各関数呼び出し時またはリンケージ時のみ)を明確に説明したソースは見つかりませんでした。これは役に立つアドバイスです。 –

2

あなたが望むのはKprobeだと思います。

カフェで言及されている別の方法は、元のルーチンにフックを追加し、モジュールにフックを登録/登録解除することです。

+0

Kprobeは面白くて便利なツールのようです。リンクありがとう。 私は別のルートをとっていましたが、これは効果的なアプローチだったと思います。 –

3

kspliceを試してみることもできます。静的にする必要はありません。

+0

無料ではありません。 FOSSの代替品がありますか? –

3

私は以前、カーネル機能の代わりに独自の機能を挿入したハイジャックモジュールの概念の証明をしました。 新しいカーネル・タイピング・アーキテクチャーは非常に似たシステムを使用しています。

私は自分のカスタム関数を指すジャンプで最初の2バイトのコードを上書きすることで、自分自身の関数をカーネルに挿入しました。実際の関数が呼び出されるとすぐに、それは元の関数と呼ばれる関数を実行した後に私の関数にジャンプします。


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

#define CODESIZE 12 

static unsigned char original_code[CODESIZE]; 
static unsigned char jump_code[CODESIZE] = 
    "\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00" /* movq $0, %rax */ 
    "\xff\xe0"           /* jump *%rax */ 
     ; 
/* FILL THIS IN YOURSELF */ 
int (*real_printk)(char * fmt, ...) = (int (*)(char *,...))0xffffffff805e5f6e; 

int hijack_start(void); 
void hijack_stop(void); 
void intercept_init(void); 
void intercept_start(void); 
void intercept_stop(void); 
int fake_printk(char *, ...); 


int hijack_start() 
{ 
    real_printk(KERN_INFO "I can haz hijack?\n"); 
    intercept_init(); 
    intercept_start(); 

    return 0; 
} 

void hijack_stop() 
{ 
    intercept_stop(); 
    return; 
} 

void intercept_init() 
{ 
    *(long *)&jump_code[2] = (long)fake_printk; 
    memcpy(original_code, real_printk, CODESIZE); 

    return; 
} 

void intercept_start() 
{ 
    memcpy(real_printk, jump_code, CODESIZE); 
} 

void intercept_stop() 
{ 
    memcpy(real_printk, original_code, CODESIZE); 
} 

int fake_printk(char *fmt, ...) 
{ 
    int ret; 
    intercept_stop(); 
    ret = real_printk(KERN_INFO "Someone called printk\n"); 
    intercept_start(); 
    return ret; 
} 

module_init(hijack_start); 
module_exit(hijack_stop); 

このようなことを試してみると、カーネルパニックやその他の悲惨な出来事に注意してください。私は仮想化された環境でこれを行うことをお勧めします。これは私が少し前に書いた概念実証コードであり、まだ動作するかどうかはわかりません。

これは本当に簡単な原則ですが、非常に効果的です。もちろん、実際のソリューションでは、上書きしている間に誰も関数を呼び出さないようにロックを使用します。

楽しくお楽しみください!

関連する問題