WPE Proのx64サポートについて

画像



地元の住民のほとんどはスニッファーの概念に精通していると思います。 最終的な目標は同じ(特定の条件を満たすパケットをインターセプトする)であるという事実にもかかわらず、彼らは完全に異なる方法でそれを達成します。 一部のソフトウェアは、指定されたネットワークインターフェイス(たとえば、 Pcapライブラリを使用して実装されているWireshark )をリッスンし、一部のソフトウェアはネットワークとの対話を担当するWinAPI関数の呼び出しをインターセプトします。 どちらの方法にも長所と短所がありますが、タスクが特定の有名なアプリケーションからのパケットキャプチャを必要とする場合、通常、2番目のオプションがより便利です。 この場合、このプログラムが使用するIPアドレスとポートを見つける必要はなく(特に非常に多く存在する可能性があるという事実を考慮すると)、「このアプリケーションのすべてのパケットを傍受したい」と言うことができます。 便利ですね。



おそらく、特定のWinAPI関数への呼び出しをインターセプトするという原則に基づいて、これまでで最も人気のあるスニファーはWPE Proです。 おそらくあなたの多くは、このスニファーが使用されるのはさまざまなゲームで利点を得るためであるため、オンラインゲーム専用のさまざまなフォーラムで彼について聞いたことがあるでしょう。 彼はタスクを完璧に実行しますが、1つの不快な欠点があります。64ビットアプリケーションの操作方法がわからないということです。 起こった問題の1つについて、64ビットアプリケーションからのパケットをインターセプトする必要があり、Wiresharkの方向を見ました。 残念ながら、この状況でそれを使用することはあまり便利ではありませんでした。調査中のアプリケーションは、新しいポートを開くたびに異なるIPアドレスにデータを送信しました。 少しグーグルで、x64をサポートするWPE Proの既製の類似物はないことがわかりました(存在する場合は、コメント内のリンクに感謝します-これはWindowsに関するものであることに注意してください)。 WPE Proの作成者は、公式Webサイトとスニファー自体に連絡先情報を残していなかったため、私は自分でそれを把握することにしました。



プロセスがどのように進んだか、そしてその結果は、カットの下で読みます。



それでは、最初に何をする必要がありますか? そうです、WPE Pro自体をダウンロードしてください。 これは、 公式スニッファーWebサイトで行うことができます。2つのバージョン(0.9aと1.3)が同時にダウンロードできます。 最新バージョンのWindowsで動作するため、バージョン0.9aを検討します。



ダウンロードしましたか? 次に、何らかのパッカーまたはトレッドで覆われているかどうかを確認しましょう。



画像



画像



今回は何も撮影する必要がないようです。 次に、 OlylyDbgを使用して、「WpePro.net.exe」を読み込みます。 たとえば、64ビットバージョンのDependency Walkerを起動し、WPE Proがインターセプトに使用可能なプロセスのリストに表示できない理由を確認しましょう。



WinAPIで現在のプロセスのリストを取得するには、主に2つの方法があります。





必要に応じて、これらの方法の違いについて読むことができます 。たとえば、 こちら



WpePro.net.exeモジュールのモジュール間呼び出しを見て、 CreateToolhelp32SnapshotEnumProcessesもここにないことがわかります。 おそらく、アプリケーションはWinAPI関数GetProcAddressを使用して実行時にアドレスを取得するので、参照されたテキスト文字列を見てみましょう。 今回はすべて正反対です-「CreateToolhelp32Snapshot」と「EnumProcesses」の両方の行が見つかりました。 これらの行にアクセスする場所にブレークを配置し、WPE Proの[ターゲットプログラム]ボタンをクリックして、停止した場所を確認します。



画像



F9キーを押すと、現在のプロセスのリストを含むウィンドウが最終的に表示される前に、同じクラックがさらに数回機能することがわかります。 Dependency Walkerが含まれていない理由を見てみましょう。 ウィンドウを閉じ、「ターゲットプログラム」ボタンをもう一度クリックして、同じブレークポイントから開始して、段階的なデバッグを実行します。 GetProcAddress呼び出しの直後に、次の関数が順番に呼び出されるループになります-OpenProcessEnumProcessModules 、これはCALL DWORD PTR DSステートメントの背後に隠されています:[EDI + 10]GetModuleFileNameExCALL DWORD PTR DS命令:[ECX + 14] )およびCloseHandle



画像



OpenProcess関数の最後の引数であるPIDがスタックにプッシュされるアドレス0x0042A910にブレークを入れて、 EAXレジスタがDependency Walkerプロセスの識別子と等しい値を取るまで待機してみましょう。 私のマシンでは、その時点で6600に相当していました。 0x19C8。



コードを実行すると、Dependency Walkerの場合、 0x0042A942に位置し、 EnumProcessModules WinAPI関数の呼び出しに続くTEST EAX、EAX命令が、その隣にある条件付きジャンプ演算子を呼び出しが位置する0x0042A9E7にジャンプすることがわかりますGetHandleFileNameEx関数の呼び出しにも到達していないため、 CloseHandleは 、もちろん、アプリケーションの通常の作業ではありません。



画像



エラーコード-0x12B(ERROR_PARTIAL_COPY)に注意してください。 これが起こる理由を見てみましょう。 EnumProcessModules関数のドキュメントを開き、次を参照してください。



この関数がWOW64で実行されている32ビットアプリケーションから呼び出された場合、32ビットプロセスのモジュールのみを列挙できます。 プロセスが64ビットプロセスの場合、この関数は失敗し、最後のエラーコードはERROR_PARTIAL_COPY(299)です。


はい、これは私たちの場合です。



GetModuleFileNameEx関数のドキュメントには類似した内容は記載されていませんが、たとえばここで説明されているように、同様の動作をします 。 この問題の解決策の1つは、 QueryFullProcessImageName関数を使用することです。これは、64ビットプロセスに適用された場合、WOW64で実行される32ビットアプリケーションからの呼び出しでも正常に動作します。



これで、 EnumProcessModules関数の呼び出しをフラッディングできます



0042A93F . FF57 10 CALL DWORD PTR DS:[EDI+10] ; EnumProcessModules 0042A942 . 85C0 TEST EAX,EAX 0042A944 90 NOP 0042A945 90 NOP 0042A946 90 NOP 0042A947 90 NOP 0042A948 90 NOP 0042A949 90 NOP 0042A94A . 8B4424 14 MOV EAX,DWORD PTR SS:[ESP+14] 0042A94E . BE 00000000 MOV ESI,0
      
      





GetModuleFileNameExQueryFullProcessImageName関数に置き換えるコードケイブを記述します。



 0042A971 /E9 DA3F0600 JMP WpePro_n.0048E950 0042A976 |90 NOP 0042A977 |90 NOP 0042A978 |90 NOP 0042A979 |90 NOP ; GetModuleFileNameEx 0042A97A |90 NOP 0042A97B |90 NOP 0042A97C |90 NOP 0042A97D |90 NOP 0042A97E |90 NOP 0042A97F |90 NOP 0042A980 |90 NOP 0042A981 |90 NOP 0042A982 |90 NOP 0042A983 |90 NOP 0042A984 |90 NOP 0042A985 |90 NOP 0042A986 |90 NOP 0042A987 |90 NOP 0042A988 |90 NOP 0042A989 |90 NOP 0042A98A |90 NOP 0042A98B |90 NOP 0042A98C |90 NOP 0042A98D |90 NOP 0042A98E > |6A 14 PUSH 14 0042A990 . |E8 7FCF0300 CALL WpePro_n.00467914
      
      





 0048E950 60 PUSHAD 0048E951 9C PUSHFD 0048E952 8D5C24 AC LEA EBX,DWORD PTR SS:[ESP-54] 0048E956 C74424 AC 040>MOV DWORD PTR SS:[ESP-54],104 0048E95E 53 PUSH EBX 0048E95F 50 PUSH EAX 0048E960 6A 00 PUSH 0 0048E962 55 PUSH EBP 0048E963 E8 777CB675 CALL KERNEL32.QueryFullProcessImageNameA 0048E968 9D POPFD 0048E969 61 POPAD 0048E96A ^ E9 1FC0F9FF JMP WpePro_n.0042A98E
      
      





WPE Proには、Dependency Walkerを含む多くの新しいプロセスが表示されるようになりました。



画像



ただし、このプロセスにアタッチしようとすると、WPE Proから不快なメッセージが表示されます。



画像



ここでは、残念ながら、何もできません。 DLLインジェクションは 、ターゲットプロセスのアドレススペースにDLLを埋め込むこと実行さ 、MSDNでは次のように言われています。



64ビットWindowsでは、64ビットプロセスは32ビットのダイナミックリンクライブラリ(DLL)をロードできません。 さらに、32ビットプロセスは64ビットDLLをロードできません


WPE Proには32ビットライブラリのみが付属していると推測するのは簡単です。



どうする? WinSockインターセプターを作成しますか? どうして?



しかし、これをどうやって行うのでしょうか? 最初に思い浮かぶのはDetoursを使用することですが、64ビットアプリケーションのサポートはProfessional Editionでのみ利用可能であることが公式ウェブサイトですぐに報告されています:



Detours Expressは、x86プロセッサー上の32ビットプロセスに制限されています


それからEasyHookを試してみましょう とりわけ、このライブラリでは、64ビットアプリケーションで作業することができます。



AnyCPU用にコンパイルされたインジェクションライブラリとホストプロセスを記述できます。これにより、すべての場合にまったく同じアセンブリを使用して、64ビットプロセスと32ビットプロセスから32ビットプロセスと64ビットプロセスにコードをインジェクトでき​​ます。


公式チュートリアルに示されている例を少し変更すると、 recvへの呼び出しをインターセプトし、WinSockから関数を送信し 、インターセプトされたメッセージバイトを16進数で表示するコードを取得します。



DLLインジェクションコード



 using EasyHook; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.Remoting; using System.Text; using System.Threading.Tasks; namespace Sniffer { public enum MsgType { Recv, Send }; [Serializable] public class Message { public byte[] Buf; public int Len; public MsgType Type; } public class InjectorInterface : MarshalByRefObject { public void OnMessages(Message[] messages) { foreach (Message message in messages) { switch (message.Type) { case MsgType.Recv: Console.WriteLine("Received {0} bytes via recv function", message.Len); break; case MsgType.Send: Console.WriteLine("Sent {0} bytes via send function", message.Len); break; default: Console.WriteLine("Unknown action"); continue; } foreach (byte curByte in message.Buf) { Console.Write("{0:x2} ", curByte); } Console.WriteLine("====================="); } } public void Print(string message) { Console.WriteLine(message); } public void Ping() { } } class Program { static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Usage: Sniffer.exe [pid]"); return; } int pid = Int32.Parse(args[0]); try { string channelName = null; RemoteHooking.IpcCreateServer<InjectorInterface>(ref channelName, WellKnownObjectMode.SingleCall); RemoteHooking.Inject( pid, InjectionOptions.DoNotRequireStrongName, "WinSockSpy.dll", "WinSockSpy.dll", channelName); Console.ReadLine(); } catch (Exception ex) { Console.WriteLine("An error occured while connecting to target: {0}", ex.Message); } } } }
      
      





ターゲットプロセスに挿入されるDLL自体のコード



 using EasyHook; using Sniffer; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace WinSockSpy { public class Main : EasyHook.IEntryPoint { public Sniffer.InjectorInterface Interface; public LocalHook RecvHook; public LocalHook SendHook; public LocalHook WSASendHook; public Stack<Message> Queue = new Stack<Message>(); public Main(RemoteHooking.IContext InContext, String InChannelName) { Interface = RemoteHooking.IpcConnectClient<Sniffer.InjectorInterface>(InChannelName); } public void Run(RemoteHooking.IContext InContext, String InChannelName) { try { RecvHook = LocalHook.Create( LocalHook.GetProcAddress("Ws2_32.dll", "recv"), new DRecv(RecvH), this); SendHook = LocalHook.Create( LocalHook.GetProcAddress("Ws2_32.dll", "send"), new DSend(SendH), this); RecvHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 }); SendHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 }); } catch (Exception ex) { Interface.Print(ex.Message); return; } // Wait for host process termination... try { while (true) { Thread.Sleep(500); if (Queue.Count > 0) { Message[] messages = null; lock (Queue) { messages = Queue.ToArray(); Queue.Clear(); } Interface.OnMessages(messages); } else { Interface.Ping(); } } } catch (Exception) { // NET Remoting will raise an exception if host is unreachable } } //int recv( // _In_ SOCKET s, // _Out_ char *buf, // _In_ int len, // _In_ int flags //); [DllImport("Ws2_32.dll")] public static extern int recv( IntPtr s, IntPtr buf, int len, int flags ); //int send( // _In_ SOCKET s, // _In_ const char *buf, // _In_ int len, // _In_ int flags //); [DllImport("Ws2_32.dll")] public static extern int send( IntPtr s, IntPtr buf, int len, int flags ); [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)] delegate int DRecv( IntPtr s, IntPtr buf, int len, int flags ); [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)] delegate int DSend( IntPtr s, IntPtr buf, int len, int flags ); static int RecvH( IntPtr s, IntPtr buf, int len, int flags) { Main This = (Main)HookRuntimeInfo.Callback; lock (This.Queue) { byte[] message = new byte[len]; Marshal.Copy(buf, message, 0, len); This.Queue.Push(new Message { Buf = message, Len = len, Type = MsgType.Recv }); } return recv(s, buf, len, flags); } static int SendH( IntPtr s, IntPtr buf, int len, int flags) { Main This = (Main)HookRuntimeInfo.Callback; lock (This.Queue) { byte[] message = new byte[len]; Marshal.Copy(buf, message, 0, len); This.Queue.Push(new Message { Buf = message, Len = len, Type = MsgType.Send }); } return send(s, buf, len, flags); } } }
      
      





もちろん、まだ対処すべきことがあります:



ただし、ここで示した機能は、タスクセットを解決するのに十分であり、残りは読者の裁量に任されています。



あとがき



前回の記事で見たように、バイナリファイルを調査および変更しても、すぐに結果が得られるとは限りません(興味がある場合は、 こちらこちらで読んでください )。 しかし、これには絶対に異常はありません。ご存知のように、経験はどれも有用であり、リバースエンジニアリングも例外ではありません。



ご清聴ありがとうございました。また、この記事が誰かに役立つことを願っています。



All Articles