【问题标题】:Unusual C# operators in decompiled source...?反编译源代码中不寻常的 C# 运算符...?
【发布时间】:2016-02-17 18:37:15
【问题描述】:

我刚刚使用 DotPeek 反编译了一些第 3 方源来调试问题。输出代码包含一些不寻常的运算符,AFAIK 不是有效的 C#,所以我想知道它们是什么意思......

摘录看起来像(包括 Dotpeek cmets,因为它们可能是相关的);

protected internal void DoReceive(ref byte[] Buffer, int MaxSize, out int Written)
{
    Written = 0;
    .
    .
    .        
    // ISSUE: explicit reference operation
    // ISSUE: variable of a reference type
    int& local = @Written;
    int num = SomeMethod();
    .
    .
    .
    // ISSUE: explicit reference operation
    ^local = num;
}

那么,那里有 3 个不寻常的运算符...int& = @Written 似乎正在分配一个指向以 @ 字符毫无意义地命名的变量的指针?

但是^local = num; 是什么???

好的,这是来自 ILSpy 的等效 sn-p,这更有意义,我猜 C# 的反编译没有产生有效的等效?

'C#'

 int& local = @Written;
 byte[] numArray2 = this.FInSpool;
 int num = (int) __Global.Min(numArray2 == null ? 0L : (long) numArray2.Length, (long) MaxSize);
 ^local = num;

IL

 byte[] expr_22 = this.FInSpool;
 Written = (int)__Global.Min((long)((expr_22 == null) ? 0 : expr_22.Length), (long)MaxSize);

所以,我猜“C#”不是很有效?该 IL 将是有效的 C#,不确定为什么 DotPeek 会产生它所做的输出。也许我会为这个坚持使用 ILSpy...?

【问题讨论】:

  • 你确定你把它反编译成C#了吗?那些是 C++/CLI 操作符。
  • 我的猜测是 ^localWritten 是一个输出参数这一事实有关。它似乎正在取消引用指针。
  • 我认为 int& 不是 C# 中的有效术语?如果可能的话,你能粘贴原始的 MSIL 吗? C# 只公开了 MSIL 的一个子集,因此它要么被错误地反编译,要么源语言不是 C#。
  • 您能否向我们展示 a) IL 和 b) C# 中的 Reflector 输出以及 c) 原始源代码? MSIL 经常在内部使用(安全!)托管指针。你正在看到他们的表现。
  • @usr -> b) 不抱歉,没有 Reflector 的许可证。 c) 不,它是非开源的第 3 方代码。将添加 IL,它确实使事情更清晰。

标签: c# syntax dotpeek


【解决方案1】:

如果您查看原始 IL(来自 ildasm,而不是通过 IL Spy 获得的 C# 等效项),这可能有助于您了解反编译器试图表达的内容。 'Out' 参数使用(托管)类型引用表示,该引用未显式公开为 C# 中的类型。这种类型的“实例”通常只能作为参数传递给接受类型化引用(“ref”或“out”参数)的方法。有关详细信息,请参阅OpCodes.Mkrefany

dotPeek 抱怨的是,这个“输出”类型的引用是从参数存储到局部变量槽中的,然后稍后通过局部槽写入。 '@' 和 '^' 是占位符,用于指示反编译器检测到的这种意外行为(ISSUE cmets 所描述的行为。)

代码可能是从 C++/CLI 编译的,因此 IL 看起来与典型的 C# 编译器输出不同。这也可能是某种程度的轻微混淆来混淆反编译器(尽管我不这么认为。)我认为这在功能上与将引用从其参数直接加载到操作堆栈上没有任何不同(避免使用一个局部变量槽),但我可能是错的。

【讨论】:

  • 这似乎适合这种情况。谢谢你的解释。
  • 其实我说错了一点;您示例中的特定方法可能不是使用类型引用(通过 mkrefany),而是使用地址引用(即类型 int&)。也就是说,基本问题仍然相同;反编译器不希望 int& 在使用之前存储到局部变量中。
【解决方案2】:

在名称前放置@ 允许您为变量使用保留名称,例如,如果我想要一个名为return 的变量,我需要这样做。

public int Weird()
{
    int @return = 0;
    return @return;
}

更多详情请见this SO question


在名字前加上^...嗯,不知道。 (会随着我的研究而更新我可以找到任何关于 ^ 不被用作 XOR 时的含义的信息)

【讨论】:

  • 谢谢,了解@的用法,我想在这种情况下没有必要,因为'Written'不是保留字?
  • written 不是从 I could tell 中保留的,但它可以是那些上下文相关的关键字 like get or set 之一。
  • 或者,标识符名称中可能有文字 @ 符号。它不是合法的 C#,但是,例如,C# 编译器本身在 lambda 函数的名称中使用了奇怪的字符,例如 <>。或者反编译器可能使用 @ 来实现某些特殊目的。
  • 在这种情况下,@ 正在生成,因为反编译器对ref locals 感到困惑并且与保留字无关。 This SO answer 解释的很好。
【解决方案3】:

这显然是一个反编译问题。由于支持广泛的语言集,任何特定语言的反编译器可能并不总能找到精确匹配,但仍会尝试产生一些输出。反编译器可能会尝试产生一些等效的输出,就像在这种情况下可能是:

protected internal void DoReceive(ref byte[] Buffer, int MaxSize, out int Written)
{
    Written = 0;
    .        
    int num = SomeMethod();
    .
    Written = num;
}

但它真的应该这样做吗?在这种情况下,反编译器实际上为您提供了一个提示,因此您可以决定这对您的特定情况是否重要,因为可能会有一些副作用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多