如何去混淆ctk.c code 2001年的IOCCC的赢家? [英] How to de-obfuscate the ctk.c code the winner of 2001's IOCCC?

查看:125
本文介绍了如何去混淆ctk.c code 2001年的IOCCC的赢家?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我见过 ctk.c 混淆code,但我怎样才能开始去混淆呢?

 的#include<&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&; SYS / time.h中>
#包括LT&;&signal.h中GT;
的#define M(B)A = B,Z = *一个;而(* ++一){Y = *一*一= Z; Z = Y;}
#定义H(U)G = U< 3;;的printf(\\ E [%UQ,L [U])
#定义C(N,S)情况下,N:S;继续
炭X [] =((((((((((((((((((((((,W [] =
\\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ B;字符 - [R [ ] = {} 92,124,47,L [] = {2,3,1
,0};字符* T [] = {|,|,%\\\\ | /%,%%%,};焦炭D = 1,P = 40,O = 40中,k = 0,*一个,Y,Z,G =
-1,G,X,** P =& T公司[4],F = 0; unsigned int类型S = 0;无效U(int i)以{INT N;的printf(
\\ 233;%uH容\\ 233L%C \\ 233;%uH容%C \\ 233;%uH容%S \\ 23322;%uH容@ \\ 23323;%uH容\\ n,* X-* W,R [D] * X + *宽
河并[d]中,X,* P,P + = k时,O);若(绝对(PX [21])GT; = W [21])的出口(0);!如果(G = G){结构itimerval T =
{0,0,0,0}; G + =((克下; G)&所述;&所述; 1)-1; t.it_interval.tv_usec = t.it_value.tv_usec = 72000 /((g取代;>
3)+1); setitimer函数(0,& T公司,0); F&放大器;&放大器;的printf(\\ E [10;%U],G + 24);} F&放大器;&放大器;的putchar(7); S + =(9-W [21]
)*((G>> 3)+1);○= P,M(X);米(宽);(N = RAND())及255 || - *宽|| ++ *宽;如果((** P放大器;!&放大器,P + || N'放大器; 7936)){
while(abs((X=rand()%76)-*x+2)-*w<6);++X;P=T;}(n=rand()&31)<3&&(d=n);!d&&--*x<=
* W&放大器;及(++ * X,+ D)||ð== 2和;&放大器; ++ * X + *并且R w 79&放大器;及( - * X, - 四);信号(I, U);}无效E(){信号(14,
SIG_IGN);输出(\\ E [0Q \\ ecScore:%U \\ N,S);系统(stty的回声-cbreak);} INT主要
(INT C,焦炭** V){的atexit(E);(C 2 || * V [1] = 113!)及及(F =(C = *(INT *)的getenv(TERM ))==(
INT)0x756E696C ||Ç==(INT)0x6C696E75);函数srand(GETPID());系统(stty的-echo CBREAK
); H(0); U(14)为(;;)开关(getchar函数()){案件113:返回0;案件91:98的情况下:C(44,K
= -1);壳体32:壳体110:C(46,K = 0);壳体93:壳体109:C(47中,k = 1); C(49,H(0)); C(50中,h (1
)); C(51,H(2)); C(52,H(3));}}

http://www.ioccc.org/2001/ctk.hint


 这是一个基于苹果游戏] [打印店伴侣复活节
鸡蛋名为DRIVER,其中的目标是尽可能快地开车
你可以下来而不运行脱长期曲折的公路
路。使用',。/,[]或国行向左走,挺直,
正确的分别。用1234来切换齿轮。 'Q'退出。该
快你去的路,更多的点,你越薄
得到。大多数的混淆是无意义的if语句
除其他事项外。它最适合在Linux控制台上:您
让发动机声音(!)和*锁定键盘灯告诉你
你在(无亮= 4)什么档。在'Q'的说法(不
龙头' - ')将寂静的声音。它不会在终端工作
比80x24小,但它工作正常与更多的(尝试在一个
xterm里使用了不可读字体和窗口最大化
垂直!)。



解决方案

第1步

使用:

  SED -e'/#包括/ D'ctk.c | GCC -E  -  | SED -e是/; /; \\ N / G'-e的/} /} \\ N / G'-e'/ ^#/ D'|缩进

我能够生成以下输出,虽然不是十全十美似乎已经是可读的好多了:

 字符X [] =((((((((((((((((((((((W [] =
  \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ B;
焦 - [R [] = {92,124,47}湖[] =
{
2,3,1,0};
字符* T [] = {|,|,%\\\\ | /%,%%%,};
炭D = 1,P = 40,O = 40,K = 0,*一个,Y,Z,G = -1,G,X,** p =&amp; T公司[4]中,f = 0;
unsigned int类型S = 0;
空虚
U(int i)以
{
  INT N;
  的printf(\\ 233;
%uH容\\ 233L%C \\ 233;
%uH容%C \\ 233;
%uH容%S \\ 23322;
%uH容@ \\ 23323;
%uH容\\ n,* X - * W,R [D] * X + * W,R [D] X,* P,P + = K,O);
  如果(ABS(P - X [21])GT; = W [21])
    出口(0);
  如果(G!= G)
    {
      结构itimerval T = {0,0,0,0}
      ;
      G + =((G&LT; G)&LT;&LT; 1) - 1;
      t.it_interval.tv_usec = t.it_value.tv_usec = 72000 /((g取代;→3)+ 1);
      setitimer函数(0,&amp; T公司,0);
      F&功放;&安培;的printf(\\ E [10;
%U],G + 24);
    }
  F&功放;&安培;的putchar(7);
  S + =(10 - W [21])*((g取代;→3)+ 1);
  O =磷;
  A = X;
  Z = *一个;
  而(* ++一)
    {
      Y = *一个;
      *一= Z;
      Z =ÿ;
    }
  ;
  一= W;
  Z = *一个;
  而(* ++一)
    {
      Y = *一个;
      *一= Z;
      Z =ÿ;
    }
  ;
  (N = RAND())及255 || - *宽|| ++ *宽;
  如果(!(** P放大器;&放大器,P + || N'放大器; 7936))
    {
      而(ABS((X =兰特()%76) - * X + 2) - * W&LT; 6);
      ++ X;
      P =笔;
    }
  (N = RAND()及31); 3和;&放大器; (D = N);
  !D&放大器;&安培; - * X&LT; = * W&放大器;&安培; (++ * X,++ D)|| ð== 2和;&放大器; ++ * X + * W&GT; 79安培;&安培; ( - * X,--D);
  信号(I,U);
}空虚
E()
{
  信号(14,SIG_IGN);
  的printf(\\ E [0Q \\ ecScore:%U \\ N,S);
  系统(stty的回声-cbreak);
}INT主(INT C,焦炭** V)
{
  的atexit(E);
  (℃下!2 || * V [1] = 113)
    &功放;&安培; (F =(C = *为(int *)的getenv(期限))==(INT)0x756E696C
    || Ç==(INT)0x6C696E75);
  函数srand(GETPID());
  系统(stty的-echo CBREAK);
  G = 0℃;&下; 3;
  的printf(\\ E [%UQL [0]);
  U(14);
  为(;;)
    开关(的getchar())
      {
      案例113:
    返回0;
      案例91:
      案例98:
      案例44:
    K = -1;
    继续;
      案例32:
      案例110:
      案例46:
    K = 0;
    继续;
      案例93:
      案例109:
      案例47:
    K = 1;
    继续;
      案例49:
    G = 0℃;&下; 3;
    的printf(\\ E [%UQL [0]);
    继续;
      案例50:
    G = 1&LT;&LT; 3;
    的printf(\\ E [%UQ,L [1]);
    继续;
      案例51:
    G = 2';&下; 3;
    的printf(\\ E [%UQL [2]);
    继续;
      案例52:
    G = 3';&下; 3;
    的printf(\\ E [%UQ,L [3]);
    继续;
      }
}

...现在?

我不认为还有更多的自动化流程将能在这一点上的术语更可读或执行少可读性从现在开始可能取决于读者的具体preferences。

一步,可以进行被去除字符串转义序列,并分别地方安置他们。事实证明,整个

 字符L [] = {2,3,1,0}

有没有别的目的而在主循环的转义序列来被利用:

 的printf(\\ E [%UQL [0]);

和等。仰望它们的含义:

  ESC [0问:清除所有LED
ESC [1,问:设置滚动锁定LED
ESC [2问:设置的Num Lock指示灯
ESC [3 Q:设置Caps Lock LED指示灯

根据口味您可能希望与宏或函数调用更有意义的你喜欢 clear_all_LEDs 等交换。

我强烈怀疑一台机器将在此被简化达成一致。事实证明,整个主循环只是似乎与由用户输入的键来工作,所以可能转弯数目成其相应的字符可能在更换增加可读性,如:

 案例113:
  返回0;
案例91:
案例98:
案例44:
  K = -1;
// ...
案例49:
  G = 0℃;&下; 3;
  的printf(\\ E [%UQL [0]);

喜欢的东西:

 情况下,Q:
  返回0;
案件 '[':
案例'B':
案件 ',':
  K = -1;
// ...
情况1':
  G = 0℃;&下; 3;
  set_Num_Lock_LED();

噢 - 而我们则是在它已经为什么不是我们想要的名字从这个比较奇怪更改为齿轮。我再次强烈怀疑一个自动化的过程会发现从重命名为齿轮任何比它重命名为蝴蝶。嗯,也许它甚至不是。

虽然美​​化的名字也许这个函数通过引用一个 U 是另一位候选人:

  U(14);

一个更有意义的名称更新可能。正如我们已经包含&LT; signal.h中&GT; 我们为什么不反混淆code进一步通过替换 14 SIGALRM 是这样的:

  upadate(SIGALRM);

正如你所看到的deobfuscating在这里需要确切的相对的,以前所​​采取的步骤。更换与该时间的宏的扩展。如何将一台机器试图决定哪一个更有用?

另外一个点,我们可能要替换别的东西裸露的数量是这一个在更新功能:

  F&放大器;&安培;的putchar(7);

为什么不替换 7 \\ A ,因为它会变成是到底是相同的。也许我们应该即使有更多的东西有意义的变化裸˚F

我再次投票agains 蝴蝶却宁愿喜欢叫它 play_sound

 如果(play_sound)
   的putchar('\\一个');

可能是我们正在寻找更可读的版本。当然,我们不应该忘记其他所有地点,以取代F。

:我们的主要功能beeing这样的罪魁祸首开始了一个正确的

圣混乱

  INT主(INT C,焦炭** V)
{
  的atexit(E);
  (℃下!2 || * V [1] = 113)
    &功放;&安培; (F =(C = *为(int *)的getenv(期限))==(INT)0x756E696C
    || Ç==(INT)0x6C696E75);

一边兴致勃勃地重命名˚F play_sound 电子到 - 不,还没有蝴蝶,这一次,我会宁愿把它称为: - 结束我们发现该函数签名似乎看起来有点怪命名约定的条款:的 ARGC 而不是 C argv的而不是 V 似乎更传统在这里。这样给我们:

  INT主(INT ARGC,CHAR *的argv [])
{
  的atexit(完)
  (ARGC&LT;!2 || *的argv [1] = 113)
    &功放;&安培; (playsound =(ARGC = *为(int *)的getenv(期限))==(INT)0x756E696C
    || ARGC ==(INT)0x6C696E75);

由于这仍然不是一个美女,我们要求我们的标准的家伙,他告诉我们,这是pretty OK取代

 (A || B)放大器;&安培; (C)

 如果(A || B){c}里

  E =(X = F)== || ^ h点¯x==我

  X = F;
如果(X == || ^ h ==点¯xI)
  A = 1;
其他
  A = 0;`

所以,也许这应该是整个code的一个更可读的版本:

 如果(ARGC 2 || *的argv [1] ='Q'!){
   ARGC = *为(int *)的getenv(期限);
   如果(ARGC ==(INT)0x756E69 || ARGC ==(INT)0x6C696E75))
     play_sound = 1;
   / *跳过这里的其他人作为BRACH是play_sound alredy初始化为0 * /
}

现在还在另一个人轮番上涨,并开始,如果存储在内存中告知我们,这取决于所谓的字节序东星奇怪的数字0x6C696E75和0x756E69会(时间preting原始字节的山谷为ASCII code)只是看起来像丽努UNIL。一个是在一个木构建筑类型和丽努另一个人UNIL,而只是另一种方式圆在其他架构不同字节。

所以,左看右看什么本质上发生在这里的是:


  • 我们得到一个指向从中我们提领从而导致存储在字符串位置为int的位模式之前typcast的指针为int的getenv(期限)的字符串。

  • 接下来我们比较,我们会得到是否曾与任一UNIL,或存储在该特定位置丽努执行相同的一此值。

也许我们只是想检查TERM环境变量设置为LINUX所以我们的版本反混淆可能想在这里执行字符串比较。

由于在另一方面,我们也不能肯定是否也允许与开头的名字终端UNIL播放声音可能是这个软件的特殊功能,所以我决定最好还是离开它的完整。

现在怎么办?

在重命名和重新编码的变量名和值那些奇怪的字符数组可能是我们的下一个受害者。下面的烂摊子看起来并不太好:

 字符X [] =((((((((((((((((((((((W [] =
  \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ b \\ B \\ B;
焦 - [R [] = {92,124,47};

所以也许他们可以更改为:

 字符x_offset [] = {
  40,40,40,40,40,40,40,40,40,40,
  40,40,40,40,40,40,40,40,40,40,
  40,40,0};字符宽度[] = {
  8,8,8,8,8,8,8,8,8,8,
  8,8,8,8,8,8,8,8,8,8,
  8,8,0};为const char边框[] =\\\\ | /;

正如你可以看到我只是选择为开关 X之间所描述的值的方式作为字符串常量为x写下来作为一个数组因为这种方式存储在这里的值的目的似乎有点更清晰的在我身上。

而在另一方面,我改研究作为再次写下来正好完全相反的方向的方式,这似乎更加清晰,以我的方式。

在追捕那些裁判到 X 是W 研究的时间可以用来命名 p 0 来 - 再次抱歉,没有蝴蝶 - POS old_pos 而重命名取值评分

更改例如:

  S + =(9  -  W [21])*((G&GT;&GT; 3)+ 1);
  O =磷;
  A = X;
  Z = *一个;
  而(* ++一)
    {
      Y = *一个;
      *一= Z;
      Z =ÿ;
    }
  ;
  一= W;
  Z = *一个;
  而(* ++一)
    {
      Y = *一个;
      *一= Z;
      Z =ÿ;
    }
  ;

  / *更新得分* /
  得分+ =(9 - 宽度[NEXT_LINE])*((G&GT;&GT; 3)+ 1);
  old_pos = POS;  / *转变x_offset * /
  A = x_offset;
  Z = *一个;
  而(* ++一){
    Y = *一个;
    *一= Z;
    Z =ÿ;
  };  / *移动宽度* /
  A =宽度;
  Z = *一个;
  而(* ++一){
    Y = *一个;
    *一= Z;
    Z =ÿ;
  };

除了可能把它变成一些其它类型的循环那里你可以做的最大的并不多美化可能为转移功能,因此可能加入适当的注释。卸下幻数 21 可能是另一种想法 NEXT_LINE 似乎没有在这里是最糟糕的选择。

标记变量单字符先按g 仍然看起来并不太好。但它重命名为类似更新周期还有还可消除另一种怪异的终端转义序列的机会:

 如果(G!= G)
    {
      结构itimerval T = {0,0,0,0}
      ;
      G + =((G&LT; G)&LT;&LT; 1) - 1;
      t.it_interval.tv_usec = t.it_value.tv_usec = 72000 /((g取代;→3)+ 1);
      setitimer函数(0,&amp; T公司,0);
      F&功放;&安培;的printf(\\ E [10;
%U],G + 24);
    }

也许看起来比更混乱一点:

  / *更新仿真速度* /
  如果(更新周期​​!=齿轮){
    结构itimerval T = {0,0,0,0};
      更新周期+ =((更新周期​​与所述;齿轮)所述;&所述; 1) - 1;
      t.it_interval.tv_usec = t.it_value.tv_usec = 72000 /((更新周期​​与GT;&→3)+ 1);
      setitimer函数(0,&amp; T公司,0);
      如果(play_sound)
        change_bell_frequency(更新周期​​+ 24);
  }

最后修订

虽然code应该是很多现在更具可读性仍然有一些讨厌的地方留下:

  D&安培;!&安培; -  * X&LT; = * W&放大器;&安培; (++ * X,++ D)|| ð== 2和;&放大器; ++ * X + * W&GT; 79安培;&安培; ( -  * X,--D);

D 和打破运营商precedence下来,你可能会喜欢的东西最终选择了另一个(希望)更有意义的名称:

 如果(曲线== CURVE_LEFT){
     - * x_offset;
    如果(* x_offset&LT; *宽){
       ++ * x_offset;
       曲线= CURVE_NONE;
    }
  }
  否则,如果(曲线== CURVE_RIGHT){
    ++ * x_offset;
    如果(* x_offset + *宽度GT; 79){
       - * x_offsett;
      曲线= CURVE_NONE;
    }
  }

而不是增加,所有这些曲线_... 秒。

适当的宏

现在仍然有那些 X P T 游逛也名可能被改变。因为这使得它的目的也更好一点可见code,我决定翻转 T的线序,我重命名​​为这肯定意味着计算也必须固定。总而言之,这是来自:

 的char * T [] = {|,|,%\\\\ | /%,%%%,};
焦X,** P =&amp; T公司[4];// ...  如果(!(** P放大器;&放大器,P + || N'放大器; 7936))
    {
      而(ABS((X =兰特()%76) - * X + 2) - * W&LT; 6);
      ++ X;
      P =笔;
    }

要类似:

 的char *树[] = {
  ,
  %%%
  \\\\%| /%,
  |
  |
};焦炭** tree_line =树;
焦炭tree_position;// ...  / *更新树行指针* /
  如果((** tree_line和放大器;!&安培; tree_line-- || N'放大器; 7936)){
    / *找到合适的地方成长* /
    而(ABS((tree_position =兰特()%76) - * x_offset + 2) - *宽6;)
      ;
    ++ tree_position;
    tree_line =安培;树[4];
  }

保持最好的部分,直到最后

虽然code似乎已经长得prettier现在对我还是有一部分失踪。这是一个的做所有的输出之一。这是此行我说的是:

 的printf(\\ 233;%uH容\\ 233L%C \\ 233;%uH容%C \\ 233;%uH容%S \\ 23322;%uH容@ \\ 23323;%uH容\\ N,
      * X - * W,R [D] * X + * W,R [D] X,* P,P + = K,O);

从寻找pretty难读,除了是甚至模糊电脑产生任何可用的结果。

我尝试了很多其他的终端模拟器上运行,更改终端设置和切换​​语言环境来回不sucess不同的东西。

所以除了事实上这种混淆似乎更完美,因为它甚至似乎混淆我的电脑我现在还不能告诉笔者这里的本意伎俩。

八进制code \\ 233 有相同的位模式作为转义字符( \\ 033 )与加设8个比特这可能是与该在这里意的功效某种方式。不幸的是,我已经告诉它没有为我工作。

幸运的是足够的转义序列仍然似乎很容易猜到,所以我用下面的更换想出了:

POS + = move_x,

  / *绘制街道* /
  的printf(\\ E [1;%呃...\\ E [L,%C
          \\ E [1;%呃...%C,
          * x_offset - *宽度,边框[曲线]
          * x_offset + *宽度,边框[曲线]);
  / *绘制树* /
  的printf(\\ E [1;%呃...%S,
          tree_position,* tree_line);  / *重绘汽车* /
  的printf(\\ E [22;%呃...@
          \\ E [23;%呃...\\ n
          POS机,
          old_pos);

考虑绘制成独立的(希望)让他们多一点点的可读性。实际的线和previous线仍然很难codeD这里作为原始版本。也许从那里提取它们如下图所示,甚至会提高可读性:

  / *绘制街道* /
  的printf(\\ E [1;%呃...\\ E [L,%C
          \\ E [1;%呃...%C,
          * x_offset - *宽度,边框[曲线]
          * x_offset + *宽度,边框[曲线]);
  / *绘制树* /
  的printf(\\ E [1;%呃...%S,
          tree_position,* tree_line);  / *重绘汽车* /
  的printf(\\ E [%U;%呃...@
          \\ E [%U;%呃...\\ n
          NEXT_LINE +1,POS机,
          NEXT_LINE +2,old_pos);

这终于给我带来了我,然后测试了不少第一个可用的版本。虽然艺术可能不是100%状态下,它似乎仍然是很容易上瘾。

最后的话

在这里,我带着最后的简易版本。正如你会看到我没有实现LED设置功能和清晰的屏幕功能,但它不应该是很难找到分散在模糊版本所需的转义序列。其实我已经提到这个职位的LED序列。清除屏幕上的人是\\ E [0Q。快乐的黑客攻击。

 的#include&LT;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&; SYS / time.h中&GT;
#包括LT&;&signal.h中GT;#定义NEXT_LINE 21#定义CURVE_LEFT 0
#定义CURVE_NONE 1
#定义CURVE_RIGHT 2烧焦x_offset [] = {
  40,40,40,40,40,40,40,40,40,40,
  40,40,40,40,40,40,40,40,40,40,
  40,40,0};字符宽度[] = {
  8,8,8,8,8,8,8,8,8,8,
  8,8,8,8,8,8,8,8,8,8,
  8,8,0};为const char边框[] =\\\\ | /;空隙change_bell_frequency(){}
空隙clear_screen(){}
空隙clear_all_LEDs(){}
空隙set_Num_Lock_LED(){}
空隙set_Scroll_lock_LED(){}
空隙set_Caps_Lock_LED(){}字符*树[] = {
  ,
  %%%
  \\\\%| /%,
  |
  |
};
焦炭** tree_line =树;
焦炭tree_position;焦曲线= CURVE_NONE;
字符* A,Y,Z;焦炭move_x = 0;
焦炭更新周期= -1;焦炭POS = 40;
焦炭old_pos = 40;焦炭play_sound = 0;
焦炭齿轮;无符号整型得分= 0;无效移动(字符X,焦炭Y){
  的printf(\\ E [%U;%呃...,X,Y);
}空隙插入件(){
  的printf(\\ E [L);
}无效更新(int i)以{
  INT N;  POS + = move_x,  / *绘制街道* /
  的printf(\\ E [1;%呃...\\ E [L,%C
          \\ E [1;%呃...%C,
          * x_offset - *宽度,边框[曲线]
          * x_offset + *宽度,边框[曲线]);
  / *绘制树* /
  的printf(\\ E [1;%呃...%S,
          tree_position,* tree_line);  / *重绘汽车* /
  的printf(\\ E [%U;%呃...@
          \\ E [%U;%呃...\\ n
          NEXT_LINE + 1,POS机,
          NEXT_LINE +2,old_pos);  / *没有我们离开的道路? * /
  如果(ABS(POS - x_offset [NEXT_LINE])&GT; =宽度[NEXT_LINE])
    出口(0);  / *更新仿真速度* /
  如果(更新周期​​!=齿轮){
    结构itimerval T = {0,0,0,0};
      更新周期+ =((更新周期​​与所述;齿轮)所述;&所述; 1) - 1;
      t.it_interval.tv_usec = t.it_value.tv_usec = 72000 /((更新周期​​与GT;&→3)+ 1);
      setitimer函数(0,&amp; T公司,0);
      如果(play_sound)
        change_bell_frequency(更新周期​​+ 24);
  }  /* 播放声音 */
  如果(play_sound)
    的putchar('\\一个');  / *更新得分* /
  得分+ =(9 - 宽度[NEXT_LINE])*((更新周期​​&GT;&GT; 3)+ 1);
  old_pos = POS;  / *转变x_offset * /
  A = x_offset;
  Z = *一个;
  而(* ++一){
    Y = *一个;
    *一= Z;
    Z =ÿ;
  };  / *移动宽度* /
  A =宽度;
  Z = *一个;
  而(* ++一){
    Y = *一个;
    *一= Z;
    Z =ÿ;
  };  / *产生新的道路* /
  N = RAND();  如果((N放大器;!255)及与放大器; *宽度大于1)
     - *宽度;  / *集树线指针* /
  如果((** tree_line和放大器;!&安培; tree_line-- || N'放大器; 7936)){
    / *找到合适的地方成长* /
    而(ABS((tree_position =兰特()%76) - * x_offset + 2) - *宽6;)
      ;
    ++ tree_position;
    tree_line =安培;树[4];
  }  / *新的偏移* /
  N =兰特()及31;
  如果(正3;)
    曲线= N;  如果(曲线== CURVE_LEFT){
     - * x_offset;
    如果(* x_offset&LT; = *宽){
      ++ * x_offset;
      曲线= CURVE_NONE;
    }
  }
  否则,如果(曲线== CURVE_RIGHT){
    ++ * x_offset;
    如果(* x_offset + *宽度GT; 79){
       - * x_offset;
      曲线= CURVE_NONE;
    }
  }  信号(SIGALRM,更新);
}
无效结束(){
  信号(SIGALRM,SIG_IGN);
  clear_all_LEDs();
  clear_screen();
  的printf(分数:%U \\ N,分);
  系统(stty的回声-cbreak);
}
INT主(INT ARGC,字符** argv的){
  的atexit(完)  如果(argc个2 || *的argv [1] ='Q'!){
    ARGC = *为(int *)的getenv(期限);
    如果(ARGC ==(INT)0x6C696E75 || ARGC ==(INT)0x756E696C)
      play_sound = 1;
  }  函数srand(GETPID());
  系统(stty的-echo CBREAK);
  齿轮= 0℃;&下; 3;  clear_all_LEDs();
  更新(14);
  为(;;)
    开关(的getchar())
      {
        案例'Q':
          返回0;
        案件 '[':
        案例'B':
        案件 ',':
          move_x = -1;
          继续;
        案件 ' ':
        案例'N':
        案件 '。':
          move_x = 0;
          继续;
        案件 ']':
        案件的m:
        案件 '/':
          move_x = 1;
          继续;
        情况1':
          齿轮= 0℃;&下; 3;
          set_Num_Lock_LED();
          继续;
        案2:
          齿轮= 1&LT;&LT; 3;
          set_Caps_Lock_LED();
          继续;
        案3:
          齿轮= 2;&下; 3;
          set_Scroll_lock_LED();
          继续;
        案4:
          齿轮= 3';&下; 3;
          clear_all_LEDs();
          继续;
      }
}

I have seen ctk.c obfuscated code, but How can I start to de-obfuscate it?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
#define m(b)a=b;z=*a;while(*++a){y=*a;*a=z;z=y;}
#define h(u)G=u<<3;printf("\e[%uq",l[u])
#define c(n,s)case n:s;continue
char x[]="((((((((((((((((((((((",w[]=
"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";char r[]={92,124,47},l[]={2,3,1
,0};char*T[]={"  |","  |","%\\|/%"," %%%",""};char d=1,p=40,o=40,k=0,*a,y,z,g=
-1,G,X,**P=&T[4],f=0;unsigned int s=0;void u(int i){int n;printf(
"\233;%uH\233L%c\233;%uH%c\233;%uH%s\23322;%uH@\23323;%uH \n",*x-*w,r[d],*x+*w
,r[d],X,*P,p+=k,o);if(abs(p-x[21])>=w[21])exit(0);if(g!=G){struct itimerval t=
{0,0,0,0};g+=((g<G)<<1)-1;t.it_interval.tv_usec=t.it_value.tv_usec=72000/((g>>
3)+1);setitimer(0,&t,0);f&&printf("\e[10;%u]",g+24);}f&&putchar(7);s+=(9-w[21]
)*((g>>3)+1);o=p;m(x);m(w);(n=rand())&255||--*w||++*w;if(!(**P&&P++||n&7936)){
while(abs((X=rand()%76)-*x+2)-*w<6);++X;P=T;}(n=rand()&31)<3&&(d=n);!d&&--*x<=
*w&&(++*x,++d)||d==2&&++*x+*w>79&&(--*x,--d);signal(i,u);}void e(){signal(14,
SIG_IGN);printf("\e[0q\ecScore: %u\n",s);system("stty echo -cbreak");}int main
(int C,char**V){atexit(e);(C<2||*V[1]!=113)&&(f=(C=*(int*)getenv("TERM"))==(
int)0x756E696C||C==(int)0x6C696E75);srand(getpid());system("stty -echo cbreak"
);h(0);u(14);for(;;)switch(getchar()){case 113:return 0;case 91:case 98:c(44,k
=-1);case 32:case 110:c(46,k=0);case 93:case 109:c(47,k=1);c(49,h(0));c(50,h(1
));c(51,h(2));c(52,h(3));}} 

http://www.ioccc.org/2001/ctk.hint:

This is a game based on an Apple ][ Print Shop Companion easter
egg named 'DRIVER', in which the goal is to drive as fast as
you can down a long twisty highway without running off the
road.  Use ',./', '[ ]', or 'bnm' to go left, straight, and
right respectively. Use '1234' to switch gears. 'q' quits. The
faster you go and the thinner the road is, the more points you
get. Most of the obfuscation is in the nonsensical if statements
among other things. It works best on the Linux console: you
get engine sound (!) and the * Lock keyboard lights tell you
what gear you're in (none lit=4th).  The 'q' argument (no
leading '-') will silence the sound. It won't work on a terminal
smaller than 80x24, but it works fine with more (try it in an
XTerm with the "Unreadable" font and the window maximized
vertically!).

解决方案

1st step

Using:

sed -e'/#include/d' ctk.c | gcc -E - | sed -e's/;/;\n/g' -e's/}/}\n/g' -e '/^#/d' | indent

I was able to generate the following output which while not perfect already seems to be readable a lot better:

char x[] = "((((((((((((((((((((((", w[] =
  "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
char r[] = { 92, 124, 47 }

, l[] =
{
2, 3, 1, 0}

;
char *T[] = { "  |", "  |", "%\\|/%", " %%%", "" }

;
char d = 1, p = 40, o = 40, k = 0, *a, y, z, g = -1, G, X, **P = &T[4], f = 0;
unsigned int s = 0;
void
u (int i)
{
  int n;
  printf ("\233;
%uH\233L%c\233;
%uH%c\233;
%uH%s\23322;
%uH@\23323;
%uH \n", *x - *w, r[d], *x + *w, r[d], X, *P, p += k, o);
  if (abs (p - x[21]) >= w[21])
    exit (0);
  if (g != G)
    {
      struct itimerval t = { 0, 0, 0, 0 }
      ;
      g += ((g < G) << 1) - 1;
      t.it_interval.tv_usec = t.it_value.tv_usec = 72000 / ((g >> 3) + 1);
      setitimer (0, &t, 0);
      f && printf ("\e[10;
%u]", g + 24);
    }
  f && putchar (7);
  s += (9 - w[21]) * ((g >> 3) + 1);
  o = p;
  a = x;
  z = *a;
  while (*++a)
    {
      y = *a;
      *a = z;
      z = y;
    }
  ;
  a = w;
  z = *a;
  while (*++a)
    {
      y = *a;
      *a = z;
      z = y;
    }
  ;
  (n = rand ()) & 255 || --*w || ++*w;
  if (!(**P && P++ || n & 7936))
    {
      while (abs ((X = rand () % 76) - *x + 2) - *w < 6);
      ++X;
      P = T;
    }
  (n = rand () & 31) < 3 && (d = n);
  !d && --*x <= *w && (++*x, ++d) || d == 2 && ++*x + *w > 79 && (--*x, --d);
  signal (i, u);
}

void
e ()
{
  signal (14, SIG_IGN);
  printf ("\e[0q\ecScore: %u\n", s);
  system ("stty echo -cbreak");
}

int main (int C, char **V)
{
  atexit (e);
  (C < 2 || *V[1] != 113)
    && (f = (C = *(int *) getenv ("TERM")) == (int) 0x756E696C
    || C == (int) 0x6C696E75);
  srand (getpid ());
  system ("stty -echo cbreak");
  G = 0 << 3;
  printf ("\e[%uq", l[0]);
  u (14);
  for (;;)
    switch (getchar ())
      {
      case 113:
    return 0;
      case 91:
      case 98:
      case 44:
    k = -1;
    continue;
      case 32:
      case 110:
      case 46:
    k = 0;
    continue;
      case 93:
      case 109:
      case 47:
    k = 1;
    continue;
      case 49:
    G = 0 << 3;
    printf ("\e[%uq", l[0]);
    continue;
      case 50:
    G = 1 << 3;
    printf ("\e[%uq", l[1]);
    continue;
      case 51:
    G = 2 << 3;
    printf ("\e[%uq", l[2]);
    continue;
      case 52:
    G = 3 << 3;
    printf ("\e[%uq", l[3]);
    continue;
      }
}

... and now?

I don't think there's much more an automated process will be able perform at this point as the term "more" readable or "less" readable from now on might depend on the specific preferences of the reader.

One step that could be performed is removing escape sequences from the strings and placing them somewhere separately. As it turns out the whole

char l[] = {2, 3, 1, 0}

has no other purpose than to be utilized in the escape sequences of the main loop:

printf ("\e[%uq", l[0]);

and so on. Looking up their meaning:

ESC [ 0 q: clear all LEDs
ESC [ 1 q: set Scroll Lock LED
ESC [ 2 q: set Num Lock LED
ESC [ 3 q: set Caps Lock LED

depending on taste you might want to exchange them with a macro or a function call more meaningful to you like clear_all_LEDs and so on.

I strongly doubt a machine would agree on this being a simplification. As it turns out the whole main loop just seems to be working with keys entered by the user, so probably turning numbers into their corresponding characters might add to readability, like in replacing:

case 113:
  return 0;
case 91:
case 98:
case 44:
  k = -1;
// ...
case 49:
  G = 0 << 3;
  printf ("\e[%uq", l[0]);

with something like:

case 'q':
  return 0;
case '[':
case 'b':
case ',':
  k = -1;
// ...
case '1':
  G = 0 << 3;
  set_Num_Lock_LED ();

Oh - and while we are at it already why wouldn't we want to change the name from this rather strange G to gear. Again I strongly doubt an automated process would have found renaming from G to gear any better than renaming it to butterfly. Well maybe it even isn't.

While beautifying names maybe this function referenced by a single u is another candidate:

u (14);

with a more meaningful name update probably. And as we already included <signal.h> why don't we deobfuscate the code further by replacing 14 with SIGALRM like this:

upadate (SIGALRM);

As you can see "deobfuscating" here requires the exact opposite step of that taken before. Replacing the expansion with a macro this time. How would a machine try to decide which one is more useful?

Another spot where we might want to replace a bare number with something else is this one in the update function:

f && putchar (7);

Why not replace the 7 with \a as it will turn out to be the same in the end. Maybe we should even change the bare f with something more "meaningful".

Again I vote agains butterfly but would rather like to call it play_sound:

if (play_sound)
   putchar ('\a');

might be the more readable version we are looking for. Sure we shouldn't forget to replace f in all other spots. The one right at the beginning of our main function beeing such a culprit:

Holy mess

int main (int C, char **V)
{
  atexit (e);
  (C < 2 || *V[1] != 113)
    && (f = (C = *(int *) getenv ("TERM")) == (int) 0x756E696C
    || C == (int) 0x6C696E75);

While happily renaming f to play_sound and e to - no, still no butterfly, this time I'll rather call it: - end we spot that the function signature seems to look a bit strange in terms of naming conventions: argc instead of C and argv instead of V would seem more conventional here. Thus giving us:

int main (int argc, char* argv[])
{
  atexit (end);
  (argc < 2 || *argv[1] != 113)
    && (playsound = (argc = *(int *) getenv ("TERM")) == (int) 0x756E696C
    || argc == (int) 0x6C696E75);

As this is still not a beauty we ask our standards guy and he informs us that it's pretty OK to replace

(A || B) && (C)

with

if (A || B) { C }

and

E = (x=F)==H || x==I

with

x = F; 
if (x==H || x==I) 
  A=1; 
else 
  A=0;` 

So maybe this should be a more readable version of the whole code:

if (argc < 2 || *argv[1] != 'q') {
   argc = *(int*) getenv ("TERM");
   if (argc == (int) 0x756E69 || argc == (int) 0x6C696E75))
     play_sound = 1;
   /* skip the else brach here as play_sound is alredy initialized to 0 */
}

Now still another guy turns up and starts to inform us, that depending on something called endianness tose strange looking numbers 0x6C696E75 and 0x756E69 if stored in memory would (when interpreting raw byte vales as ASCII code) just look like "linu" or "unil". One being "unil" on one architecure type and "linu" the other one while just the other way round on the other architecture with different endianness.

So taking a closer look what's essentially happening here is:

  • we get a pointer to a string from getenv ("TERM") which we typcast to a pointer to an int before dereferencing it thus leading the bit pattern stored at the string location as an int.
  • next we compare this value with the one we would get if had performed the same with either "unil" or "linu" stored at that specific location.

Probably we just want to check if the TERM environment variable is set to "linux" so our deobfuscated version might want to perform a string comparison here.

As on the other hand we can't be sure if also allowing terminals with names starting with "unil" to play sound might be a special feature of this software so I decided to probably better leave it intact.

What now ?

While renaming and re-encoding variable names and values those strange char arrays could be our next victims. The following mess doesn't look too nice:

char x[] = "((((((((((((((((((((((", w[] =
  "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
char r[] = { 92, 124, 47 };

So maybe they could be changed to:

char x_offset[] = {
  40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
  40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
  40, 40, 0 };

char width[] = {
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 0 };

const char border[] = "\\|/";

As you can see I just chose to switch the way the values are described between x as string constant to x written down as an array as this way the purpose of the values stored here seemed a little bit clearer to me.

While on the other hand I changed the way the way r is written down just in exactly the opposite direction as again this seemed a lot clearer to me.

While hunting down all those refs to x, w and r the time could be used to rename p and o to - sorry again no butterfly - pos and old_pos while renaming s to score.

Changing for example:

  s += (9 - w[21]) * ((g >> 3) + 1);
  o = p;
  a = x;
  z = *a;
  while (*++a)
    {
      y = *a;
      *a = z;
      z = y;
    }
  ;
  a = w;
  z = *a;
  while (*++a)
    {
      y = *a;
      *a = z;
      z = y;
    }
  ;

to:

  /* update score */
  score += (9 - width[NEXT_LINE]) * ((g >> 3) + 1);
  old_pos = pos;

  /* shift x_offset */
  a = x_offset;
  z = *a;
  while (*++a) {
    y = *a;
    *a = z;
    z = y;
  };

  /* shift width */
  a = width;
  z = *a;
  while (*++a) {
    y = *a;
    *a = z;
    z = y;
  };

Besides the possibility to turn it into some other kind of loop there's not much beautification possible for both shifting functions so probably adding an appropriate comment is the maximum you can do. Removing the magic number 21 might be another idea NEXT_LINE didn't seem to be the worst choice here.

The single character labeled variable g still doesn't look too good. But renaming it to something like update_interval there's also the chance to eliminate another weird terminal escape sequence:

 if (g != G)
    {
      struct itimerval t = { 0, 0, 0, 0 }
      ;
      g += ((g < G) << 1) - 1;
      t.it_interval.tv_usec = t.it_value.tv_usec = 72000 / ((g >> 3) + 1);
      setitimer (0, &t, 0);
      f && printf ("\e[10;
%u]", g + 24);
    }

Maybe looks a little bit more confusing than:

  /* update simulation speed */
  if (update_interval != gear) {
    struct itimerval t = { 0, 0, 0, 0 }  ;
      update_interval += ((update_interval < gear) << 1) - 1;
      t.it_interval.tv_usec = t.it_value.tv_usec = 72000 / ((update_interval >> 3) + 1);
      setitimer (0, &t, 0);
      if (play_sound)
        change_bell_frequency (update_interval + 24);
  }

Last fixes

Although the code should look a lot more readable by now there are still some nasty parts left:

!d && --*x <= *w && (++*x, ++d) || d == 2 && ++*x + *w > 79 && (--*x, --d);

Choosing another (hopefully) more meaningful name for d and breaking operator precedence down you might end up with something like:

  if (curve == CURVE_LEFT) {
    --*x_offset;
    if (*x_offset < *width) {
       ++*x_offset;
       curve = CURVE_NONE;
    }
  }
  else if (curve == CURVE_RIGHT) {
    ++*x_offset;
    if (*x_offset + *width > 79) {
      --*x_offsett;
      curve = CURVE_NONE;
    }
  } 

instead adding appropriate macros for all those CURVE_...s.

Now there are still those X, P and T names hanging around that also might be changed. As it makes its purpose also a little bit better visible in code I decided to flip the line order of T that I renamed to tree which sure means the calculation also has to be fixed. All in all it's from:

char *T[] = { "  |", "  |", "%\\|/%", " %%%", "" };
char X, **P = &T[4];

// ...

  if (!(**P && P++ || n & 7936))
    {
      while (abs ((X = rand () % 76) - *x + 2) - *w < 6);
      ++X;
      P = T;
    }

To something like:

char *tree[] = {
  "",
  " %%%",
  "%\\|/%",
  "  |",
  "  |",
};

char **tree_line = tree;
char tree_position;

// ...

  /* update tree line pointer */
  if (!(**tree_line && tree_line-- || n & 7936)) {
    /* find the right spot to grow */
    while (abs ((tree_position = rand () % 76) - *x_offset + 2) - *width < 6)
      ;
    ++tree_position;
    tree_line = &tree[4];
  }

Keeping the best part until the end

Although the code already seems to looks a lot prettier to me now there's still one part missing. That's the one that's doing all the output. It's this line I'm talking about:

 printf ("\233;%uH\233L%c\233;%uH%c\233;%uH%s\23322;%uH@\23323;%uH \n",
      *x - *w, r[d], *x + *w, r[d], X, *P, p += k, o); 

That apart from looking pretty hard to read was even to obfuscated for computer to produce any usable result.

I tried a lot of different things running in other terminal emulators, changing terminal settings and switching locales back and forth without sucess.

So besides the fact this kind of obfuscation seemed to be more that perfect as it even seems to confuse my computer I still can't tell what trick the author intended here.

The octal code \233 has the same bit-pattern as the escape character (\033) with the 8-th bit set additionally which probably is in some way related to effect that was intended here. Unfortunately as I already told it didn't work for me.

Fortunately enough the escape sequences still seemed easy enough to guess, so I came up with the following replacement:

pos += move_x,

  /* draw street */
  printf ("\e[1;%uH" "\e[L" "%c"
          "\e[1;%uH" "%c",
          *x_offset - *width, border[curve],
          *x_offset + *width, border[curve]);
  /* draw tree */
  printf ("\e[1;%uH" "%s",
          tree_position, *tree_line);

  /* redraw car */
  printf ("\e[22;%uH" "@"
          "\e[23;%uH" " " "\n",
          pos,
          old_pos);  

Taking drawing down into separate to (hopefully) make them a little bit more readable. The actual line and the previous line are still hard coded here as in the original version. Maybe extracting them from there as shown below would even improve readability:

  /* draw street */
  printf ("\e[1;%uH" "\e[L" "%c"
          "\e[1;%uH" "%c",
          *x_offset - *width, border[curve],
          *x_offset + *width, border[curve]);
  /* draw tree */
  printf ("\e[1;%uH" "%s",
          tree_position, *tree_line);

  /* redraw car */
  printf ("\e[%u;%uH" "@"
          "\e[%u;%uH" " " "\n",
          NEXT_LINE +1, pos,
          NEXT_LINE +2, old_pos);

This finally brought me to the first usable version which I then "tested" a lot. While probably not 100% state of the art it still seems to be very addictive.

Last words

Here the final unobfuscated version that I came with. As you'll see I didn't implement the LED setting functions and the clear screen function but it shouldn't be to hard to find the needed escape sequences scattered throughout the obfuscated version. In fact I already mentioned the LED sequences in this post. The one to clear the screen is "\e[0q". Happy hacking.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>

#define NEXT_LINE 21

#define CURVE_LEFT 0
#define CURVE_NONE 1
#define CURVE_RIGHT 2

char x_offset[] = {
  40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
  40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
  40, 40, 0 };

char width[] = {
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 0 };

const char border[] = "\\|/";

void change_bell_frequency () {}
void clear_screen () {}
void clear_all_LEDs () {}
void set_Num_Lock_LED () {}
void set_Scroll_lock_LED () {}
void set_Caps_Lock_LED () {}



char *tree[] = {
  "",
  " %%%",
  "%\\|/%",
  "  |",
  "  |",
};


char **tree_line = tree;
char tree_position;

char curve = CURVE_NONE;
char *a, y, z;

char move_x = 0;
char update_interval = -1;

char pos = 40;
char old_pos = 40;

char play_sound = 0;
char gear;

unsigned int score = 0;

void move (char x, char y) {
  printf ("\e[%u;%uH", x, y);
}

void insert () {
  printf ("\e[L");
}

void update (int i) {
  int n;

  pos += move_x,

  /* draw street */
  printf ("\e[1;%uH" "\e[L" "%c"
          "\e[1;%uH" "%c",
          *x_offset - *width, border[curve],
          *x_offset + *width, border[curve]);
  /* draw tree */
  printf ("\e[1;%uH" "%s",
          tree_position, *tree_line);

  /* redraw car */
  printf ("\e[%u;%uH" "@"
          "\e[%u;%uH" " " "\n",
          NEXT_LINE + 1, pos,
          NEXT_LINE +2, old_pos);

  /* did we leave the road ? */
  if (abs (pos - x_offset[NEXT_LINE]) >= width[NEXT_LINE])
    exit (0);

  /* update simulation speed */
  if (update_interval != gear) {
    struct itimerval t = { 0, 0, 0, 0 }  ;
      update_interval += ((update_interval < gear) << 1) - 1;
      t.it_interval.tv_usec = t.it_value.tv_usec = 72000 / ((update_interval >> 3) + 1);
      setitimer (0, &t, 0);
      if (play_sound)
        change_bell_frequency (update_interval + 24);
  }

  /* play sound */
  if (play_sound)
    putchar ('\a');

  /* update score */
  score += (9 - width[NEXT_LINE]) * ((update_interval >> 3) + 1);
  old_pos = pos;

  /* shift x_offset */
  a = x_offset;
  z = *a;
  while (*++a) {
    y = *a;
    *a = z;
    z = y;
  };

  /* shift width */
  a = width;
  z = *a;
  while (*++a) {
    y = *a;
    *a = z;
    z = y;
  };

  /* generate new road */
  n = rand ();

  if (!(n & 255) && *width > 1)
    --*width;

  /* set tree line pointer */
  if (!(**tree_line && tree_line-- || n & 7936)) {
    /* find the right spot to grow */
    while (abs ((tree_position = rand () % 76) - *x_offset + 2) - *width < 6)
      ;
    ++tree_position;
    tree_line = &tree[4];
  }

  /* new offset */
  n = rand () & 31;
  if (n < 3)
    curve = n;

  if (curve == CURVE_LEFT) {
    --*x_offset;
    if (*x_offset <= *width) {
      ++*x_offset;
      curve = CURVE_NONE;
    }
  }
  else if (curve == CURVE_RIGHT) {
    ++*x_offset;
    if (*x_offset + *width > 79) {
      --*x_offset;
      curve = CURVE_NONE;
    }
  }

  signal (SIGALRM, update);
}


void end () {
  signal (SIGALRM, SIG_IGN);
  clear_all_LEDs ();
  clear_screen ();
  printf ("Score: %u\n", score);
  system ("stty echo -cbreak");
}


int main (int argc, char **argv) {
  atexit (end);

  if (argc < 2 || *argv[1] != 'q') {
    argc = *(int*) getenv ("TERM");
    if (argc == (int) 0x6C696E75 || argc == (int) 0x756E696C)
      play_sound = 1;
  }

  srand (getpid ());
  system ("stty -echo cbreak");
  gear = 0 << 3;

  clear_all_LEDs ();
  update (14);
  for (;;)
    switch (getchar ())
      {
        case 'q':
          return 0;
        case '[':
        case 'b':
        case ',':
          move_x = -1;
          continue;
        case ' ':
        case 'n':
        case '.':
          move_x = 0;
          continue;
        case ']':
        case 'm':
        case '/':
          move_x = 1;
          continue;
        case '1':
          gear = 0 << 3;
          set_Num_Lock_LED ();
          continue;
        case '2':
          gear = 1 << 3;
          set_Caps_Lock_LED ();
          continue;
        case '3':
          gear = 2 << 3;
          set_Scroll_lock_LED ();
          continue;
        case '4':
          gear = 3 << 3;
          clear_all_LEDs ();
          continue;
      }
}

这篇关于如何去混淆ctk.c code 2001年的IOCCC的赢家?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆