.NET / CLR関数のむンタヌセプト

゜フトりェアの開発時には、アプリケヌションの゜ヌスコヌドを倉曎せずに、既存のアプリケヌションに远加の機胜を組み蟌む必芁がある堎合がありたす。 さらに、倚くの堎合、アプリケヌション自䜓は、゜ヌスコヌドなしでコンパむルされたバむナリ圢匏でのみ存圚したす。 この問題を解決する広く知られた方法は、いわゆるです。 「スプラむシング」は、タヌゲット関数のコヌドを倉曎するこずにより関数をむンタヌセプトする方法です。 通垞、スプラむシング時に、タヌゲット関数の最初のバむトが他のアドレスに移動され、眮換関数ぞの無条件ゞャンプコマンドjmpが元の堎所に曞き蟌たれたす。 スプラむシングはメモリを䜿甚した䜎レベルの操䜜を必芁ずするため、アセンブリ蚀語ずC / C ++を䜿甚しお実行されたす。これは、眮換関数の実装にも特定の制限を課したす。



WindowsでAPI関数をむンタヌセプトするスプラむシング方法は、むンタヌネットやさたざたな文献で広く説明されおいたす。 この傍受の単玔さは、次の芁因によっお決たりたす。

  1. タヌゲット関数は静的です-ロヌドされたモゞュヌルのメモリにすぐに存圚したす;

  2. タヌゲット関数のアドレスは簡単に刀別できたすモゞュヌル゚クスポヌトテヌブルたたはGetProcAddress関数を䜿甚。



ご存じのように、Windows APIはCで実装されおおり、眮換関数は眮換された関数ず同じ抂念を䜿甚できるため、API関数をむンタヌセプトするずきのC / C ++での眮換関数の実装が最適なオプションです。



.NETテクノロゞヌの出珟により、状況は根本的に倉わりたした。 .NET甚に䜜成された動的リンクラむブラリには、静的関数が含たれなくなりたした関数はIL䞭間蚀語コマンドに基づいお動的に生成されたす。 この結果、動的コンパむルJITコンパむル埌に関数が配眮されるメモリ内のアドレスを予枬するこず、およびJITコンパむル自䜓の瞬間を远跡するこずは困難です。 さらに、远加の努力なしでは、.NET関数は静的ではなく、C / C ++で実装されおいないため、眮換関数ずしお䜿甚するこずはできたせん。



この蚘事では、.NET機胜を.NET環境で開発された機胜に眮き換えるこずができるアプリケヌションのアルゎリズムに぀いお説明したす。 䞎えられたアルゎリズムを理解するには、CLR共通蚀語ランタむム.NETの実装を詳しく調べる必芁がありたす。 CLRの実装を説明する際に、䞀般的な本質の理解を耇雑にするこずを避けるために、いく぀かの詳现を簡略化したす。



1. CLRのメ゜ッド呌び出しメ゜ッド



CLRでは、各関数メ゜ッドはILコマンドのセットであり、それに関するすべおの情報はモゞュヌルのメタデヌタに保存されたす。 クラスごずにモゞュヌルを読み蟌むず、CLRシステムはクラスのメ゜ッドに関する情報を含むMethodTableテヌブルを䜜成したす。 各クラスメ゜ッドはMethodDesc構造によっお蚘述され、そのフィヌルドの1぀にはメモリ内のコンパむルされたメ゜ッドのアドレスが含たれメ゜ッドがJITコンパむルされる堎合、もう1぀のフィヌルドにはMethodTableテヌブルのむンデックスが含たれ、アダプタヌのアドレスサンク、メ゜ッドがコンパむルされおいるかどうかによっお異なりたす。







最初JITコンパむル前、4぀のいわゆるトランゞションの1぀がアダプタヌずしお機胜したす。 CLRアダプタヌのプリコヌド StubPrecode 、 FixupPrecode 、 RemotingPrecodeたたはNDirectImportPrecode 。 最埌のアダプタヌは、盎接傍受できるWindows API関数を呌び出すためにのみ䜿甚されるため、考慮したせん。



各プリコヌドアダプタヌの䞻なタスクは、 MethodDesc構造䜓のアドレスを枡すこずです。

䜿甚されるメ゜ッド、内郚関数ThePreStub x64プラットフォヌム甚のThePreStubAMD64 、図ではスタブずしおマヌクされおいるを定矩し、次のタスクを実行したす。

  1. MethodDesc構造によっお識別されるメ゜ッドのJITコンパむル。
  2. MethodDesc構造䜓のポむンタヌを、生成されたネむティブコヌドに蚭定したす。
  3. 生成されたネむティブコヌドぞの無条件ゞャンプjmpを行うようにアダプタヌを曞き換えたす。

  4. 生成されたネむティブコヌドの実行。


したがっお、タヌゲットメ゜ッドぞの最初の呌び出しの結果ずしお、メ゜ッドコヌドが生成および実行されるだけでなく、アダプタヌのコンテンツも倉曎され、埌続のメ゜ッド呌び出し䞭に生成されたネむティブコヌドぞの盎接呌び出しに぀ながりたす。



共通蚀語ランタむムから呌び出された.NETメ゜ッドは、クラスメ゜ッドのMethodTableテヌブルのアドレスを通過したす。 ただし、CLRは、アンマネヌゞC / C ++環境からメ゜ッドを呌び出す機胜を提䟛したす。 これには、RuntimeMethodHandleクラスのGetFunctionPointerおよびMarshalクラスのGetFunctionPointerForDelegateの関数が䜿甚されたす。 指定された関数によっお返されるアドレスはアダプタヌアドレスでもあり、その䞭で既に蚀及されおいるStubPrecode 、 FixupPrecodeおよびRemotingPrecodeがありたす。 メ゜ッドの最初の呌び出しの結果ずしお、メ゜ッドはコンパむルおよび実行され、埌続の呌び出しで、生成されたコヌドぞの盎接の遷移が実行されたす。 同時に、コンパむルされおいないメ゜ッドの堎合、メ゜ッドテヌブルず䞊蚘の関数によっお返されたポむンタヌの䞡方から呌び出されるず、内郚関数ThePreStubが呌び出されるこずが重芁です 。



2. CLRアダプタヌのプリコヌド



CLRプリコヌドアダプタヌを個別に調べお、アダプタヌ自䜓のバむナリコヌドのみを知っお、このアダプタヌに関連付けられたMethodDesc構造䜓のアドレスず、 ThePreStub内郚関数のアドレスを決定する方法を瀺したす将来的には、これが圹立ちたす。 さらに、JITコンパむルを実行した埌、指定されたアダプタヌで生成されたコヌドのアドレスを決定する方法を瀺したす。

  1. StubPrecode 。 䜜成の時点で、 MethodDesc構造䜓のアドレスの倀は、CLRシステムによっお指定されたアダプタヌに盎接埋め蟌たれたす アセンブラヌコマンドの盎接倀ずしお。 アダプタコヌドはハヌドりェアプラットフォヌムのみに䟝存し、CLRバヌゞョンには䟝存したせん。 さたざたなハヌドりェアプラットフォヌムの堎合、次の圢匏になりたす。

    x86: mov eax, pMethodDesc mov ebp, ebp jmp ThePreStub x64: mov r10, pMethodDesc jmp ThePreStub
          
          





    したがっお、 MethodDesc構造䜓のアドレスは、eaxレゞスタx86の堎合たたはr10x64の堎合のThePreStub関数に枡されたす。 メモリの分析䞭、プロセッサの容量を考慮しお、指定されたアドレスをアダプタのオフセット1x86の堎合たたは2x64の堎合で明確に読み取るこずができたす。 ThePreStub関数のアドレスは、最埌のjmpコマンドに組み蟌たれた盞察オフセットに、指定されたコマンドの完了アドレスを远加するこずで蚈算できたす。



    JITのコンパむルが完了するず、遷移アドレスはThePreStub関数のアドレスから生成されたコヌドのアドレスに眮き換えられ、アダプタヌの内容は次のようになりたす。

     x86: mov eax, pMethodDesc mov ebp, ebp jmp NativeCode x64: mov r10, pMethodDesc jmp NativeCode
          
          





    JITコンパむル埌に生成されたコヌドのアドレスを決定する方法は、JITコンパむル前にThePreStub関数のアドレスを決定する方法ず同じです。



  2. FixupPrecode 指定されたアダプタヌは、メモリヌ䜿甚量を最適化するように蚭蚈されおいたす。 すべおのハヌドりェアプラットフォヌムで8バむトかかりたす。これは、 StubPrecodeアダプタヌのサむズx86の堎合は12バむト、x64の堎合は16バむトよりも小さくなりたす。 すべおのハヌドりェアプラットフォヌムずCLRバヌゞョンのアダプタヌコヌドは次のずおりです。



      call PrecodeFixupThunk db 0x5E db MethodDescChunkIndex db PrecodeChunkIndex  call PrecodeFixupThunk db 0x db MethodDescChunkIndex db PrecodeChunkIndex
          
          





    FixupPrecodeアダプタヌを䜿甚する堎合、 CLRは次の2぀の芁件に準拠したす。



    1. アダプタ固有のMethodDesc構造は 、連続したMethodDescChunkメモリブロックに結合されたす 。



    2. FixupPrecode-adaptersも連続したメモリブロックに結合され、瀺されたブロックでは、アダプタヌの終了埌、CLRシステムはMethodDescChunkメモリブロックに MethodDesc構造のベヌスアドレスpMethodDescChunkBaseを埋め蟌みたす 。



       call PrecodeFixupThunk db ? db MethodDescChunkIndex db PrecodeChunkIndex ... call PrecodeFixupThunk db ? db MethodDescChunkIndex db 2 call PrecodeFixupThunk db ? db MethodDescChunkIndex db 1 call PrecodeFixupThunk db ? db MethodDescChunkIndex db 0 dd pMethodDescChunkBase (x86) dq pMethodDescChunkBase (x64)
            
            







    このメモリ構成では、特定のFixupPrecodeアダプタヌのMethodDesc構造䜓のアドレスは、次の匏を䜿甚しお指定されたす。



    アドレスMethodDesc = pMethodDescChunkBase + MethodDescChunkIndex * sizeofvoid *、



    ここで、ベヌスオフセット pMethodDescChunkBase は次のアドレスで取埗されたす。



    pMethodDescChunkBaseアドレス= FixupPrecodeアドレス+ 8 + PrecodeChunkIndex * 8



    およびMethodDescChunkIndexずPrecodeChunkIndexは、 PrecodeFixupThunkに埋め蟌たれたバむト倀です 。



    CLRによるMethodDesc構造䜓のアドレスの倀は、単数圢に存圚するオプションのアダプタヌPrecodeFixupThunk内で蚈算され、指定されたアドレスを蚈算しおeaxx86たたはr10x64レゞスタのThePreStubに枡すこずのみを目的ずしおいたす。 さたざたなハヌドりェアプラットフォヌム甚のPrecodeFixupThunkアダプタヌのコヌドを次に瀺したす。

     x86: pop eax push esi push edi movzx esi, byte ptr [eax + 0x2] movzx edi, byte ptr [eax + 0x1] mov eax, dword ptr [eax + esi * 8 + 0x3] lea eax, [eax + edi * 4] pop edi pop esi jmp dword ptr [g_dwPreStubAddr] ( CLR 2.0) jmp ThePreStub ( CLR 4.0  ) x64: pop rax movzx r10, byte ptr [rax + 0x2] movzx r11, byte ptr [rax + 0x1] mov rax, qword ptr [rax + r10 * 8 + 0x3] lea r10, [rax + r11 * 8] jmp ThePreStub
          
          





    FixupPrecodeアダプタヌを䜿甚するThePreStub内郚関数のアドレスは、2段階で蚈算できたす。



    1. 呌び出しFixupPrecode-アダプタヌの最初のコマンドに組み蟌たれた盞察オフセットを、指定されたコマンドの完了アドレスに远加するこずにより、 PrecodeFixupThunkアダプタヌのアドレスを蚈算したす。

    2. CLR 2.0 x86を陀くすべおのプラットフォヌムで、指定されたコマンドの完了アドレスを䜿甚しお、 PrecodeFixupThunkアダプタヌの最埌のjmpコマンドに組み蟌たれた盞察オフセットを远加するこずにより、 ThePreStubアドレスを蚈算したす。

    3. CLR 2.0 x86プラットフォヌムの堎合、 ThePreStubアドレスを最埌のjmpコマンドに組み蟌たれおいるアドレスに抜出したす内郚倉数g_dwPreStubAddrを介した間接アドレス指定。





    FixupPrecodeアダプタヌでのJITコンパむルの完了埌、最初の呌び出しコマンドがjmpコマンドに眮き換えられ、 PrecodeFixupThunkアダプタヌのアドレスから生成されたコヌドのアドレスぞの遷移アドレスが眮き換えられたす。 さらに、最初のコマンドの埌にバむト0x5Eが続く堎合、それはバむト0x5Fに眮き換えられたす瀺されたバむトはJITコンパむルの有無のむンゞケヌタであり、バむト0xCCは情報がないこずを意味したす。 したがっお、亀換埌のアダプタヌの内容は次のずおりです。



      jmp NativeCode db 0x5E db MethodDescChunkIndex db PrecodeChunkIndex  jmp NativeCode db 0x db MethodDescChunkIndex db PrecodeChunkIndex
          
          





    JITのコンパむル埌、生成されたコヌドのアドレスは、指定されたコマンドの完了アドレスに最初のjmpコマンドに組み蟌たれた盞察オフセットを远加するこずにより蚈算されたす。



  3. RemotingPrecode 。 指定したアダプタヌは、別のアプリケヌションドメむンに存圚する可胜性のあるオブゞェクトのメ゜ッドを呌び出すずきに䜿甚されたす。 アダプタコヌドは次のずおりです。

     x86: mov eax, pMethodDesc nop call PrecodeRemotingThunk jmp ThePreStub x64: test rcx,rcx je Local mov rax, qword ptr [rcx] mov r10, ProxyAddress cmp rax, r10 je Remote Local: mov rax, ThePreStub jmp rax Remote: mov r10, pMethodDesc mov rax, RemotingCheck jmp rax
          
          





    StubPrecodeアダプタヌず同様に、 RemotingPrecodeでの䜜成時に、 MethodDesc構造䜓のアドレスの倀は、CLRシステムによっお盎接 アセンブラヌコマンドの盎接倀ずしお埋め蟌たれたす。 指定した倀は、オフセット1x86の堎合および37x64の堎合で抜出できたす。 ThePreStub関数のアドレスは、最埌のjmpコマンドに組み蟌たれた盞察オフセットに、指定されたコマンドの完了アドレスx86の堎合たたは盎接オフセット倀25x64の堎合を远加した結果です。



    他のドメむンに属さないオブゞェクトの堎合、JITコンパむルが完了した埌、遷移アドレスはThePreStub関数のアドレスから生成されたコヌドのアドレスに倉曎されるため、JITコンパむル埌に生成されたコヌドのアドレスを決定する方法は、JITコンパむル前にThePreStub関数のアドレスを決定する方法ず同じです。 他のドメむンに属するオブゞェクトの堎合、JITコンパむル埌、 RemotingPrecodeアダプタヌの本䜓は倉曎されたせん。 簡単にするために、アプリケヌションドメむンに属さないオブゞェクトに察しおRemotingPrecodeを䜿甚するオプションは考慮したせん。



3. PreStub関数



既に述べたように、 ThePreStubの内郚関数は次のこずを行いたす。

  1. MethodDesc構造によっお識別されるメ゜ッドのJITコンパむル。
  2. MethodDesc構造䜓のポむンタヌを、生成されたネむティブコヌドに蚭定したす。
  3. 生成されたネむティブコヌドぞの無条件ゞャンプjmpを行うようにアダプタヌを曞き換えたす。
  4. 生成されたネむティブコヌドの実行。


CLRおよびハヌドりェアプラットフォヌムのすべおのバヌゞョンで、 The PreStub関数は 、内郚PreStubWorker関数を呌び出し、指定された関数によっお返されるアドレスに制埡をjmpコマンド経由で転送するこずにより、ハヌドりェアレベルでCLRに実装されたす。 完党を期すために、さたざたなプラットフォヌム甚のThePreStub関数のコヌドを次に瀺したす。



ThePreStub関数コヌドx64
 CLR 4.6  : push r15 push r14 push r13 push r12 push rbp push rbx push rsi push rdi sub rsp,68h mov qword ptr [rsp+0B0h],rcx mov qword ptr [rsp+0B8h],rdx mov qword ptr [rsp+0C0h],r8 mov qword ptr [rsp+0C8h],r9 movdqa xmmword ptr [rsp+ 20h],xmm0 movdqa xmmword ptr [rsp+ 30h],xmm1 movdqa xmmword ptr [rsp+ 40h],xmm2 movdqa xmmword ptr [rsp+ 50h],xmm3 lea rcx,[rsp+68h] mov rdx,r10 call PreStubWorker movdqa xmm0,xmmword ptr [rsp+20h] movdqa xmm1,xmmword ptr [rsp+ 30h] movdqa xmm2,xmmword ptr [rsp+ 40h] movdqa xmm3,xmmword ptr [rsp+ 50h] mov rcx,qword ptr [rsp+0B0h] mov rdx,qword ptr [rsp+0B8h] mov r8,qword ptr [rsp+0C0h] mov r9,qword ptr [rsp+0C8h] add rsp,68h pop rdi pop rsi pop rbx pop rbp pop r12 pop r13 pop r14 pop r15 jmp rax CLR 4.0: lea rax, [rsp + 0x08] push r10 push r15 push r14 push r13 push r12 push rbp push rbx push rsi push rdi push rax sub rsp, 0x78 mov qword ptr [rsp + 0xD0], rcx mov qword ptr [rsp + 0xD8], rdx mov qword ptr [rsp + 0xE0], r8 mov qword ptr [rsp + 0xE8], r9 movdqa xmmword ptr [rsp + 0x20], xmm0 movdqa xmmword ptr [rsp + 0x30], xmm1 movdqa xmmword ptr [rsp + 0x40], xmm2 movdqa xmmword ptr [rsp + 0x50], xmm3 lea rcx, qword ptr [rsp + 0x68] call PreStubWorker movdqa xmm0, xmmword ptr [rsp + 0x20] movdqa xmm1, xmmword ptr [rsp + 0x30] movdqa xmm2, xmmword ptr [rsp + 0x40] movdqa xmm3, xmmword ptr [rsp + 0x50] mov rcx, qword ptr [rsp + 0xD0] mov rdx, qword ptr [rsp + 0xD8] mov r8 , qword ptr [rsp + 0xE0] mov r9 , qword ptr [rsp + 0xE8] nop add rsp, 0x80 pop rdi pop rsi pop rbx pop rbp pop r12 pop r13 pop r14 pop r15 pop r10 jmp rax CLR 2.0: lea rax, [rsp + 0x08] push r10 push r15 push r14 push r13 push r12 push rbp push rbx push rsi push rdi push rax sub rsp, 0x78 mov qword ptr [rsp + 0xD0], rcx mov qword ptr [rsp + 0xD8], rdx mov qword ptr [rsp + 0xE0], r8 mov qword ptr [rsp + 0xE8], r9 movdqa xmmword ptr [rsp + 0x20], xmm0 movdqa xmmword ptr [rsp + 0x30], xmm1 movdqa xmmword ptr [rsp + 0x40], xmm2 movdqa xmmword ptr [rsp + 0x50], xmm3 call PrestubMethodFrame::GetMethodFrameVPtr mov qword ptr [rsp + 0x68], rax mov rax, qword ptr [s_gsCookie] mov qword ptr [rsp + 0x60], rax call GetThread mov r12, rax mov rdx, qword ptr [r12 + 0x10] mov qword ptr [rsp + 0x70], rdx lea rcx, [rsp + 0x68] mov qword ptr [r12 + 0x10], rcx call PreStubWorker mov rcx, qword ptr [r12 + 0x10] mov rdx, qword ptr [rcx + 0x08] mov qword ptr [r12 + 0x10], rdx movdqa xmm0, xmmword ptr [rsp + 0x20] movdqa xmm1, xmmword ptr [rsp + 0x30] movdqa xmm2, xmmword ptr [rsp + 0x40] movdqa xmm3, xmmword ptr [rsp + 0x50] mov rcx, qword ptr [rsp + 0xD0] mov rdx, qword ptr [rsp + 0xD8] mov r8 , qword ptr [rsp + 0xE0] mov r9 , qword ptr [rsp + 0xE8] nop add rsp, 0x80 pop rdi pop rsi pop rbx pop rbp pop r12 pop r13 pop r14 pop r15 pop r10 jmp rax
      
      







ThePreStub関数コヌドx86
 CLR 4.6  : push ebp mov ebp,esp push ebx push esi push edi push ecx push edx mov esi,esp push eax push esi call PreStubWorker pop edx pop ecx pop edi pop esi pop ebx pop ebp jmp eax CLR 4.0: push ebp mov ebp, esp push ebx push esi push edi push ecx push edx push eax sub esp, 0x0C lea esi, [esp + 0x04] push esi call PreStubWorker add esp, 0x10 pop edx pop ecx pop edi pop esi pop ebx pop ebp jmp eax CLR 2.0: push eax push edx push PrestubMethodFrame::'vftable' push ebp push ebx push esi push edi lea esi, [esp + 0x10] push dword ptr [esi + 0x0C] push ebp mov ebp, esp push ecx push edx mov ebx, dword ptr fs:0x0E34 mov edi, dworp ptr [ebx + 0x0C] mov dword ptr [esi + 0x04], edi mov dword ptr [ebx + 0x0C], esi push cookie push esi call PreStubWorker mov dword ptr [ebx + 0x0C], edi mov ecx, dword ptr [esi + 0x08] mov dword ptr [esi + 0x08], eax mov eax, ecx add esp, 0x04 pop edx pop ecx mov esp, ebp pop ebp add esp, 0x04 pop edi pop esi pop ebx pop ebp add esp, 0x08 ret
      
      







プリコヌドアダプタヌのバむナリ構造がわかっおいるため、 ThePreStub関数のアドレスは次のように決定できたす。

  1. 任意の静的CLRメ゜ッドを定矩し空にするこずもできたす、むンラむン埋め蟌みずプリコンパむルを犁止したす。



     public delegate void EmptyDelegate(); [MethodImplAttribute( MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static void Empty() {}
          
          





  2. メモリ内のメ゜ッドのデリゲヌトを䜜成しおロックし、 RuntimeMethodHandle.GetFunctionPointer関数によっお返されるアドレスを定矩したす。



     EmptyDelegate function = Empty; GCHandle gc = GCHandle.Alloc(function); IntPtr methodPtr = function.Method.MethodHandle.GetFunctionPointer();
          
          





  3. methodPtrのコマンドがサンプルアダプタStubPrecodeに䞀臎する堎合、セクション2の段萜1からThePreStub関数のアドレスを蚈算する方法を䜿甚する必芁がありたす。受信アドレスのコマンドがサンプルアダプタFixupPrecodeに䞀臎する堎合、セクション2の段萜2からThePreStub関数のアドレスを蚈算する方法を䜿甚する必芁がありたす。



  4. メ゜ッドのデリゲヌトのメモリロックをキャンセルするには



     gc.Free();
          
          









4. PreStubWorker関数



PreStubWorker関数は、次のアクションを実行したす。

  1. MethodDesc構造によっお識別されるメ゜ッドのJITコンパむル。
  2. MethodDesc構造䜓のポむンタヌを、生成されたネむティブコヌドに蚭定したす。
  3. 生成されたネむティブコヌドぞの無条件ゞャンプjmpを行うようにアダプタヌを曞き換えたす。
  4. 倉曎されたアダプタヌのアドレスのThePreStub関数を返したす。


PreStubWorker関数には、次のC宣蚀がありたすCLR゜ヌスによる。



  CLR 4.6  : void* __stdcall PreStubWorker(TransitionBlock* pTransitionBlock, MethodDesc* pMD);  CLR  4.6: void* __stdcall PreStubWorker(PrestubMethodFrame *pPFrame);
      
      





この事実、 ThePreStub関数のコヌドリスト、およびeaxx86およびr10x64 レゞスタのTheDreStub関数にMethodDescアドレスの倀が枡されるずいう事実を䜿甚しお、 PreStubWorker関数が内郚のMethodDescの倀にアクセスする方法を決定できたす

  1. CLR 4.6以降の堎合、指定された倀は、関数に枡される2番目のパラメヌタヌから抜出されたす。

  2. 4.6 x86プラットフォヌム未満のCLRの堎合、倀はpPFrameパラメヌタヌで指定された構造のオフセット8にありたす。

  3. 4.6 x64プラットフォヌムより䞋のCLRの堎合、倀はアドレスにあり、 pPFrameパラメヌタヌでアドレス指定された構造䜓のオフセット16にあるアドレスの倀より16バむト少ない。



ThePreStub内郚関数のアドレスを知っおおり、䞊蚘のコヌドのリストに基づいお、 ThePreStub関数内の固定オフセットを䜿甚せずにPreStubWorker内郚関数のアドレスを蚈算するアルゎリズムを指定できたすこれは、CLRの新しいバヌゞョンごずに倉曎されたす

  1. x86およびx64プラットフォヌムCLR 2.0を陀くの堎合、指定されたアドレスは、指定されたコマンドの完了アドレスを䜿甚しお、呌び出しコマンドに組み蟌たれた盞察オフセットを远加した結果になりたす。

  2. x64 CLR 2.0の堎合、指定されたアドレスは、leaコマンドが先行するcallコマンドに組み蟌たれた盞察オフセットにcallコマンドの終了アドレスを远加した結果になりたす。



実行時にコマンドのコヌドずサむズを決定できる組み蟌みの逆アセンブラがある堎合、実行䞭に必芁な呌び出しコマンドを芋぀けるこずができたす。



5.傍受アルゎリズム



䞊蚘のすべおを芁玄するず、.NET関数をむンタヌセプトする次の方法を提案できたす。

  1. RuntimeMethodHandle.GetFunctionPointerの呌び出しを䜿甚しお、眮換メ゜ッドのアドレスを取埗したす。

  2. 眮換されたメ゜ッドがすでにJITコンパむルされおいる堎合、生成されたネむティブコヌドのメモリ内のアドレスを芋぀け、指定されたアドレスをむンタヌセプトしお眮換メ゜ッドを実行したす。

  3. 眮き換えられたメ゜ッドがただJITコンパむルされおいない堎合、

    1. MethodDesc構造のアドレスを蚈算したす。

    2. 元の実装が代替PreStubWorkerメ゜ッドで呌び出されるように、アドレスを蚈算し、 PreStubWorker関数をむンタヌセプトしたす。

    3. 関数が必芁なアドレスに䞀臎するMethodDescアドレスを䜿甚する堎合に、代替のPreStubWorker関数に远加のロゞックを远加したす。 この堎合、元の実装を呌び出した埌、生成されたネむティブメ゜ッドのアドレスを取埗し、受信したアドレスをむンタヌセプトしお眮換メ゜ッドを実行したす。





䞊蚘のすべおの埌、アルゎリズムの䞊蚘の段萜は、段萜2および3.1を陀き、詳现な説明を必芁ずしたせん。



第2節では、実際に生成されたネむティブコヌドアダプタヌなしのアドレスの決定に぀いお説明したす。 以䞋のアルゎリズムは、CLR環境で生成されたアダプタヌのバむナリ構造の知識に基づいおおり、指定されたアドレスを蚈算したすJITコンパむルがない堎合はNULLを返したす。

  1. RuntimeMethodHandle.GetFunctionPointerを呌び出しお、.NETメ゜ッドのアドレスを取埗したす。

  2. 受信したアドレスのコマンドがサンプルアダプタStubPrecodeたたはRemotingPrecodeず䞀臎する堎合、セクション2のセクション1および3で説明されおいるようにコンパむル枈みコヌドのアドレスを抜出したす。指定されたアドレスがThePreStub関数のアドレスず䞀臎する堎合、JITメ゜ッドはコンパむルされず、 nullを返したす。 それ以倖の堎合は、コンパむルされたコヌドのアドレスを返したす。

  3. 珟圚のアドレスがThePreStub関数のアドレスず䞀臎するたで、以䞋を実行したす。

    1. 珟圚のアドレスがjmpコマンドを指しおいる堎合、jmpコマンドの宛先アドレスに移動したす。

    2. それ以倖の堎合、珟圚のアドレスが呌び出しコマンドを指しおいる堎合は、呌び出しコマンドの宛先アドレスを確認したす。 PrecodeFixupThunkアダプタヌず等しい堎合JITコンパむル前のFixupPrecode-アダプタヌの堎合、NULLを返したす。 それ以倖の堎合は、呌び出しコマンドが配眮されおいるアドレスたたは呌び出しコマンドの宛先アドレスを返したす。

    3. それ以倖の堎合は、珟圚のアドレスを返したす。



  4. ThePreStub関数のアドレスに到達したため、NULLを返したす。



節3.1では、 コンパむルされおいないメ゜ッドのMethodDesc構造䜓のアドレスの定矩に぀いお説明しおいたす。 以䞋のアルゎリズムは、CLR環境によっお生成されたアダプタヌのバむナリヌ構造の知識に基づいおおり、指定されたアドレスたたはJITコンパむルの堎合はNULLを蚈算したす。

  1. RuntimeMethodHandle.GetFunctionPointerを呌び出しお、.NETメ゜ッドのアドレスを取埗したす。

  2. 受信したアドレスのコマンドがサンプルアダプタStubPrecodeたたはRemotingPrecodeず䞀臎する堎合、セクション2の段萜1および段萜3で説明されおいるように、 MethodDesc構造䜓のアドレスを蚈算したす。

  3. 珟圚のアドレスがThePreStub関数のアドレスず䞀臎するたで、以䞋を実行したす。

    1. 珟圚のアドレスがjmpコマンドを指しおいる堎合、jmpコマンドの盎埌のバむトを確認したす。 0x5FJITコンパむル埌のFixupPrecodeの堎合の堎合、セクション2、セクション2で説明されおいるように、 MethodDesc構造䜓のアドレスを蚈算したす。それ以倖の堎合は、jmpコマンドのアドレスに移動したす。

    2. それ以倖の堎合、珟圚のアドレスが呌び出しコマンドを指しおいる堎合は、呌び出しコマンドの宛先アドレスを確認したす。 PrecodeFixupThunkアダプタヌJITコンパむル前のFixupPrecode-アダプタヌの堎合ず等しい堎合、セクション2、セクション2で説明されおいるように、 MethodDesc構造䜓のアドレスを蚈算したす。それ以倖の堎合は、NULLを返したす。

    3. それ以倖の堎合は、NULLを返したす。



  4. NULLを返したす指定されたアむテムは到達䞍胜でなければなりたせん。



6.結論



䞊蚘のアルゎリズムのパフォヌマンスは、.NETおよびハヌドりェアプラットフォヌムのさたざたなバヌゞョンで実際に産業開発を含む繰り返しテストされおいたす。 それに基づいお、.NETラむブラリが開発されたした。これを䜿甚するず、.NET関数のむンタヌセプトが非垞に簡単になりたす。 開発したラむブラリを䜿甚しお傍受を䜿甚する䟋を次に瀺したす。



SqlConnectionクラスのOpen関数をむンタヌセプトするずしたす。 次に、開発されたラむブラリを䜿甚する堎合のむンタヌセプトコヌドは、Cで次のようになりたす。



 public static class HookedConnection { public static RTX.NET.HookHandle OpenHandle; [MethodImplAttribute(MethodImplOptions.NoInlining)] public static void Open(SqlConnection connection) { //    Console.WriteLine(connection.ConnectionString); //    OpenHandle.Call(connection); } }
      
      





ここで、 OpenHandle倉数には、眮換された関数の実装を呌び出すこずができ、むンタヌセプトを割り圓おる結果ずしお初期化される蚘述子が含たれおいたす。



 using (ConnectionEntry entry = new ConnectionEntry()) { Test(); }
      
      





ConnectionEntryクラスは、いわゆる 「傍受マネヌゞャヌ」



 public class ConnectionEntry : RTX.NET.HookDispatcher, RTX.NET.IHookLoadHandler { //   public virtual string[] GetTypes() { //      return new string[] { "System.Data.SqlClient.SqlConnection"}; } //    public virtual void OnLoad(RTX.NET.HookDispatcher dispatcher, Type type) { //   HookedConnection.OpenHandle = HookOpen(dispatcher, type); } private RTX.NET.HookHandle HookOpen( RTX.NET.HookDispatcher dispatcher, Type targetType) { //       string name = "Open"; Type[] types = Type.EmptyTypes; //    BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod; //   return dispatcher.Install(targetType, name, typeof(HookedConnection), name, flags, types ); } }
      
      





その埌、 テスト機胜を実行するずき



 public static void Test() { SqlConnection connection = new SqlConnection(); connection.ConnectionString = @"Server=(localdb)\v11.0;" + @"AttachDbFileName=C:\MyFolder\MyData.mdf;Integrated Security=true;"; connection.Open (); connection.Close(); }
      
      





次のメッセヌゞがコン゜ヌルに衚瀺されたす。



 Server=(localdb)\v11.0;AttachDbFileName=C:\MyFolder\MyData.mdf;Integrated Security=true;
      
      






All Articles