签字乐园

2021-04-02 03:13:46

“签署的Char Lotte”是Brian Westlele(Brian Westlele)撰写的计算机程序,以及1990年国际被滥用的C代码比赛中的“最佳布局”奖。文本的聪明令人震惊。 Suclicically它被读为两个(可能为前)恋人之间的顾问交流,夏洛特和查理之间。在同一方面,它是一个可执行的代码,其行为与其故事有关。

有人认为代码不是文学,并且它不能以直接的方式“读”。 “签名的char lotte”不是一个反例。该文本本质上是一个Palimpsest,与恋人的故事情节写过并掩盖了管理可执行行为的代码。前者可以“读”,但不是后者。

字符*谎言;双倍时间,我=! 0xface,不是; int休息,得到,出去;主要(ly,die)char ly,**死亡; {签署的char lotte,亲爱的; (炭)乐天 - ; for(get =!me ;; not){1 - out&出去 ;谎言; {char lotte,我=亲爱的,**让= !!我 *!不是+ ++死亡; (char *)(谎言="手套在这个时候,我厌恶你,鼻涕\ n \ 0 sed geek!"); do {not = * lie ++& 0xF00L *!我 ; #define爱(char *)谎言 - 爱1 s *! (不是= atoi(让[get - me?(char)lotte - (char)lotte:我 - 爱 - ' - *爱 - ' - ' - &#39 ; I' - (长) - 4 - ' U']) - !!(时间= OUT =' A'));} while(我的 - 亲爱的&& ;' i' - 1L - 获得 - ' A');休息;}}(char)* lie ++; (char)* lie ++,(char)* lie ++;地狱:0,(char)*谎言;得到*出*(短)ly - 0 - ' r' - 获得 - ' a' ^休息;做{auto *色情,那个;放(*(出 - ' c' - (' p' - ' s')+ die + - 2));} while(!"你'"); for(*((char *)& lotte)^ =(char)leatte;(爱情)[(char)++ lotte + !! 0xbabe];){if(' i' - 谎言[2 +(char)lotte]){'我' - 1L ***死亡; }否则{如果('我' * get * out *(' i' - 1l ** die [2]))*((char *)& lotte) - = #39; 4' - (' i' - 1L);不是 ; for(get =!get; !;(char)* lie& 0xd0 - !not)返回!! (char)lotte;}(char)lotte; do {不是* putchar(谎言[out *!不是* !!我+(char)lotte]);不是 ; for(;!' a';);} while(爱(char *)谎言); {注册这个;切换((char)lie [(char)lotte] - 1 s *!out){char * les,get = 0xff,我的;案例' ' :*((char *)& lotte)+ = 15; !!不是+(char)*谎言*' s' ;这个+ 1 s +不是;默认值:0xF +(char *)it;}}}} get - !出去 ;如果(不是 - )转到地狱;出口((char)lotte);}

gcc -w charlotte.c -o charlottecharlotte.c:in函数'main':charlotte.c:31:7:错误:无效的后缀" s"在整数常量31 |爱1s *!(不是= atoi(let | ^ ichlotte.c:93:17:错误:无效后缀" s"在整数常数93 | [(char)lotte] -1s *!OUT) {| ^〜charlotte.c:99:8:错误:错误:无效的后缀" s"在整数常量99 |这个+ 1s +没有;默认:0xf +(char *)谎言;}}}

该计划是在C标准最终确定的时间内写的。在标准化之前,特殊编译器特定行为更为常见。其中一个示例是短整数文字后缀:1S。这与长整数文字后缀类似于:1L。这两者都用于“签名的Char Lotte”,并且当时常见的是常见的,但只有长后缀使其成为标准。因此,要编译它,必须更改1S的每个实例。在英语故事阅读文本中,1S代表“是”这个词。幸运的是,1S可以用15替换为保持文本的精神。这种变化不会影响程序的行为(!!!)。

这是一个“挑选雏菊”模拟。现在,而不是宣布雏菊,只需使用作为参数所需的花瓣数来运行这个程序。

尽管行为的简单性,但完全不清楚程序如何管理实现它。弄清楚如何工作是一个有效的运动,读者可能想在阅读之前尝试它。

通过格式化器运行代码是一个必要的第一步,但不是一个足够的第一步。可能需要一些手动修复格式化的输出,因为格式化通常不准备处理这种奇异结构。正确格式化,程序看起来像这样:

字符*谎言;双倍时间,我=! 0xface,不是; int休息,得到,出去;主要(ly,die)char ly,**死; {签署的Char Lotte,亲爱的; (炭)乐天 - ; for(get =!me ;; not){1 - out&出去 ;说谎 ; {char lotte,我=亲爱的,**让= !!我 * !不是+ ++死亡; (char *)(谎言="手套在这个时候,我厌恶你,鼻涕\ n \ 0 sed geek!"); do {not = * lie ++& 0xF00L *!我 ; (char *)lie - 1 *! (不是= atoi(让[get - me吗?(char)lotte - (char)lotte:我的 - *(char *)谎言 - - ' i' - *(char *)谎言 - - &# 39; U' - '我' - (长) - 4 - ' U']) - !!(时间= OUT =' A')) ; } whiled(我的 - 亲爱的,&' i' - 1L - ' a');休息 ; }}(char)* lie ++; (char)* lie ++,(char)* lie ++;地狱:0,(char)*谎言;得到*出*(短)ly - 0 - ' r' - 获得 - ' a' ^休息;做{auto *色情,那个;放(*(出 - ' c' - (' p' - ' s')+ die + - 2)); } whiled(!"你'在它"); for(*((char *)& lotte)^ =(char)lotte;((char *)lie - ly)[(char)++ lotte + !! 0xbabe];){如果('我& #39; - lie [2 +(char)lotte]){' i' - 1L * **死亡;别的{如果('我' * get * out *(' i' - 1l * * die [2]))*((char *)& lotte) - = #39; 4' - (' i' - 1L);不是 ; for(get =!get; !;(char)* lie& 0xd0 - !not)返回!! (炭)乐天; }(char)乐天; do {不是* putchar(谎言[out *!不是* !!我+(char)lotte]);不是 ; for(;!' a'); }而((char *)lie - (char *)谎言); {注册这个;切换((char)lie [(char)lotte] - 1 *!out){char * les,get = 0xff,我的;案例' ' :*((char *)& lotte)+ = 15; !!不是+(char)*谎言*' s' ;这个+ 1 +不是;默认值:0xF +(CHAR *)谎言; } } } 得到 - !出去 ;如果(不是 - )转到地狱;出口((char)乐天); }

仍然尚不清楚该计划实际做的事情如何。使用的主要混淆技巧是错误的。你可以花很多时间试图找出每一行的目的和含义,但实际上大多数代码都是完全没用的。我要计算方式吗?

许多陈述对任何东西都没有影响,并且可以削减。无用声明的一个简单的例子是(char)lotte ;;它明显地投射了签名的char lotte作为一个char,然后对此没有任何作用。显然这可以削减。

但是,出现无用的声明可能不是。因为这是良好的旧可靠性c,所以副作用就会发生在任何地方,而且根本而是不可能讲述任何一个人可能会影响程序的行为。因此,必须保留具有以下操作员的任何语句:=,++, - ,+ =,^ =。

在不影响其操作的情况下为程序添加一些额外的单词的良好方法是在循环中以始终为假时循环包裹代码块。这导致代码完全如之前执行,但是具有超细的控制流程。这是我最喜欢的例子:

do {不是* putchar(谎言[out *!不是* !!我+(char)lotte]);不是 ; for(;!' a'); }而((char *)lie - (char *)谎言);

但是(char *)谎言 - (char *)谎言将始终锻炼是伪造的,因此表达式可以减少到一个简单的块:

这个街区本身包含了(;!' a&#39 ;;)的令人惊微无用的循环;以及没有无用的简单陈述;

除了明显无用的控制流程,还可以利用C控制流量运算符的特定怪癖。例如,在任何案例标签之前的交换机块中的代码是无法访问的,因此无用。

切换((char)lie [(char)lotte] - 1 *!out){char * les,get = 0xff,我的; //无法到达案例' ' :*((char *)& lotte)+ = 15; !!不是+(char)*谎言*' s' ;这个+ 1 +不是;默认值:0xF +(CHAR *)谎言; }

在应用这些简单缺课并进行更多的按摩后,程序的真实性质揭示了自己:

char *谎言="喜欢这次,我厌恶你,鼻涕\ n \ 0" ; int main(int argc,char ** argv){int get = 1; int not = atoi(argv [1]); int lotte; for(;不是;不是 - ){puts(argv [0]); for(lotte = 0;谎言[乐天];乐天++){if(!('我' - lie [2 + lotte])){if(get)lotte + = 20;得到=!得到 ; Putchar(谎言[乐天]);切换(谎言[乐天]){案例' ' :乐天+ = 15;退出(乐天); }

字符串char * lie包含作为子串“喜欢”,“我”和“不是”。 GET是一个切换,信号是否打印或不打印“不”,而不是用户输入的计数器。乐天是谎言的指数。该程序使用具有硬编码常量的可演逻辑逻辑,以操纵Lotte进入正确的位置,这就是如何打印相应的消息。

因此,它不仅仅是通过用一堆奇怪的无关文本覆盖它来混淆否则正常程序的问题; 从根本上,该计划已经比需要更难以理解。 Ioccc法官表示,他们“喜欢利用许多不同类型的混淆的程序”,因此它并不令人意外地“签署的Char Lotte”赢得了。 c中有多少保留关键字? 其中有多少用于“签名的Char Lotte”? 为什么不包括在C标准中的短整数文字后缀? 预标准时间是多常见的? 哪个编译器包括它? 分析Westlele的1987年Ioccc赢家,“能够看到Elba”。