【问题标题】:How To Modify .Net Assembly At Runtime And Keep These Changes如何在运行时修改 .Net 程序集并保留这些更改
【发布时间】:2020-03-23 22:48:48
【问题描述】:

你好,

我只是问在 C#.Net 中是否有办法在运行时修改程序集, 我的意思是说 如果我有此类

public static class TimesTracker
{
    public static int Rest = 3;
}

现在,我需要在运行时将 Rest 更改为 2,但保留这些更改。

所以,下次我打开我的程序时,我会找到Rest=2

我知道,我可以使用配置文件,数据库,...但我只是问这种可能性

背景

我知道在运行时替换方法实现,也知道在运行时使用 Roslyn 调用/编译字符串代码

【问题讨论】:

  • 有了这个级别的信息,这很可能是一个 x/y 问题。为什么需要这样做?因为您可能需要重新考虑您想做什么。
  • @MichaelRandall 我认为 eziriz 软件,例如 IntelliLock 使用这种类型的修改来做一些事情我试图监控发生了什么,但我得到的只是程序集被改变了,我知道如何实现这不是一个复杂的话题:但是在我的实现中,我需要一个围绕主程序集的包装器,然后在执行时我将调用内部编译文件exe from string {roslyn} 来修改我需要的内容,然后用当前包含的程序集替换它,最后合并再次两个程序集[因为在运行时移动 exe 文件没有限制],但是这种行为使反编译器
  • @MichaelRandall 所以这与开发特洛伊木马的步骤相同,并使任何防病毒软件抱怨发生的事情,因为这可能会从远程执行代码,并且您知道大多数防病毒软件的黑名单中定义的此行为签名软件,这与 Rats 中使用的想法相同,例如 NjRat,... 所以我需要知道 .net 中是否有任何可能性来更改程序集中的值而无需所有那些漫长而危险的步骤?你说这是 x/y 问题?那么如何?

标签: c# .net .net-assembly


【解决方案1】:

有趣。但是我想不出任何用例。

我之前在 MSBuild 目标中使用 Mono.Cecil 来动态修改程序集并保存回磁盘。

这里是一个例子,你问的怎么做:

var filePath = "Assembly path";
var updatedFilePath = "Updated assembly path";

// Read assembly
using (var assembly = AssemblyDefinition.ReadAssembly(filePath))
{
    // Find type TimesTracker. This example was done in LINQPad, so TimesTracker is nested type inside UserQuery
    var type = assembly.MainModule.Types.SelectMany(t => t.NestedTypes).First(td => td.Name == "TimesTracker");
    // Find static constructor
    var cctor = type.Methods.First(md => md.Name == ".cctor");

    // Get IL
    var il = cctor.Body.GetILProcessor();

    // The IL of Rest = 3 is:
    // IL_0000: ldc.i4.3
    // IL_0001: stsfld int32 UserQuery / TimesTracker::Rest
    var op = il.Body.Instructions.First(i => i.OpCode == OpCodes.Ldc_I4_3);
    // Replace 3 with 7
    var newOp = il.Create(OpCodes.Ldc_I4_7);
    il.Replace(op, newOp);

    // Write updated to another assembly file
    assembly.Write(updatedFilePath);
}

// Load updated assembly into AppDomain
var updatedAssembly = Assembly.LoadFile(updatedFilePath);
// Use reflection to get value of Rest
var updatedType = updatedAssembly.DefinedTypes.First(t => t.Name.Contains("TimesTracker"));
var field = updatedType.GetField("Rest", BindingFlags.Public | BindingFlags.Static);

// value is now 7
var value = (int)field.GetValue(null);

注意以上代码只能修改:

public class TimesTracker
{ 
    public static int Rest = 3;
}

但是,由于 C# 不允许重新加载程序集,除非卸载整个 AppDomain,因此使用/加载更新的程序集将是一个挑战。

【讨论】:

  • 谢谢,我知道使用Mono,很抱歉我没有提到,无论如何我会等待3天如果没有人提供答案,我会将这个答案标记为已接受的答案。跨度>
  • 但是对于用例,我在用户将运行程序的许可系统中使用它:程序第一次将计算 Bios 和主板的 GUID,然后将调用远程服务器以获取多部分license [PKey, LInfo] 并且这种行为只允许一次,所以当用户再次运行这个应用程序时,即使用户更改系统,我也可以限制运行时间/次数,我真的不需要这种类型的许可,但我想它的可能性并尝试在我的空闲时间实现它,所以我可以坚持我的实现
  • 鉴于用户已经需要调用远程服务器,我认为使用后端服务/数据库会比这种“本地”计数器更好,以防它受到损害。或者如果它必须是本地的,可以考虑使用 Windows 注册表?
猜你喜欢
  • 2015-05-21
  • 2021-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-21
  • 1970-01-01
  • 2013-12-14
  • 1970-01-01
相关资源
最近更新 更多