この投稿では、 Linuxカーネルカーネルモジュール(LKM)が通常の実行可能ファイルではなく、いくつかのソースファイルから構築されているかどうかを判断する方法の兆候の検索について説明します。ソースの目的に関する情報がないか、意図的にそれを隠そうとしているとします。
Upd :コードの量は4 GBを
超えているため、カーネルモジュールを実装するソースのみをすばやく選択する必要があります。
#01 __KERNEL__
ソースコードをビルドすると、プリプロセッサシンボル
__KERNEL__が定義されます。
Alessandro RubiniとJonathan Corbet
がLinuxデバイスドライバーで書いているよう
に:「モジュールはどの標準ライブラリにもリンクしないため、モジュールのソースコードには通常のヘッダーファイルを含めないでください。 カーネルモジュールは、カーネルによってエクスポートされた関数のみを使用できます。 カーネルに関連するすべてのヘッダーファイルは、カーネルソースを含むディレクトリツリー内のinclude / linuxおよびinclude / asmディレクトリにあります(通常、これは/ usr / src / linuxディレクトリです)。
Linuxの以前のバージョン(libcバージョン5以前に基づく)は、/ usr / include / linuxおよび/ usr / include / asmからカーネルソースの実際のディレクトリへのシンボリックリンクをインストールしたため、libcヘッダーツリーはカーネルヘッダーファイルを参照できます。 。 これにより、必要に応じてユーザーアプリケーションにカーネルヘッダーファイルを含めることができました。
しかし、今でも、カーネルヘッダーファイルがアプリケーションプログラムで使用されるヘッダーファイルから分離されている場合、通常のヘッダーファイルにはない定義を利用するために、ユーザースペースで実行されているプログラムにそれらを含める必要がある場合があります。 ただし、カーネルヘッダーファイルのほとんどの定義はカーネルのみを参照し、これらの定義へのアクセスは#ifdef __KERNEL__ブロックで囲まれているため、通常のアプリケーションでは「見えません」。 ちなみに、これは、モジュールをビルドするときに__KERNEL__シンボルを定義する必要がある理由の1つです。
たとえば、「CFLAGS = -D__KERNEL__」という行がメイクファイルに存在する場合があります。
または、「-D__KERNEL__」はビルドログにあります。
#02モジュール
モジュールがコアに静的にリンクしない場合、文字列「-DMODULE」がCFLAGS変数に存在します。 このプリプロセッサシンボルは、
linux / module.hファイルを
含める前に定義する必要があります。
#03すべての名前は静的と宣言されており、一意のプレフィックスがあります
したがって、開発者はカーネル名前空間の「汚染」を回避します。そうしないと、デバッグ時に、すべてのカーネル名の中でモジュールの名前をキャッチする必要があります。 プレフィックスを使用すると、カーネル名前空間にすでに存在する名前と一致しない一意の名前を思い付く義務から解放されます。
#04 printk()
ソースコードでは、printf()関数の代わりにprintk()関数が使用されます。
「Linuxデバイスドライバー」は言う:「printk関数はカーネルで定義され、その動作はC標準ライブラリのprintf関数に似ています。なぜ、カーネルには独自の関数があるのですか? すべてが単純です-カーネル、これはC言語ライブラリの助けなしに構築されるスタンドアロンコードです。
#05 init_moduleおよびcleanup_module
「Linuxデバイスドライバー」は言う:
「アプリケーションは、最初から最後まで完全なタスクとして実行されます。 モジュールは、カーネルに登録するだけで、可能なリクエストを処理できるように準備し、その主な機能は呼び出しの直後に作業を終了します。 つまり、init_module関数(エントリポイント)のタスクは、後続の呼び出しのためにモジュール関数を準備することです。 コアに言っているようだ:「ねえ! 私はここにいます! これが私にできることです!」 モジュールへの2番目のエントリポイント-cleanup_module-は、モジュールがアンロードされる直前に呼び出されます。 彼女は核心を語る:「私は去る! 他のことを聞かないでください!」 „
更新:より信頼できる兆候は、テキスト内に
cleanup_module関数が存在することです。 この名前の関数は、「
init_module 」という名前の場合よりも約20倍少ない頻度で検出されます。 どうやら、「
init_module 」という名前は、カーネルモジュール作成者の間だけでなく人気があります。
#06 current->を使用する
「Linuxデバイスドライバー」は言う:
"<...>カーネルコードは、グローバル要素currentを介してモジュールにアクセスした現在のプロセスを判別できます。これは、ファイル<asm / current.h>のカーネル2.4で宣言されているstruct task_structへのポインターです。 現在のポインターは現在のユーザープロセスを参照しますreadやwriteなどのシステムコールを行う場合、現在のプロセスが呼び出しを行ったプロセスであり、カーネルは必要に応じて現在のポインターを使用して現在のプロセス情報を使用できます。
実際、以前のように、 currentはもはやグローバルカーネル変数ではありません。 開発者は、スタックに移動することにより、現在のプロセスを記述する構造へのアクセスを最適化しました。 現在の実装は<asm / current.h>ファイルにあります。 ただし、このファイルを調査する前に、LinuxはSMP互換システム(英語版SMP-Symmetric Multi-Processing)であるため、ここでは単純なグローバル変数は適用できないことに注意してください。 実装の詳細は他のカーネルサブシステムにありますが、デバイスドライバーは<linux / sched.h>ヘッダーファイルを添付して、 現在のポインターを参照できます。
モジュールの観点から見ると、 currentはprintkなどの通常の外部参照です。 モジュールは、適切と判断したときにcurrentを呼び出すことができます。 たとえば、次のコードはプロセス識別子(ID)とプロセスを開始したコマンドの名前を表示します。
printk( "プロセスは\"%s \ "(pid%i)\ n"、current-> comm、current-> pid);
コマンド名はcurrent-> commフィールドに保存され、プログラムファイルの名前を表します。
また、ソースレベルでのカーネルモジュールと実行可能ファイルのその他の違いは何ですか?
関連リンク:
iznakurnozhの 「Linux用ドライバーの作成」alexzoidbergによる 「組み込みLinux用のLCDディスプレイ用ドライバの作成」Lampusによる 「SPIバスの概要と組み込みLinuxのスレーブSPIデバイスのドライバー開発(パート1、概要)」Lampusによる 「SPIバスの概要と組み込みLinux用のスレーブSPIデバイスのドライバー開発(パート2、実用的)」Vorbの Linuxカーネルモジュールの操作sindoから
HTTPS用のカーネルモジュール(Netfilter)またはトランスペアレントプロキシを記述することを学ぶkmu1990の 「Linuxカーネルファイルシステムの作成」milabsによる「 DKOM を使用したLinuxカーネルモジュールのシンプルなマスキング」