Cecil修改UnityDll,不使用反射就能调用internal的函数

简介

在Unity Editor开发过程中,我们会经常使用反射调用一些unity还没开放的接口,比如s_LastControlID, 但每个程序集都写一边反射不免显得有些麻烦。
本篇文章将介绍注入InternalsVisibleToAttribute注解到unity dll的方法,来帮助大家更便捷地调用unity 的内部函数。

思路

InternalsVisibleToAttribute可以为当前程序集添加友元程序集,友元程序集可以调用当前程序集的internal函数。

Cecil可以编辑当前程序集的IL代码和元数据。 那么如果我们在unity dll的元数据中将Assembly-Csharp程序集添加为友元,那么就能实现Assembly-Csharp调用对应unity dll的internal函数。

代码及使用方法

核心类是AssemblyHacker, 通过传入目标程序集的绝对路径和友元程序集的FullName,可以实现添加友元程序集的效果。

    internal class AssemblyHacker
    {

        private string targetAsmFileName;
        private AssemblyDefinition assemblyDef;

        private DefaultAssemblyResolver assemblyResolver;


        //targetAsms是 需要修改的dll绝对路径的集合
        //friedns是友元程序集的FullName
        public static void Hack(ICollection<string> targetAsms, ICollection<string> friends)
        {

            foreach (var targetAsm in targetAsms)
            {
                var hacker = new AssemblyHacker();
                hacker.SetAssembly(targetAsm);

                hacker.Work(friends);

                //dispose Assembly不会清除 resolver里面缓存的 引用Assembly,需要手动清理
                hacker.assemblyResolver.Dispose();
            }
        }

        private void SetAssembly(string assemblyName)
        {
            targetAsmFileName = assemblyName;

            if (assemblyResolver == null)
            {

                assemblyResolver = new DefaultAssemblyResolver();
                var targetDirName = Path.GetDirectoryName(targetAsmFileName);
                var targetDirName2 = Path.GetDirectoryName(targetDirName);
                assemblyResolver.AddSearchDirectory(targetDirName);
                assemblyResolver.AddSearchDirectory(targetDirName2);
            }

            assemblyDef = AssemblyDefinition.ReadAssembly(targetAsmFileName,
                new ReaderParameters { AssemblyResolver = assemblyResolver, ReadWrite = true });


        }


        private void Work(ICollection<string> friends)
        {

            AddFriendForTarget(friends);

        }

        private void AddFriendForTarget(ICollection<string> friends)
        {

            var friendAssemblyList = friends.ToList();

            var assemblyAttrs = assemblyDef.CustomAttributes;

            //如果已经是友元程序集,忽略它。
            foreach (var assemblyAttr in assemblyAttrs)
            {
                if (assemblyAttr.AttributeType.FullName.Equals(typeof(InternalsVisibleToAttribute).FullName))
                {
                    var oldFriendAssembly = assemblyAttr.ConstructorArguments[0].Value.ToString();

                    friendAssemblyList.Remove(oldFriendAssembly);
                }
            }


            //var attrType = GetInternalsVisibleType();
            var attrType = assemblyDef.MainModule.ImportReference(typeof(InternalsVisibleToAttribute));
            var attCtor = attrType.Resolve().Methods.First(x => x.Name == ".ctor" && x.Parameters.Count == 1);

            MethodReference attrCtorMethodRef;
            attrCtorMethodRef = assemblyDef.MainModule.ImportReference(attCtor);


            foreach (var friend in friendAssemblyList)
            {
                var attr = new CustomAttribute(attrCtorMethodRef);
                attr.ConstructorArguments.Add(new CustomAttributeArgument(attrType, friend));

                assemblyAttrs.Add(attr);
            }

            //var newAssemblyFile = targetAsmFileName.Replace(".dll", "New.dll");
            assemblyDef.Write();

            assemblyDef.Dispose();

            //Console.WriteLine("write result to " + newAssemblyFile);

        }

        private TypeReference GetInternalsVisibleType()
        {
            foreach (var attr in assemblyDef.CustomAttributes)
            {
                if (attr.AttributeType.FullName.Equals("System.Runtime.CompilerServices.InternalsVisibleToAttribute"))
                {
                    Console.WriteLine("attrType Asm is " + attr.AttributeType.Module.FileName);
                    return attr.AttributeType;
                }
            }

            throw new Exception("cannot find InternalsVisibleToAttribute Type in assembly Definition");
        }



    }

入口函数

    /// <summary>
    /// 传入的参数是 需要修改的程序集的 文件名
    /// 当前程序会将 对应的程序集
    /// </summary>
    internal class Program
    {
        static void Main(string[] args)
        {
            PrivilegeCheck.Check();


            var lines=File.ReadAllLines("MonoCecilProxy.txt");
            var friendlAsmStrs = lines[0].Trim().Split(' ');

            List<string> targetAsms = new List<string>();
            for (int i = 1; i < lines.Length; i++) 
            { 
                var line = lines[i].Trim();
                if (line.Equals(string.Empty))
                    break;
                targetAsms.Add(lines[i].Trim());
            }

            AssemblyHacker.Hack(targetAsms, friendlAsmStrs);

            Console.WriteLine("finished");

            Console.ReadKey();
        }

    }


Assembly-CSharp 
C:\Program Files\Unity\Hub\Editor\2022.3.32f1\Editor\Data\Managed\UnityEngine\UnityEditor.CoreModule.dll
C:\Program Files\Unity\Hub\Editor\2022.3.32f1\Editor\Data\Managed\UnityEngine\UnityEngine.IMGUIModule.dll

// 这里第一行是需要添加进去的 友元程序集的 名称
// 剩下的几行是需要被修改的程序集的 文件名FullPath 

程序权限

由于unity dll位于C盘,这里我们需要提升程序的权限。

csproj项目文件里添加app.manifest

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net462</TargetFramework>
    <ApplicationManifest>app.manifest</ApplicationManifest>

  </PropertyGroup>
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">

		  <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

      </requestedPrivileges>
    </security>
  </trustInfo>



</assembly>

posted @   dewxin  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
历史上的今天:
2023-01-19 使用PInvoke.net
点击右上角即可分享
微信分享提示