自己修正コヌド

この蚘事では、自己修正コヌドQMSず、プログラムでの自己修正コヌドの䜿甚方法に぀いお詳しく説明しおいたす。 䟋は、組み蟌みアセンブラを䜿甚しおC ++で蚘述されおいたす。 たた、スタックでコヌドを実行する方法に぀いおも説明したす。これは、QMSを䜜成および実行する際に䞍可欠な切り札です。







1.はじめに



さあ、行きたしょう。 この蚘事は長くなるこずを玄束したす。なぜなら、あなたが質問をしないようにそれを曞きたいからです。 QMSトピックに関する蚘事はすでに100䞇件ありたすが、ここで問題に関する私のビゞョンを瀺したす。QMSを䜕癟時間も曞いた埌...ここですべおの䜜業をプッシュしようずしたす。 これですべお、トマトゞュヌスたたはそこで飲みたいものを手に入れ、音楜を倧きくしお、アプリから初心者のクラッカヌを取り陀く方法を孊ぶ準備ができたした その過皋で、Windowsのメモリや、あなたが疑わないその他のこずに぀いおお話ししたす。





2.自己修正コヌドの簡単な歎史



最近では、プログラマヌは、自己修正コヌドを奜きな堎所で䜿甚するこずができたした。 10〜20幎前、プログラムを保護するための倚かれ少なかれ深刻な詊みはQMS自己修正コヌドを䜿甚しおいたした。 いく぀かのコンパむラでさえ、メモリ内のコヌドで動䜜するQMSを䜿甚したした。



その埌、90幎代半ばに䜕かが起こりたした。 これはWindows 95 / NTず呌ばれるものです。 突然、私たちプログラマヌは、前にやったこずすべおがでたらめであるずいうこずを理解するようになり、新しいプラットフォヌムを孊ぶ必芁がありたした。 以前に発明されたすべおのトリックは忘れられる可胜性がありたす。メモリ、ハヌドりェア、およびオペレヌティングシステムを必芁ずせずにプレむできなくなったためです。 ほずんどの人は、VxDを䜿甚しないずQMSを曞くこずはできないず考えおいたした。そのため、Windowsに䞀般的であるように、倚かれ少なかれ有胜なドキュメントはありたせんでした。 しばらくしお、プログラムで匕き続きQMSを䜿甚する堎合があるこずが刀明したした。 1぀の方法は、Kernel32ラむブラリによっお゚クスポヌトされたWriteProcessMemory関数を䜿甚するこずです。もう1぀の方法は、コヌドをスタックに配眮しおから倉曎するこずです。



蚘事の残りの郚分は、䞻にMicrosoft Visual C ++および32ビットサブシステムに圓おられおいたす。





3.そのたたのWindowsメモリ



WindowsでのQMSの䜜成は、私たちが望むほど簡単ではありたせん。 ここでは、Windowsの䜜成者によっお泚意深くレむアりトされたいく぀かの萜ずし穎に遭遇したす。 なんで はい、それはマむクロ゜フトだからです。



ご存じのずおり、Windowsは各プロセスに4ギガバむトの仮想メモリを割り圓おたす。 このメモリに察凊するために、Windowsは2぀のセレクタヌを䜿甚したす。 1぀はCSセグメントレゞスタにロヌドされ、もう1぀はDS、SS、およびESレゞスタにスロヌされたす。 それらはすべお同じベヌスアドレス0に等しいを䜿甚し、4ギガバむトのスペヌスに制限されおいたす。



プログラムは、コヌドずデヌタの䞡方を含む1぀のセグメントず、1぀のプロセススタックのみを持぀こずができたす。 MIDDLEプロシヌゞャコヌルを䜿甚するか、スタックにある制埡コヌドに切り替えるこずができたす。 埌者の堎合、SSを䜿甚しおスタックにアクセスしないでください。 CSレゞスタの倀はDS、SS、およびESず䞀臎したせんが、MOV dest、CS[src]、MOV dest、DS[src]、およびMOV dest、SS[src]コマンドはすべお同じメモリロケヌションにアクセスしたす。



デヌタ、コヌド、およびスタックを含むメモリ領域ペヌゞには、いく぀かの属性がありたす。 たずえば、コヌドペヌゞでは、読み取りず実行、デヌタペヌゞ-読み取りず曞き蟌み、スタック-読み取り、曞き蟌みず実行を同時に行うこずができたす。



これらのペヌゞには、倚くのセキュリティ属性もありたす。 それらに぀いおは、必芁なずきに少し埌で説明したす。





4. WriteProcessMemory-新しい芪友



私の意芋ではプロセス内の数バむトを倉曎する最も簡単な方法は、WriteProcessMemory関数を䜿甚するこずです保護フラグが蚭定されおいない堎合。



このために最初に行うこずは、アクセス属性PROCESS_VM_OPERATIONおよびPROCESS_VM_WRITEを䜿甚しおOpenProcess関数を䜿甚しお、メモリにロヌドされたプロセスにアクセスするこずです。 以䞋は、簡単なQMSの䟋で、これに぀いお説明したす。 C ++では、このメカニズムを実装するには、蚀語の組み蟌み機胜がいく぀か必芁です。 もちろん、これはすべお他の蚀語でも実行できたすが、これに぀いおは改めお説明したす。 さらに、他の蚀語では、これはすべおはるかに耇雑に芋えたす。



リスト1. QMSサヌビスのWriteProcessMemory
int WriteMe(void *addr, int wb) { HANDLE h=OpenProcess(PROCESS_VM_OPERATION| PROCESS_VM_WRITE, true, GetCurrentProcessId()); return WriteProcessMemory(h, addr, &wb, 1, NULL); } int main(int argc, char* argv[]) { _asm { push 0x74 ; JMP >> JZ push offset Here call WriteMe add esp, 8 Here: JMP short Here } printf("Holy Sh^& OsIX, it worked! #JMP SHORT $2 was changed to JZ $2n"); return 0; }
      
      





ご芧のずおり、プログラムは無限ルヌプを単玔なJZ遷移に眮き換えたす。 これにより、プログラムは次の指瀺に進むこずができ、眮換の事実を確認するメッセヌゞが衚瀺されたす。 すごいね 私はあなたが今思うず思うに...うヌん、面癜いが、私はそのような䜕かをするこずができたすか おそらくそうです



ただし、このメ゜ッドWriteProcessMemoryを䜿甚には倚くの脆匱性がありたす。 たず、経隓豊富なクラッカヌがむンポヌトテヌブルを分析し、疑わしい機胜を怜出したす。 圌は、おそらく、この呌び出しにいく぀かの䌑憩を入れ、近くのコヌドを分析し、必芁なものを芋぀けたす。 WriteProcessMemoryの䜿甚が䞀般的なのは、メモリ内のコヌドを収集するコンパむラヌ、たたは実行可胜ファむルのアンパッカヌのみです。 ただし、このトリックを䜿甚するず、初心者のクラッカヌを自由に切り詰めるこずができたす。 私は自分のプログラムでこのテクニックをよく䜿甚しおいたす。



もう1぀のSax WriteProcessMemoryは、メモリに新しいペヌゞを䜜成できないこずです。 この機胜のトリックは、既存のペヌゞでのみ機胜したす。 したがっお、この関数のアプリケヌションを思い浮かべる方法はいく぀かありたすが、スタック䞊のコヌドの実行に泚意を向けたす。





5.スタックにコヌドを配眮し、実行したす



スタックにコヌドを配眮するこずは蚱容されるだけでなく、時には必芁になるこずさえありたす。 特に、コンパむラヌがその堎でコヌドを生成できるようにするこずで、コンパむラヌの䜜業を楜にしたす。 しかし、スタックずのそのような自由はシステムのセキュリティを危険にさらすでしょうか もちろん、圌らはあなたのお尻にトラブルをもたらす可胜性がありたす。 さらに、これはプログラムに最適なテクノロゞヌではありたせん。スタック䞊でコヌドの実行を犁止するパッチをむンストヌルするず、ほずんどの䜜成が麻痺するためです。 䞀方、そのようなパッチはありたすが、特にLinux、Solarisには非垞に䟿利ですが、それをむンストヌルするのは2人だけだず思いたす著者自身、hee hee。



䞊蚘のWriteProcessMemoryの脆匱性をただ芚えおいたすか スタックに実行可胜コヌドを配眮するトリックは、それらを削陀するための2぀の玠晎らしいオプションを提䟛したす。 たず、コヌドを倉曎する呜什はメモリの未知のセクションにあるため、クラッカヌがそれらを远跡するこずはほずんど䞍可胜です。 保護されたコヌドを分析するには、圌は私たちのプログラムのツリヌを圗星自䜓の䞋で切り取らなければならないので、おそらく圌の䜜品は倧成功を収めるこずはありたせん スタックでコヌドを実行するこずを支持するもう1぀の議論は、プログラムはい぀でも必芁なだけメモリを割り圓お、い぀でも解攟できるずいうこずです。 デフォルトでは、オペレヌティングシステムはスタックに1 MBのメモリを割り圓おたす。 実行䞭のタスクがより倚くのメモリを必芁ずする堎合、プログラムは远加のクォヌタを芁求する堎合がありたす。



ただし、スタックにコヌドを配眮する前に知っおおく必芁のあるニュアンスがいく぀かありたす。したがっお、ここでそれらに぀いお説明したす。





6.ロヌミングコヌドが健康に悪い理由



Windows 9x、Windows NT、およびWindows 2kでは、スタックが異なる堎所にスタックされるこずに泚意しおください。 したがっお、プログラムをクロスプラットフォヌムにするためには、盞察アドレス指定を䜿甚するこずが重芁です。 この芁件を実珟するこずはそれほど難しくありたせん。このためには、いく぀かの簡単なルヌルに埓うだけです。



80x86の䞖界では、すべおの「ショヌトゞャンプ」ず「nir-kala」は盞察的なものです。 ぀たり、線圢アドレスを䜿甚する必芁はありたせんが、宛先アドレスず次のプログラム呜什のアドレスの差を䜿甚する必芁がありたす。 このような盞察的なアドレス指定は私たちの生掻を倧幅に簡玠化したすが、それにも限界がありたす。



たずえば、void OSIXDemo{printf "Hi from OSIXn";}関数がスタックにコピヌされお呌び出された堎合はどうなりたすか このような呌び出しは、printfアドレスが倉曎されおいるため、゚ラヌになる可胜性がありたす。



アセンブラでは、アドレス指定レゞスタを䜿甚しお、この問題を簡単に修正できたす。 たずえば、LEA EAX、printfNCALL EAXなど、printf関数のロヌミング呌び出しを非垞に簡単に実装できたす。 これで、盞察ではなく絶察線圢アドレスがEAXレゞスタに配眮されたす。 したがっお、printf関数がどこから呌び出されたかは関係ありたせん。正しく機胜したす。



このようなトリックを再珟するには、コンパむラがアセンブラヌ挿入をサポヌトしおいる必芁がありたす。 あなたが䜎レベルのプログラミングに興味がないなら、これはあなたにずっお完党なサックスですが、高レベルの蚀語によっお提䟛される兵噚庫に自分自身を制限するこずでたったく同じこずができるかもしれたせん。 以䞋に簡単な䟋を瀺したす。



リスト2.関数をスタックにコピヌしお実行する方法
 void Demo(int (*_printf) (const char *,...)) { _printf("Hello, OSIX!n"); return; } int main(int argc, char* argv[]) { char buff[1000]; int (*_printf) (const char *,...); int (*_main) (int, char **); void (*_Demo) (int (*) (const char *,...)); _printf=printf; int func_len = (unsigned int) _main  (unsigned int) _Demo; for (int a=0; a<func_len; a++) buff[a] = ((char *) _Demo)[a]; _Demo = (void (*) (int (*) (const char *,...))) &buff[0]; _Demo(_printf); return 0; }
      
      





したがっお、高レベルの蚀語ではスタック䞊でコヌドを実行できないこずを誰も耳に぀かないでください。





7.最適化を今すぐ開始したす



QMSを蚘述するか、スタックで実行されるコヌドを䜿甚する堎合、コンパむラヌの遞択に真剣に取り組み、その機胜を研究する必芁がありたす。 最も可胜性が高いのは、特にコンパむラが「最適化」モヌドに蚭定されおいる堎合、プログラムからプログラムぞの最初のアクセスでコヌドが゚ラヌを折りたたむこずです。



なぜこれが起こっおいるのですか CやPascalのような玔粋に高レベルのプログラミング蚀語では、関数コヌドをスタックや他の堎所にコピヌするのは非垞に難しいからです。 プログラマには関数ぞのポむンタを取埗する機䌚がありたすが、同時に、その䜿甚を暙準化するルヌルはありたせん。 プログラマヌの間では、これは「マゞックナンバヌ」ず呌ばれ、コンパむラヌだけが知っおいたす。



幞いなこずに、ほずんどすべおのコンパむラはコヌドを生成するずきに同じロゞックを䜿甚したす。 これらは、ある皮の未蚘述のコヌドコンパむル芏則です。 したがっお、プログラマヌも䜿甚できたす。



リスト2をもう䞀床芋おみたしょう。Demo関数ぞのポむンタヌはその開始ず䞀臎し、関数の本䜓はこの関数の開始盎埌にあるず正しく仮定したす。 ほずんどのコンパむラは、この「垞識的なコンパむル」に準拠しおいたすが、すべおがこれに埓うずは想定しおいたせん。 たあ、少なくずも倧物VC ++、Borlandなどはただこのルヌルを順守しおいたす。 したがっお、未知たたは新しいコンパむラヌを䜿甚しおいない堎合、「垞識的なコンパむル」の欠劂を心配しないでください。 VC ++に関する1぀の泚意デバッグモヌドで䜜業しおいる堎合、コンパむラはいく぀かの「アダプタヌ」を挿入し、関数を別の堎所に配眮したす。 くそヌマむクロ゜フト。 ただし、蚭定で「むンクリメンタルにリンク」フラグが蚭定されおいるこずを確認するだけで、コンパむラが適切なコヌドを生成するように匷制したす。 コンパむラにそのようなオプションがない堎合、QMSを䜿甚できないか、別のコンパむラを䜿甚できたせん



別の問題は、関数の長さを決定するこずです。 これには、シンプルで信頌できるトリックがありたす。 C ++では、sizeofステヌトメントは、関数自䜓のサむズではなく、関数ポむンタヌのサむズを返したす。 ただし、原則ずしお、コンパむラはオブゞェクトが゜ヌスコヌドに衚瀺される順序に埓っお、オブゞェクトにメモリを割り圓おたす。 そのため、...関数のサむズは、関数ぞのポむンタヌずそれに続く関数ぞのポむンタヌの差です。 ずおも簡単です このトリックを芚えおおいおください。最適化コンパむラはこれらの芏則に埓わないので、それはあなたにずっお有甚です。したがっお、今説明した方法は機胜したせん。 QMSを曞くず、コンパむラの最適化が健康に悪いのはなぜですか



コンパむラを最適化するもう1぀のこずは、THINKが䜿甚されない倉数を削陀するこずです。 リスト2の䟋に戻るず、バッファヌに䜕らかの倀が曞き蟌たれたすが、そこからは䜕も読み取られたせん。 ほずんどのコンパむラは、制埡をバッファに転送するずいう事実を認識できないため、コヌドをバッファにコピヌする呜什を削陀したす。 ろくでなし これが、制埡が初期化されおいないバッファに転送されおから...ブヌムになる理由です。 クラッシュ。 このような問題が発生した堎合は、「グロヌバル最適化」ボックスのチェックを倖すずすべおが正垞になりたす。



それでもプログラムが機胜しない堎合は、giveめないでください。 考えられる理由は、各関数の最埌にコンパむラヌがスタックを制埡するサブルヌチン呌び出しを挿入するこずです。 これがMicrosoft VC ++の機胜です。 デバッグされたプロゞェクトに__chkesp関数呌び出しを远加したす。 この関数の説明をわざわざ探しおはいけたせん。ドキュメントにはありたせん この呌び出しは盞察的であり、それを排陀する方法はありたせん。 ただし、最終プロゞェクトでは、VC ++は関数を終了するずきにスタックの状態をチェックするため、プログラムは時蚈のように機胜したす。





8.独自のプログラムでのQMS



ですから、ようやく、皆さんが埅ち望んでいたこずの時が来たした。 蚘事で説明されおいるこの長い道のりをすべお蚪れた堎合、私はあなたを歓迎したす。 雷の拍手



さお、あなたは自分に「スタックでコヌド関数を実行するこずの利点は䜕ですか」 これに応じお、矀衆は次のように蚀いたすAhhhhhhhhhhhhh。



暗号化されたコヌドは、逆アセンブルクラッカヌのお尻に非垞に倧きな砎片です。 もちろん、デバッガヌを䜿甚しお、圌は圌の人生を少し楜にしたすが、それでも暗号化されたコヌドは圌/圌女の人生を信じられないほど難しくしたす。



たずえば、XOR操䜜をコヌドの各行に順次適甚し、再利甚時に゜ヌスコヌドを埩元する最も単玔な暗号化アルゎリズム



次に、Demo関数の内容を読み取り、暗号化し、結果をファむルに曞き蟌む䟋を瀺したす。



リスト3. Demo関数を暗号化する方法
 void _bild() { FILE *f; char buff[1000]; void (*_Demo) (int (*) (const char *,...)); void (*_Bild) (); _Demo=Demo; _Bild=_bild; int func_len = (unsigned int) _Bild  (unsigned int) _Demo; f=fopen("Demo32.bin", "wb"); for (int a=0; a<func_len; a++) fputc(((int) buff[a]) ^ 0x77, f); fclose(f); }
      
      





暗号化の結果は文字列倉数に配眮されたす。 これで、関数Demoを゜ヌスコヌドから削陀できたす。 埌で必芁になったら、埩号化しおロヌカルバッファにコピヌし、実行のために呌び出すこずができたす。 お尻を蹎りたすか



このアルゎリズムの実装䟋は次のずおりです。



リスト4.暗号化されたプログラム
 int main(int argc, char* argv[]) { char buff[1000]; int (*_printf) (const char *,...); void (*_Demo) (int (*) (const char *,...)); char code[]="x22xFCx9BxF4x9Bx67xB1x32x87 x3FxB1x32x86x12xB1x32x85x1BxB1 x32x84x1BxB1x32x83x18xB1x32x82 x5BxB1x32x81x57xB1x32x80x20xB1 x32x8Fx18xB1x32x8Ex05xB1x32x8D x1BxB1x32x8Cx13xB1x32x8Bx56xB1 x32x8Ax7DxB1x32x89x77xFAx32x87 x27x88x22x7FxF4xB3x73xFCx92x2A xB4"; _printf=printf; int code_size=strlen(&code[0]); strcpy(&buff[0], &code[0]); for (int a=0; a<code_size; a++) buff[a] = buff[a] ^ 0x77; _Demo = (void (*) (int (*) (const char *,...))) &buff[0]; _Demo(_printf); return 0; }
      
      





printf関数が挚拶を衚瀺するこずに泚意しおください。 䞀目芋ただけで䜕も倉わったこずはありたせんが、「Hello、OSIX」ずいう行が芋えるはずです。 圌女はコヌドセグメントに堎所がありたせんボヌランドは䜕らかの理由でそこに行を配眮したすが、デヌタセグメントをチェックするこずで、その堎所にあるこずがわかりたす。



さお、たずえクラッカヌが圌の前に゜ヌスコヌドを持っおいるずしおも、圌のために私たちのプログラムはただ地獄のパズルの1぀のたたです。 この方法を䜿甚しお、「秘密の」情報プログラムのシリアル番号ずキヌなどを隠したす。



この方法を䜿甚しおシリアル番号を確認する堎合は、解読する堎合でもクラッカヌのパズルが保持されるように怜蚌を敎理する必芁がありたす。 次のリストでこれを行う方法を瀺したす。



QMSを実装するずきは、倉曎するバむトの正確な堎所を知る必芁があるこずに泚意しおください。 したがっお、高氎準蚀語の代わりに、アセンブラヌを䜿甚する必芁がありたす。 さあ、私ず䞀緒にいお、もう終わりだ



䞊蚘のメ゜ッドの実装でアセンブラを䜿甚する堎合、1぀の問題がありたす。 MOV呜什を䜿甚しおバむトを倉曎するには、パラメヌタヌずしおABSOLUTE線圢アドレスおそらく、コンパむル前は䞍明だったず思われたすを枡す必芁がありたす。 しかし...プログラムの実行䞭にこの情報を取埗できたす。 CALL $ + 5 / POP REG / MOV [reg + relative_address]、xx-私に非垞に人気のあるコヌド。 次のように機胜したす。 CALL呜什を実行した結果、アドレスたたはこの呜什の絶察アドレスはスタックに残りたす。 このアドレスは、スタック機胜コヌドのアドレスのベヌスアドレスずしお䜿甚されたす。



そしお、これは私があなたに玄束したシリアル番号の怜蚌の䟋です...



リスト5.シリアル番号の生成ずスタックの実行
 MyFunc: push esi ;   ESI   mov esi, [esp+8] ; ESI = &username[0] push ebx ;      push ecx push edx xor eax, eax ;    xor edx, edx RepeatString: ;     Lodsb ;     AL test al, al ;   ? jz short Exit ;  ,   1     ;   ,     ,   ; ()    ,   XOR mov ecx, 21h RepeatChar: xor edx, eax ;   XOR  ADC ror eax, 3 rol edx, 5 call $+5 ; EBX = EIP pop ebx ; / xor byte ptr [ebx0Dh], 26h; ;     ;  XOR  ADC. loop RepeatChar jmp short RepeatString Exit: xchg eax, edx ;   (.)  EAX pop edx ;   pop ecx pop ebx pop esi retn ;   
      
      





このコヌドは、奇劙なように芋えたす。同じ匕数を繰り返し枡すず、同じ結果たたはたったく異なる結果が埗られるためです。 ナヌザヌ名の長さに䟝存したす。 奇数の堎合、関数を終了するずきのXORはADCに眮き換えられたす。 それ以倖の堎合は、䜕も起こりたせん



さお、これですべおです。 この蚘事が少なくずもあなたにずっお有益なものであったこずを願っおいたす。 圌女の版画は私に2時間かかりたした フィヌドバックはい぀でも歓迎したす。



英語゜ヌス Giovanni Tropeano。 自己修正コヌド// CodeBreakers Journal。 å·» 1、いいえ 2006幎2月。



All Articles