6

ctk.c の難読化されたコードを見たことがありますが、難読化を解除するにはどうすればよいですか?

#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!).
4

1 に答える 1

21

1段目

使用:

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

私は次の出力を生成することができましたが、これは完全ではありませんが、より読みやすくなっているようです。

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;
      }
}

... そしていま?

「読みやすい」または「読みにくい」という用語は、読者の特定の好みに依存する可能性があるため、この時点で自動化されたプロセスが実行できることはこれ以上ないと思います。

実行できる 1 つの手順は、文字列からエスケープ シーケンスを削除し、別の場所に配置することです。全体が分かるように

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

メイン ループのエスケープ シーケンスで使用する以外の目的はありません。

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

等々。それらの意味を調べる:

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

好みに応じて、より意味のあるマクロや関数呼び出しなどと交換したい場合がありますclear_all_LEDs

これが単純化されていることにマシンが同意するかどうか、私は強く疑っています。結局のところ、メインループ全体がユーザーが入力したキーで機能しているように見えるため、おそらく数字を対応する文字に変換すると、次のように読みやすくなる可能性があります。

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

次のようなもので:

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

Gああ、私たちはすでにそれに取り組んでいますが、名前をこのかなり奇妙なものから に変更したくないのはなぜですかgear。繰り返しになりますが、自動化されたプロセスで、 から への名前変更が への名前変更よりも優れていることが判明したとは思えGませgearbutterfly。そうではないかもしれません。

名前を美しくしている間、おそらくシングルによって参照されるこの関数uは別の候補です:

u (14);

updateおそらくもっと意味のある名前で。そして、すでに含まれているように、次のように置き換えて<signal.h>コードをさらに難読化しないでください:14SIGALRM

upadate (SIGALRM);

ご覧のとおり、ここでの「難読化解除」には、以前とは正反対の手順が必要です。今回は展開をマクロに差し替え。機械は、どちらがより有用かをどのように判断しようとしますか?

裸の数値を何か別のものに置き換えたいと思うかもしれないもう 1 つの場所は、更新関数の次の場所です。

f && putchar (7);

最終的には同じになるので、 を に7置き換えてみませんか。もっと「意味のある」ものに\a変えるべきかもしれません。f

もう一度投票しますbutterflyが、むしろそれを呼びたいplay_soundです:

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

私たちが探しているより読みやすいバージョンかもしれません。確かに、他のすべての場所で f を置き換えることを忘れてはなりません。メイン関数の最初にあるのは、そのような犯人です。

聖なる混乱

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

喜んfで toplay_soundおよびeto に名前を変更している間 - いいえ、まだいいえbutterfly、今回はむしろそれを呼びます: -関数シグネチャendが命名規則の点で少し奇妙に見えることに気付きました: . したがって、次のようになります。argcCargvV

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

これはまだ美しいものではないので、標準担当者に尋ねたところ、交換しても問題ないとのことでした。

(A || B) && (C)

if (A || B) { C }

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

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

したがって、これはコード全体のより読みやすいバージョンになるはずです。

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 */
}

エンディアンと呼ばれるものに応じて、奇妙に見える数字 0x6C696E75 と 0x756E69 がメモリに保存されている場合 (未加工のバイト値を ASCII コードとして解釈する場合) は、"linu"またはのように見えるということ"unil"です。1 つは 1 つのアーキテクチャ タイプで「unil」、もう 1 つは「linu」ですが、エンディアンが異なる他のアーキテクチャではその逆です。

したがって、ここで本質的に何が起こっているかを詳しく見てみると、次のようになります。

  • getenv ("TERM") から文字列へのポインターを取得し、これを逆参照する前に int へのポインターに typcast して、文字列の場所に格納されているビット パターンを int として導きます。
  • 次に、この値を、その特定の場所に保存されている「unil」または「linu」のいずれかで同じことを実行した場合に得られる値と比較します。

おそらく、TERM 環境変数が「linux」に設定されているかどうかを確認したいだけなので、難読化を解除したバージョンでここで文字列比較を実行する必要があるかもしれません。

一方、「unil」で始まる名前の端末でもサウンドを再生できるようにすることが、このソフトウェアの特別な機能である可能性があるかどうかは定かではないため、おそらくそのままにしておくことにしました。

今何?

変数名と値の名前を変更して再エンコードしている間、これらの奇妙な char 配列が次の犠牲者になる可能性があります。次の混乱は見栄えがよくありません。

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 };

したがって、次のように変更できます。

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[] = "\\|/";

ご覧のとおり、ここに格納されている値の目的が少し明確に思えたので、値の記述方法を文字列定数から配列として書き留めた xに切り替えることにしました。x

一方で、書き方rをまったく反対の方向に変更しましたが、これもまた、私にとってははるかに明確に思えました。

への参照をすべて探し出している間xに、その時間を名前の変更 に使用できます。wまた、申し訳ありませんが、.rpobutterflyposold_possscore

たとえば、次のように変更します。

  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;
    }
  ;

に:

  /* 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;
  };

それを他の種類のループに変える可能性に加えて、両方のシフト機能で可能な美化はあまりないので、おそらく適切なコメントを追加することがあなたができる最大のことです. マジック ナンバーを削除する21ことは別のアイデアかもしれませんNEXT_LINEが、ここでは最悪の選択ではないようです。

1 文字のラベル付き変数gは、まだ見栄えがよくありません。しかし、次のような名前に変更すると、update_interval別の奇妙なターミナル エスケープ シーケンスを排除できる可能性もあります。

 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);
    }

たぶん、次よりも少し混乱しているように見えます。

  /* 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);
  }

最終修正

コードはかなり読みやすくなっているはずですが、まだ厄介な部分がいくつか残っています。

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

別の (できれば) 意味のある名前を選択しd、演算子の優先順位を下げると、次のような結果になる可能性があります。

  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;
    }
  } 

代わりに、これらすべての に適切なマクロを追加しますCURVE_...

今でもそれらXPあり、T名前も変更される可能性があります。その目的もコードで少し見やすくするため、T名前を変更した行の順序を反転することにしましたtree。これは、計算も修正する必要があることを意味します。全体として、それは次のとおりです。

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

// ...

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

次のようなものに:

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];
  }

いいところを最後まで残す

コードはすでにかなりきれいに見えますが、まだ 1 つの部分が欠けています。それがすべての出力を行っているものです。私が話しているのはこの行です:

 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); 

かなり読みにくいように見えるだけでなく、コンピューターが使用可能な結果を​​生成するために難読化することさえありました.

他の端末エミュレーターでさまざまなことを実行してみました。端末の設定を変更したり、ロケールを切り替えたりして成功しませんでした。

したがって、この種の難読化は、私のコンピューターを混乱させるように見えるので、完璧に見えたという事実に加えて、作者がここで意図したトリックが何であるかはまだわかりません.

8 進コード\233には、エスケープ文字 ( ) と同じビット パターンがあり、\033さらに 8 番目のビットが設定されています。これは、おそらくここで意図された効果に何らかの形で関連しています。残念ながら、私がすでに言ったように、それは私にはうまくいきませんでした。

幸いなことに、エスケープ シーケンスはまだ簡単に推測できるように思えたので、次の置換を思い付きました。

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);  

描画を個別に (できれば) もう少し読みやすくするために取り下げます。元のバージョンと同様に、実際の行と前の行はここでもハードコーディングされています。以下に示すようにそこからそれらを抽出すると、読みやすさが向上する可能性があります。

  /* 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);

これにより、最終的に最初の使用可能なバージョンにたどり着き、それを何度も「テスト」しました。おそらく 100% 最新の状態ではありませんが、それでも非常に中毒性があるようです。

最後の言葉

これが、難読化されていない最終的なバージョンです。ご覧のとおり、LED 設定関数とクリア スクリーン関数は実装していませんが、難読化されたバージョン全体に散らばっている必要なエスケープ シーケンスを見つけるのは難しくありません。実際、私はすでにこの投稿で LED シーケンスについて言及しました。画面をクリアするのは「\e[0q」です。ハッピーハッキング。

#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;
      }
}
于 2013-03-27T22:37:42.287 に答える