在 C (GCC) 中的宽字符字符串上调用 goto

Calling goto on a wide-char string in C (GCC)

我找到了这段代码:

goto*&L"\xfeeb";

它导致程序永远挂起,显然是通过调用 x64 指令 0xEB0xFE(按此顺序,由于 x64 的小字节序)。 0xEB 是根据 x86 Opcode and Instruction Reference.

的 JMP

我明白代码做了什么,它相当于一个函数运行指令0xEB 0xFE,所以它也可以写成int (*foo)() = L"\xfeeb"; foo();,或者如果我们想要真正混淆,((int(*)())L"\xfeeb")();。这是因为字符串在 Linux.

上默认标记为可执行

不过,goto真的很严格。我完全不明白为什么 goto*&L"\xfeeb"; 有效,或者疯狂的指针魔术 *& 在做什么,或者为什么宽标记 L 是必要的。有人可以解释一下吗?

如果我敢猜测,写代码的人在滥用 GCC's Labels as Values extension。此功能旨在制作跳转表或可移植 JIT 编译器或其他 switch...case 太慢的东西。

C 让你 goto 一个标签。

label1:
...
goto label1;

GCC 允许您使用 && 运算符获取标签的地址。要跳转到地址 foo,您只需 goto *foo;.

label1:
void *ptr = &&label1;
...
goto *ptr;

回顾一下,C 标准指定 goto 语句后面的参数是 label 类型的标记。 GCC 添加了一个扩展,其中 goto 语句后面的参数也可以是一个 指向可执行代码 .

的指针

因此,您可以 goto 任何有指针的内存。在 Linux 上,这包括字符串文字。

goto *&"\xe8\r[=12=][=12=][=12=]Hello, World!Yj[j\rZjX\xcd\x80,\f\xcd\x80";

Try it online!

L"\xfeeb" 是一个 宽字符串文字 ,由 wchar_t 类型的字符组成,而不是 char。写成老式的字符串文字,它将是 "\xeb\xfe"。我怀疑你的字符串文字中的 L 是为了 MacGuffin.