ブラりザでビデオを傍受するか、Windows甚のTCPスニファヌを膝の䞊でパヌト1

か぀お、さほど昔ではないが、YouTube、Facebookビデオ、GoogleVideoなどを芋ながらストリヌミングビデオをディスクに盎接蚘録できるJakstaずいうバックグラりンドロッキングストリヌミングビデオを勧められたした。 そのむンストヌルの結果、Windowsを起動するたびに安定したBSODが埗られたした。 セヌフモヌドに切り替えお、この䜜成のnafigを取り壊したしたが、質問がありたした。

この゜フトりェアの簡単な調査により、NDIS Miniportドラむバヌがむンストヌルされおいるこずがわかりたした。これは、特にシステム䞊でブヌト時に停止し始めたした。 「これらの問題はどのようなものですか」私は考え、ドラむバヌなしでブラりザからストリヌミングビデオを傍受する実装を実隓するこずにしたした。



たえがき



この説明は、Windows、WinAPI、および少しのC ++の知識があるこずを前提ずしおいるので、私にずっお明らかな点がより詳现な説明を必芁ずする堎合は、質問しおください。 この投皿で抂説した原則に基づいお、ビデオを傍受するための既補のプログラムがないこずをすぐに明確にしたす少なくずもそのようなこずは曞いおいたせん。 䞻にNDISミニポヌトドラむバヌずブルヌスクリヌンの決定の拮抗剀ずしお動機付けられた、いく぀かの空癜ず理論的な停造がありたす。

仮に、ブラりザからのHTTPたたはTCP / IPパケットをむンタヌセプトできる特定のモゞュヌルがあるず仮定した堎合、ビデオをどのように正確にキャッチできたすか 次の2぀のオプションがありたす。



  1. アドレスをURLずしお解析したす。

    これを行うには、HTTP GETを含む発信パケットをむンタヌセプトし、このGETが正確にどこに向けられおいるかを調べる必芁がありたす。 特定のサむトに関する特定の知識が必芁なため、この決定はかなり疑わしいものです。 䞀方、「 www.youtube.com/watch?v=o78nFVB1tJA 」などのテンプレヌトに適したアドレスを䜿甚するず、ストリヌムを受信する前にビデオのストリヌミング芁求を盎接フィルタリングできたす。
  2. サヌバヌからの応答を確認する

    これを行うには、着信パケットをむンタヌセプトし、Content-TypeのHTTPヘッダヌを確認する必芁がありたす。 明らかに、ビデオの堎合、特定の圢匏に固有のものになりたす。 たずえば、サク゜フォンを挔奏する YouTubeからのFlash Videoぞの前述のリンクの堎合、サヌバヌの応答にはヘッダヌ「Content-Typevideo / x-flv」が含たれ、Flash Videoが芋出しに埓っおいるこずが明確にわかりたす。 MPEG4の堎合、ヘッダヌにはvideo / mp4などが含たれたす。


最終的な゜リュヌションでは、おそらく1ず2の組み合わせが効果的に機胜する必芁がありたすが、この投皿ではたずパケットキャプチャに焊点を圓おたす。



DLLトラップず実装



むンタヌセプタヌを蚘述するために最初に思い浮かぶのは、ブラりザヌプロセスにDLLを導入するこずです。 この堎所の䞀郚は、すべおが明確であるため、読み取りを停止したす。 すべおを理解できる人は誰でも、ここから゜ヌスを、ここからコンパむル枈みバヌゞョンをダりンロヌドできたす はい、3Kbのみ。 すべおの仕組みを詊しおみるこずにした堎合、32ビットブラりザヌを䜿甚しお、 AdMuncher hello Murrayshannowなど、同様のトリックを䜿甚するすべおの゜フトりェアを削枛するこずを匷くお勧めしたす。修正可胜。 TEMPの.logファむルずしお結果を探したす。



システムトラップずWindowsプロセスでのDLLの導入はかなりハックされたトピックですが、他のすべおの人にずっおは、誰かが䜕かを説明する必芁がありたす。 DLLをブラりザヌプロセスに埋め蟌むこずの最終結果は、次のようになりたす。



画像



぀たり、各ブラりザヌプロセス内に、必芁なパッケヌゞをむンタヌセプトする汎甚DLLが配眮されたす。 2぀の質問がすぐに出おきたす。



  1. DLLを実装する方法は
  2. パケットをキャッチする方法は


以䞋に答えおみたしょう...



DLLを他の誰かのプロセスに挿入する



最初の段階では、メむンアプリケヌションずDLLずいう2぀のモゞュヌルを蚘述する必芁があるこずは明らかです。 メむンアプリケヌションがデプロむされ、それに応じおDLLがデプロむされたす。 さらに苊劎せずに、Vistual Studioを起動し、すぐにメむンアプリケヌションInjector.cppを䜜成したす。



#pragma comment(linker, "/entry:WinMain /nodefaultlib") void APIENTRY winMain() { HMODULE interceptor = LoadLibrary(TEXT("Interceptor.dll")); if (interceptor != NULL) { HOOKPROC cbtHook = (HOOKPROC) GetProcAddress(interceptor, (LPCSTR) 1); HHOOK hHook = (HHOOK) SetWindowsHookEx(WH_CBT, cbtHook, interceptor, 0); if (hHook != NULL) { MessageBox(NULL, TEXT("Press OK to terminate."), TEXT("Interceptor is working."), MB_OK); UnhookWindowsHookEx(hHook); } FreeLibrary(interceptor); } }
      
      





䞊蚘のコヌドは䜕をしたすか 最初の行では、コヌドのコンパクトさの理由から、アプリケヌションぞの゚ントリポむントを前戯なしでWinMainに盎接配眮したす。 ゜ヌスでは、通垞、MSVCRTを䞍必芁にカットしたす。

次に、むンタヌセプタヌを読み蟌み、その䞭の゚クスポヌト関数番号1序数によるむンポヌトを芋぀け、䜜成されたパラメヌタヌを䜿甚しおCBTタむプのグロヌバルトラップを蚭定したす。 次に、モヌダルメッセヌゞを衚瀺し、[OK]ボタンを抌しお完了し、アストラルプレヌンに移動したす。 それだけです これは、Windowsでの䜜業にUser32 WinAPIを䜕らかの圢で䜿甚するすべおのプロセスにDLLを実装するのに十分です。

CBT、コンピュヌタヌベヌスのトレヌニングの略。 WH_CBTトラップは䞀般的に優れおいたす。 システムは、りィンドりをアクティブ化、䜜成、砎棄、最小化、最倧化、移動、たたはサむズ倉曎する前にこのトラップを呌び出したす。 たた、システムコマンドを完了する前、キヌボヌドたたはマりスむベントをメッセヌゞキュヌから削陀する前、入力フォヌカスを蚭定する前、およびシステムメッセヌゞキュヌず同期する前に 」MSDNの無料翻蚳。 実際、これは、暙準のりィンドりアヌキテクチャに埓っお蚘述されたアプリケヌションの99で機胜するこずを意味したす。

この方法の利点は、Windowsトラップシステム自䜓を気にする必芁がないこずです。



DLLを曞き始めたす



他のものではなくむンタヌセプタヌを䜜成しおいるので、圌にずっおはそれで十分です

  1. プロセスはプロセスにロヌドするずきにDLLを初期化したす
  2. DLLは、完了するたでプロセスのアドレス空間に残りたす
  3. 完了時たたは倖郚むベントにより、アプリケヌションはアンロヌドする前にDLLの初期化を解陀したす


この手法では、ビット深床が䞀臎するモゞュヌルが必芁であるず蚀う䟡倀がありたす。 これは、64ビットブラりザヌが64ビットDLLむンタヌセプタヌを必芁ずするこずを意味したす。32ビットアプリケヌションでも同様です。 32ビットのむンタヌセプタヌが64ビットのアプリケヌションにロヌドされるこずを期埅すべきではありたせん。逆の堎合も同様です。

それでは、将来のむンタヌセプタヌInterceptor.cppのスケルトンを曞きたしょう。



 HINSTANCE g_hDllInstance; //  ,     LRESULT CALLBACK CBT_Hook(int nCode, WPARAM wParam, LPARAM lParam) { return 0; } //        BOOL onLoad() { return TRUE; } //        BOOL onUnload() { return TRUE; } BOOL WINAPI DllMain(HINSTANCE hDllInstance, DWORD dwReason, LPVOID lpRsrv) { switch(dwReason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hDllInstance); g_hDllInstance = hDllInstance; return onDllLoad(); break; case DLL_PROCESS_DETACH: return onDllUnload(); break; default: break; } return TRUE; }
      
      





さらに、゚クスポヌトに番号1を指定する必芁がありたす。このために、暙準DEFファむルInterceptor.defを䜜成し、/ DEFパラメヌタヌを介しおリンカヌにフィヌドするこずを忘れないでください。



 LIBRARY Intercept EXPORTS CBT_Hook @1
      
      





それだけです これで、DLLがプロセスに接着され、完了するたでそれらの䞭に眮かれたす。 䞍芁なプロセスに䟵入せず、メむンアプリケヌション内で正しく動䜜しないようにはい、DLLをロヌドしお初期化するため、远加のチェックを行いたす。



 const char *appsToIntercept[] = { "chrome.exe", "iexplore.exe", "opera.exe", "firefox.exe", "safari.exe", 0}; char thisProcessPath[MAX_PATH], *thisProcessName; char thisDllPath[MAX_PATH], *thisDllName; BOOL onLoad() { BOOL rv = FALSE; //       thisProcessPath     thisProcessName GetModuleFileName(NULL, thisProcessPath, sizeof(thisProcessPath) - 1); GetFullPathName(thisProcessPath, sizeof(thisProcessPath), thisProcessPath, &thisProcessName); *(TCHAR*) ((TCHAR*) (thisProcessName - sizeof(TCHAR))) = 0; //     DLL  thisDllPath   DLL  thisDllName GetModuleFileName(g_hDllInstance, thisDllPath, sizeof(thisDllPath) - 1); GetFullPathName(thisDllPath, sizeof(thisDllPath), thisDllPath, &thisDllName); *(TCHAR*) ((TCHAR*) (thisDllName - sizeof(TCHAR))) = 0; //      ,       if (!lstrcmpi(thisProcessPath, thisDllPath)) return TRUE; //        for (int i = 0; appsToIntercept[i] != 0; i++) { if (!lstrcmpi(thisProcessName, appsToIntercept[i])) { rv = TRUE; break; } } //          ,   if (!rv) return FALSE; //     WinSock2 return installHooks(); }
      
      





したがっお、アプリケヌションが䞍明な堎合、そのアプリケヌションにはロヌドされたせん。 次に、WinSock関数のむンタヌセプトを盎接凊理したす。



機胜傍受メカニズム



たず、明らかなこずを明確にする必芁がありたす。 この行たですでにすべおが明確な人はただ読み䞊げられないので、むンデントしたす。 驚いたこずに、非垞に賢明で高床なプログラマヌの䞭には、Windowsプロセスでシステムラむブラリがどのように機胜するかを必ずしも明確に想像しおいない人もいるこずに気付きたした。 この点で、Habréの別の投皿「Windows実行可胜ファむルEXEのステップバむステップガむド」をご芧になるこずを匷くお勧めしたす。



これを理解するこずは重芁です



画像



システムラむブラリおよびPEヘッダヌでむンポヌトずしお指定されたものを含むすべおのDLLは、それらを䜿甚するアプリケヌションのアドレススペヌスに盎接ロヌドされたす。 論理的な芳点から、実行䞭の各アプリケヌションには、システムおよび他のDLLのコピヌの独自の個別のセットがありたす。

したがっお、ブラりザでパケットをむンタヌセプトする最も簡単な方法は、パケットの送受信を担圓するシステムラむブラリ内の特定の関数ぞの呌び出しをむンタヌセプトするこずです。 この時点で、䞀郚は再び読むのをやめたす。なぜなら、すべおが再び明確になり、新しいものは䜕もないからです。 しかし、他の皆のために私は続けたす。

傍受は、WinHTTP、WinINet、WinSockなどのさたざたなレベルで実行できたす。 私にずっお、最も普遍的なのは、WS2_32.DLLラむブラリからのWinSock関数のむンタヌセプトです。 特にHTTPSを䜿甚する堎合パケットが暗号化されおいる堎合、欠点がありたす。 HTTPSの堎合、私の芳点から芋るず、WinHTTP関数やOpenSSLラむブラリをフックするこずが最善の解決策です。 しかし、簡単なものから始めたしょう。



それで、私たちがする必芁があるこずの䞻なポむントを匷調したす

  1. むンタヌセプトする関数のアドレスを決定したす
  2. ゚ントリポむントで関数呌び出しを曞き換えお、独自のハンドラヌが呌び出されるようにしたす
  3. 独自のハンドラヌで、元の関数を呌び出す前にいく぀かのアクションを実行したす
  4. 初期関数を呌び出す
  5. 結果を保存
  6. 独自のハンドラヌで、元の関数を呌び出した埌にいく぀かのアクションを実行したす
  7. 結果を呌び出し手続きに返す


Windowsの䞋䜍互換性の叀くからの䌝統によれば、異なる機胜を呌び出すこずで同じこずを行う方法はいく぀かあるため、すべおをキャッチしようずしたす。 このタスクでは、WS2_32から次のものをむンタヌセプトするだけで十分です。



さらに、最埌の3぀は、特定の接続ある堎合に関連付けられたコンテキストを䜜成および砎棄するためだけに必芁です。 この䟋では、䞀般的なコンテキストを取り陀こうずしたす。 ただし、実際には、HTTP芁求ずHTTP応答のペアを正しく収集するために必芁になりたす。 同時に、connectずWSAConnectのフックは厳密には必芁ありたせん。新しい゜ケットの新しいコンテキストは、最初に曞き蟌たれたずきに事実䞊䜜成できるためです。

したがっお、DLLに構造を䜜成しお、WinSock関数の゚ントリポむントを曞き換えお埩元する方法は次のずおりです。



 //      typedef struct _APIHOOK { BOOL isInstalled; //    ? const TCHAR *moduleName; //   () const TCHAR *functionName; //    LPVOID newAddr; //    LPVOID oldAddr; //    DWORD oldCodeSize; //      char newCode[HOOK_CODE_SIZE]; //   char oldCode[HOOK_CODE_SIZE]; //   } APIHOOK, *PAPIHOOK;
      
      





HOOK_CODE_SIZEの抂芁ず䟝存関係に぀いおは、先を読んでください。



アセンブラヌのビット



゚ントリポむントで関数呌び出しをむンタヌセプトするには、コヌドにパッチを適甚する必芁がありたす。 したがっお、最も簡単なアルゎリズムは次のようになりたす。

  1. ハンドラヌを定矩したす。

    この堎合、cdeclたたはstdcallの呌び出しのタむプ、およびすべおの入力パラメヌタヌは、元の関数ずたったく同じでなければなりたせん。そうしないず、スタックが砎損したす。
  2. 関心のある機胜ぞの゚ントリポむントを決定する

    これにより、すべおが簡単になり、kernel32.dllからGetProcAddressを呌び出す必芁がありたす。
  3. 関数の゚ントリポむントからコヌドを保存する

    ここでも、すべおがシンプルです-バむトで人里離れた堎所にコピヌしたす
  4. パッチ゚ントリポむント

    おおたかに蚀っお、それはすべおあなたがそれを呌び出すずきにハンドラヌぞの移行があるように、゚ントリポむントでコヌドを曞き換えるこずになりたす


画像



機胜的な芳点から、むンタヌセプトにはいく぀かの異なる方法がありたす。 最も簡単な方法は、゚ントリポむントの最初にあるコヌドを元のコヌドから独自のコヌドに、たたはその逆に絶えず曞き換えるこずです元の関数を呌び出すずき。 垞にコヌドを曞き換える必芁はありたせんが、むンタヌセプタヌを元の関数の途䞭に正しく埋め蟌むために呜什パヌサヌを蚘述する必芁がある、より耇雑なメ゜ッドがありたす。 最も簡単なもの-コヌドの先頭で曞き換える-に぀いお説明したしょう。

繰り返したすが、ハンドラヌを呌び出す方法はいく぀かありたす。 詳现には觊れずに、そのうちの2぀を匷調したす。無条件遷移ず呌び出しスタックでの戻りです。 最初の堎合、抂念は次のずおりです。



 MyFuncHandler: <blablablablabla> OriginalFunction: JMP MyFunHandler
      
      





これは非垞に単玔で、32ビット匏では5バむトが必芁です。1぀はJMP無条件ゞャンプ呜什コヌド甚で、4぀は盞察アドレス甚です。 なぜ盞察的なのか、埌で。 2番目の堎合、抂念はわずかに異なりたす。



 MyFuncHandler: <blablablablabla> OriginalFunction: PUSH MyFuncHandler RETN
      
      





これには6バむトが必芁です。呜什コヌドPUSH <32ビットDWORD>およびRETNには2バむト、絶察アドレスには4バむトが必芁です。 はい、はい。 最初のケヌスでは、アドレスは実行可胜コヌドの珟圚のアドレスに関連するず芋なされたす。 2぀目は、定数であり、アドレス空間の先頭に関連するず芋なされたす。 最初の方法に行きたす。

むンタヌセプタヌむンストヌラヌを䜜成したす。



 //    #define HOOK_CODE_SIZE 5 // JMP XX XX XX XX //    BOOL hookInstall(PAPIHOOK thisHook) { UCHAR asmJMP = 0xE9; if (!thisHook || thisHook->isInstalled == TRUE) { SetLastError(ERROR_ALREADY_EXISTS); return FALSE; //   ,      } //      if (thisHook->moduleName && thisHook->functionName && !(thisHook->oldAddr = GetProcAddress( GetModuleHandle(thisHook->moduleName), thisHook->functionName) ) ) { SetLastError(ERROR_NOT_FOUND); return FALSE; //   ,      } //      if (IsBadReadPtr(thisHook->oldAddr, HOOK_CODE_SIZE)) { SetLastError(ERROR_INVALID_ADDRESS); return FALSE; //   } //            if ( *(DWORD*)((PBYTE) thisHook->oldAddr + 1) == ((DWORD) thisHook->newAddr - (DWORD) thisHook->oldAddr - HOOK_CODE_SIZE) && *(BYTE*) thisHook->oldAddr == asmJMP) { return TRUE; //       ,     } //           DWORD oldFlags; if (!VirtualProtect(thisHook->oldAddr, HOOK_CODE_SIZE, PAGE_EXECUTE_READWRITE, &oldFlags) || IsBadWritePtr(thisHook->oldAddr, HOOK_CODE_SIZE)) { SetLastError(ERROR_WRITE_PROTECT); return FALSE; //     } //       memcpy(thisHook->oldCode, thisHook->oldAddr, HOOK_CODE_SIZE); //  JMP thisHook->newCode[0] = asmJMP; //    *(DWORD *) &thisHook->newCode[1] = ((DWORD) thisHook->newAddr - HOOK_CODE_SIZE - (DWORD) thisHook->oldAddr); //     thisHook->isInstalled = TRUE; //      #define hookEnable(p) memcpy(p->oldAddr, p->newCode, HOOK_CODE_SIZE); //      #define hookDisable(p) memcpy(p->oldAddr, p->oldCode, HOOK_CODE_SIZE); //   hookEnable(thisHook); return TRUE; }
      
      





䞊蚘のコヌドに぀いおさらに説明する必芁はないず思いたす。 関数むンタヌセプタヌに関する情報のコンテンツを担圓する構造䜓にパッチ枈みコヌドを最初に䜜成し、memcpyを䜿甚しおパッチマクロを盎接䜜成するこずは泚目に倀したす。 矎孊のファンはそこにロックを远加するこずができたすが、私の意芋ではこれは䞍芁です、なぜだず思いたすか

トラップを有効にするには、独自のハンドラヌのアドレスぞの遷移のみを含む新しいコヌドをコピヌしたす。 トラップをオフにするには、oldCodeずいう名前で配列に保存されおいる5぀の元のバむトを埩元したす。

関数にトラップのむンストヌルを蚘述したので、関数コヌドの初期状態の埩元を曞く䟡倀がありたす。



 //     BOOL hookRemove(PAPIHOOK thisHook) { //   ,     if (!thisHook->isInstalled) return FALSE; //    hookDisable(thisHook); //      thisHook->isInstalled = FALSE; //        thisHook->newAddr = (LPVOID) NULL; thisHook->oldAddr = (LPVOID) NULL; return TRUE; }
      
      





プロセスに䟵入し、トラップを蚭定、削陀、オン、オフする方法がわかったので、次は独自のハンドラヌを実行したす。



WinSockネむティブ関数ハンドラヌ



それでは、たず最初に、むンタヌセプトされた関数のプロトタむプの配列を定矩したしょう。 䞊蚘のように、最も必芁な機胜のみをむンタヌセプトしようずしたす。



 //      #define DECLARE_HOOK(module, name) {0, module, #name, my_##name} //       APIHOOK hookList[] = { DECLARE_HOOK(winSockDll, send), DECLARE_HOOK(winSockDll, WSASend), DECLARE_HOOK(winSockDll, recv), DECLARE_HOOK(winSockDll, WSARecv), DECLARE_HOOK(winSockDll, WSAGetOverlappedResult), DECLARE_HOOK(winSockDll, closesocket), 0};
      
      





マクロからわかるように、抜象名がnameの各システム関数には、my_nameずいう独自のハンドラヌがありたす。 次に、配列で指定されたハンドラヌを定矩する必芁がありたす。 䟋ずしおsendを䜿甚しおこれを実行したしょう。



 int WSAAPI my_send(SOCKET s, char *buf, int len, int flags) { PAPIHOOK thisHook = hookFind(my_send); if (NULL == thisHook) return (int) 0; hookDisable(thisHook); int rv; rv = send(s, buf, len, flags); hookEnable(thisHook); return rv; }
      
      





これは、元のコヌドを呌び出す以倖の䜕もしないシステム関数の空のラッパヌのように芋えたす。 ラッパヌは、それ自䜓のすべおのハンドラヌで本質的に同じであるため、マクロを定矩するのに䟿利なように、いく぀かのマクロを䜜成するのが理にかなっおいたす。



 //        #define DEFINE_HOOK(RTYPE, CTYPE, NAME, ARGS)\ RTYPE CTYPE my_##NAME ##ARGS \ { \ PAPIHOOK thisHook = hookFind(my_##NAME); \ if (NULL == thisHook) \ return (RTYPE) 0; \ hookDisable(thisHook); \ RTYPE rv;
      
      





たた、関数を終了するには



 //        #define LEAVE_HOOK() } \ hookEnable(thisHook); \ return rv;
      
      





次に、これらのマクロを䜿甚しお、配列から残りのトラップを決定したす。



トラップを蚭定する



OnLoadの最埌の行は、特定のマゞック関数InstallHooksを呌び出したす。 ゜リュヌションのすべおのコンポヌネントがあるので、定矩されたすべおのトラップのバッチむンストヌルを䜜成したす。



 //      BOOL installHooks() { BOOL rv = FALSE; for (int i = 0; hookList[i].moduleName; i++) { if (hookInstall(&hookList[i])) rv = TRUE; } return rv; }
      
      





簡朔に。 バッチトラップの削陀も簡朔です。



 //      BOOL removeHooks() { BOOL rv = FALSE; for (int i = 0; hookList[i].moduleName; i++) { if (hookRemove(&hookList[i])) rv = TRUE; } return rv; }
      
      







HTTPパケットキャプチャ



さお、私たちはスムヌズに最も興味深いものに到達したした。 したがっお、HTTPリク゚ストずHTTPレスポンスの2皮類のパケットがありたす。 したがっお、前者はsend型の関数によっお送信され、埌者はrecv型の関数によっお受け入れられたす。 送信関数は、元のコヌドを呌び出す前にむンタヌセプトする必芁がありたすが、送信バッファヌは未凊理のたたです。 それぞれの受信関数は、初期コヌドの実行埌にむンタヌセプトする必芁がありたす。そうしないず、䜕が正確に受け入れられるかがわかりたせん。

非同期関数もありたす。 そこでの考え方は簡単です。 WSASendたたはWSARecvを呌び出すず、むベントが登録されるWSAOVERLAPPED構造が指定されたす。 非同期関数は即座に終了し、完了時にGetLastErrorをWSA_IO_PENDINGに蚭定しおSOCKET_ERRORをスロヌしたす。 次に、メむンアプリケヌションは、WaitForSingleObjectなど、䜕らかの方法でむベントを埅機し、むベントのステヌタスが蚭定されるずすぐに、WSAGetOverlappedResultを介しおバッファヌを読み取りたす。

同期関数からデヌタを削陀するのが難しくない堎合、非同期では少し手を加える必芁がありたす。 投皿の冒頭で、コンテキストを完党に削陀するこずはできないず述べたしたが、非同期操䜜がその理由です。 より詳现に。 WSAGetOverlappedResult呌び出しは、送信バッファヌたたは受信バッファヌに関する情報を䌝えたせん。 したがっお、コンテキストを䜜成し、そこにバッファぞのポむンタを保存する必芁があるこずは明らかです。

コンテキストが必芁な別の理由がありたす。 ストリヌミングビデオの傍受にはHTTPリク゚ストずレスポンスが必芁なため、最も論理的な解決策は、send、recvを別々に呌び出すこずです。 それでは、コンテキストの構造を䜜成したしょう。これは、HTTPリク゚ストずレスポンスのペアを収集し、

非同期関数



 struct REQUEST { SOCKET socket; char *request; LPWSABUF wsaBuf; PREQUEST next; }
      
      





䞊蚘のすべおが必芁なのはなぜですか ゜ケット番号により、リク゚ストずレスポンスの察応を決定したす。 ぀たり、メむンアプリケヌションは同じTCP゜ケット䞊でリク゚ストを送受信したす。そうしないず、リク゚ストを送受信できたせん。 芁求ポむンタヌはHTTP芁求を参照したす。 LPWSABUFポむンタヌは、非同期関数に䜿甚されたす。 ぀たり、WSASend/ WSARecvが呌び出されるず、バッファヌぞのポむンタヌを保存し、WSAGetOverlappedResultの完了時にそこから削陀したす。 繰り返したすが、䞀臎は゜ケット番号によっお決定されたす。

今埌、WSASendの堎合、この投皿ずむンタヌセプタヌブランクの䜜成䞭にテストしたブラりザヌのいずれでも非同期呌び出しは䜿甚されたせん。

次は䜕ですか 単䞀リンクリストを敎理したす。 芁求ず応答のペアのコンテキストは、倱われないようにどこかに配眮する必芁があるのは論理的です。 プログラムのサむズを膚らたせず、STLテンプレヌトのようなものを䜿甚しないために、孊校のオリンピックの問題を解決し、単玔に接続されたリストの実装を蚘述するのが最も簡単でした。 それはあなたにずっおより良いでしょう、あなた自身で芋おください。

詳现に入るこずなく、リク゚スト/レスポンスコンテキストのリンクリストを操䜜するための関数に぀いお説明したす詳现に぀いおは、゜ヌスを参照しおください。



 //      PREQUEST findRequest(SOCKET s); //    -     PREQUEST addRequest(SOCKET s, char *request); //     void delRequest(SOCKET s);
      
      





次に、すべおのsend関数の共通ハンドラヌを䜜成したす。



 //      BOOL commonSendHandler(PAPIHOOK thisHook, SOCKET s, char *buf, int len, BOOL isWsa) { //      'GET '    if 
 //  HTTP  char *request = getHttpHeaders((const char *) buf, len); if (request != NULL) addRequest(s, request); return TRUE; }
      
      





そしお、すべおのrecvの共通ハンドラヌ



 //           BOOL commonRecvHandler(PAPIHOOK thisHook, SOCKET s, char *buf, int len, BOOL isWsa) { //      'HTTP'    if 
 //  ,    PREQUEST req = findRequest(s); if (NULL == req) return FALSE; //  HTTP  char *response = getHttpHeaders((const char *) buf, len); if (response != NULL) { //       ... delRequest(s); } return TRUE; }
      
      





テレマヌケット。 ただ远い぀いおいる人のために説明したす。 パケットを送信するずき、パケットの内容は最初に「GET」が存圚するかどうかチェックされたす。HTTP GETの堎合、HTTPヘッダヌが切り取られ、開いおいる゜ケットのコンテキストに保存されたす。それに応じお、パケットを受信するず、 'HTTP'が存圚するかどうかコンテンツをチェックしたす。これが答えである堎合、゜ケットコンテキストから以前に送信されたGETを芋぀けようずしおいたす。芁求ず応答が芋぀かった堎合は、さらに分析できたす。この䟋では、単にTEMP\ <プロセス名.exe>-<プロセスID> .logのログファむルにダンプし

たす。非同期関数を凊理するために残りたす。そのため、䟋ずしおWSARecvを䜿甚したす。



 // WSARecv() DEFINE_HOOK(int, WSAAPI, WSARecv, (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)) { rv = WSARecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine); //   if (!rv && NULL != lpNumberOfBytesRecvd) { commonRecvHandler(thisHook, s, lpBuffers->buf, *lpNumberOfBytesRecvd, TRUE); } else //   if (rv == SOCKET_ERROR && WSAGetLastError() == WSA_IO_PENDING) { //  WSARecv ,   WSA ,     PREQUEST req = findRequest(s); if (req != NULL) req->wsaBuf = lpBuffers; } LEAVE_HOOK(); }
      
      





぀たり、呌び出しが非同期の堎合、゜ケットのコンテキストを芋぀け、そこにバッファヌぞのポむンタヌを曞き蟌みたす。次に、それを抜出し、readのようなすべおの関数の汎甚ハンドラヌにすべおを枡したす。



 // WSAGetOverlappedResult() DEFINE_HOOK(BOOL, WSAAPI, WSAGetOverlappedResult, (SOCKET s, LPWSAOVERLAPPED lpOverlapped, LPDWORD lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags)) { rv = WSAGetOverlappedResult(s, lpOverlapped, lpcbTransfer, fWait, lpdwFlags); if (rv && NULL != lpcbTransfer && *lpcbTransfer > MIN_HTTP_HEADER_SIZE) { // ,    ? PREQUEST req = findRequest(s); if (req != NULL && req->wsaBuf != NULL) commonRecvHandler(thisHook, s, req->wsaBuf->buf, *lpcbTransfer, TRUE); } LEAVE_HOOK(); }
      
      





叙事詩の結論ずしお、応答を受信する前に゜ケットが死ぬこずを蚱可する堎合、䞍必芁なコンテキストを打぀ためにclosesocketハンドラヌを䜜成したす。掗緎、コンパむル、起動、ブラりザの起動、YouTubeぞの移動...



そしお、悪名高いアドレスwww.youtube.com/watch?v=o78nFVB1tJAログから盎接取埗でビデオを芖聎するためにGoogle Chromeから取埗した興味深いパッケヌゞを次に瀺したす。



 [22:28:48] [SOCKET = 0EB0, REQUEST = 1327 bytes, RESPONSE = 329 bytes] ->GET /videoplayback?algorithm=throttle-factor&burst=40&cp=U0hTS1RRU19OTUNOM19MS1dBOlR1eGNSd1JHRkdy&expire=1346465093&factor=1.25&fexp=926900%2C910103%2C922401%2C920704%2C912806%2C924412%2C913558%2C912706&gcr=fi&id=a3bf27155075b490&ip=91.155.190.10&ipbits=8&itag=34&keepalive=yes&key=yt1&ms=au&mt=1346441292&mv=m&range=13-1781759&signature=7415093589702691B2E46681B2EF24EC370C2F1F.D6D55168E2211687994A3F47D8919AC5470C567D&source=youtube&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cgcr%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&sver=3&upn=GlJDbjcQ-2w HTTP/1.1 Host: oo---preferred---elia-hel1---v11---lscache1.c.youtube.com Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.26 Safari/537.4 Accept: */* Referer: http://www.youtube.com/watch?v=o78nFVB1tJA Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 Cookie: VISITOR_INFO1_LIVE=UxycPwPFJBs; __utma=27069237.1349026492.1343302158.1343302158.1343302158.1; __utmz=27069237.1343302158.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); use_hitbox=d5c5516c3379125f43aa0d495d100d6ddAEAAAAw; recently_watched_video_id_list=697d12b6b10771c1d93bb1bb4cf53148WwEAAABzCwAAAG83OG5GVkIxdEpB; PREF=fv=11.3.31; ACTIVITY=1346441327664 <-HTTP/1.1 200 OK Last-Modified: Wed, 09 May 2012 00:20:14 GMT Content-Type: video/x-flv Date: Fri, 31 Aug 2012 19:28:48 GMT Expires: Fri, 31 Aug 2012 19:28:48 GMT Cache-Control: private, max-age=23465 Accept-Ranges: bytes Content-Length: 1781747 Connection: keep-alive X-Content-Type-Options: nosniff Server: gvs 1.0
      
      





実際、そこのビデオストリヌムはHTTPヘッダヌの最埌の行の盎埌から始たりたす。



この埌も、ドラむバヌなしでストリヌミングビデオ甚のむンタヌセプタヌを䜜成できるかどうか疑問に思っおいたす。個人的には、タスクは基本的に達成されおいるず思うので、結論に進みたす。



結論



むンタヌセプタヌは、既に説明したずおりに機胜したす。通垞の実装では、倚くのむンタヌセプタヌずメむンむンゞェクタヌアプリケヌション間の通信甚にIPCを蚘述する必芁がありたす。から遞択するいく぀かのオプションがありたす。

  1. ビデオURLをむンタヌセプタヌからメむンアプリケヌションに転送し、そこからプルしたす。
  2. 芖聎䞭にむンタヌセプタヌからビデオ自䜓を転送したす。トラフィックはIPCを介しお耇補されたすが、ロヌカルトラフィックをポンピングするコストはそれほど重芁ではないため、これは恐ろしいこずではありたせん。
  3. むンタヌセプタヌからストリヌムをディスクに盎接曞き蟌み、プロセスの進行状況のみをメむンアプリケヌションに通知したす。


別のポむント。これたでのずころ、YouTube / Flash Videoのみを䜿甚しおきたした。他のサむトおよびビデオコヌデックには、他の機胜がありたす。それでも、90のケヌスで、「Content-type」ヘッダヌのコンテンツにのみ焊点を圓おたマルチメディアストリヌムをむンタヌセプトするこずは可胜です。

この実装の欠点



他に䜕 2぀の偎面



  1. この手法は、スレッドを傍受したり、スニファヌのようなアプリケヌションを実装したりするためだけに䜿甚するこずはできたせん。たずえば、必芁に応じお、HTTPトラフィック甚のフィルタヌを䜜成したり、広告をトリミングしたりできたす。さらに、すべおのブラりザがTCP / IPレベルで機胜するずいう根本的な違いはありたせん。
  2. この手法は、WinSockだけで機胜したせん。぀たり、原則ずしお、わずかな倉曎を加えるだけで、むンタヌセプタヌをどこにでも貌り付けお、奜きな機胜をむンタヌセプトできたす。これにより、行動ず思考の逃避の範囲が䞎えられたす。


この投皿が誰かにずっお興味深いものになるこずを願っおいたす。



UPD2番目の郚分は、垞にコヌドを曞き換えるこずなく、トラップに぀いお曞かれおいたす。



よろしく

// st



All Articles