【问题标题】:Is it possible to get method address in C#? [closed]是否可以在 C# 中获取方法地址? [关闭]
【发布时间】:2021-02-18 12:46:16
【问题描述】:

我想获取方法地址,以便稍后在我的自定义作弊保护中使用它(我想检查方法是否被钩住)。我目前正在尝试通过使用来获取指针 typeof(MyClass).GetMethod("MyMethod").MethodHandle.GetFunctionPointer(); 但这并没有给我正确的偏移量。对不起,如果我的问题看起来很愚蠢,但我认为这是做我需要的唯一方法。谢谢。

UPD:我将我的偏移量与 Cheat Engine 的偏移量进行比较。我知道地址总是不同的

UPD 2:我想读取我自己的应用程序的内存,所以我可以检查它是否被更改(我只对非常重要的方法这样做)

【问题讨论】:

  • 简而言之,你不能因为 C# 方法没有单个内存地址(因为方法可以在解释模式下运行由 CLR 提供,它们可以在内存中有多个 JIT 表示,并且每个不同的泛型参数集都将存在泛型方法(和泛型类型的非泛型方法),依此类推。
  • “但这并没有给我正确的偏移量” - 相对于什么的偏移量?您如何确定地址是否“正确”?
  • 帮助页面上的备注说:The returned IntPtr is meant for internal runtime usage and should not be used to call the function from native code. Use Marshal.GetFunctionPointerForDelegate in order to get a function pointer that can be passed to native code.。因此,请尝试使用 Marshal.GetFunctionPointerForDelegate
  • 之后你想用那个“指针”做什么?我怀疑您是否真的需要方法的内存地址,而只是对它的 reference 以便稍后调用它。
  • “我想检查方法是否被钩住” - 我想你误解了钩子的工作原理。 “挂钩”是 Win32 特有的(例如,它是 Win32 的设计特性),但您不能在软件中“挂钩”任意方法/函数。

标签: c# function-pointers memory-address


【解决方案1】:

您有一个X/Y problem,由于您对 CLR 和托管代码在 Windows 台式计算机中的执行方式缺乏了解,情况变得更糟。我会详细说明。

我要获取方法地址

不是 AOT 编译为本机代码的计算机程序,例如用 C#/.NET、Java 和 JavaScript 编写的程序,将它们的函数/方法放在内存中的单个位置.

这是因为:

  • 函数可以通过解释器执行,通常称为字节码解释器或类似的。发生这种情况时,根本就没有函数的本机代码表示。
  • 当一个函数最终被 JIT 编译时,它可以存在于内存中的多个位置,因为 JIT 编译器可能会根据运行时行为多次重新编译一个函数(例如,JIT 可以重新编译一个函数并删除所有的if( x == null ) 检查它是否意识到在遵循热路径时它们是不必要的。
  • 当使用 monomorphicreified 泛型系统(用于 .NET,但不用于 Java)时,当存在任何泛型函数时(包括泛型类型的非泛型成员) 对于通用类型参数参数的每个不同组合,这些函数将在内存中实例化
    • 这是因为编译后的List<String>.Add(String s) 方法不能用于List<FileInfo>,更不用说List<Int32>(请记住,.NET 不使用类型擦除)。

简而言之:C# 方法没有单一地址。

稍后在我的自定义作弊保护中使用它

如果这对你来说是一个学习练习,那么继续前进 - 忽略那些仇恨者和他们的口号:“不要推出你自己的加密货币”、“在数学上不可能建立一个完美的 DRM 系统”和“建立您自己的作弊保护只是在欺骗自己”:执行傻瓜差事可能是了解为什么这是傻瓜差事的最佳方法 - 只是不要在生产中使用它所以您不会让其他用户面临风险。

我想检查方法是否被钩住

我认为你误解了钩子的工作原理。 “挂钩”是 Win32 特有的(例如,它是 Win32 的设计功能),但您不能在软件中“挂钩”任意方法/函数。

我目前正在尝试使用 typeof(MyClass).GetMethod("MyMethod").MethodHandle.GetFunctionPointer(); 获取指针,但这并没有给我正确的偏移量。

您应该阅读文档

The documentation for MethodHandle says(强调我的):

获取方法的内部元数据表示的句柄。

The documentation for GetFunctionPointer says(强调我的):

返回的IntPtr 用于内部运行时使用,不应用于从本机代码调用函数。使用Marshal.GetFunctionPointerForDelegate 以获得可以传递给本机代码的函数指针。

一般来说,如果某事说“内部”,您可能不应该使用它。

我知道 MethodHandleGetFunctionPointerForDelegate 的文档确实建议它返回一个 C 样式的函数指针,但就您而言,文档不正确。

(实际上,如果您在 MethodInfo 上调用 GetFunctionPointerForDelegate 以获得开放通用方法,您将获得 InvalidProgramException!- 根本没有记录,呃) .

UPD:我将我的偏移量与 Cheat Engine 的偏移量进行比较。我知道地址总是不同的

由于我已经讨论过的原因,Cheat Engine 不适用于非本地程序。

如果您在 Cheat Engine 的论坛中搜索“.NET”或“Java”,您会发现数百篇来自与您遇到相同问题的人的帖子。

UPD 2:我想读取我自己的应用程序的内存,所以我可以检查它是否被更改(我只对非常重要的方法这样做)

由于上述原因,您不能对任何非确定性 JIT 编译的软件执行此操作。

【讨论】:

  • “可能会根据运行时行为多次重新编译一个函数”——你有没有观察到这一点或者可以证明这一点?恕我直言,JIT 编译器需要向 JIT 编译代码添加检测,这会显着降低性能。到目前为止,我只观察到针对不同类型的泛型方法的重新编译。我仍在寻找一个例子来证明你的观点。
  • @ThomasWeller 我的言论是在“可能”中表达的 - 我知道目前(截至 2021 年第一季度)桌面 .NET Framework 4.8 CLR 从不解释代码,也从不重新编译代码 - 但是 .NET Compact Framework CLR(演变为 .NET Core)确实,根据 2009 年的回答:stackoverflow.com/questions/1255803/…
  • @ThomasWeller 此外,仅仅因为 CLR 现在可能不会做某事,因此其他运行时的 JIT(如 Java 的 HotSpot 和 Google Chrome 的V8) 最终可能会在未来出现在 .NET 的 JIT 中——事实上,我们在 .NET Core 的重新编译中看到了这一点(例如ICorProfilerInfo4::RequestReJIT
  • @ThomasWeller 实际上,似乎ICorProfilerInfo4::RequestReJIT 出现在 .NET Framework 4.5 中,所以如果我理解正确的话,那里确实存在 rejit 代码的能力。
  • 感谢您的信息。我很清楚你声明中的“可能”,我完全同意。在程序的正常运行时,我仍在寻找它的实际用途。 RequestReJIT 仅对作为开发期间使用的外部工具的分析器有意义。探查器将根据用户请求执行此操作,例如当开发人员标记要分析的方法时。然后分析器会收到一个回调,它可以在其中注入不同的 IL 代码,包括检测代码。所以这在应用程序的正常运行期间不会发生。
猜你喜欢
  • 2021-01-15
  • 2011-07-19
  • 1970-01-01
  • 1970-01-01
  • 2010-10-25
  • 2011-07-31
  • 2012-03-25
  • 2011-06-06
  • 1970-01-01
相关资源
最近更新 更多