如何在Roslyn中从已有Assembly创建CSharpCompilation?.NET6替代方案
解决方案
Roslyn的核心定位是基于源代码的编译工作流,无法直接从已编译的Assembly对象生成可编辑的CSharpCompilation实例,因此不适合你的场景。针对.NET 6+中修改已编译程序集并保存的需求,推荐以下两种可行方案:
一、第三方成熟方案:使用Mono.Cecil
Mono.Cecil是专门用于操作已编译.NET程序集的开源库,API简洁易用,支持修改IL代码、添加/删除成员、加密方法体等操作,完全可以替代原AssemblyBuilder的场景,是当前最便捷的选择。
实现示例
- 安装NuGet包:
Mono.Cecil - 编写修改逻辑:
private void Protect(FileInfo dllSourceFile, FileInfo targetFile) { // 加载源程序集 using var assemblyDef = AssemblyDefinition.ReadAssembly(dllSourceFile.FullName); // 遍历所有模块、类型、方法,修改目标内容 foreach (var module in assemblyDef.Modules) { foreach (var type in module.Types) { // 跳过系统类型与编译器生成类型 if (type.IsRuntimeSpecialName || type.Namespace.StartsWith("System.")) continue; foreach (var method in type.Methods) { // 跳过无方法体的抽象/接口方法 if (!method.HasBody || method.IsAbstract) continue; // 获取原方法IL字节并加密 byte[] originalIl = method.Body.GetILAsByteArray(); byte[] encryptedIl = EncryptIl(originalIl); // 替换方法体的IL内容 method.Body.Instructions.Clear(); method.Body.SetIL(encryptedIl); // 注意:若加密了IL,需在方法开头插入对应的解密逻辑(否则运行时无法执行) // 示例:导入解密方法并插入调用指令 // var decryptMethod = module.ImportReference(typeof(IlDecryptor).GetMethod("Decrypt")); // method.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0)); // method.Body.Instructions.Insert(1, Instruction.Create(OpCodes.Call, decryptMethod)); } } } // 保存修改后的程序集到目标文件 assemblyDef.Write(targetFile.FullName); } // 自定义IL加密逻辑(替换为你的实际算法) private byte[] EncryptIl(byte[] ilBytes) { for (int i = 0; i < ilBytes.Length; i++) { ilBytes[i] = (byte)(ilBytes[i] ^ 0xBB); } return ilBytes; }
二、官方底层方案:使用System.Reflection.Metadata
.NET 6+提供了System.Reflection.Metadata和System.Reflection.Metadata.Ecma335 NuGet包,可直接操作程序集的元数据与IL字节,逻辑上接近原AssemblyBuilder的底层实现,但API偏底层,需要熟悉.NET程序集格式。
实现示例
- 安装NuGet包:
System.Reflection.Metadata、System.Reflection.Metadata.Ecma335 - 编写读取、修改、生成程序集的逻辑:
private void Protect(FileInfo dllSourceFile, FileInfo targetFile) { byte[] sourceBytes = File.ReadAllBytes(dllSourceFile.FullName); // 读取源程序集元数据 using var sourceStream = new MemoryStream(sourceBytes); using var peReader = new PEReader(sourceStream); if (!peReader.HasMetadata) throw new InvalidOperationException("无效的.NET程序集"); MetadataReader reader = peReader.GetMetadataReader(); var metadataBuilder = new MetadataBuilder(); var ilBlobBuilder = new BlobBuilder(); // 遍历并修改方法IL(需手动复制所有元数据,示例仅处理方法) foreach (var methodHandle in reader.MethodDefinitions) { var method = reader.GetMethodDefinition(methodHandle); if (!method.ILBody.IsNil) { // 读取原IL并加密 var ilReader = reader.GetBlobReader(method.ILBody); byte[] originalIl = ilReader.ReadBytes((int)ilReader.RemainingBytes); byte[] encryptedIl = EncryptIl(originalIl); // 将加密后的IL写入新Blob int ilOffset = ilBlobBuilder.Count; ilBlobBuilder.WriteBytes(encryptedIl); BlobHandle newIlHandle = metadataBuilder.GetOrAddBlob(ilBlobBuilder, ilOffset); // 复制方法定义并替换IL句柄 metadataBuilder.AddMethodDefinition( method.Attributes, method.Name, method.Signature, method.ImplAttributes, newIlHandle, method.ParameterList ); } else { // 无IL的方法直接复制 metadataBuilder.AddMethodDefinition( method.Attributes, method.Name, method.Signature, method.ImplAttributes, method.ILBody, method.ParameterList ); } } // 生成新PE文件并保存 using var outputStream = new MemoryStream(); var peBuilder = new ManagedPEBuilder( new PEHeaderBuilder(), new MetadataRootBuilder(metadataBuilder), new BlobBuilder() ); peBuilder.Serialize(outputStream); File.WriteAllBytes(targetFile.FullName, outputStream.ToArray()); } // 自定义IL加密逻辑 private byte[] EncryptIl(byte[] ilBytes) { for (int i = 0; i < ilBytes.Length; i++) { ilBytes[i] = (byte)(ilBytes[i] ^ 0xAA); } return ilBytes; }
关于AssemblyBuilder在.NET 6+的限制
.NET 6+中AssemblyBuilder仍存在,但已移除Save方法,仅支持在内存中生成并加载程序集。若强行使用,需通过反射获取内部PE字节再保存到文件,但这依赖未公开API,稳定性无法保证,不推荐生产环境使用。
内容的提问来源于stack exchange,提问作者Florian
相关产品推荐
相关产品推荐

