邪修 C 语言,10 段“直接劝退”的黑魔法代码
——仅供技术猎奇,切勿上生产!
编译器警告、UB(未定义行为)、UBSAN 狂怒,请自备防护眼镜 🧨
1️⃣ 一行 main
都没有的程序
#include <stdio.h>
void _start(){puts("邪修C");}
链接时加
-nostartfiles
,直接跳过 CRT,Linux 裸奔。
2️⃣ Duff’s Device —— switch 跳进 while
void evil_memcpy(char *dst, char *src, size_t n){
switch(n & 7){
case 0: while(n--){ *dst++ = *src++;
case 7: *dst++ = *src++;
case 6: *dst++ = *src++;
case 5: *dst++ = *src++;
case 4: *dst++ = *src++;
case 3: *dst++ = *src++;
case 2: *dst++ = *src++;
case 1: *dst++ = *src++;
}break;
}
}
循环展开 + switch 套 while,上古时代“手动 SIMD”。
3️⃣ 宏黑魔法:让 if
变成代码块
#define evil_if(x) if(x);else
evil_if(0)
puts("你猜我会打印吗?");
宏定义末尾的分号把
else
直接悬空,编译器当场裂开。
4️⃣ 指针运算玩出花
int a[5] = {1,2,3,4,5};
printf("%d\n", 3[a]); // 输出 4
a[3]
与3[a]
等价,编译器:你们开心就好。
5️⃣ 结构体“零长度数组”末班车
struct msg {
int len;
char data[0]; // 邪修柔性数组
};
struct msg *m = malloc(sizeof *m + 100);
strcpy(m->data, "hello");
0 长度数组是 GNU 私货,标准 C99 请用
char data[]
。
6️⃣ 位域重叠 + 联合体重写
union {
struct { unsigned a:4, b:4; } nibble;
unsigned char byte;
} u = {.byte = 0xAB};
printf("%x %x\n", u.nibble.a, u.nibble.b); // 平台决定大小端
一步踏进 未定义行为 的大坑。
7️⃣ 自修改机器码(mmap 版)
#include <sys/mman.h>
char *code = mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
unsigned char payload[] = {0x48,0xC7,0xC0,0x01,0x00,0x00,0x00,0xC3}; // x86-64: mov rax,1; ret
memcpy(code, payload, sizeof payload);
int (*ret1)(void) = (int (*)(void))code;
printf("ret=%d\n", ret1());
运行时生成 shellcode,杀毒软件直接报警。
8️⃣ 多重 sizeof
套娃
printf("%zu\n", sizeof sizeof sizeof "abc"); // 输出 8(64 位指针)
连续
sizeof
把任何表达式剥成size_t
,毫无营养。
9️⃣ 逗号运算符“一行流”
int x = (printf("a\n"), printf("b\n"), 3);
printf("%d\n", x); // 依次打印 a b 3
把副作用写进表达式,debug 时想打人。
🔟 GOTO 地狱 + 标签运算
int main(){
static const void *tab[] = {&&l0, &&l1, &&l2};
int i = 1;
goto *tab[i];
l0: puts("zero"); return 0;
l1: puts("one"); return 0;
l2: puts("two"); return 0;
}
用 计算型 goto(GNU C 扩展)手写跳转表。
邪修口诀
“内存随便指,宏里藏杀机,UB 当特性,编译器哭泣。”