您拥有的字符串文字:
"\b3\bc\77\7\de\ed\44\93\75\ce\c0\9\19\59\c8\f\be\c6\30\6"
将产生根据C89的未定义行为(不确定 C89 的源是否可以信任,但我在下面的观点仍然成立)和根据C11标准的实现定义的行为。特别是,\d
, \e
, \9
,\c
是标准中未定义的转义序列。gcc
不会抱怨\e
,因为它是代表 ESC 的 GNU 扩展。
由于存在实现定义的行为,因此我们有必要知道您使用的是什么编译器,因为结果可能会有所不同。
另一件事是,您没有清楚地表明您在编译后知道字符串的内容。(更清晰的显示方法是包含字符串在内存中的样子的十六进制转储,并显示您如何知道转义序列)。
这就是编译器识别看起来像十六进制字符串的方式:
String: \b 3 \b c \77 \7 \d e \e d \44 \9 3 \75 \c e \c 0 \9 \1 9 \5 9 \c 8 \f \b e \c 6 \20 \6
Char: \b 3 \b c \77 \7 d e \e d \44 \9 3 \75 c e c 0 9 \1 9 \5 9 c 8 \f \b e c 6 \20 \6
Hex: 08 33 08 63 3f 07 64 65 1b 64 24 39 33 3d 63 65 63 30 39 01 39 05 39 63 38 0c 08 65 63 36 18 06 00
拐弯抹角就够了。假设您正在使用gcc
编译代码(忽略警告)。运行代码时,整个代码char[]
使用fwrite
. 我还假设源代码中只使用小写字符。
您应该将所有可能的转义序列\xy
(看起来像 2 位十六进制数)映射到 1 或 2 个字节的序列。它们的数量并不多,您可以编写一个程序来模拟编译器的行为:
- 如果
x
是a
, b
, f
(其他转义序列,如\n
不是十六进制数字)和e
(由于 GNU 扩展)中的任何一个。它被映射到特殊字符。
- (如果您在源代码中使用大写字符,请注意
\E
映射到 ESC)
- 如果
xy
形成一个有效的八进制序列。它映射到具有相应值的字符。
- 如果
x
形成一个有效的八进制序列。它映射到具有相应值的字符。
- 否则,
x
保持不变。
- 如果
y
不消耗,则y
保持不变。
请注意,实际char
可能来自 2 种不同的方式。例如,\f
and\14
将映射到相同的char
. 在这种情况下,可能无法取回源中的字符串。您最多可以猜测源中的字符串是什么。
以您的字符串为例,开头,08
并且33
可以来自\b3
,但也可以来自\10\63
。
使用映射生成,有些情况下映射很清楚:大于3f
不能来自八进制转义序列的十六进制,并且必须来自对原始字符串中字符的直接解释。由此可知,如果e
遇到,一定是looks-like-hex序列中的第二个字符。
您可以使用地图作为指导,并将模拟作为检查地图是否会返回 ASCII 码的方法。在对源代码中声明的字符串一无所知的情况下,您最多可以得出源代码中原始(损坏)字符串的候选列表。如果您至少知道源代码中字符串的长度,则可以减少候选列表的大小。