Perl2Exeの開梱





perlスクリプトからスタンドアロンアプリケーションを作成し、あらゆる種類の保護を整理するために最も一般的に使用される製品の1つはIndigoStar Perl2Exeです。 定期的に、スクリプトのソースコードが失われ、このプログラムの助けを借りて受け取ったexeファイルだけが手元にある場合に状況が発生しますが、確かにその種に到達したいのです。 それを行う方法を理解しましょう。



開始するには、製品自体をダウンロードし(詳細な説明はWindows用のPerl2Exe V11.00に基づいています )、意図した目的に使用します。添付の​​sample.plスクリプトを本格的なexeファイルに変換します。 これを行うには、コンソールで単純なコマンドperl2exe sample.plを入力するか、エクスプローラーでsample.plをperl2exeにドラッグアンドドロップします。

そのため、ソースコードを抽出する可能性について調べる必要があるsample.exeがあります。 ありきたりから始めましょう:

16進エディタを使用して、ファイルの本文でスクリプトコードの要素を探してみてください。 無駄に、コードはパック/暗号化された形式で保存されているようですが、これは非常に論理的です。



API Monitorユーティリティを使用して、起動後にsample.exeが作成するファイルへのアクセスを分析します(代わりに、Mark Russinovichが作成したProcess Monitorを使用できます)。 これを行うには、インターセプトされた関数のリストでCreateFileA、CreateFileWを選択し、Ctrl + Hを押して、監視が実行されるコンテキストでプロセスを指定します。







繰り返しますが、スクリプトのソースコードを読み取り可能な形式で保存するための中間ファイルは使用されません。



さて、デバッガを用意して、プログラムの内部構造の大まかな研究に進みます。 目的は、関心のあるコードが開いた形でプロセスメモリに表示されるかどうか、およびプログラム実行のどの段階でインターセプトおよびコピーするのが最も簡単かを判断することです。 この場合、私はOllyDbgを使用することを好みますが、一般的には、ほとんどすべてのデバッガー(例えば、WinDbg、IDA、または例えばSyser)が使用します。

プログラムをデバッガーにロードし、標準のCRTプロローグを観察します。







特にそれについて考えることなく、F5を押します。プログラムが正常に実行され、ntdllが私たちを腸に投げ込みます。 プロセスメモリ(Alt + M)を開き、スクリプトの一部、たとえば「This is sample」という文字列を探します。 出来上がり、ソースコードはメモリにあります。 プログラムが終了するまでに、関心のあるデータがメモリに書き込まれる可能性があるため、このアプローチは信頼できないことに注意する価値があります。これは、セキュリティの観点からは正しいアプローチですメモリ内)。

このコードがメモリに表示されるステージを確認します。 CRTイントロをスキップして、コードの最初の重要な部分の研究に進みましょう。







p2x5142.dllの動的ライブラリファイルのロードを観察し、エクスポートされたRunPerl関数のアドレスとその呼び出しを取得します。 いくつかの簡単な操作の後、興味のあるミステリーがRunPerl内で発生することがわかります。 その内容を研究しています。







WinAPI関数の呼び出しは興味がありませんが、接頭辞perl(Perl_sys_init3、perl_alloc、perl_contruct、perl_parse、perl_run、...)が付いた関数の呼び出しの興味深いシーケンスがあり、調査実験を行った後、コードが開いた形でメモリに表示されることがわかりますperl_parseを呼び出した後。 perl_parseの中身を見てみましょう。 繰り返しますが、perl接頭辞と別のがらくたを使って関数呼び出しが行われますが、その研究は自然な怠andと「はい、私は一般的にこの記事をバスに座って書いています」によって妨げられています。

他の方法で行きましょう。 プログラムを数回実行し、スクリプトのソースコードのメモリが同じ場所に割り当てられていることを確認します(これも信頼性の低いアプローチです。mallocをインターセプトし、関数を解放し、アドレス可能な領域を分析する方が論理的です)。 ブレークポイントを設定して、目的のアドレスにデータを書き込むコードを見つけます。

プログラムを再起動し、ここのどこかにドロップアウトします。







しかし、サイクルの途中でデータを傍受することはおそらく最良の選択ではありません。 関数から戻る前に、何があるか見てみましょう。







しかし、これは私たちに合う以上のものです。 ebxレジスタとスタックには、ソースコードで関心のあるメモリ領域を指すアドレスがあります。 メモ帳でretn命令の仮想アドレスを記述し、p2x5142.dllライブラリがメモリにロードされたアドレスを確認します。 一方から他方を減算し、後で役立つオフセットを取得します。



ここで、このプロセスを自動化することをお勧めします。 これを行うために、sample.exeメモリに読み込む単純なライブラリを作成します。 ライブラリ自体は、ベクトル例外ハンドラをインストールし、p2x5142.dllライブラリがメモリにロードされる瞬間を監視し、retn命令を以前に受信したオフセットのint3に置き換え、レジスタがアドレスするメモリの内容をファイルにダンプします。

ライブラリインジェクションコードを新しい方法で記述しないために、「C ++ in 21 days」という本の一部をマスターした、私がかつて書いたクラスを使用します。 彼はそこにいます。 そのサイズは...親指の厚さ程度でなければなりません。 これは次のようなものです。 また、MicrosoftのDetoursライブラリを使用して、 LoadLibrary呼び出しをインターセプトします。 もちろん、このトピックに関するMASMの開発に目を向けることは可能ですが、残念なことに、金曜日は低レベル言語でのプログラミングの日ではありません。 書き始めましょう。 ランチャープログラムから始めましょう。ランチャープログラムは、サスペンド状態でsample.exeを実行し、作成されたプロセスにライブラリを挿入して、作業を再開します。



//    #include <iostream> #include <Windows.h> #include "injector.hpp" using namespace std; //       //     ,   ,       wstring str2wstr(const char * aIn); int main(int argc, char *argv[]) { //       ,     if(argc != 3) { cout<<"Usage: launcher sample.exe inject.dll"<<endl; return 1; } //   STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; //  ,      // CREATE_SUSPENDED   ,      ""  if(CreateProcess(str2wstr(argv[1]).c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi) == 0) { cout<<"Failed to create process"<<endl; return 1; } //    injector injector a; //     (     ) a.set_blocking(false); try { //       a.inject(pi.dwProcessId, str2wstr(argv[2])); } catch(const injector_exception &e) { //  -   ,         e.show_error(); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); TerminateProcess(pi.hProcess, 1); return 1; } //    ResumeThread(pi.hThread); //  ,      CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; }
      
      







ライブラリのソースコードに移りましょう



 //   #include <iostream> #include <sstream> #include <fstream> #include <Windows.h> #include "detours.h" #pragma comment(lib, "detours.lib") using namespace std; // ,        static unsigned int i = 0; //   ,     int3 static const DWORD retn_offset = 0xB40E9; static const wstring dump_directory = L"dump"; static const wstring file_prefix = L"src_"; static const wstring perl_dll_name = L"p2x5142.dll"; //  ANSI ?      (   ) HMODULE (WINAPI * real_loadlibrary)(LPCSTR lpFileName) = LoadLibraryA; //  ,     string wstr2str(const wchar_t * aIn); //    void hook() { //  ,       void * base_address = GetModuleHandle(perl_dll_name.c_str()); if(base_address == NULL) return; DWORD pr; //      base_address = reinterpret_cast<void *>(reinterpret_cast<DWORD>(base_address) + retn_offset); //    ,  int3,    VirtualProtect(base_address, 1, PAGE_READWRITE, &pr); CopyMemory(base_address, "\xCC", 1); VirtualProtect(base_address, 1, pr, &pr); } // ,      void dump_data(char * buffer, unsigned int size) { DWORD pr; wstringstream ss; //      ss << dump_directory << L"\\" << file_prefix << i++ << L".txt"; ofstream file(ss.str(), ofstream::binary); file.exceptions(0); if(file.is_open()) { //        VirtualProtect(buffer, size, PAGE_READONLY, &pr); file.write(buffer, size); VirtualProtect(buffer, size, pr, &pr); file.close(); } } // ,     LoadLibraryA HMODULE WINAPI my_loadlibrary(LPCSTR lpFileName) { HMODULE h = real_loadlibrary(lpFileName); //    ,    if ( strstr(lpFileName, wstr2str(perl_dll_name.c_str()).c_str()) && i == 0 ) { i++; hook(); } return h; } //   ,     int3 LONG CALLBACK VEH(PEXCEPTION_POINTERS ExceptionInfo) { if ( //   ,   Eax   ,   Ebx    ExceptionInfo->ContextRecord->Eax > 0 && ExceptionInfo->ContextRecord->Eax < 0xFFFFF && //      -  ,     "" //         ExceptionInfo->ContextRecord->Ebx < 0x77000000 && ExceptionInfo->ContextRecord->Ebx > reinterpret_cast<DWORD>(GetProcessHeap()) ) dump_data(reinterpret_cast<char *>(ExceptionInfo->ContextRecord->Ebx), ExceptionInfo->ContextRecord->Eax); //   Eip            4  // ,  ,   retn ExceptionInfo->ContextRecord->Eip = *reinterpret_cast<DWORD *>(ExceptionInfo->ContextRecord->Esp); ExceptionInfo->ContextRecord->Esp += sizeof(DWORD); //    -    return EXCEPTION_CONTINUE_EXECUTION; } BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) { if(dwReason == DLL_PROCESS_ATTACH) { //       CreateDirectory(dump_directory.c_str(), NULL); AddVectoredExceptionHandler(1, VEH); //         detours DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&reinterpret_cast<PVOID &>(real_loadlibrary), my_loadlibrary); DetourTransactionCommit(); } else if(dwReason == DLL_PROCESS_DETACH) { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&reinterpret_cast<PVOID &>(real_loadlibrary), my_loadlibrary); DetourTransactionCommit(); } return TRUE; }
      
      







結果のソリューションをテストするためだけに残ります。







どうやら、すべてが完全にダンプされます。 目標を達成しました。

興味のある方は、 PerlAppを解凍する同様の例を見ることができます。



記事のソースコード: ダウンロード



All Articles