home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2005 November / PCWELT_11_2005.ISO / pcwsoft / Commandbar-Source.z.exe / ZCommon / ApiInterceptor.cs next >
Encoding:
Text File  |  2002-06-10  |  6.5 KB  |  211 lines

  1. // Pavel Zolnikov[http://www.codeproject.com/script/profile/whos_who.asp?id=35980], 2002
  2.  
  3. using System;
  4. using System.Diagnostics;
  5. using System.Runtime.InteropServices;
  6. using System.Reflection;
  7. using System.Reflection.Emit;
  8. using System.Globalization;
  9.  
  10.  
  11. namespace ZCommon
  12. {
  13.     /// <summary>
  14.     /// Provides aids for an Import Adress Table(IAT)-based API interception.
  15.     /// </summary>
  16.     /// <remarks>
  17.     /// Based on the ReplaceIATEntry function by J.Richter
  18.     /// </remarks>
  19.     public class ApiInterceptor : MarshalByRefObject, IDisposable
  20.     {
  21.         protected Delegate    procedure;
  22.         protected String    callerName;
  23.         protected String    targetName;
  24.         protected String    procedureName;
  25.         protected IntPtr    signatureBytes;
  26.         protected IntPtr    oldAddress;
  27.  
  28.         /// <summary>
  29.         /// Interception starts once object is constructed.
  30.         /// </summary>
  31.         /// <param name="caller">Name of the module that calls API function.</param>
  32.         /// <param name="target">Name of the modile that implements API function.</param>
  33.         /// <param name="procName">Name of the API function to intercept.</param>
  34.         /// <param name="newProc">Delegate to the function that is to be called instead of intercepted.</param>
  35.         /// <remarks>
  36.         /// newProc delegate must contain only 'blittable' parameters (of types bool, int, IntPtr and so on).
  37.         /// </remarks>
  38.         public ApiInterceptor(String caller, String target, String procName, Delegate newProc)
  39.         {
  40.             procedure = newProc;
  41.             callerName = caller;
  42.             targetName = target;
  43.             procedureName = procName;
  44.  
  45.             SignatureHelper signature = 
  46.                 SignatureHelper.GetMethodSigHelper(
  47.                 null,
  48.                 procedure.Method.CallingConvention ,
  49.                 procedure.Method.ReturnType 
  50.                 );
  51.  
  52.             foreach( ParameterInfo param in procedure.Method.GetParameters() )
  53.             {
  54.                 signature.AddArgument( param.ParameterType );
  55.             }
  56.                         
  57.             byte[] bytes = signature.GetSignature();
  58.             signatureBytes = Marshal.AllocCoTaskMem(bytes.Length);
  59.             for(int i = 0; i< bytes.Length; i++)
  60.             {
  61.                 Marshal.WriteByte( signatureBytes, i, bytes[i]);
  62.             }
  63.  
  64.             IntPtr newAddress = Marshal.GetUnmanagedThunkForManagedMethodPtr(
  65.                 procedure.Method.MethodHandle.GetFunctionPointer(),
  66.                 signatureBytes,
  67.                 bytes.Length
  68.                 );
  69.  
  70.             ReplaceIatEntry(caller, target, procName, newAddress);
  71.         }
  72.  
  73.         protected virtual void Dispose(bool disposing)
  74.         {
  75.             GC.KeepAlive(this);//FxCop requirement
  76.             ReplaceIatEntry(callerName, targetName, procedureName, oldAddress);
  77.             Marshal.FreeCoTaskMem(signatureBytes);
  78.         }
  79.  
  80.         /// <summary>
  81.         /// Stops interception, disposes of resources.
  82.         /// </summary>
  83.         public void Dispose()
  84.         {
  85.             Dispose(true);
  86.             GC.SuppressFinalize(this);
  87.         }
  88.  
  89.         ~ApiInterceptor()
  90.         {
  91.             Dispose(false);
  92.         }
  93.  
  94.         /// <summary>
  95.         /// </summary>
  96.         /// <remarks>
  97.         /// See also J.Richter's "Programming Applications for MS Windows 4th Edition".
  98.         /// </remarks>
  99.         protected void ReplaceIatEntry(String caller, String target, String procName, IntPtr newProc)
  100.         {
  101.             GC.KeepAlive(this);
  102.  
  103.             Int32 hmodCaller = (Int32)Win32.GetModuleHandle(caller);
  104.             Int32 hmodTarget = (Int32)Win32.GetModuleHandle(target);
  105.             Int32 pfnCurrent = (Int32)Win32.GetProcAddress( (IntPtr)hmodTarget, procName);
  106.  
  107.             UInt32 Size;
  108.             Int32 pImpDesc = (Int32)ImageDirectoryEntryToData(
  109.                 (IntPtr)hmodCaller,
  110.                 true, 
  111.                 IMAGE_DIRECTORY_ENTRY_IMPORT,
  112.                 out Size );
  113.  
  114.             if( pImpDesc == 0 ) return;  // This module has no import section.
  115.  
  116.  
  117.             // Find the import descriptor containing references 
  118.             // to callee's functions.
  119.             IMAGE_IMPORT_DESCRIPTOR ImportDesc = (IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure((IntPtr)pImpDesc,typeof(IMAGE_IMPORT_DESCRIPTOR) );
  120.             while( ImportDesc.Name != 0 )
  121.             {
  122.                 String ModName = Marshal.PtrToStringAnsi((IntPtr)(hmodCaller + ImportDesc.Name));
  123.                     
  124.                 if( 0 == String.Compare(ModName, target, true, CultureInfo.InvariantCulture) )  break;
  125.  
  126.                 pImpDesc += Marshal.SizeOf(typeof(IMAGE_IMPORT_DESCRIPTOR));
  127.                 ImportDesc = (IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure((IntPtr)pImpDesc,typeof(IMAGE_IMPORT_DESCRIPTOR) );
  128.             }
  129.  
  130.             if( ImportDesc.Name == 0 ) return; // This module doesn't import any functions from this callee.
  131.  
  132.             // Get caller's import address table (IAT) 
  133.             // for the callee's functions.
  134.             Int32 pThunk = hmodCaller + ImportDesc.FirstThunk;
  135.  
  136.             IMAGE_THUNK_DATA32 Thunk = (IMAGE_THUNK_DATA32)Marshal.PtrToStructure((IntPtr)pThunk,typeof(IMAGE_THUNK_DATA32));
  137.             // Replace current function address with new function address.
  138.             while( Thunk.Address!= 0 ) 
  139.             {
  140.                 if( Thunk.Address == pfnCurrent )
  141.                 {                        
  142.                     // The addresses match; change the import section address.
  143.                     oldAddress = ExchangeIntPtrs((IntPtr)pThunk, newProc);
  144.  
  145.                     return;  // We did it; get out.
  146.                 }
  147.                     
  148.                 pThunk += Marshal.SizeOf(typeof(IMAGE_THUNK_DATA32));
  149.                 Thunk = (IMAGE_THUNK_DATA32)Marshal.PtrToStructure((IntPtr)pThunk,typeof(IMAGE_THUNK_DATA32));
  150.             }
  151.  
  152.             // If we get to here, the function
  153.             // is not in the caller's import section.
  154.  
  155.         }
  156.  
  157.         static IntPtr ExchangeIntPtrs(IntPtr valueAddres, IntPtr newValue)
  158.         {
  159.             IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf(typeof(IntPtr)) );
  160.             Marshal.WriteIntPtr( buffer, newValue );
  161.  
  162.             IntPtr old = Marshal.ReadIntPtr(valueAddres);
  163.  
  164.             Int32 num;
  165.             Win32.WriteProcessMemory(
  166.                 Process.GetCurrentProcess().Handle,
  167.                 valueAddres,
  168.                 buffer,
  169.                 Marshal.SizeOf(typeof(IntPtr)),
  170.                 out num );
  171.  
  172.             Marshal.FreeCoTaskMem( buffer );
  173.  
  174.             return old;
  175.         }
  176.  
  177.  
  178.         [DllImport("dbghelp.dll")]
  179.         extern static IntPtr ImageDirectoryEntryToData(
  180.             IntPtr Base,            
  181.             bool MappedAsImage,  
  182.             UInt16 DirectoryEntry,  
  183.             out UInt32 Size            
  184.             );
  185.  
  186.         const UInt16 IMAGE_DIRECTORY_ENTRY_IMPORT = 1;
  187.  
  188.         [StructLayout(LayoutKind.Sequential)]
  189.             class IMAGE_IMPORT_DESCRIPTOR
  190.         {
  191.             public Int32   Characteristics_or_OriginalFirstThunk = 0;    // 0 for terminating null import descriptor
  192.             // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
  193.                 
  194.             public Int32   TimeDateStamp = 0;    // 0 if not bound,
  195.             // -1 if bound, and real date\time stamp
  196.             //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
  197.             // O.W. date/time stamp of DLL bound to (Old BIND)
  198.  
  199.             public Int32   ForwarderChain = 0;    // -1 if no forwarders
  200.             public Int32   Name = 0;
  201.             public Int32   FirstThunk = 0;      // RVA to IAT (if bound this IAT has actual addresses)
  202.         }
  203.  
  204.         [StructLayout(LayoutKind.Sequential)]
  205.             struct IMAGE_THUNK_DATA32 
  206.         {
  207.             public Int32 Address;//works only on Intel32
  208.         } 
  209.     }
  210.  
  211. }