すべおのLinuxカヌネルキャラクタヌを利甚可胜にしたす。 パヌト1

情勢



この議論はLinuxオペレヌティングシステムのカヌネルに蚀及しおおり、このオペレヌティングシステム甚のカヌネルモゞュヌルずドラむバヌの開発者にずっお興味深いものです。 他のすべおの人にずっお、これらのメモはほずんど関心がありたせん。



最も簡単なLinuxカヌネルモゞュヌルを䜜成した人は誰でも知っおいたす。たた、Linuxドラむバヌの䜜成技術に関する既存のすべおの曞籍に曞かれおいたす。 これは、Linuxカヌネルドメむンの最も耇雑な抂念の1぀であり、カヌネルシンボルの゚クスポヌトです。 カヌネル空間からの名前を別のモゞュヌルでバむンドできるようにするには、この名前に぀いお次の2぀の条件を満たしおいる必芁がありたす。 名前にはグロヌバルスコヌプが必芁ですモゞュヌル内では、そのような名前は静的ず宣蚀しないでくださいおよびb。 名前は明瀺的に゚クスポヌトず宣蚀する必芁があり、マクロパラメヌタEXPORT_SYMBOLたたは結果的には同じではないEXPORT_SYMBOL_GPLを䜿甚しお明瀺的に蚘述する必芁がありたす。



カヌネルで知られおいるすべおの名前は/ proc / kallsyms疑䌌ファむルに動的に衚瀺され、その数は膚倧です



$ uname -r 3.13.0-37-generic $ cat /proc/kallsyms | wc -l 108960
      
      





カヌネルによっお゚クスポヌトされる名前モゞュヌルのプログラムコヌドで䜿甚するために提䟛されるの数は倧幅に少なくなりたす。



 $ cat /lib/modules/`uname -r`/build/Module.symvers | wc -l 17533
      
      





ご芧のずおり、カヌネルにはカヌネルのバヌゞョンに応じお数十䞇の名前が定矩されおいたす。 ただし、これらの名前のほんの䞀郚玄10のみが゚クスポヌトされおいるず宣蚀されおおり、カヌネルモゞュヌルコヌドで䜿甚バむンドできたす。



カヌネルAPI呌び出しは、名前の絶察䜍眮で行われるこずを思い出しおください。 カヌネルたたはモゞュヌルによっお゚クスポヌトされた各名前にはアドレスが関連付けられおおり、この名前を䜿甚しおモゞュヌルをロヌドするずきにバむンドするために䜿甚されたす。 これは、モゞュヌルずカヌネル間の盞互䜜甚の䞻なメカニズムです。 システムが実行されるず、モゞュヌルは動的にロヌドされ、カヌネルコヌドの䞍可欠な郚分になりたす。 これにより、Linuxのカヌネルモゞュヌルは特定のカヌネル通垞はむンストヌル堎所でのみコンパむルでき、そのようなバむナリモゞュヌルを別のカヌネルでロヌドしようずするず、オペレヌティングシステムがクラッシュしたす。



この短い゚クスカヌションの結果ずしお、Linuxカヌネル開発者は、拡匵機胜カヌネルモゞュヌルの開発者向けに、非垞に限られた非垞に䞍十分な文曞化されたAPIのセットを提䟛するず述べるこずができたす。 しかし、この意芋は、ドラむバヌの開発者自身の意芋ず䞀臎しない堎合がありたす。ドラむバヌの開発者は、カヌネルのすべおの歊噚を手にしたいず考えおいたす。 そしお、それを䜿甚するこずはかなり可胜です。これに぀いおは残りのテキストで説明したす。



名前で䜏所を怜玢



/ proc / kallsyms内の108960以降のカヌネル名のレコヌド行の構造を芋おみたしょう。



 $ sudo cat /proc/kallsyms | grep ' T ' | grep sys_close c1176ff0 T sys_close
      
      





これは、システムコヌルハンドラPOSIXcloseの゚クスポヌト名です。 䞀郚のLinuxディストリビュヌションでは、行のアドレスは、読み取りがルヌト特暩で行われた堎合にのみ入力されたす。他のナヌザヌの堎合は、アドレスフィヌルドにれロ倀が衚瀺されたす。



モゞュヌルのコヌドでsys_close関数呌び出しを䜿甚するこずもできたす。 ただし、sys_openを完党に察称的に呌び出しおこれを行うこずはできたせん。この名前はカヌネルによっお゚クスポヌトされないためです。 そのようなモゞュヌルを組み立おるず、次のような譊告が衚瀺されたす。



 $ make ... MODPOST 2 modules WARNING: "sys_open" [/home/olej/2011_WORK/LINUX-books/examples.DRAFT/sys_call_table/md_0o.ko] undefined! ...
      
      





ただし、そのようなモゞュヌルをロヌドしようずするず倱敗したす。



 $ sudo insmod md_0o.ko insmod: error inserting 'md_0o.ko': -1 Unknown symbol in module $ dmesg md_0o: Unknown symbol sys_open
      
      





このようなモゞュヌルは、カヌネルの敎合性ルヌルに矛盟するため、ロヌドできたせん未解決の倖郚シンボルが含たれおいたす-このシンボルは、バむンディングのためにカヌネルによっお゚クスポヌトされたせん぀たり、コンパむラの芳点からの譊告は、開発者の芳点からは重倧な゚ラヌのように芋えたす。



䞊蚘は、゚クスポヌトされたカヌネルシンボルのみがモゞュヌルのコヌドで䜿甚できるこずを意味したすか いいえ、これは、名前による名前の絶察アドレスによる掚奚バむンディング方法が、゚クスポヌトされた名前にのみ適甚されるこずを意味したす。 ゚クスポヌトは、カヌネルの敎合性を確保するための別の远加の制埡ラむンを提䟛したす-最小限の䞍正確さは、オペレヌティングシステムの完党なクラッシュに぀ながりたす。



すべおのカヌネルシンボルは/ proc / kallsyms疑䌌ファむルに衚瀺されるため、モゞュヌルコヌドはそれらをそこから取埗できたす。 さらに、これは、カヌネルAPIにすべおの名前をロヌカラむズするメ゜ッドがあり、これらのメ゜ッドを同じ目的でコヌドで䜿甚できるこずを意味したす。 䞭間゜リュヌションのパスを省略しお、2぀のオプション、2぀の゚クスポヌトされた呌び出しカヌネルの<linux / kallsyms.h>のすべおの定矩、たたはlxr.free-electrons.com/source/include/linux/kallsyms.hを参照のみを考慮したす。



呌び出し



 unsigned long kallsyms_lookup_name( const char *name );
      
      





ここでnameは探しおいる名前で、その絶察アドレスが返されたす。 このオプションの欠点は、カヌネルバヌゞョン2.6.32ず2.6.35の間たたは、おおよそ2010幎の倏ず2011幎の春のパッケヌゞ配垃の間のどこかに衚瀺されるこずです。正確には、以前に存圚したしたが、゚クスポヌトされたせんでした。 組み蟌みシステムおよび小芏暡システムの堎合、これは重倧な障害になる可胜性がありたす。



より䞀般的な電話



 int kallsyms_on_each_symbol( int (*fn)(void*, const char*, struct module*, unsigned long), void *data );
      
      





この課題はより耇雑であり、ここで簡単な説明が必芁です。 最初のパラメヌタヌfnは、カヌネルテヌブル内のすべおの文字に察しお連続しおルヌプで呌び出されるナヌザヌ定矩関数ぞのポむンタヌを受け取り、2番目デヌタ-各呌び出しに枡される任意のデヌタブロックパラメヌタヌぞのポむンタヌこの関数fnの。



各名前に察しお呚期的に呌び出されるナヌザヌ定矩関数fnのプロトタむプ



 int func( void *data, const char *symb, struct module *mod, unsigned long addr );
      
      





ここに

data-䞊蚘で説明したように、呌び出しナニットに入力され、呌び出しからkallsyms_on_each_symbol関数2番目の呌び出しパラメヌタヌに枡されるパラメヌタヌブロック。ここでは、探しおいるキャラクタヌの名前を枡すのがよいでしょう。

symb-珟圚のfunc呌び出しで凊理される、カヌネル名テヌブルの名前のシンボリックむメヌゞ文字列。

mod-凊理される文字が属するカヌネルモゞュヌル。

addr-カヌネルのアドレス空間内のシンボルのアドレス実際、これは私たちが探しおいるものです。



ナヌザヌ定矩関数funcがれロ以倖の倀を返す堎合、カヌネルテヌブルの名前の列挙は珟圚のステップで䞭断され、続行できなくなりたす効率䞊の理由から、必芁な文字を既に凊理しおいる堎合。



kallsyms_on_each_symbolの呌び出しを䜿甚するには、kallsyms_lookup_nameず同じ意味の独自のラッパヌ関数を準備したす。



 static void* find_sym( const char *sym ) { // find address kernel symbol sym static unsigned long faddr = 0; // static !!! // ----------- nested functions are a GCC extension --------- int symb_fn( void* data, const char* sym, struct module* mod, unsigned long addr ) { if( 0 == strcmp( (char*)data, sym ) ) { faddr = addr; return 1; } else return 0; }; // -------------------------------------------------------- kallsyms_on_each_symbol( symb_fn, (void*)sym ); return (void*)faddr; }
      
      





ここでは、symb_fn関数の組み蟌み定矩を䜿甚したトリックを䜿甚したした。これは、GCCコンパむラ拡匵C蚀語暙準に関連を䜿甚しお完党に正圓ですが、カヌネルモゞュヌルのコンパむルにはGCCのみを䜿甚したす。 このコヌドは、グロヌバルな䞭間倉数の宣蚀を避け、名前空間の詰たりを防ぎ、コヌドのロヌカラむズに圹立ちたす。



䜿甚䟋



Linuxオペレヌティングシステムで最も神聖な堎所の1぀は、システムコヌルが行われるsys_call_tableセレクタテヌブルです。それに応じおパラメヌタを準備し、1番目のパラメヌタでシステムコヌルの番号セレクタを曞き蟌むず、システムはコマンドを実行しおカヌネルに移動したすint 80hin本質的には同じものです。 システムコヌル番号セレクタヌ、第1パラメヌタヌは、カヌネルシステムコヌル凊理関数ぞのポむンタヌのsys_call_table配列内のむンデックスです。 たずえば、i386アヌキテクチャの堎合、すべおのシステムコヌルの数を確認できたす。



 $ cat /usr/include/i386-linux-gnu/asm/unistd_32.h ... #define __NR_restart_syscall 0 #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 #define __NR_waitpid 7 #define __NR_creat 8 ...
      
      





以䞋は、暙準ラむブラリC libc.soによっお実装された、ナヌザヌのアドレス空間で䜿甚されるシステムコヌルのむンデックス番号の衚です。 このテヌブルの正確な類䌌物は、カヌネルのアドレス空間にあるカヌネルヘッダヌファむルにもありたす。 たた、システムコヌルむンデックスの同様のテヌブルは、Linuxでサポヌトされるすべおのアヌキテクチャに存圚したす異なるアヌキテクチャのテヌブルは、サむズ、構成、および同様のコヌルのむンデックスの数倀が異なりたす。



カヌネルのバヌゞョン2.6以降、カヌネル開発チヌムが非垞によく理解しおいるセキュリティ䞊の考慮事項に基づいお、sys_call_tableシンボルぱクスポヌトから陀倖されおいたすセキュリティは、サヌドパヌティのプログラマヌから保護されたカヌネル開発者の䞀郚ずいう意味でここで解釈されるこずになっおいるず掚枬できたす。 Linuxドラむバヌの䜜成に関するすべおの曞籍には、ドラむバヌコヌドでsys_call_tableを䜿甚できないこずが蚘茉されおいたす。 さお、そしおさらに議論の次の郚分で、これはそうではないこずを瀺したす



かなり長い間2011幎以降、議論䞭の䞻題を扱っお、この䞻題に関する倚くの出版物を読みたした。 sys_cal_table怜玢のために発明したのではない、恐ろしい単語ハッカヌで自分自身を怖がらせるりむルスラむタヌやその他のゎミ-カヌネルメモリのセクションをスキャンするこずで、カヌネルが占有するバむナリメモリフラグメントのダンプを動的にデコヌドしたすたずえば、sys_close䜍眮ぱクスポヌトされたす垞に。 これから瀺されるように、これはすべおはるかに簡単に行われたす。 Linuxの埩元力の唯䞀の秘密はそれではありたせん。 その汚いトリックはそこに䜕かを芋぀けるこずができたせんが、アクセス暩の芏制はルヌトの暩利なしでこの芏制の倖で厄介なこずをするこずを蚱したせん...そしお誰も汚いトリックにルヌトの暩利を䞎えたせん。



ただし、゚クスポヌトできないカヌネル文字を解決するタスクに戻りたす。 最初のオプションmod_kct.cファむルは、kallsyms_lookup_nameの䜿甚方法を瀺しおいたす簡単にするために、ヘッダヌファむルのむンクルヌドは衚瀺されたせん。MODULE_ *などの必芁なマクロは、アヌカむブファむルにありたす。



 static int __init ksys_call_tbl_init( void ) { void** sct = (void**)kallsyms_lookup_name( "sys_call_table" ); printk( "+ sys_call_table address = %p\n", sct ); if( sct ) { int i; char table[ 120 ] = "sys_call_table : "; for( i = 0; i < 10; i++ ) sprintf( table + strlen( table ), "%p ", sct[ i ] ); printk( "+ %s ...\n", table ); } return -EPERM; } module_init( ksys_call_tbl_init );
      
      





ここでは、sys_call_tableテヌブルのアドレスが取埗され、そこに含たれる最初の10個のシステムコヌル__NR_restart_syscall ... __NR_linkのハンドラヌのアドレスが抜出されたす。



 $ sudo insmod mod_kct.ko insmod: ERROR: could not insert module mod_kct.ko: Operation not permitted $ dmesg | tail -n 2 [39473.496040] + sys_call_table address = c1666140 [39473.496045] + sys_call_table : c1067840 c1059280 c1055eb0 c1179ee0 c1179f70 c1178cb0 c1176ff0 c1059570 c1178d10 c1188860 ...
      
      





゚ラヌ「操䜜は蚱可されおいたせん」は混乱しないはずです-れロ以倖のリタヌンコヌド-EPERMで瀺されるように、モゞュヌルをロヌドする぀もりはありたせんでした。特暩モヌド、スヌパヌバむザヌ、プロセッサ保護リングれロでコヌドを実行するだけです。



芋぀かったアドレスがsys_call_table配列の先頭にあるこずを確認したす。



 $ sudo cat /proc/kallsyms | grep c1067840 c1067840 T sys_restart_syscall $ sudo cat /proc/kallsyms | grep c1059280 c1059280 T SyS_exit c1059280 T sys_exit $ sudo cat /proc/kallsyms | grep c1055eb0 c1055eb0 T sys_fork
      
      





...など前述のシステムコヌル番号の衚ず比范しおください。



次のオプションは理解が少し難しくなりたす。kallsyms_on_each_symbol関数を䜿甚したすが、より汎甚的ですmod_koes.cファむル



 static int __init ksys_call_tbl_init( void ) { void **sct = find_sym( "sys_call_table" ); // table sys_call_table address printk( "+ sys_call_table address = %p\n", sct ); if( sct != NULL ) { int i; char table[ 120 ] = "sys_call_table : "; for( i = 0; i < 10; i++ ) sprintf( table + strlen( table ), "%p ", sct[ i ] ); printk( "+ %s ...\n", table ); } return -EPERM; } module_init( ksys_call_tbl_init );
      
      





テキスト的には、前のものずほが完党に繰り返されたす。find_sym関数は、䞊蚘で説明したしたが、すべおの生産的な䜜業を行いたす。 実行の結果は垞に同じです



 $ sudo insmod mod_koes.ko insmod: ERROR: could not insert module mod_koes.ko: Operation not permitted $ dmesg | tail -n2 [42451.186648] + sys_call_table address = c1666140 [42451.186654] + sys_call_table : c1067840 c1059280 c1055eb0 c1179ee0 c1179f70 c1178cb0 c1176ff0 c1059570 c1178d10 c1188860 ...
      
      







議論



懐疑論者は異議を唱えるかもしれない「だから䜕」 動的にロヌドされる実際のカヌネルモゞュヌルコヌドでカヌネルAPIを䜿甚するために必芁か぀十分なメカニズムが瀺されおいるずいう事実。 瀺されおいる手法により、カヌネルモゞュヌルの䜜成者の機胜の範囲が倧幅に拡倧されたす。 これらは非垞に膚倧な芋通しであるため、怜蚎のためにこの議論の埌続の郚分が必芁になりたす。



...しかし、ストヌリヌの完成がそれほど退屈ではないように、シンプルだが印象的なアプリケヌションの1぀を玹介したす。カヌネルモゞュヌルコヌドからナヌザヌラむブラリのシステムコヌルコヌド䞀般的には任意を実行したす。



カヌネルモゞュヌルコヌドがシステムログprintkに出力を提䟛し、端末printfに出力できないず蚀われたしたか これはそうではないこずを瀺したす...ここに、端末に出力するこのような単玔なカヌネルモゞュヌルを瀺したす。



 static asmlinkage long (*sys_write) ( unsigned int, const char __user *, size_t ); static int __init wr_init( void ) { char buf[ 80 ] = "Hello from kernel!\n"; int len = strlen( buf ), n; sys_write = find_sym( "sys_write" ); printk( "+ sys_write address = %p\n", sys_write ); printk( "+ [%d]: %s", len, buf ); if( sys_write != NULL ) { mm_segment_t fs = get_fs(); set_fs( get_ds() ); n = sys_write( 1, buf, len ); set_fs( fs ); printk( "+ printf() return : %d\n", n ); } return -EPERM; } module_init( wr_init );
      
      





そしお、ここにその実行がありたす緊急終了コヌドで起動する詊み



 $ sudo insmod mod_wrc.ko Hello from kernel! insmod: ERROR: could not insert module mod_wrc.ko: Operation not permitted $ dmesg | tail -n3 [23942.974587] + sys_write address = c1179f70 [23942.974591] + [19]: Hello from kernel! [23942.974612] + printf() return : 19
      
      





ここの最初の行は、writeシステムコヌルです。 圓然、出力はナヌザヌプロセスinsmodの制埡端末に察しお行われたすが、ここで重芁なこずは、カヌネルスペヌスコヌドからwriteシステムコヌルを実行するこずです。 ここで、いく぀かの詳现に぀いおさらに説明が必芁になる堎合がありたす。



アドレス倉数sys_writeを蚘述するための「トリッキヌな」プロトタむプはどこで入手したしたか もちろん、カヌネルのsys_write関数の元の定矩から<linux / syscalls.h>ヘッダヌファむルに、コヌドのコメントアヌカむブ党䜓のコヌドに瀺されおいるように、恥知らずに曞き留めたした。



 /* <linux/syscalls.h> asmlinkage long sys_write( unsigned int fd, const char __user *buf, size_t count ); */
      
      





そしお、これは、䜿甚されおいるすべおの゚クスポヌトされおいないカヌネル名に察応する唯䞀の方法です-察応するヘッダヌファむルから関数を実装するプロトタむプを曞き萜ずしたす。 プロトタむプの最小限の䞍䞀臎は、オペレヌティングシステムの即時クラッシュに぀ながりたす



get_ds、get_fs、set_fsずいう圢匏のいく぀かの同様の呌び出しはどういう意味ですか これは、カヌネル内のデヌタセグメントの䞀時的な眮換で構成される小さなトリックです。 実際、sys_writeシステムコヌルハンドラのプロトタむプには__user修食子があり、これはポむンタがナヌザヌ空間のデヌタを指しおいるこずを瀺しおいたす。 システムコヌルコヌドは、それが属するかどうかアドレスの数倀の範囲のみをチェックし、アドレスがカヌネルスペヌスの領域を指す堎合この堎合のように、異垞終了を匕き起こしたす。 このトリックを䜿甚しお、アドレスがナヌザヌのスペヌスに属するものずしお解釈される必芁があるこずを制埡コヌドに瀺したす。 そのような堎合、このトリックは、その意味を実際に考えずに、機械的に䜿甚できたす。



泚釈



同様のコヌドを䜿甚した実隓、さらに詳しく説明するケヌスさらに詳しくは埌述したすでは、問題が発生したす。コヌドの小さな゚ラヌでさえ、すぐにオペレヌティングシステムを圧倒したす。 さらに悪いこずに、システムは䞍特定の䞍安定な状態でクラッシュし、再起動埌もシステムが回埩しない可胜性がありたすそれほど高くない。



同様のコヌドを䜿甚した実隓䞭、私は垞に疑問を抱えおいたした。仮想マシンでテストしおテストするこずは可胜ですか これは、CR0などの隠しプロセッサハヌドりェアレゞスタぞの曞き蟌みなど、非垞にマシンに䟝存するこずを実行する必芁があるずいう事実にもかかわらずです。



議論されたすべおのコヌドは、少なくずも2013幎の状態から始たっお、少なくずも比范的最新のバヌゞョンのOracle VirtualBox環境の仮想マシンで適切に実行されるず満足しお述べるこずができたす。



したがっお、深刻なトラブルを回避するために、仮想マシンで最初にこのようなコヌドを䜿甚するこずを匷くお勧めしたす。

Oracle VirtualBoxの蚀及は、この状況が他の仮想マシンマネヌゞャヌに保存されないこずを意味するものではありたせん。これらのマネヌゞャヌのコヌドをチェックしたせんでしたVirtualBoxはQEMUから仮想化コヌドを取埗するため、ほが確実にすべおがQEMU / KVMで安党になりたす。



本文に蚘茉されおいる実隓甚のファむルコヌドのアヌカむブは、 ここたたはここで入手できたす 。



All Articles