Linuxカーネルへの埋め込み:システムコールのインターセプト

プログラミングおよびコンピューターテクノロジにおける「システムコール」という用語は、アプリケーションがオペレーティングシステム(OS)のカーネルを操作して操作を実行することを指します。 このような相互作用が主な相互作用であるという事実により、システムコールのインターセプトは統合の最も重要な段階であると思われます。 OSカーネルの重要なコンポーネントであるシステムコールインターフェイスを制御できます。これにより、カーネルサービスに対するアプリケーションソフトウェアのリクエストを検査できます。



この記事は、課せられたセキュリティ機能の実装、特にソフトウェアシステムへの統合の特定の問題に向けられた以前に発表されたサイクルの続きです。







I.アプローチの埋め込み





Linuxカーネルへのシステムコールをインターセプトするには、さまざまな方法があります。 まず、単一のシステムコールをインターセプトするために、カーネル関数をインターセプトする以前に検討された方法を使用できることに注意する価値があります。 実際、ほとんどのシステムコールは対応する関数( sys_openなど )で表されるため、それらをインターセプトするタスクは、これらの関数をインターセプトするタスクと同等です。 ただし、インターセプトされるシステムコールの数の増加と「ビジネスロジック」の複雑さにより、このアプローチは制限される場合があります。



より普遍的な方法は、システムコールのロジックを実装する関数へのポインターを含むシステムコールテーブルのエントリを変更することです(以下の表を参照)。 テーブルは、ハンドラー関数へのポインターが、後続の実行でアプリケーションによって要求されたシステムコールの数によって対応するテーブルから選択されるときに、スケジューリングでカーネルによって使用されます。 このようなポインターを置き換えると、システムコールの処理に関してカーネルのロジックを変更できます。 今後、このメソッドの実装を成功させるには、テーブル自体を何らかの方法で見つける必要があることに注意してください。 それらはエクスポートされません。 最終的に、システムコールのインターセプトは、単にテーブル要素をオーバーライドすることで構成されます。



システムコールをインターセプトする最も普遍的な方法は、システムコールマネージャコードの変更であり、システムサービスを要求するスレッドのコンテキストの前処理と後処理が提供されるように変更されたままです。 このオプションは、以前のオプションと比較して柔軟性を高めます。 ハンドラー関数の前後に状態監視の単一ポイントを導入します。



次に、ディスパッチャコードを変更してLinuxカーネルシステムコールをインターフェイスに埋め込む方法のを詳細に説明します。



II。 Linuxカーネルでのシステムコールのディスパッチ





システムコールのディスパッチは多くのニュアンスを持つかなり複雑なプロセスですが、ディスパッチプロセス自体(システムコールに対応する関数のフェッチと実行)を除いて、実装について他に何も知る必要がないため、この記事のフレームワークでは多くの詳細を省略します。



従来、Linuxカーネルは、x86アーキテクチャ向けに次のシステムコール機能をサポートしています。







以下は、使用したオプション応じて、私が借りたシステムコールの優れた例です。



画像



ご覧のとおり、32ビットアプリケーションはINT 80hおよびSYSENTERメカニズムを使用してシステムコールを行い、64ビットアプリケーションはSYSCALLを使用します。 同時に、64ビット環境で32ビットコードを実行する機能のサポートがあります(いわゆる互換モード-エミュレーション/互換モード;カーネルオプションCONFIG_IA32_EMULATION



)。 この点に関して、カーネルにはsys_call_table



ia32_sys_call_table



(エミュレーションモードでのみ使用可能)の2つのエクスポート不可能なテーブルがあり、システムコールを処理する関数のアドレスが含まれています。



一般的なケースでは、可能なすべてのメカニズムが64ビットカーネルで表される場合、対応するディスパッチャーのロジックを決定する4つのエントリポイントがあります。







何らかの方法で、アプリケーションがシステムコールを行うと、カーネルが制御を取得します。 考慮された各ケースのシステムコールマネージャは他のケースとは異なりますが、一般性を失うことなく、 system_callの例を使用して一般的な構造を検討できます。



  0xffffffff81731670 <+0>: swapgs 0xffffffff81731673 <+3>: mov %rsp,%gs:0xc000 0xffffffff8173167c <+12>: mov %gs:0xc830,%rsp 0xffffffff81731685 <+21>: sti 0xffffffff81731686 <+22>: data32 data32 xchg %ax,%ax 0xffffffff8173168a <+26>: data32 xchg %ax,%ax 0xffffffff8173168d <+29>: sub $0x50,%rsp 0xffffffff81731691 <+33>: mov %rdi,0x40(%rsp) 0xffffffff81731696 <+38>: mov %rsi,0x38(%rsp) 0xffffffff8173169b <+43>: mov %rdx,0x30(%rsp) 0xffffffff817316a0 <+48>: mov %rax,0x20(%rsp) 0xffffffff817316a5 <+53>: mov %r8,0x18(%rsp) 0xffffffff817316aa <+58>: mov %r9,0x10(%rsp) 0xffffffff817316af <+63>: mov %r10,0x8(%rsp) 0xffffffff817316b4 <+68>: mov %r11,(%rsp) 0xffffffff817316b8 <+72>: mov %rax,0x48(%rsp) 0xffffffff817316bd <+77>: mov %rcx,0x50(%rsp) 0xffffffff817316c2 <+82>: testl $0x100801d1,-0x1f78(%rsp) 0xffffffff817316cd <+93>: jne 0xffffffff8173181e <tracesys> 0xffffffff817316d3 <+0>: and $0xbfffffff,%eax 0xffffffff817316d8 <+5>: cmp $0x220,%eax /* <-------- cmp $__NR_syscall_max,%eax */ 0xffffffff817316dd <+10>: ja 0xffffffff817317a5 <badsys> 0xffffffff817316e3 <+16>: mov %r10,%rcx 0xffffffff817316e6 <+19>: callq *-0x7e7fec00(,%rax,8) /* <-------- call *sys_call_table(,%rax,8) */ 0xffffffff817316ed <+26>: mov %rax,0x20(%rsp) 0xffffffff817316f2 <+0>: mov $0x1008feff,%edi 0xffffffff817316f7 <+0>: cli 0xffffffff817316f8 <+1>: data32 data32 xchg %ax,%ax 0xffffffff817316fc <+5>: data32 xchg %ax,%ax 0xffffffff817316ff <+8>: mov -0x1f78(%rsp),%edx 0xffffffff81731706 <+15>: and %edi,%edx 0xffffffff81731708 <+17>: jne 0xffffffff81731745 <sysret_careful> 0xffffffff8173170a <+19>: mov 0x50(%rsp),%rcx 0xffffffff8173170f <+24>: mov (%rsp),%r11 0xffffffff81731713 <+28>: mov 0x8(%rsp),%r10 0xffffffff81731718 <+33>: mov 0x10(%rsp),%r9 0xffffffff8173171d <+38>: mov 0x18(%rsp),%r8 0xffffffff81731722 <+43>: mov 0x20(%rsp),%rax 0xffffffff81731727 <+48>: mov 0x30(%rsp),%rdx 0xffffffff8173172c <+53>: mov 0x38(%rsp),%rsi 0xffffffff81731731 <+58>: mov 0x40(%rsp),%rdi 0xffffffff81731736 <+63>: mov %gs:0xc000,%rsp 0xffffffff8173173f <+72>: swapgs 0xffffffff81731742 <+75>: sysretq
      
      







ご覧のとおり、最初の命令( swapgs



)はデータ構造を(ユーザーからswapgs



)切り替えます。 次に、スタックが構成され、割り込みが許可され、処理中に必要なレジスタフローコンテキスト( pt_regs



構造体)がスタック上に形成されます。 上記のリストに戻って、次のコマンドに特別な注意を払う必要があります。



  0xffffffff817316d8 <+5>: cmp $0x220,%eax /* <-------- cmp $__NR_syscall_max,%eax */ 0xffffffff817316dd <+10>: ja 0xffffffff817317a5 <badsys> 0xffffffff817316e3 <+16>: mov %r10,%rcx 0xffffffff817316e6 <+19>: callq *-0x7e7fec00(,%rax,8) /* <-------- call *sys_call_table(,%rax,8) */ 0xffffffff817316ed <+26>: mov %rax,0x20(%rsp)
      
      







最初の行は、要求されたシステムコール番号(レジスタ%rax



%rax



最大許容値( __NR_syscall_max



)と%rax



かどうかを確認します。 検証が成功した場合、システムコールがディスパッチされます。つまり、コントロールは対応するロジックを実装する関数に移動します。



したがって、システムコールを処理するプロセスのキーポイントはディスパッチコマンドであり、これは関数call *sys_call_table(,%rax,8)



call *sys_call_table(,%rax,8)



)です。 このコマンドを変更して、さらに埋め込みを実行します。



III。 埋め込み方法





前述のように、ディスパッチャに埋め込む普遍的な方法は、システムコール(処理)のロジックを実装する機能を実行する前、およびその実行(後処理)の機能を実行する前に、スレッドのコンテキストを制御する機能を提供するようにコードを変更することです。



説明した方法で埋め込みを実装するには、ディスパッチコマンドを変更して( call *sys_call_table(,%rax,8)



)ディスパッチャをわずかにパッチし、その上に無条件ジャンプコマンド( JMP REL32



)をservice_stub



ハンドラに書き込むことをおJMP REL32



ます。 この場合、そのようなハンドラーの一般的な構造は次のようになります(以下、擬似コードと呼びます)。



 system_call: swapgs .. jmp service_stub /* <--------   call *sys_call_table(,%rax,8) */ mov %rax,0x20(%rsp) /* <--------     service_stub */ ... swapgs sysretq service_stub: ... call ServiceTraceEnter /* void ServiceTraceEnter(struct pt_regs *) */ ... call sys_call_table[N](args) ... call ServiceTraceLeave(regs) /* void ServiceTraceLeave(struct pt_regs *) */ ... jmp back
      
      







ここで、 ServiceTraceEnter()



ServiceTraceLeave()



は、それぞれ前処理関数と後処理関数です。 それらのパラメーターは、ストリームのコンテキストを表すレジスター構造であるpt_regs



へのポインターです。 最後の命令は、このハンドラーの呼び出しが以前に行われた場所から、システム呼び出しディスパッチャーコードに制御を移すコマンドです。



以下は、 system_call



(SYSCALL命令)をインターセプトするための例として使用されるservice_syscall64ハンドラーコードです。



 .global service_syscall64 service_syscall64: SAVE_REST movq %rsp, %rdi call ServiceTraceEnter RESTORE_REST LOAD_ARGS 0 movq %r10, %rcx movq ORIG_RAX - ARGOFFSET(%rsp), %rax call *0x00000000(,%rax,8) // origin call movq %rax, RAX - ARGOFFSET(%rsp) SAVE_REST movq %rsp, %rdi call ServiceTraceLeave RESTORE_REST movq RAX - ARGOFFSET(%rsp), %rax jmp 0x00000000
      
      







ご覧のように、上記の構造を持っています。 ポインターとオフセットの正確な値は、モジュールのロードプロセス中に構成されます(これについては後で説明します)。 さらに、上記のフラグメントには追加の要素( SAVE_REST



RESTORE_REST



LOAD_ARGS



)が含まれています。その目的は、主にServiceTraceEnter



およびServiceTraceLeave



呼び出す前にストリームのコンテキスト( pt_regs



)をServiceTraceLeave



です。



IV。 埋め込みの機能





何らかの方法でLinuxカーネルのシステムコールのスケジューリングメカニズムに統合を実装すると、次の実際的な問題を解決する必要があります。







システムコールディスパッチャのアドレスの定義



システムに複数のディスパッチャが存在することは、アドレスを決定する必要があることを意味します。 上記のように、各ディスパッチャは、システムコールを要求する独自の「メソッド」に対応していることに注意してください。 したがって、適切なメカニズムを使用して、必要なアドレスを決定します。







したがって、必要なアドレスはそれぞれ簡単に決定されます。



システムコールディスパッチテーブルのアドレスの決定



上記のように、 sys_call_table



およびia32_sys_call_table



エクスポートさia32_sys_call_table



ません。 アドレスを決定するにはさまざまな方法がありますが、前のステップでディスパッチャのアドレスを決定すると、テーブルcall sys_call_table[N]



という形式のディスパッチ命令を検索するだけで決定されます。



これらの目的のために、逆アセンブラーudis86 )を使用するのが合理的です。 最初の命令から順に命令を順番にソートすることにより、目的のコマンドに到達できます。そのコマンドの引数は対応するテーブルのアドレスになります。 ディスパッチャーの構造が十分に確立されているという事実により、目的のコマンド(長さ7バイトのCALL)の特性を明確に決定し、高度な信頼性で必要なテーブルアドレス値を取得することができます。



何らかの理由でこれで十分でない場合は、受信したアドレスの検証を強化できます。 これを行うには、たとえば、提案されたテーブルの番号__NR_open



持つセルの値がsys_open関数のアドレスと等しいかどうかを確認できます。 ただし、この例では、このような追加のチェックは実行されません。



ディスパッチャコードの変更



システムコールディスパッチャのコードを変更する場合、コードが読み取り専用(ReadOnly)であることを考慮する必要があります。 さらに、動作中のシステムでのコード変更はアトミックである必要があります そのため、変更プロセス中に、スレッドのいずれかが部分的に完了したレコードを見たときに、未定義の状態はありません。



前回の記事では、一時表示の作成を使用して書き込み保護されたページに書き込む正しい方法について説明しました。 ここで何かを繰り返す必要はありません。 原子性に関しては、この問題は核の機能の傍受のトピックが検討されときに以前に議論されました。



したがって、一時的なマッピングとLinuxカーネルの特別なインターフェースstop_machine



を使用して、書き込み保護されたコードを変更することをお勧めします。



ハンドラーの構成



提示された埋め込み方法に従って、7バイトのディスパッチコマンドCALL MEM32



が対応するJMP REL32



ハンドラーへの5バイトの無条件ジャンプコマンドに置き換えられるように、各ディスパッチャーのコードが変更されます。 その結果、移行の範囲に特定の制限が課せられます。 ハンドラーは、 JMP REL32



場所から±2 GB以内に配置する必要があります。



ハンドラーの構造に応じて、正確な引数を指定する必要があるコマンド(JMPおよびCALL)が含まれています(たとえば、アドレスまたはシステムコールテーブルのアドレスを返す)。 このような値は、モジュールのコンパイルまたはロードの段階では利用できないため、ロード後、作業を​​開始する前に「手動」で付加する必要があります。



ハンドラーを設定する際のもう1つの重要な機能は、システムの正常性を維持しながらモジュールをアンロードできるようにする必要があることです。 これらの目的のために、メインモジュールをアンロードした後でも、ハンドラコードはシステムに残っている必要があります(これについては後で説明します)。



モジュールをアンロードする



システムのメンテナンス中にモジュールのアンロードを実行する必要があります。 これは、モジュールがアンロードされた後、システムが正常に機能することを意味します。 このタスクは、モジュールのアンロードにより、モジュールで使用されるすべてのコードがアンロードされるため、簡単ではありません。



たとえば、システムコールを行うスレッドがカーネル内でスリープ状態になる状況を想像できます。 彼が目覚める瞬間まで、誰かがモジュールをアンロードしようとします。 原則として、システムがこれを行うことを妨げるものはありません。 その結果、問題のスレッドがウェイクアップし、要求されたシステムコールを完了すると、制御は適切なハンドラに戻ります(そのため、アンロードしないでください)。



ただし、モジュールのアンロード後にシステムの操作性を維持するための条件は、ハンドラーのコードをアンロードしないことだけではありません。 ハンドラー内の実際のシステムコールは、 ServiceTraceEnterおよびServiceTraceLeaveトレース関数への2、3の呼び出しで「ラップ」され、そのコードはアンロードされたモジュールにあったことを思い出しください



したがって、システムコールから戻るときに、スレッドが物理的に存在しない関数を呼び出そうとする状況に陥らないようにするために、各ハンドラーのコードを繰り返し変更し、それから無効な呼び出しを排除する必要があります(つまり、NOPでそれらを詰まらせます)。



V.カーネルモジュールの実装の機能





次に、Linuxカーネルカーネルシステムコールディスパッチメカニズムを実装するカーネルモジュールの構造について説明します。



モジュールの主要な構造はstruct scentryです。これは、対応するディスパッチャーに埋め込むために必要な情報を含む構造です。 構造には次のフィールドが含まれます。



 typedef struct scentry { const char *name; const void *entry; const void *table; const void *pcall; void *pcall_map; void *stub; const void *handler; void (*prepare)(struct scentry *); void (*implant)(struct scentry *); void (*restore)(struct scentry *); void (*cleanup)(struct scentry *); } scentry_t;
      
      







構造は、どのパラメーターをどのように埋め込むかを決定する配列に結合されます。



 scentry_t elist[] = { ... { .name = "system_call", /* SYSCALL: MSR(LSTAR), kernel/entry_64.S (1) */ .handler = service_syscall64, .prepare = prepare_syscall64_1 }, { .name = "system_call", /* SYSCALL: MSR(LSTAR), kernel/entry_64.S (2) */ .handler = service_syscall64, .prepare = prepare_syscall64_2 }, ... };
      
      







指定されたフィールドを除き、構造の残りの要素は自動的に入力されます-これはprepare



機能が担当します。 以下は、SYSCALLコマンドのディスパッチャへの埋め込みを準備する関数の実装です。



 extern void service_syscall64(void); static void prepare_syscall64_1(scentry_t *se) { /* * searching for -- 'call *sys_call_table(,%rax,8)' * http://lxr.free-electrons.com/source/arch/x86/kernel/entry_64.S?v=3.13#L629 */ se->entry = get_symbol_address(se->name); se->entry = se->entry ? se->entry : to_ptr(x86_get_msr(MSR_LSTAR)); if (!se->entry) return; se->pcall = ud_find_insn(se->entry, 512, UD_Icall, 7); if (!se->pcall) return; se->table = to_ptr(*(int *)(se->pcall + 3)); }
      
      







ご覧のとおり、まず、シンボル名をそれに対応するアドレスに解決しようとします( se->entry



)。 この方法でアドレスを決定できない場合、各ディスパッチャに固有のメカニズムが機能します(この場合、番号MSR_LSTARのMSRレジスタを読み取ります)。



次に、見つかったディスパッチャについて、ディスパッチコマンドが検索され( se->pcall



)、成功すると、ディスパッチャが使用するシステムコールテーブルのアドレスが決定されます。



準備フェーズの完了は、修正後にディスパッチャが使用するハンドラコードの作成です。 以下は、これを行うstub_fixup関数です。



 static void fixup_stub(scentry_t *se) { ud_t ud; memset(se->stub, 0x90, STUB_SIZE); ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, se->handler, STUB_SIZE); while (ud_disassemble(&ud)) { void *insn = se->stub + ud_insn_off(&ud); const void *orig_insn = se->handler + ud_insn_off(&ud); memcpy(insn, orig_insn, ud_insn_len(&ud)); /* fixup sys_call_table dispatcher calls (FF.14.x5.xx.xx.xx.xx) */ if (ud.mnemonic == UD_Icall && ud_insn_len(&ud) == 7) { x86_insert_call(insn, NULL, se->table, 7); continue; } /* fixup ServiceTraceEnter/Leave calls (E8.xx.xx.xx.xx) */ if (ud.mnemonic == UD_Icall && ud_insn_len(&ud) == 5) { x86_insert_call(insn, insn, orig_insn + (long)(*(int *)(orig_insn + 1)) + 5, 5); continue; } /* fixup jump back (E9.xx.xx.xx.xx) */ if (ud.mnemonic == UD_Ijmp && ud_insn_len(&ud) == 5) { x86_insert_jmp(insn, insn, se->pcall + 7); break; } } se->pcall_map = map_writable(se->pcall, 64); }
      
      







ご覧のとおり、この関数の主な役割は、ハンドラーのコピーを作成し、ハンドラーを現在のアドレスに構成することです。 ここでは逆アセンブラも積極的に使用されています。 ハンドラーの単純な構造により、複雑なロジックなしで実行できます。 ループを終了する信号は、ディスパッチャに制御を返すJMP REL32



検出です。



準備段階の後に、コードをカーネルコードに埋め込む段階が続きます。 このフェーズは非常に単純で、各システムサービスのコードに単一の命令( JMP REL32



)を記述することで構成されます。



モジュールがアンロードされると、 復元フェーズが最初に実行されます。これは、システムコールディスパッチャーのコードの復元と、ハンドラーのコードの変更で構成されます。



 static void generic_restore(scentry_t *se) { ud_t ud; if (!se->pcall_map) return; ud_initialize(&ud, BITS_PER_LONG, \ UD_VENDOR_ANY, se->stub, STUB_SIZE); while (ud_disassemble(&ud)) { if (ud.mnemonic == UD_Icall && ud_insn_len(&ud) == 5) { memset(se->stub + ud_insn_off(&ud), 0x90, ud_insn_len(&ud)); continue; } if (ud.mnemonic == UD_Ijmp) break; } debug(" [o] restoring original call instruction %p (%s)\n", se->pcall, se->name); x86_insert_call(se->pcall_map, NULL, se->table, 7); }
      
      







ご覧のとおり、ハンドラコードでは、見つかった5バイトのCALLコマンドはすべてNOPのシーケンスに置き換えられ、システムコールから戻ったときに存在しないコードを実行しようとする試みを除外します。 これについては前に説明しました。



注入および復元フェーズに対応する機能はstop_machine



のコンテキストで実行されるため、使用するすべてのマッピングを事前に準備する必要があります。



アンロード中の最後のフェーズはclenupフェーズで、内部リソース( pcall_map



)がpcall_map



ます。



モジュールがアンロードされた後、ハンドラーのコードを含む領域は常にカーネルメモリに残ることに注意してください。 前述のように、これはモジュールをアンロードした後にシステムを正しく動作させるための前提条件です。



したがって、カーネルシステムコールをシステムコールのメカニズムに埋め込む基本原則を例を使用して分析し、それらをインターセプトする可能性を示します。



VI。 テストとデバッグ





テストのために、 open(2)



システムコールをインターセプトします。 以下は、 ServiceTraceEnterハンドラーを使用してこのインターセプトを実装するtrace_syscall_entry関数です。



 static void trace_syscall_entry(int arch, unsigned long major, \ unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) { char *filename = NULL; if (major == __NR_open || major == __NR_ia32_open) { filename = kmalloc(PATH_MAX, GFP_KERNEL); if (!filename || strncpy_from_user(filename, (const void __user *)a0, PATH_MAX) < 0) goto out; printk("%s open(%s) [%s]\n", arch ? "X86_64" : "I386", filename, current->comm); } out: if (filename) kfree(filename); } void ServiceTraceEnter(struct pt_regs *regs) { if (IS_IA32) trace_syscall_entry(0, regs->orig_ax, \ regs->bx, regs->cx, regs->dx, regs->si); #ifdef CONFIG_X86_64 else trace_syscall_entry(1, regs->orig_ax, \ regs->di, regs->si, regs->dx, regs->r10); #endif }
      
      







モジュールの組み立てとロードは、標準的な方法で実行されます。



 $ git clone https://github.com/milabs/kmod_hooking_sct $ cd kmod_hooking_sct $ make $ sudo insmod scthook.ko
      
      







その結果、次の情報がカーネルログに表示されるはずです( dmesg



):



 [ 5217.779766] [scthook] # SYSCALL hooking module [ 5217.780132] [scthook] # prepare [ 5217.785853] [scthook] [o] prepared stub ffffffffa000c000 (ia32_syscall) [ 5217.785856] [scthook] entry:ffffffff81731e30 pcall:ffffffff81731e92 table:ffffffff81809cc0 [ 5217.790482] [scthook] [o] prepared stub ffffffffa000c200 (ia32_sysenter_target) [ 5217.790484] [scthook] entry:ffffffff817319a0 pcall:ffffffff81731a36 table:ffffffff81809cc0 [ 5217.794931] [scthook] [o] prepared stub ffffffffa000c400 (ia32_cstar_target) [ 5217.794933] [scthook] entry:ffffffff81731be0 pcall:ffffffff81731c75 table:ffffffff81809cc0 [ 5217.797517] [scthook] [o] prepared stub ffffffffa000c600 (system_call) [ 5217.797518] [scthook] entry:ffffffff8172fcb0 pcall:ffffffff8172fd26 table:ffffffff81801400 [ 5217.800013] [scthook] [o] prepared stub ffffffffa000c800 (system_call) [ 5217.800014] [scthook] entry:ffffffff8172fcb0 pcall:ffffffff8172ff38 table:ffffffff81801400 [ 5217.800014] [scthook] # prepare OK [ 5217.800015] [scthook] # implant [ 5217.800052] [scthook] [o] implanting jump to stub handler ffffffffa000c000 (ia32_syscall) [ 5217.800054] [scthook] [o] implanting jump to stub handler ffffffffa000c200 (ia32_sysenter_target) [ 5217.800054] [scthook] [o] implanting jump to stub handler ffffffffa000c400 (ia32_cstar_target) [ 5217.800055] [scthook] [o] implanting jump to stub handler ffffffffa000c600 (system_call) [ 5217.800056] [scthook] [o] implanting jump to stub handler ffffffffa000c800 (system_call) [ 5217.800058] [scthook] # implant OK
      
      







open(2)



インターセプトを正しく開発すると、同じログに次のようなメッセージが表示されます。



 [ 5370.999929] X86_64 open(/usr/share/locale-langpack/en_US.utf8/LC_MESSAGES/libc.mo) [perl] [ 5370.999930] X86_64 open(/usr/share/locale-langpack/en_US/LC_MESSAGES/libc.mo) [perl] [ 5370.999932] X86_64 open(/usr/share/locale-langpack/en.UTF-8/LC_MESSAGES/libc.mo) [perl] [ 5370.999934] X86_64 open(/usr/share/locale-langpack/en.utf8/LC_MESSAGES/libc.mo) [perl] [ 5370.999936] X86_64 open(/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo) [perl] [ 5371.001308] X86_64 open(/etc/login.defs) [cron] [ 5372.422399] X86_64 open(/home/ilya/.cache/awesome/history) [awesome] [ 5372.424013] X86_64 open(/dev/null) [awesome] [ 5372.424682] I386 open(/etc/ld.so.cache) [skype] [ 5372.424714] I386 open(/usr/lib/i386-linux-gnu/libXv.so.1) [skype] [ 5372.424753] I386 open(/usr/lib/i386-linux-gnu/libXss.so.1) [skype] [ 5372.424789] I386 open(/lib/i386-linux-gnu/librt.so.1) [skype] [ 5372.424827] I386 open(/lib/i386-linux-gnu/libdl.so.2) [skype] [ 5372.424856] I386 open(/usr/lib/i386-linux-gnu/libX11.so.6) [skype] [ 5372.424896] I386 open(/usr/lib/i386-linux-gnu/libXext.so.6) [skype] [ 5372.424929] I386 open(/usr/lib/i386-linux-gnu/libQtDBus.so.4) [skype] [ 5372.424961] I386 open(/usr/lib/i386-linux-gnu/libQtWebKit.so.4) [skype] [ 5372.425003] I386 open(/usr/lib/i386-linux-gnu/libQtXml.so.4) [skype] [ 5372.425035] I386 open(/usr/lib/i386-linux-gnu/libQtGui.so.4) [skype] [ 5372.425072] I386 open(/usr/lib/i386-linux-gnu/libQtNetwork.so.4) [skype] [ 5372.425103] I386 open(/usr/lib/i386-linux-gnu/libQtCore.so.4) [skype] [ 5372.425151] I386 open(/lib/i386-linux-gnu/libpthread.so.0) [skype] [ 5372.425191] I386 open(/usr/lib/i386-linux-gnu/libstdc++.so.6) [skype] [ 5372.425233] I386 open(/lib/i386-linux-gnu/libm.so.6) [skype] [ 5372.425265] I386 open(/lib/i386-linux-gnu/libgcc_s.so.1) [skype] [ 5372.425292] I386 open(/lib/i386-linux-gnu/libc.so.6) [skype] [ 5372.425338] I386 open(/usr/lib/i386-linux-gnu/libxcb.so.1) [skype] [ 5372.425380] I386 open(/lib/i386-linux-gnu/libdbus-1.so.3) [skype] [ 5372.425416] I386 open(/lib/i386-linux-gnu/libz.so.1) [skype] [ 5372.425444] I386 open(/usr/lib/i386-linux-gnu/libXrender.so.1) [skype] [ 5372.425475] I386 open(/usr/lib/i386-linux-gnu/libjpeg.so.8) [skype] [ 5372.425510] I386 open(/lib/i386-linux-gnu/libpng12.so.0) [skype] [ 5372.425546] I386 open(/usr/lib/i386-linux-gnu/libxslt.so.1) [skype] [ 5372.425579] I386 open(/usr/lib/i386-linux-gnu/libxml2.so.2) [skype]
      
      







, 32- (, Skype) , , I386, X86_64. , open(2)



.



VII. おわりに





Linux , . , , , . .



, , github .



All Articles