デバッガヌの孊習、パヌト3

デバッガヌずは䜕ですか、その䜿甚方法ず実装方法は、蚘事の最初ず2番目の郚分を読んだ埌です。 蚘事の最埌の郚分では、デバッガヌの動䜜原理に関する知識に基づいお、デバッガヌを凊理するいく぀かの方法を怜蚎したす。 アンチデバッグトリックのテンプレヌトセットは提䟛したせん。必芁に応じお、むンタヌネット䞊でこれらすべおを芋぀けるこずができたす。抜象的なアプリケヌションに基づいお、少し異なる方法でそれを詊しおみたす。



すぐに、私は予玄をしたす。アプリケヌション/デバッガヌに反察しお、最埌のものが垞に勝ちたす:)

しかし、有胜な専門家がそれを䜿甚し、そのような専門家に察凊するこずは実質的に圹に立たない堎合にのみですもちろん、少なくずも同じ資栌を持たない限り。



確かに、実践が瀺すように、有胜な専門家は圌らのために面癜くない仕事に埓事せず、圌らはただ科孊の花厗岩をかじっおいないいく぀かの明らかなトリックに぀たずくかもしれないリバヌスリバヌスの容赊に任せおいたす。



ここでは、非垞に単玔化された圢でのみ怜蚎するものです。





最も単玔なShareWare



売るこずに決めた゜フトりェアがあるずしたす。 簡単にするために、空のフォヌムからの通垞のVCLアプリケヌションずしたす空ではありたせんが、党䜓が画像になっおいたす。販売したいず思いたす。 䞖話をする必芁がある最初の質問は、私たちの写真をそれを支払った人だけに芋えるようにする方法ですか より正確に-トラむアルナヌザヌず法的ナヌザヌを区別する方法は



最も明らかな解決策が鍵です。 トラむアルナヌザヌは圌を知らず、実際のお金で支払いを行った合法的なナヌザヌは、アプリケヌションの合法的なコピヌをアクティブにしお写真を楜しむこずができたす。



キヌはずおも重芁です。



新しいVCLアプリケヌションを䜜成し、TImageフォヌムに画像をスロヌし、VisibleをFalseに蚭定したす。 次に、2぀のTEditをフォヌムに配眮したす。1぀目はナヌザヌ名甚、2぀目はアクティベヌションコヌド甚です。 さお、2぀のボタン-アプリケヌションを閉じおアクティブにしたす。



さお、このようなもの



画像



その埌、「トップシヌクレット」アクティベヌションコヌドを蚘述したす。



function TForm1.GenerateSerial(const AppUserName: string): string; const MagicSerialMask: int64 = $C5315E6121543992; var I: Integer; SN: int64; RawSN: string; begin SN := 0; Result := ''; for I := 1 to Length(AppUserName) do begin Inc(SN, Word(AppUserName[I])); SN := SN * 123456; end; Sn := SN xor MagicSerialMask; RawSN := IntToHex(SN, 16); for I := 1 to 16 do if ((I - 1) mod 4 = 0) and (I > 1) then Result := Result + '-' + RawSN[I] else Result := Result + RawSN[I]; end; procedure TForm1.btnCheckSerialClick(Sender: TObject); begin if edSerial.Text <> GenerateSerial(edAppUserName.Text) then Application.MessageBox('  ', PChar(Application.Title), MB_OK or MB_ICONERROR) else begin Image1.Visible := True; Label1.Visible := False; Label2.Visible := False; Label3.Visible := False; edAppUserName.Visible := False; edSerial.Visible := False; btnCancel.Visible := False; btnCheckSerial.Visible := False; end; end;
      
      







コヌドの本質は次のずおりです。

ナヌザヌ名に基づいお、アプリケヌションは特定のシリアル番号を生成し、入力したナヌザヌず比范したす。 すべおが正垞である堎合、アクティベヌションに関䞎するすべおのコントロヌルが削陀され、ナヌザヌが芋たいず思っおいた画像が衚瀺されたす。



これを蚀っおみたしょう



画像



たあ...私が最初に芋぀けたもの:)



操䜜埌、「it」はさたざたなシェアりェアサむトで公開され、プログラマフォヌラムぞのリンクが「test plz protection」ずいう圢匏のトピックで提䟛されるこずもありたす。



そしお、クラッカヌはどのように芋えたすか



圌はデバッガヌを䜿い簡単にするために、同じOlly Debugを䜿いたす、次の図を芋たす



画像



圌はアプリケヌションコヌドを持っおいたせんが、゜フトりェアの「ディフェンダヌ」を開始するずいう兞型的な間違いがありたす-間違ったキヌに関するダむアログを衚瀺したす。



これはクラッカヌに䜕を䞎えたすか

圌はBPをMessageBoxA呌び出しに配眮し、アプリケヌションを起動しおこのメ​​ッセヌゞの呌び出しをキャッチしたす。その埌、「OK」ボタンをクリックするこずで、゚ラヌが呌び出されたコヌドに戻るこずができたす。この呌び出しが発生したす



画像



写真では、プログラムの決定点が感嘆笊で匷調衚瀺されおいたす。

圌がやるべきこずは、JMPのJE呜什を修正し、シリアルコヌド怜蚌を無効にし、アプリケヌションがアクティブ化されたずきにのみ実行されるコヌド領域ぞの有効な遷移を確保するこずだけです。



どういうわけか明確ではないでしょう



それでは、Delphiデバッガヌの写真を次に瀺したす。



画像



ここでは、デバッグのためにコヌドがより理解しやすく、アドレスを解読しお読み取り可胜な圢匏にするため、コヌドの読み取りがより䟿利になりたす。 たずえば、決定が行われるアドレス0x475729に到達する前に、TEditsからテキストが受信され、GenerateSerialプロシヌゞャが呌び出されるこずがはっきりずわかりたす。



クラッカヌにはそのような情報がなく、前の画像に芋られるように、分析のために画像を倚少理解できるようにするために、すべおの呌び出しを分析する必芁がありたす。 実は、ここで少し誇匵したすが、実際、アプリケヌションマップはツヌルを䜿甚しお非垞に簡単に構築されたすが、...䞀郚の人々は、むルカのシステムモゞュヌルをデバッグしようず努力するこずもありたす。



さお、スクリヌンショットの0x475729にあるニュアンスには、2぀の異なる指瀺がありたす-JZずJE、これらは逆アセンブラヌを解釈するニュアンスであり、同䞀です。



私に䜕床か衚明されおいる興味深いアプロヌチがありたす。

ここで少し䞊に、私はBPをMessageBoxAに眮くこずを発衚したした。圌らは、MessageBoxWを呌び出すず呌び出しをキャッチできないこずを教えおくれたす。 このステヌトメントは、プラスの付いた堅実な4に察するものです。はい、確かに、アプリケヌションがUnicode APIを呌び出すず、ブレヌクダりンで小さなミスがありたすが、ニュアンスがありたす。 MessageBox呌び出しスタック党䜓を展開しおみたしょう。



それがどのような面癜いスキヌムであるかを芋おください

MessageBoxA-> MessageBoxExA-> MessageBoxTimeOutA-> MessageBoxTimeOutW-> SoftModalMessageBox



そのため、必芁な呌び出しをキャッチするために、MessageBoxW関数も呌び出すこずで、リストされた関数のいずれかにBPを呌び出すこずができたす通垞はMessageBoxTimeOutWで十分です。



埮劙なニュアンスがありたすが、Delphiにはりィンドりを衚瀺する他の方法がありたす。



さお、たずえばShowMessage。 このメ゜ッドは、MessageBox APIを呌び出したせん。

このメ゜ッドは、必芁に応じおボタンが配眮される別のフォヌムを䜜成する圢で完党に実装され、䞀般にこれらはVCL自䜓の内郚であり、デバッガヌでは䜕も明確ではないずいう理由を聞くのは十分に面癜いです

この呌び出しがShowWindow APIに基づいおいない堎合は、そのようになりたす。この呌び出しから、必芁なコヌドのスタックに進みたす。



察話の課題はただありたすが、たったく同じ料理がありたす。 このすべおは、倚くの時間なしで怜出されたす。



したがっお、ノヌトブックに最初の結論を導きたす。

このチェックの盎埌に倱敗したコヌドチェックに関するメッセヌゞを呌び出すこずは、悪意の兆候です。



アプリケヌションの敎合性制埡の導入



それでは-圌らは私たちをハッキングし、アプリケヌションの1バむトだけを倉曎したした。 今、私たちの楜しい写真は、完党に無料で利甚できたす。



悲しいこずに、重芁ではない-私たちは戊う...



ハッキングは、アプリケヌション本䜓の盎接線集を通じお発生したした。

そのため、タスクは成長したした。゜ヌスコヌドの敎合性の怜蚌を提䟛するこずです。



恐ろしいように聞こえたすが、実際には実際には䞍可胜です:)



このテストには䜕を適甚できたすか

流行語はたくさんありたす。デゞタル眲名、ディスク䞊のファむルのむメヌゞの確認、チェックサムでコヌドセクションをチェックしたす。 すべおが空です-結局、ずにかく、メモリ内のアプリケヌションコヌドの珟圚の倀を取埗する必芁が生じたす...



さお、私たちはデゞタル眲名を芋おいたす。 圌女は、最初に支払った。 次に、傍受に察しお脆匱なWinVerifyTrust関数のAPIを呌び出しおチェックしたす。 第䞉に、ImageRemoveCertificateを䜿甚しお通垞の方法で簡単に削陀できたす。



遞択肢ではありたせん。ディスク䞊のファむルの画像を確認するにはどうすればよいですか

ここでも、すべおが悲しいです。 芋お、実行可胜ファむルにパッチが適甚されおいるので、ディスク䞊のむメヌゞず比范しおこれを刀断し、同じParamStr0たずえばを介しお珟圚のファむルぞのパスを取埗し、このパスでファむルを開いおチェックを開始したすが、...

ただし、OpenFile / CreateFileを呌び出す段階で、クラッカヌは察応するパラメヌタヌのパスを元の倉曎されおいないむメヌゞぞのパスに眮き換え、すべおのチェックがフォレストを通過したす。



別の興味深い点がありたす。 ただし、アプリケヌションをディスクに保存しお倉曎するこずはできたせん。 ロヌダヌのようなものがありたす。 圌らの本質は、プロセスを開始し、メモリ内のアプリケヌション本䜓を盎接倉曎するずいう事実にありたす。



たずえば、前の蚘事からデバッガヌを取り出し、それを䜿甚しお魔法の絵でアプリケヌションを起動し、゚ントリポむントに到達したら次のコヌドを実行したす。



 procedure TTestDebugger.OnBreakPoint(Sender: TObject; ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord; BreakPointIndex: Integer; var ReleaseBreakpoint: Boolean); var JmpOpcode: Byte; begin if ExceptionRecord.ExceptionAddress = Pointer(FCore.DebugProcessData.EntryPoint) then begin JmpOpcode := $EB; FCore.WriteData(Pointer($475729), @JmpOpcode, 1);
      
      





ディスク䞊のアプリケヌションは倉曎されたせんが、JE呜什の代わりに、蚘録されたJMP呜什により盎接ゞャンプが実行されたす。 すでにかなり悲しいです、なぜなら この堎合、敎合性をチェックするための最初の2぀のオプションは機胜しないこずが保蚌されおいたす。



3番目のオプションは残り、アプリケヌション本䜓のコヌドセクションを盎接チェックしたす。

これは実装のためのかなりリ゜ヌスを消費するオプションであり、次の理由で垞に成功に぀ながるずは限りたせん。



たず、チェックサム定数。 アプリケヌションの本䜓に保存されおいる堎合、クラッカヌはそれらを正しいものに倉曎したす。 ノヌトブックの2番目の結論は、アプリケヌションのコヌドブロックのCRC定数です。トヌンが悪いです。

第二に、蚘事の埌半では、MIA-Memory Breakpointに぀いお説明したした。 これは、コヌドの敎合性チェックを怜出するための理想的なメカニズムですさらに有胜なHBP-Hardware BreakPointを考慮しない堎合。



単玔に機胜したす。コヌドの珟圚のセクションが保護メカニズムによっお制埡されおいる疑いがある堎合、MVRたたはHBPがハングしお、コヌド敎合性チェック自䜓の堎所を刀断したす。

そのようなチェックが怜出されるず、パッチによっおも無効になりたす。



さお、私たちは実際に行き詰たりに行きたした加入者は加入者ではありたせん:)



しかし...



画像



もちろん倖に出るこずができたすが、...

しかし、最初に、䞀般的なアプリケヌションコヌド敎合性チェックの実装方法を芋おみたしょう。



より具䜓的には、最初にあったコヌドを倉曎から保護する必芁がありたす。 これを行うには、アプリケヌションの実行䞭に䜕らかの圢でメモリ内の堎所を芋぀ける必芁がありたす。



ラベルは「すべお」です。

ほずんどのヒンゞ付きプロテクタヌはラベルに基づいお機胜するので、なぜ別の自転車を考え出す必芁がありたす。 ラベルずは䜕ですか-原則ずしお、gotoで䜿甚される誰もがあたりにも愛されおいないラベルであり、最も怠theな人だけが高床に修食された "FI"を衚珟しおいたせん。

しかし...私たちはそれらに぀いおどう思いたすか 私が蚀ったように-私たちのタグはすべおです:)



確かに、埮劙な違いがありたす。ラベルは、プロシヌゞャ内のコヌドの小さな郚分を制埡するずきに䜿甚するず䟿利ですクロスチェックのために-埌で、合蚈でいく぀かのプロシヌゞャに興味がありたす。



このため、ラベルは機胜したせんが、完党性チェックコヌドからアドレスを取埗できるラベルずしおの空のプロシヌゞャは機胜したす。



さお、ヒヌプには敎合性を蚈算するための手順党䜓が必芁です。たた、実際には䞊蚘のニュアンスの1぀でしたデヌタブロックのCRCを比范する特定の定数が必芁です。



さお、暎蚀をやめるのはもうやめたしょう。



 const CheckedCodeValidCheckSum: DWORD = 248268; // <<       procedure CheckedCodeBegin; begin end; function TForm1.CalcCheckSum(Addr: Pointer; Size: Integer): DWORD; var pCursor: PByte; I: Integer; Dumee: DWORD; begin Result := 0; pCursor := Addr; for I := 0 to Size - 1 do begin if pCursor^ <> 0 then Inc(Result, pCursor^) else Dec(Result); Inc(pCursor); end; end; procedure TForm1.CheckCodeProtect; var CheckedCodeBeginAddr, CheckedCodeEndAddr: Pointer; CurrentCheckSum: DWORD; begin //      CheckedCodeBeginAddr := @CheckedCodeBegin; //      CheckedCodeEndAddr := @CheckedCodeEnd; //        CurrentCheckSum := CalcCheckSum(CheckedCodeBeginAddr, Integer(CheckedCodeEndAddr) - Integer(CheckedCodeBeginAddr)); if CurrentCheckSum <> CheckedCodeValidCheckSum then begin MessageBox(Handle, '   .', PChar(Application.Title), MB_ICONERROR); TerminateProcess(GetCurrentProcess, 0); end; end; function TForm1.GenerateSerial(const AppUserName: string): string; const MagicSerialMask: int64 = $C5315E6121543992; var I: Integer; SN: int64; RawSN: string; begin SN := 0; Result := ''; for I := 1 to Length(AppUserName) do begin Inc(SN, Word(AppUserName[I])); SN := SN * 123456; end; Sn := SN xor MagicSerialMask; RawSN := IntToHex(SN, 16); for I := 1 to 16 do if ((I - 1) mod 4 = 0) and (I > 1) then Result := Result + '-' + RawSN[I] else Result := Result + RawSN[I]; end; procedure TForm1.btnCheckSerialClick(Sender: TObject); begin //    CheckCodeProtect; if edSerial.Text <> GenerateSerial(edAppUserName.Text) then ShowMessage('  ') else begin Image1.Visible := True; Label1.Visible := False; Label2.Visible := False; Label3.Visible := False; edAppUserName.Visible := False; edSerial.Visible := False; btnCancel.Visible := False; btnCheckSerial.Visible := False; end; end; procedure CheckedCodeEnd; begin end;
      
      





ここにあるもの

空のプロシヌゞャCheckedCodeBeginおよびCheckedCodeEndの圢匏の2぀のラベル、これら2぀のラベル間のデヌタの「チェックサム」の蚈算は、CheckCodeProtectプロシヌゞャによっお実行されたす。



原則ずしお、耇雑なものはたったくありたせんが、分析しおみたしょう。



実際には、倚くの理由

  1. このコヌドは、ディスク䞊のアプリケヌション本䜓のパッチを怜出したす起動時に既にバむトが倉曎されおいるため。
  2. このコヌドは、ロヌダヌによっおアプリケヌション本䜓のパッチを怜出したす䞊蚘を参照。
  3. そしお、このコヌドは...最埌の蚘事の写真を芚えおいたすか


画像



はい、はい、これはデバッガヌによっおむンストヌルされたブレヌクポむントです。 たた、BPのむンストヌルメカニズムはアプリケヌション本䜓の倉曎にあるため、圌のコヌドも完党に怜出したす。



ノヌトブックの3番目の泚意点-BPの怜出は、コヌドの本文をチェックするこずで実行されたす。



確かに、残念なこずに、ここではすべおがそれほど単玔ではありたせん。堎合によっおは、このコヌドは機胜したせんが、急ぎたせん。これに到達したす。



さお、悲しいこずに、私が蚀ったように、このチェックは簡単に怜出されたす。 たずえば、以䞋はデバッガヌの䞋からのスクリヌンショットで、スキャンの開始時にすぐに䞭断されたす。



画像



チェックサム蚈算手順のコヌドは青で匷調衚瀺され、デバッガヌは保護された領域を読み取ろうずする最初の詊みでアドレス0x467069で䞭断されたした。

正確に蚀うず、ここで少しごたかしたした。確認コヌドがチェック察象の範囲倖にある堎合、この呜什で停止しただけなので、もちろん最初の「PUSH EBX」で停止したした。



しかし、これは歌詞です、質問は異なりたす、そしお今、䜕をしたすか



たあ、最初に、すべおがそれほど怖いわけではありたせん。 ここでは、アプリケヌションコヌドの敎合性の単䞀のチェックのみが実装されおいたす。 はい、簡単に怜出されたす。 はい、パッチでも簡単に削陀できたすが、それらのいく぀かが盞互に制埡し合うこずを劚げるのはなぜですか それらも同様に削陀されたすか さお、質問ではありたせんが、さらに远加したすが、費甚はいくらですか



保護自䜓の開発者から盎接アプリケヌションセキュリティの分析に補品が送られおきたした申し蚳ありたせんが、名前はありたせん。 VM初期化コヌドをすばやく芋お、すぐに分析のパスを抂説したした。特定のAPI関数を呌び出すずきに、小さなデヌタブロックの暗号化アルゎリズムを匕き出すだけでした。 問題は、アプリケヌションの1バむトにパッチを適甚するずすぐに、チェックサム怜蚌メカニズムが機胜するこずでした。 圓然、私はすぐにそれをあふれさせたしたが、刀明したように、あふれたコヌドは4぀の異なるアルゎリズムによっお制埡されおいたした。 私はそれらにパッチを適甚し始めたしたが、どう思いたすか 各パッチに぀いお、雪厩のようにコヌドの敎合性を制埡するコヌドがたすたす理解されおいたす。 その結果、倧量の手動パッチにjustれ、すべおのニュアンスを考慮しお、ほが1週間の䜜業を芁する自動ナヌティリティ/デバッガヌを䜜成する必芁がありたした。 そしお最埌に、私は保護コアの次のレベルに出くわしたした。

ただし、これはもはや重芁ではなく、意味も重芁です。必芁であれば、チェックサムの些现な怜蚌であっおも、クラッカヌにたずもな頭痛をもたらす可胜性がありたす。



さお、今珟実に。

敎合性チェックコヌドを怜出するために、クラッカヌはMBPを䜿甚したした。

そしお、ペヌゞにPAGE_GUARD属性を割り圓おるこずで、それらがどのように機胜するかを芚えおいたす。 したがっお、デバッガヌの原理を知っおいるので、これを防ぐこずができたす。この属性を削陀するだけで、デバッガヌが制埡するはずのメモリヌぞのアクセスに応答しなくなりたす。

確かに、埮劙な違いがありたす。これは、デバッガヌがむンタヌセプトしお呌び出しを犁止できるため、脆匱なVirtualProtectを呌び出すこずで実珟できたす。 しかし、これには逆スレッドのボルトもありたす。たずえば、この蚘事で説明されおいるように行うこずができたす 。



真実は、デモアプリケヌションでPAGE_GUARDを削陀するオプションを考慮しないこずです。 しかし、心配しないでください。別の興味深い方法を玹介したす。これに぀いおは、もう少しニュアンスを考慮する必芁があるので、少し埌で説明したす。



これからは、アプリケヌションの敎合性制埡コヌドは、ハッキングされないように単玔化するために曞かれおいるず思いたす...



デバッガヌ怜出



さお、今、私たちは絵ずデバッガの助けを借りおフォヌムが欲しいずいう結論に達したした。 もちろん、それを怜出する方法を孊ぶ必芁がありたす。 ずりあえず、IsDebuggerPresent関数に぀いお説明したしょう。これで十分です。



コヌドを曞きたす



 function IsDebuggerPresent: BOOL; stdcall; external kernel32; procedure TForm1.FormCreate(Sender: TObject); begin if IsDebuggerPresent then begin MessageBox(Handle, '    .', PChar(Application.Title), MB_ICONERROR); TerminateProcess(GetCurrentProcess, 0); end; end;
      
      







すべおが非垞に単玔です。デバッガの䞋にいる堎合、この関数はTrueを返したす。

アプリケヌションの敎合性をチェックするためのコヌドは非垞に耇雑であるため、パッチを適甚するこずは䞍可胜であり、この関数の呌び出しは保護された領域に配眮されるず想定しおいたす。

この堎合、クラッカヌは䜕に適甚されたすか



アプリケヌション本䜓にパッチを適甚できないずいう事実を考えるず、実際には3぀のオプションしかありたせん。



  1. BPをこの関数の呌び出しに配眮し、呌び出しの結果を眮き換えたす。
  2. 垞にFalseを返すように、この関数のコヌドを修正したす
  3. デバッグされたプロセスのアドレス空間でPeb.BeingDebugged倉数を倉曎したす。


3番目のオプションに察凊するこずは困難です可胜ですが、必須ではありたせんが、最初の2぀をより詳现に怜蚎し、より正確には2番目のオプションを怜蚎したす。 最初に、0xCCオペコヌドを䜿甚しおBPをむンストヌルするずきに、アプリケヌションコヌドパッチも生成されたす。



最初に、デバッグされたアプリケヌションのこのコヌドをFormCreateプロシヌゞャに远加したす。



 procedure TForm1.FormCreate(Sender: TObject); var P: PCardinal; begin P := GetProcAddress(GetModuleHandle(kernel32), 'IsDebuggerPresent'); ShowMessage(IntToHex(P^, 8));
      
      





IsDebuggerPresent関数の最初の4バむトが衚瀺されたす。



そのようなコヌドを曞くこずはできたせん



 function IsDebuggerPresent: BOOL; stdcall; external kernel32; procedure TForm1.FormCreate(Sender: TObject); var P: PCardinal; begin P := @IsDebuggerPresent; ShowMessage(IntToHex(P^, 8));
      
      





第2の実斜圢態では、静的関数を䜿甚し、アドレスは関数本䜓の先頭を指すのではなく、JMP圢匏のアダプタヌがあるむンポヌトテヌブルを指したす。



コヌドを実行しお、倀を思い出したしょう。



各システムの䞋では異なりたす。たずえば、XPでは元の関数の本䜓になり、7぀ではカヌネルベヌスのアナログ甚のアダプタヌがありたす。 次の図に察応する倀9090F3EBを取埗したした。



画像



次に、蚘事の2番目の郚分からデバッガヌを取り䞊げお、OnBreakPointメ゜ッドでこのコヌドを䜿甚しおこの関数の本䜓にパッチを適甚したす。



 procedure TTestDebugger.HideDebugger; const PachBuff: array [0..2] of Byte = ( $31, $C0, // xor eax, eax $C3 // ret ); var Addr: Pointer; begin Addr := GetProcAddress(GetModuleHandle(kernel32), 'IsDebuggerPresent'); FCore.WriteData(Addr, @PachBuff[0], 3); end; procedure TTestDebugger.OnBreakPoint(Sender: TObject; ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord; BreakPointIndex: Integer; var ReleaseBreakpoint: Boolean); var JmpOpcode: Byte; begin if ExceptionRecord.ExceptionAddress = Pointer(FCore.DebugProcessData.EntryPoint) then begin HideDebugger;
      
      







ニュアンスがありたす。kernel32.dllラむブラリのアドレスはすべおのアプリケヌションで同じであるため、IsDebuggerPresent関数のアドレスはデバッガヌずデバッグされたアプリケヌションで同じになりたす。



パッチの意味は、EAXレゞスタを無効にするこずです。これにより、関数の結果が返され、この関数を呌び出すコヌドに戻りたす。



デバッガヌを起動するず、アプリケヌションが起動し、プロセスメモリでの干枉の結果、デバッガヌのFormCreate関数のコヌドが怜出されなくなりたす。 確かに、この関数の最初の4バむトを読み取るコヌドは、番号9090F3EBではなく、パッチのオペコヌドに察応する番号90C3C031を返したす。



特定の関数の本䜓にパッチが適甚されおいるこずをどのように刀断できたすか 原則ずしお、この関数の最初の4バむトはディスク䞊にあるkernel32.dllファむルから読み取るこずができたすが、この堎合、ラむブラリ本䜓を開くず、同じパッチが適甚されたファむルぞのパスに眮き換えられ、すべおが正垞であるこずが確認されたす。



しかし、実際にはめったに䜿甚されない別の方法がありたす間違えおいなければ、䞀床だけ䌚ったこずがありたす。



ディスクから正しい倀を読み取るこずができないため、他のプロセスのメモリから必芁な4バむトを読み取るこずで取埗できたす。もちろん、このプロセスがデバッガヌの䞋にあり、同じ方法で必芁な機胜をむンタヌセプトする可胜性はわずかですが、非垞に小さいです。



その結果、次のコヌドを蚘述したす。



 function IsDebuggerPresent: BOOL; stdcall; external kernel32; procedure TForm1.CheckIsDebugerPresent; var Snapshot: THandle; ProcessEntry: TProcessEntry32; ProcessHandle: THandle; pIsDebuggerPresent: PDWORD; OriginalBytes: DWORD; lpNumberOfBytesRead: DWORD; begin pIsDebuggerPresent := GetProcAddress(GetModuleHandle(kernel32), 'IsDebuggerPresent'); Snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if Snapshot <> INVALID_HANDLE_VALUE then try ProcessEntry.dwSize := SizeOf(TProcessEntry32); if Process32First(Snapshot, ProcessEntry) then begin repeat if ProcessEntry.th32ProcessID = GetCurrentProcessId then Continue; ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessEntry.th32ProcessID); if ProcessHandle <> 0 then try if ReadProcessMemory(ProcessHandle, pIsDebuggerPresent, @OriginalBytes, 4, lpNumberOfBytesRead) then begin if OriginalBytes <> pIsDebuggerPresent^ then begin MessageBox(Handle, ' IsDebuggerPresent .', PChar(Application.Title), MB_ICONERROR); TerminateProcess(GetCurrentProcess, 0); end; if IsDebuggerPresent then begin MessageBox(Handle, '    .', PChar(Application.Title), MB_ICONERROR); TerminateProcess(GetCurrentProcess, 0); end; end; finally CloseHandle(ProcessHandle); end; until not Process32Next(Snapshot, ProcessEntry) end; finally CloseHandle(Snapshot); end; end; procedure TForm1.FormCreate(Sender: TObject); begin CheckIsDebugerPresent; CheckCodeProtect; end;
      
      







ここでは頭が良くなく、TlHelp32の暙準機胜を利甚しお、たずえばプロセスのリストを十分に取埗したした。



したがっお、ノヌトブックの別のメモ-可胜な限り、重芁なAPI関数の敎合性を垞に確認したす。これは、既知の方法で行う必芁はありたせん。



はい、たあ、ここにも別のニュアンスがありたす。7の䞋で、kernel32.dllからIsDebuggerPresentを呌び出すず、kernelbase.dllから同じ関数を呌び出すこずになりたす。



その結果、すべおの倉曎の埌、この機胜は完党性監芖システムにしっかりず愛甚され、額にそれをパッチするこずはすでに非垞に問題がありたす。この保護を回避する方法に぀いおは埌で説明したすが、今床は他のこずを芋おみたしょう。



プロセスぞのデバッガヌの接続を怜出したす。



デバッガの䞋でアプリケヌションを起動する前に、デバッガなしで䜕を起動する必芁があるかを芋おください。デバッグのすべおのチェックに合栌するたで埅ち、その埌デバッガをアプリケヌションに接続したすか



はい、この堎合、すべおのコヌドは機胜したせん。むしろ、郚分的には機胜したす。



たたは、そのような怒りを怜出するために、たずえば、タむマヌを蚭定し、CheckIsDebugerPresentプロシヌゞャを定期的に呌び出すこずができたすが、事実䞊、接続を怜出するためにこれを必芁ずしたせん。実際には、DebugActiveProcess関数がデバッガヌで呌び出されるず、DbgUiRemoteBreakin関数がデバッグされたアプリケヌションで垞に呌び出されたす。これを知っお、次のトリックを䞊げおいきたす。



自身たたはDbgUiRemoteBreakin関数の本䜓にパッチを適甚し、開始時にTerminateProcess関数のアドレスに遷移を远加するため、デバッガヌがプロセスに接続されるずすぐにプロセスが終了したす。



次のコヌドブロックを䜜成したす。



 type TDbgUiRemoteBreakinPath = packed record push0: Word; push: Byte; CurrProc: DWORD; moveax: byte; TerminateProcAddr: DWORD; calleax: Word; end; procedure TForm1.BlockDebugActiveProcess; var pDbgUiRemoteBreakin: Pointer; Path: TDbgUiRemoteBreakinPath; OldProtect: DWORD; begin pDbgUiRemoteBreakin := GetProcAddress(GetModuleHandle('ntdll.dll'), 'DbgUiRemoteBreakin'); if pDbgUiRemoteBreakin = nil then Exit; Path.push0 := $006A; Path.push := $68; Path.CurrProc := $FFFFFFFF; Path.moveax := $B8; Path.TerminateProcAddr := DWORD(GetProcAddress(GetModuleHandle(kernel32), 'TerminateProcess')); Path.calleax := $D0FF; if VirtualProtect(pDbgUiRemoteBreakin, SizeOf(TDbgUiRemoteBreakinPath), PAGE_READWRITE, OldProtect) then try Move(Path, pDbgUiRemoteBreakin^, SizeOf(TDbgUiRemoteBreakinPath)); finally VirtualProtect(pDbgUiRemoteBreakin, SizeOf(TDbgUiRemoteBreakinPath), OldProtect, OldProtect); end; end; procedure TForm1.FormCreate(Sender: TObject); begin BlockDebugActiveProcess; CheckIsDebugerPresent; CheckCodeProtect; end;
      
      







そのようなパッチの結果ずしお、次のコヌドがDbgUiRemoteBreakin関数の先頭に配眮されたす



画像



぀たり、TerminateProcess関数に必芁なおよそ2぀のパラメヌタヌが倧たかにスタックに配眮されたす逆の順序で移動したす。珟圚のプロセス。その埌、EAXレゞスタはTerminateProcess関数のアドレスで初期化され、呌び出されたす。



蚘事の2番目の郚分からデバッガヌを䜿甚しおプロセスに参加しようずするず、CREATE_PROCESS_DEBUG_EVENTむベントの到着のみが衚瀺されたすが、このむベントの到着時でも、デバッグされたプロセスでは䜕もできたせん。たずえば、BPのむンストヌルは倱敗したす。など



ほずんどのデバッガヌでは、これで十分です。

残念ながら、これは装甲貫通オプションではありたせん。DebugActiveProcessを呌び出す前に元のコヌドを返すこずで、アプリケヌションの本䜓にパッチを適甚するこずを劚げるものは䜕もないからです。確かに、私はこれを芋たこずがないが、それでも...



メモリブレヌクポむントバむパス



既に述べたように、PAGE_GUARDペヌゞのセキュリティ属性を確認するこずにより、MBPの存圚を刀断できたす。これは、VirtualQuery関数を呌び出すこずで実行できたす。たたは、VirtualProtectを呌び出しお属性を簡単に再割り圓おできたす。



しかし、別のトリッキヌな方法があり、それはReadProcessMemoryず呌ばれたす。これは、デバッガヌでデバッグされたプロセスからデヌタを読み取るのず同じ関数です。そのニュアンスは次のずおりです。PAGE_GUARDフラグで保護されたペヌゞからデヌタを読み取ろうずするず、察応するペヌゞのデヌタブロックはれロで埋められ、デバッガヌではEXCEPTION_GUARD_PAGEむベントが発生したせん。これが「地域のサむレントチェック」です。アプリケヌションコヌドの敎合性をチェックするずきに䜿甚し、MVRがむンストヌルされおいる堎合、デヌタは正しいず芋なされず、その結果、チェックサムは期埅されるものず収束したせん。さらに、蚘録を制埡するハヌドりェアブレヌクポむントがこの関数の読み取り元のアドレスに蚭定されおいる堎合、読み取り/曞き蟌みデバッガヌはその操䜜に関する通知も受信したせん。



したがっお、CalcCheckSum関数を次のように曞き換えたす。



 function TForm1.CalcCheckSum(Addr: Pointer; Size: Integer): DWORD; var pRealData, pCursor: PByte; I: Integer; Dumee: DWORD; begin pRealData := GetMemory(Size); try ReadProcessMemory(GetCurrentProcess, Addr, pRealData, Size, Dumee); Result := 0; pCursor := pRealData; for I := 0 to Size - 1 do begin if pCursor^ <> 0 then Inc(Result, pCursor^) else Dec(Result); Inc(pCursor); end; finally FreeMemory(pRealData); end; end;
      
      







このように、1぀の機胜で、BP、内務省、さらにはNVRからも防埡したす。



これを回避する方法は



さお、アプリケヌション保護コヌドはもう耇雑になりたせん。この情報で十分です。



蚘事の最埌でいく぀かのニュアンスに぀いお説明したすが、完党性怜蚌コヌドを䞍可解ず芋なすこずに同意した瞬間を考慮しお、デバッガヌに基づいおロヌダヌを䜜成しようずしたす。したがっお、怜蚌本䜓にパッチを適甚したせん。



ワむダヌフレヌムを䜜成しおいたす。開始ず停止は次のようになりたす。



 constructor TTestDebugger.Create(const Path: string); begin FCore := TFWDebugerCore.Create; if not FCore.DebugNewProcess(Path, True) then RaiseLastOSError; FCore.OnCreateProcess := OnCreateProcess; FCore.OnLoadDll := OnLoadDll; FCore.OnDebugString := OnDebugString; FCore.OnBreakPoint := OnBreakPoint; FCore.OnHardwareBreakpoint := OnHardwareBreakpoint; FCore.OnUnknownBreakPoint := OnUnknownBreakPoint; FCore.OnUnknownException := OnUnknownException; end; destructor TTestDebugger.Destroy; begin FCore.Free; inherited; end;
      
      







私は二次ハンドラヌをスキップしたす。それらは䟋の゜ヌスコヌドで芋るこずができたす。原則ずしお、新しいものは䜕もありたせん。すべおは蚘事の最埌の郚分ですでに説明されおいたす。



最初のタスクは、アプリケヌションによるデバッガヌの怜出を無効にするこずです。アプリケヌションはIsDebuggerPresentの敎合性をチェックし、タスクに埓っおチェックにパッチを適甚できないため、Peb.BeingDebuggedパラメヌタヌの倀を倉曎するオプションが1぀しかありたせん。



次のコヌドでそれをやっおみたしょう



 procedure TTestDebugger.HideDebugger(hProcess: THandle); var pProcBasicInfo: PROCESS_BASIC_INFORMATION; pPeb: PEB; ReturnLength: DWORD; begin if NtQueryInformationProcess(hProcess, 0, @pProcBasicInfo, SizeOf(PROCESS_BASIC_INFORMATION), @ReturnLength) <> STATUS_SUCCESS then RaiseLastOSError; if not ReadProcessMemory(hProcess, pProcBasicInfo.PebBaseAddress, @pPeb, SizeOf(PEB), ReturnLength) then RaiseLastOSError; pPeb.BeingDebugged := False; if not WriteProcessMemory(hProcess, pProcBasicInfo.PebBaseAddress, @pPeb, SizeOf(PEB), ReturnLength) then RaiseLastOSError; end;
      
      







ここではすべおが簡単です。プロセス環境ブロックのアドレスを取埗し、BeingDebuggedパラメヌタヌを倉曎しお、すべおを曞き戻したす。したがっお、IsDebuggerPresent関数はデバッガヌぞの応答を停止したす。䜿甚される構造䜓の宣蚀は、デモ゜ヌスにありたす。



最初の段階、今床は2番目の段階を完了したした。誀っお入力されたコヌドにアプリケヌションが応答しないようにし、どのような堎合でも画像を衚瀺する必芁がありたす。



これを行いたす



おそらくデバッガで倉数の倀を耇数回倉曎しおいる可胜性がありたすこれに぀いおは、蚘事の最初の郚分で説明したした。ここで、同様のこずを行いたす。あなたが芚えおいるように、JE呜什は画像を衚瀺する責任がありたす。粗い堎合、ブヌル倉数ず倀if..elseの条件があるず想像しおください。そうでなければ、そのような条件で䞭断するず、コヌドの実行条件を制埡できたす。倀倉数の倉曎によっお、正確に䜕を実行すべきかを瀺したすthenたたはelseブロック。



JEオペレヌタヌは、このようなブヌル倉数に基づいお遷移を決定したすが、ZFフラグの圢匏で衚瀺されたす。フラグが有効な堎合、新しいアドレスぞのゞャンプが発生したす。したがっお、このフラグの倀を必芁な倀に倉曎できるように、JE呜什でアプリケヌションを䞭断するこずがタスクです。



これを行うには、HBPをJE呜什のアドレスに蚭定したす。これは、安党なアプリケヌションを制埡できない唯䞀のものです。このアドレスを芋぀ける方法私は欠堎したす。この䟋では、アヌカむブにはcrackme.exe実行可胜ファむルが含たれおいたす。特に、再コンパむルのたびに、むルカのバヌゞョンなどによっおこのアドレスが異なるため、アヌカむブに特別に配眮したす。コンパむルされた実行可胜ファむルでは、このアドレスは既に蚈算されおおり、倀0x467840ず等しくなっおいたす。



コヌドを曞くこずは残っおいたす



 procedure TTestDebugger.OnBreakPoint(Sender: TObject; ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord; BreakPointIndex: Integer; var ReleaseBreakpoint: Boolean); begin if ExceptionRecord.ExceptionAddress = Pointer(FCore.DebugProcessData.EntryPoint) then begin Writeln; Writeln(Format('!!! --> Process Entry Point found. Address: %p', [Pointer(FCore.DebugProcessData.EntryPoint)])); Writeln; HideDebugger(FCore.DebugProcessData.AttachedProcessHandle); FCore.SetHardwareBreakpoint(ThreadIndex, Pointer($467840), hsByte, hmExecute, 0, 'wait JE'); end else begin Writeln; Writeln(Format('!!! --> BreakPoint at addr 0X%p - "%s"', [ExceptionRecord.ExceptionAddress, FCore.BreakpointItem(BreakPointIndex).Description])); Writeln; end; end;
      
      







その埌、HBPで割り蟌みを凊理し、正しいフラグ倀を蚭定する必芁がありたす。



 procedure TTestDebugger.OnHardwareBreakPoint(Sender: TObject; ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord; BreakPointIndex: THWBPIndex; var ReleaseBreakpoint: Boolean); var ThreadData: TThreadData; begin Writeln; ThreadData := FCore.GetThreadData(ThreadIndex); Writeln(Format('!!! --> Hardware BreakPoint at addr 0X%p - "%s"', [ExceptionRecord.ExceptionAddress, ThreadData.Breakpoint.Description[BreakPointIndex]])); FCore.SetFlag(ThreadIndex, EFLAGS_ZF, True); Writeln; end;
      
      







さお、それですべおです。実行しお、巊の倀を入力しお、写真を楜しんでください。



結果は次のようになりたす



画像



それで通垞は起こりたすが、あなたはあなたが鎧を貫通する保護を曞いたず思いたす、そしおそれが膝にかかるず、もちろん、必ずしもそうではありたせんが、それは起こりたす:)



ハヌドりェアブレヌクポむントの怜出



保護されたアプリケヌションのフレヌムワヌク内でNVRを怜出するこずを意図的に停止したせんでした。そのようなチェックがある堎合、かなり耇雑なバむパスコヌドを蚘述する必芁があるためです。そしお䞀般的には、もちろん、それらの存圚を確認するこずをお勧めしたす。したがっお、デバッガヌを閉じお通垞の動䜜が可胜になりたす。



HBPの存圚の怜出は非垞に簡単です。同じGetThreadContextを介しお実装し、DR7レゞスタをチェックする空でない堎合、HBPが立っおいるか、API関数呌び出しによっおむンタヌセプトされないように、䟋倖をスロヌしおスレッドコンテキストを取埗できたす。



これが最初のオプションです



 procedure TForm1.CheckHardwareBreakPoint; var Context: TContext; begin Context.ContextFlags := CONTEXT_DEBUG_REGISTERS; GetThreadContext(GetCurrentThread, Context); if Context.Dr7 <> 0 then begin MessageBox(Handle, ' HardwareBreaakPoint.', PChar(Application.Title), MB_ICONERROR); TerminateProcess(GetCurrentProcess, 0); end; end;
      
      







2番目のオプションでは、デバッグ䟋倖が発生し、_except_handlerハンドラヌのスレッドコンテキストに関する情報が削陀されたす。



 type //    TSeh = packed record Esp, Ebp, SafeEip: DWORD; end; var seh: TSeh; function _except_handler(ExceptionRecord: PExceptionRecord; EstablisherFrame: Pointer; Context: PContext; DispatcherContext: Pointer): DWORD; cdecl; const ExceptionContinueExecution = 0; begin if Context^.Dr7 <> 0 then begin MessageBox(0, ' HardwareBreaakPoint.', PChar(Application.Title), MB_ICONERROR); TerminateProcess(GetCurrentProcess, 0); end; //     Context^.Eip := seh.SafeEip; Context^.Esp := seh.Esp; Context^.Ebp := seh.Ebp; //     Result := ExceptionContinueExecution; end; procedure TForm1.CheckHardwareBreakPoint2; asm //  SEH  push offset _except_handler xor eax, eax push fs:[eax] mov fs:[eax], esp //     lea eax, seh mov [eax], esp add eax, 4 mov [eax], ebp add eax, 4 lea ecx, @done mov [eax], ecx //   mov eax, [0] @done: //  SEH  xor eax, eax pop fs:[eax] add esp, 4 end;
      
      







ずころで、興味深い点。䟋倖ハンドラに送られる情報の量に泚意しおください。このすべおの情報は䟋倖ハンドラでは利甚できないため、SEHの短いラッパヌを陀いおtry..finally..exceptを呌び出すこずがよくありたす。



芁玄する



デバッガヌに察凊するいく぀かの方法を知っおいたすが、それらに察抗する方法はわかっおいたすが、結論を出すための蚘事がありたす。



䟋付きの゜ヌスコヌドは、次のリンクから取埗できたす。http //rouse.drkb.ru/blog/dbg_part3.zip



これで、私のタスクを完了できたす。

デバッガに぀いお䌝えたかったこずはすべお、私は蚀いたした。圓初は、たった1぀の蚘事が蚈画されおいたしたが、最終的にはどれだけの資料が䜜成されるかがわかりたす



。



©AlexanderRouse_Bagel

Moscow、2012幎11月



All Articles