【问题标题】:Where can I find a list of escaped characters in MSIL string constants?在哪里可以找到 MSIL 字符串常量中的转义字符列表?
【发布时间】:2012-02-03 02:10:02
【问题描述】:

我编写了一个程序(用 C# 编写),它读取和操作从 C# 程序生成的 MSIL 程序。我曾经错误地认为 MSIL 字符串常量的语法规则和 C# 的语法规则是一样的,但后来我遇到了以下情况:

这个 C# 语句

string s = "Do you wish to send anyway?";

被编译成(在其他 MSIL 语句中)这个

IL_0128:  ldstr      "Do you wish to send anyway\?"

我没想到用来转义问号的反斜杠。现在我显然可以将这个反斜杠作为我处理的一部分考虑在内,但主要是出于好奇,我想知道当 C# 编译器将 C# 常量字符串转换为 MSIL 常量字符串时,是否有一个列表在某处字符被转义。

谢谢。

【问题讨论】:

  • 为什么要使用 CIL 的字符串表示?或者二进制文件中也存在转义?
  • @svick:我编写了自己的混淆程序,它运行 ILDAsm.exe,操纵 MSIL 源,然后运行 ​​ILAsm.exe。我从未使用过 MSIL 二进制文件,但我认为那里没有转义,这真的没有意义。
  • 我认为直接使用二进制文件会更容易。您可以使用 Mono Cecil 库来做到这一点。
  • @RenniePet - 仅供参考,我现在已经回答了这个问题。

标签: c# .net cil


【解决方案1】:

更新

基于使用 C# 编译器 + ildasm.exe 进行的实验:可能没有转义字符列表的原因是因为太少了:正好 6 个。

来自 ildasm 生成的 IL,来自 Visual Studio 2010 编译的 C# 程序

  • IL 是严格的 ASCII
  • 三个传统的空白字符被转义
    • \t:0x09:(标签)
    • \n : 0x0A : (换行符)
    • \r : 0x0D : (回车)
  • 三个标点字符被转义:
    • \" : 0x22 : (双引号)
    • \? : 0x3F : (问号)
    • \\ : 0x5C : (反斜杠)
  • 文字字符串 0x20 - 0x7E 中仅包含以下字符(不包括三个标点字符)
  • 所有其他字符,包括 0x20 以下的 ASCII 控制字符以及从 0x7F 开始的所有字符,都将转换为字节数组。或者更确切地说,包含除上述 92 个文字和 6 个转义字符之外的任何字符的任何字符串都将转换为字节数组,其中字节是 UTF-16 字符串的 little-endian 字节。

示例 1: ASCII 高于 0x7E:一个简单的重音 é (U+00E9)

C#:"é""\u00E9" 变为(E9 字节出现first

ldstr      bytearray (E9 00 )

示例 2: UTF-16:求和符号 ∑ (U+2211)

C#:"∑""\u2211" 变为(11 字节位于 first

ldstr      bytearray (11 22 )

示例 3: UTF-32:Double-struck mathematical? (U+1D538)

C#:"?" 或 UTF-16 代理对 "\uD835\uDD38" 变为(字符内的字节颠倒,但整体顺序为双字节字符)

ldstr      bytearray (35 D8 38 DD )

示例 4: 字节数组转换是针对包含非 Ascii 字符的整个字符串

C#:"In the last decade, the German word \"über\" has come to be used frequently in colloquial English." 变为

ldstr      bytearray (49 00 6E 00 20 00 74 00 68 00 65 00 20 00 6C 00  
                      61 00 73 00 74 00 20 00 64 00 65 00 63 00 61 00  
                      64 00 65 00 2C 00 20 00 74 00 68 00 65 00 20 00  
                      47 00 65 00 72 00 6D 00 61 00 6E 00 20 00 77 00  
                      6F 00 72 00 64 00 20 00 22 00 FC 00 62 00 65 00  
                      72 00 22 00 20 00 68 00 61 00 73 00 20 00 63 00  
                      6F 00 6D 00 65 00 20 00 74 00 6F 00 20 00 62 00  
                      65 00 20 00 75 00 73 00 65 00 64 00 20 00 66 00  
                      72 00 65 00 71 00 75 00 65 00 6E 00 74 00 6C 00  
                      79 00 20 00 69 00 6E 00 20 00 63 00 6F 00 6C 00  
                      6C 00 6F 00 71 00 75 00 69 00 61 00 6C 00 20 00  
                      45 00 6E 00 67 00 6C 00 69 00 73 00 68 00 2E 00 )

直接,“你不能”(查找 MSIL 字符串转义列表),但这里有一些有用的花絮......

ECMA-335 包含 CIL 的严格定义,没有指定 QSTRING 文字中必须转义的字符,只是它们可以使用反斜杠 \ 字符进行转义。最重要的注意事项是:

  • Unicode 文字显示为八进制,而不是十六进制(即\042,而不是\u0022)。
  • 字符串可以使用\ 字符跨多行分布--见下文

唯一明确提及的转义符是制表符\t、换行符\n八进制 数字转义符。这对您来说有点烦人,因为 C# 没有八进制文字——您必须自己进行提取和转换,例如使用 Convert.ToInt32([string], 8) 方法。

除此之外,对于规范中描述的“假设的 IL 汇编器”,转义的选择是“特定于实现的”。因此,您的问题正确地询问了 MSIL 的规则,这是 Microsoft 对 CIL 的严格实施。据我所知,MS 没有记录他们选择的逃跑方式。至少询问 Mono 的人他们使用什么可能会有所帮助。除此之外,可能需要自己生成列表——编写一个程序,为每个字符 \u0000 声明一个字符串文字——不管怎样,看看编译后的 ldstr 语句是什么。如果我先得到它,我一定会发布我的结果。

补充说明:

要正确解析 *IL 字符串文字(称为 QSTRINGS 或 SQSTRINGS),您必须考虑的不仅仅是字符转义。以代码内字符串连接为例(这是 Partition II::5.2 中的逐字记录):

“+”运算符可用于连接字符串文字。这样,可以通过在每行上使用“+”和一个新字符串来将一个长字符串分成多行。另一种方法是使用“\”作为一行中的最后一个字符,在这种情况下,该字符及其后面的换行符不会输入到生成的字符串中。 “\”和下一行的第一个非空白字符之间的任何空白字符(空格、换行、回车和制表符)都将被忽略。 [注意:要在 QSTRING 中包含双引号字符,请使用八进制转义序列。尾注]

示例:下面的结果相当于“Hello World from CIL!”的字符串:

ldstr "Hello " + "World " + "from CIL!"

ldstr "Hello World\ 
       \040from CIL!"

【讨论】:

  • 不过,这似乎并没有回答关于转义的问题。
  • 感谢您的回答。我看过ECMA文档,很有意思。但是我没有看到任何需要甚至允许转义问号字符的东西。我想我应该接受它作为 Microsoft 解释/实现的怪癖,不要担心它。
  • @svick 你是对的,我知道。对于一个普通的旧评论来说,它似乎太大了。我不会期待“已回答问题”的检查,但我相信它仍然与 OP 的问题(以及未来的读者)直接相关和有帮助
  • @RenniePet 我刚刚做了一些重大修改。本身仍然不是答案,但希望对您有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-02
  • 1970-01-01
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 2020-07-22
相关资源
最近更新 更多