【文章标题】: .net 2.0下利用动态Assembly与Reflect机制向DHTML暴露.net应用里的对象与.net framework
【文章作者】: dreaman
【作者邮箱】: dreaman_163@163.com
【作者主页】: http://dreaman.haolinju.net
【作者声明】: 主要用于为WebBrowser控件提供扩展对象,不过涉及动态代码编写,也许对crack有用。
--------------------------------------------------------------------------------
【详细过程】
  .net 2.0提供的WebBrowser控件对IE的封装十分酷,特别是其ObjectForScripting特性,只要将一个.net里的对象实例赋给它,就可以在DHTML中用window.external访问该.net对象了(有一个限制是该.net对象必须使用ComVisible属性修饰成COM可见的),如果用IE作界面,DHTML可以认为是UI与控制器,.NET中则实现对象模型,如果可以在DHTML中自由的访问.NET里的对象,那会让人很舒服.
  我想到的方法是在external上提供一个CreateObject方法,直接由脚本来创建.net里的对象实例,但是有一个问题,.net里有许多对象并不是COM可见的,这样的对象实例无法返回给DHTML,所以必须对这样的对象作些处理.
  对非COM可见的对象,要让DHTML来访问,必须作一个代理,代理对象设成COM可见的,然后代理对象具有原对象一样的访问接口.因为目标客户是javascript脚本,它访问对象是通过IDispatch[Ex]接口来实现的,所以我们的代理对象实际上并不需要与原对象有完全一致的接口,这样,有三种可能的方式:
  1、直接修原.NET对象,让它变成COM可见的;
  2、在.NET中实现一个对象,它实现IDispatch接口,实现的功能是访问原.net对象的属性与方法;
  3、利用动态装配与反射,动态的生成一个代理类,接口与原.net类相同,并设置为COM可见的。
  方法1我不知道如何做,因为.net框架没有提供对现有类的修改的方式,如果是cracker的方式,我想可能会比较复杂并且不通用(更重要的是,我不知道怎么弄,谁要是知道,请告诉我,谢了)。
  方法2理论上是绝对可行的,但是我打算用C#来写,也就是说企图用托管代码实现IDispatch接口,实验失败,MSDN上好象是说,CLR总是会替.net对象实现IUnknown与IDispatch接口;如果是用C++,我想IDispatch实现成非托管的,然后其实现功能利用C++互操作调.NET的相关实现,然后将非托管IDispatch实例返回并使用自己的定制Marshal应该是可行的(没有试验过,只是想法)。
  现在只剩下方法3了,我试验了这种方法,可行。
  不多说了,直接看代码(试验性的,其中的定制Marshal是试验用的,现在的实现与.NET默认的Marshal是一样的,所以是可以去掉的)
      //定制Marshal,实现与.net运行时作的工作一样,可以去掉
      public class MyMarshal : ICustomMarshaler
      {
          #region ICustomMarshaler 成员
          public void CleanUpManagedData(object ManagedObj)
          {}
          public void CleanUpNativeData(IntPtr pNativeData)
          {}
          public int GetNativeDataSize()
          {
              return -1;
          }
          public IntPtr MarshalManagedToNative(object o)
          {
              IntPtr i = Marshal.GetIDispatchForObject(o);
              return i;
          }
          public object MarshalNativeToManaged(IntPtr pNativeData)
          {
              throw new Exception("The method or operation is not implemented.");
          }
          #endregion
          static public ICustomMarshaler GetInstance(string pstrCookie)
          {
              return ins;
          }
          static private MyMarshal ins = new MyMarshal();
      }
      //扩展对象的实现  
      [ComVisible(true)]
      public class External
      {
    //判断一个.net对象实例是否是COM可见的,如果不可见则实时生成一个COM可见的代理类,并返回代理类的实例。
          [method: ComVisible(false)]
          static private object WrapAsComVisible(Type t, object o)
          {
              Attribute[] attrs = Attribute.GetCustomAttributes(t);
              foreach (Attribute a in attrs)
              {
                  if (a is ComVisibleAttribute)
                  {
                      ComVisibleAttribute cva = a as ComVisibleAttribute;
                      if (cva.Value)
                          return o;
                  }
              }
        //现在开始生成COM可见的代理类
              AssemblyName an = new AssemblyName("__" + t.Namespace + "_" + t.Name + "_" + t.GetHashCode());
              AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
              ModuleBuilder mob = ab.DefineDynamicModule("_" + t.Name + "_" + t.GetHashCode());
              TypeBuilder tb = mob.DefineType(t.Name + "_" + t.GetHashCode(), TypeAttributes.Public);
              Type[] cts = new Type[] { typeof(bool) };
              ConstructorInfo ci = typeof(ComVisibleAttribute).GetConstructor(cts);
              CustomAttributeBuilder cab = new CustomAttributeBuilder(ci, new object[] { true });
              tb.SetCustomAttribute(cab);
              tb.DefineDefaultConstructor(MethodAttributes.Public);
              FieldBuilder innerFieldInfo = tb.DefineField("innerObj", t, FieldAttributes.Public);
              MemberInfo[] memberInfos = t.GetMembers();
              foreach (MemberInfo memberInfo in memberInfos)
              {
                  switch (memberInfo.MemberType)
                  {
                      case MemberTypes.Field:
                          {
                              FieldInfo fi = memberInfo as FieldInfo;
                              PropertyBuilder pb = tb.DefineProperty(fi.Name, System.Reflection.PropertyAttributes.None, fi.FieldType, null);
                              if (true)
                              {
                                  MethodBuilder getMB = tb.DefineMethod("get_" + fi.Name, MethodAttributes.Public | MethodAttributes.SpecialName, fi.FieldType, null);
                                  ILGenerator ilg = getMB.GetILGenerator();
                                  ilg.Emit(OpCodes.Ldarg_0);
                                  ilg.Emit(OpCodes.Ldfld, innerFieldInfo);
                                  ilg.Emit(OpCodes.Ldfld, fi);
                                  ilg.Emit(OpCodes.Ret);
                                  pb.SetGetMethod(getMB);
                              }
                              if (true)
                              {
                                  Type[] tis = new Type[] { fi.FieldType };
                                  MethodBuilder setMB = tb.DefineMethod("set_" + fi.Name, MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), tis);
                                  ILGenerator ilg = setMB.GetILGenerator();
                                  ilg.Emit(OpCodes.Ldarg_0);
                                  ilg.Emit(OpCodes.Ldfld, innerFieldInfo);
                                  ilg.Emit(OpCodes.Ldarg_1);
                                  ilg.Emit(OpCodes.Stfld, fi);
                                  ilg.Emit(OpCodes.Ret);
                                  pb.SetSetMethod(setMB);
                              }
                          }
                          break;
                      case MemberTypes.Property:
                          {
                              PropertyInfo pi = memberInfo as PropertyInfo;
                              ParameterInfo[] paras = pi.GetIndexParameters();
                              Type[] tis0 = new Type[paras.Length];
                              for (int i = 0; i < tis0.Length; i++)
                              {
                                  tis0[i] = paras[i].ParameterType;
                              }
                              PropertyBuilder pb = tb.DefineProperty(pi.Name, System.Reflection.PropertyAttributes.None, pi.PropertyType, tis0);
                              if (pi.CanRead)
                              {
                                  if (paras.Length > 0)
                                  {
                                      Type[] tis = new Type[paras.Length];
                                      for (int i = 0; i < tis.Length; i++)
                                      {
                                          tis[i] = paras[i].ParameterType;
                                      }
                                      MethodBuilder getMB = tb.DefineMethod("get_Item", MethodAttributes.Public | MethodAttributes.SpecialName, pi.PropertyType, tis);
                                      ILGenerator ilg = getMB.GetILGenerator();
                                      ilg.Emit(OpCodes.Ldarg_0);
                                      ilg.Emit(OpCodes.Ldfld, innerFieldInfo);
                                      for (int i = 0; i < tis.Length; i++)
                                      {
                                          switch (i)
                                          {
                                              case 0:
                                                  {
                                                      ilg.Emit(OpCodes.Ldarg_1);
                                                  }
                                                  break;
                                              case 1:
                                                  {
                                                      ilg.Emit(OpCodes.Ldarg_2);
                                                  }
                                                  break;
                                              case 2:
                                                  {
                                                      ilg.Emit(OpCodes.Ldarg_3);
                                                  }
                                                  break;
                                              default:
                                                  {
                                                      ilg.Emit(OpCodes.Ldarg_S, i + 1);
                                                  }
                                                  break;
                                          }
                                      }
                                      ilg.EmitCall(OpCodes.Call, pi.GetGetMethod(), tis);
                                      ilg.Emit(OpCodes.Ret);
                                      pb.SetGetMethod(getMB);
                                  }
                                  else
                                  {
                                      MethodBuilder getMB = tb.DefineMethod("get_" + pi.Name, MethodAttributes.Public | MethodAttributes.SpecialName, pi.PropertyType, null);
                                      ILGenerator ilg = getMB.GetILGenerator();
                                      ilg.Emit(OpCodes.Ldarg_0);
                                      ilg.Emit(OpCodes.Ldfld, innerFieldInfo);
                                      ilg.EmitCall(OpCodes.Call, pi.GetGetMethod(), null);
                                      ilg.Emit(OpCodes.Ret);
                                      pb.SetGetMethod(getMB);
                                  }
                              }
                              if (pi.CanWrite)
                              {
                                  if (paras.Length > 0)
                                  {
                                      Type[] tis = new Type[paras.Length + 1];
                                      for (int i = 0; i < tis.Length; i++)
                                      {
                                          if (i < paras.Length)
                                              tis[i] = paras[i].ParameterType;
                                          else
                                              tis[i] = pi.PropertyType;
                                      }
                                      MethodBuilder setMB = tb.DefineMethod("set_Item", MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), tis);
                                      ILGenerator ilg = setMB.GetILGenerator();
                                      ilg.Emit(OpCodes.Ldarg_0);
                                      ilg.Emit(OpCodes.Ldfld, innerFieldInfo);
                                      for (int i = 0; i < tis.Length; i++)
                                      {
                                          switch (i)
                                          {
                                              case 0:
                                                  {
                                                      ilg.Emit(OpCodes.Ldarg_1);
                                                  }
                                                  break;
                                              case 1:
                                                  {
                                                      ilg.Emit(OpCodes.Ldarg_2);
                                                  }
                                                  break;
                                              case 2:
                                                  {
                                                      ilg.Emit(OpCodes.Ldarg_3);
                                                  }
                                                  break;
                                              default:
                                                  {
                                                      ilg.Emit(OpCodes.Ldarg_S, i + 1);
                                                  }
                                                  break;
                                          }
                                      }
                                      ilg.EmitCall(OpCodes.Call, pi.GetSetMethod(), tis);
                                      ilg.Emit(OpCodes.Ret);
                                      pb.SetSetMethod(setMB);
                                  }
                                  else
                                  {
                                      Type[] tis = new Type[] { pi.PropertyType };
                                      MethodBuilder setMB = tb.DefineMethod("set_" + pi.Name, MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), tis);
                                      ILGenerator ilg = setMB.GetILGenerator();
                                      ilg.Emit(OpCodes.Ldarg_0);
                                      ilg.Emit(OpCodes.Ldfld, innerFieldInfo);
                                      ilg.Emit(OpCodes.Ldarg_1);
                                      ilg.EmitCall(OpCodes.Call, pi.GetSetMethod(), tis);
                                      ilg.Emit(OpCodes.Ret);
                                      pb.SetSetMethod(setMB);
                                  }
                              }
                          }
                          break;
                      case MemberTypes.Method:
                          {
                              MethodInfo mi = memberInfo as MethodInfo;
                              MethodBuilder mb = tb.DefineMethod(mi.Name, MethodAttributes.Public);
                              ParameterInfo[] paras = mi.GetParameters();
                              Type[] tis = new Type[paras.Length];
                              for (int i = 0; i < tis.Length; i++)
                              {
                                  tis[i] = paras[i].ParameterType;
                              }
                              mb.SetReturnType(mi.ReturnType);
                              mb.SetParameters(tis);
                              ILGenerator ilg = mb.GetILGenerator();
                              ilg.Emit(OpCodes.Ldarg_0);
                              ilg.Emit(OpCodes.Ldfld, innerFieldInfo);
                              for (int i = 0; i < tis.Length; i++)
                              {
                                  switch (i)
                                  {
                                      case 0:
                                          {
                                              ilg.Emit(OpCodes.Ldarg_1);
                                          }
                                          break;
                                      case 1:
                                          {
                                              ilg.Emit(OpCodes.Ldarg_2);
                                          }
                                          break;
                                      case 2:
                                          {
                                              ilg.Emit(OpCodes.Ldarg_3);
                                          }
                                          break;
                                      default:
                                          {
                                              ilg.Emit(OpCodes.Ldarg_S, i + 1);
                                          }
                                          break;
                                  }
                              }
                              ilg.EmitCall(OpCodes.Call, t.GetMethod(mi.Name), tis);
                              ilg.Emit(OpCodes.Ret);
                          }
                          break;
                  }
              }
              Type tt = tb.CreateType();
        //创建代理类实例并初始化指向原.net类实例的指针。
              object oo = Activator.CreateInstance(tt);
              tt.InvokeMember("innerObj", BindingFlags.SetField, null, oo, new object[] { o });
              return oo;
          }
          [method: ComVisible(false)]
          static private object CreateObjectFromFileX(string aname, string tname,params object[] args)
          {
              Assembly a = Assembly.LoadFile(aname);
              Type t = a.GetType(tname);
              object o=Activator.CreateInstance(t, args);
              return WrapAsComVisible(t,o);
          }
          [method: ComVisible(false)]
          static private object CreateObjectX(string tname, params object[] args)
          {
              Type t = Type.GetType(tname);
              object o = Activator.CreateInstance(t, args);
              return WrapAsComVisible(t,o);            
          }
    /*
            后续的方法是前面两个方法的特定参数数的版本,因为客户是用访问COM对象的方式访问External,我们
            为其提供参数固定的访问形式。
    */
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile0(string aname,string tname)
          {
              return CreateObjectFromFileX(aname,tname);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject0(string tname)
          {
              return CreateObjectX(tname);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile1(string aname, string tname, object arg1)
          {
              return CreateObjectFromFileX(aname, tname, arg1);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject1(string tname, object arg1)
          {
              return CreateObjectX(tname, arg1);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile2(string aname, string tname, object arg1, object arg2)
          {
              return CreateObjectFromFileX(aname, tname, arg1, arg2);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject2(string tname, object arg1, object arg2)
          {
              return CreateObjectX(tname, arg1, arg2);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile3(string aname, string tname, object arg1, object arg2, object arg3)
          {
              return CreateObjectFromFileX(aname, tname, arg1, arg2, arg3);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject3(string tname, object arg1, object arg2, object arg3)
          {
              return CreateObjectX(tname, arg1, arg2, arg3);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile4(string aname, string tname, object arg1, object arg2, object arg3, object arg4)
          {
              return CreateObjectFromFileX(aname, tname, arg1, arg2, arg3, arg4);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject4(string tname, object arg1, object arg2, object arg3, object arg4)
          {
              return CreateObjectX(tname, arg1, arg2, arg3, arg4);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile5(string aname, string tname, object arg1, object arg2, object arg3, object arg4, object arg5)
          {
              return CreateObjectFromFileX(aname, tname, arg1, arg2, arg3, arg4, arg5);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject5(string tname, object arg1, object arg2, object arg3, object arg4, object arg5)
          {
              return CreateObjectX(tname, arg1, arg2, arg3, arg4, arg5);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile6(string aname, string tname, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6)
          {
              return CreateObjectFromFileX(aname, tname, arg1, arg2, arg3, arg4, arg5, arg6);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject6(string tname, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6)
          {
              return CreateObjectX(tname, arg1, arg2, arg3, arg4, arg5, arg6);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile7(string aname, string tname, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7)
          {
              return CreateObjectFromFileX(aname, tname, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject7(string tname, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7)
          {
              return CreateObjectX(tname, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObjectFromFile8(string aname, string tname, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8)
          {
              return CreateObjectFromFileX(aname, tname, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
          }
          [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshal))]
          public object CreateObject8(string tname, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8)
          {
              return CreateObjectX(tname, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
          }
      }
  用法很简单,实例化一个External并将它赋给WebBrowser控件实例的ObjectForScripting,然后在DHTML里用window.external.CreateObject系列方法来创建.net里的对象(包括当前EXE中实现的与.net框架库及dll的),可以看出,相当于向DHTML暴露了整个.net framework,呵呵。 
  
  代理的实现在WrapAsComVisible中。
  
--------------------------------------------------------------------------------
【经验总结】
  .net中的reflect与动态装配使得我们可以在运行时构造并执行代码(相当于win32程序里在内存
  中写入并执行指令),不过我不知道如何修改已经存在的代码与数据,如果可以,则目前win32代
  码钩子所能做的一切.NET里都可以做到(如果哪位知道怎么在运行时修改.net现行的IL与元数据
  ,请告诉我)。
  
--------------------------------------------------------------------------------

                                                       2006年05月22日 12:24:28