TokigunStudio obfuscation collection

Whirl 인터프리터와 유틸리티 (revision 3)

이해하는 데 아주 골이 터지는 프로그래밍 언어인 Whirl의 인터프리터입니다. 처음에는 캐릭터 모양으로 만들어 볼까 했으나 하다가 꼬여서 그냥 때려 치고 간단하게 만들었지요... :)

Whirl 홈페이지에도 올라 와 있습니다. :D 이 인터프리터는 GNU LGPL에 따라 배포됩니다.

코드

다음은 현재 최신 버전인, 2005년 6월 17일에 나온 revision 3입니다. 호환성을 살짝 높이고 쓸데 없는 코드를 좀 줄였습니다.

#include/* by Kang Seonghoon <tokigun@gmail.com> */<stdio.h>
FILE*f;int P[99999],*d=P,*p=P,U[99999],*u=U,q,s,t,r[2],v[2],
w[2];int main(int i,char**I){if(i-2)return!puts("TokigunStu\
dio Whirl Interpreter by Kang Seonghoon <tokigun@gmail.com>"
);if(f=**++I-45||1[*I]?fopen(*I,"r"):stdin){while((*d=fgetc(
f))>=0  )*d/2-24  ?0  :(*d  ++      -=      48)  ;for(;p<d;q
=!*p++  &&!q)if(  *p  )v[s  ]=(v  [s]-  r[s  ]+  13)%12;else
#define  O(  n)  ;;}        else  /**/      if(  !(v[s]-n)){
{r[s]^=  2;  if  (q)  {t=w  [s];  if(s  ){;  if  (0){O(1)t=*
u;O(2)*u        =t;O  (3)t  +=*u  O(4)  t*=  *u  O(5)t/=*u;O
(6)t=0;O(  7)  t=t<*  u;O(  8)      t=  t>*  u;       O(9)t=
t==*u;O(10)t=!t;O(11)t=-t;}O(1)return 0;O(2)t=1;O(3)t=0;O(4)
t=*u;O(5)*u=t;O(6)p+=t-1;O(7)u+=t;O(8)t=*u&&t;O(9)p+=*u?t-1:
0;O(10)t?printf("%d",*u):scanf("%d",u);O(11)*u=t?putchar(*u)
:getchar();}w[s]=t;s=!s;if(p<P)p=P;}}fclose(f);}else printf(
"File Not Found: %s\n",*I);return!f;}/*20050617rev3tokigun*/

다음은 2005년 1월 8일에 나온 revision 2입니다. 파일 이름 대신에 -를 지정해서 코드를 표준 입력으로 입력할 수 있습니다.

#include/* by Kang Seonghoon <tokigun@gmail.com> */<stdio.h>
FILE*f;int P[99999],*d=P,*p=P,U[99999],*u=U,q,s,t,r[2],v[2],
w[2];int main(int i,char**I){if(i-2)return!puts("TokigunStu\
dio Whirl Interpreter by Kang Seonghoon <tokigun@gmail.com>"
);if(f=**++I-45||1[*I]?fopen(*I,"r"):stdin){while(~(*d=fgetc
(f)))(  *d|1)-49  ?0  :(*d  ++      -=      48)  ;for(;p<d;q
=!*p++  &&!q)if(  *p  )v[s  ]=(v  [s]-  r[s  ]+  13)%12;else
#define  O(  n)  ;;}        else  /**/      if(  !(v[s]-n)){
{r[s]^=  2;  if  (q)  {t=w  [s];  if(s  ){;  if  (0){O(1)t=*
u;O(2)*u        =t;O  (3)t  +=*u  O(4)  t*=  *u  O(5)t/=*u;O
(6)t=0;O(  7)  t=t<*  u;O(  8)      t=  t>*  u;       O(9)t=
t==*u;O(10)t=!t;O(11)t=-t;}O(1)return 0;O(2)t=1;O(3)t=0;O(4)
t=*u;O(5)*u=t;O(6)p+=t-1;O(7)u+=t;O(8)t=*u&&!!t;O(9)p+=*u?t-
1:0;O(10)t?printf("%d",*u):scanf("%d",u);O(11)t?putchar(*u):
(*u=getchar());}w[s]=t;s=!s;if(p<P)p=P;}}fclose(f);return 0;
}return!!printf("File Not Found: %s\n",*I);}/*20050108rev2*/

다음은 2005년 1월 6일에 나온 revision 1입니다.

#include/* by Kang Seonghoon <tokigun@gmail.com> */<stdio.h>
FILE*f;int P[99999],*d=P,*p=P,U[99999],*u=U,q,s,t,r[2],v[2],
w[2];int main(int i,char**I){if(i-2)return!puts("TokigunStu"
"dio Whirl Interpreter by Kang Seonghoon <tokigun@gmail.com"
">");f=fopen(*++I,"r");if(!f)return!!printf(".error: File N"
"ot F"  "ound --  %s  \n",  *I      );      42;  while(~(*d=
fgetc(  f)))if((  *d  |1)-  49);  else  {*d  ++  -=48;}for(\
fclose(  f)  ;p  <d;        q=!(  *p++      ||q  )){if(*p)v[
s]=(v[s  ]-  r[  s]+  13)%  014;  else  {r[  s]  ^=2;if(q){t
# define        O(n)  else  if(!  (v[s  ]-n  ))  /*2k50106*/
=w[s];if(  s)  {if(0  );O(  1)      t=  *u;  O(       2)*u=t
;O(3)t+=*u;O(4)t*=*u;O(5)t/=*u;O(6)t=0;O(7)t=t<*u;O(8)t=t>*u
;O(9)t=t==*u;O(10)t=!t;O(11)t=-t;}O(1)return 0;O(2)t=1;O(3)t
=0;O(4)t=*u;O(5)*u=t;O(6)p+=t-1;O(7)u+=t;O(8)t=*u&&!!t;O(9)p
+=*u?t-1:0;O(10)t?printf("%d",*u):scanf("%d",u);O(11)!t?(*u=
getchar()):putchar(*u);w[s]=t;s=!s;if(p<P)p=P;}}}return 0;;}

덤으로, 아래 코드는 Whirl 개발자가 마지막 몇 분의 게으름을 못 참고 만들지 못 했던-_- 바로 그 바이너리-Whirl 코드 변환기입니다.

#include/*tokigun*/<stdio.h>
int p,q;int main(int i,char*
*j){if(i-2)printf("Usage:\n\
\t%s e < plain > binary\n\t\
%s d < binary > plain\n",*j,
*j);else if(**++j==100)while
(~(i=getchar()))for(p=128;p;
p/=2)putchar(i&p?49:48);else
if(**j==101){while(~(i=getc\
har())){if((i|1)==49){p=2*p+
i%2;if(++q>7){putchar(p);p=q
=0;}}}q&&putchar(p<<8>>q|255
>>q);}/*20050108*/return 0;}

사용법

이 코드는 Microsoft Visual C++ 6.0과 gcc 2.x에서 제대로 컴파일되며, 아마도 웬만한 컴파일러에서는 다 잘 되지 않을까 생각하고 있습니다. gcc의 경우 -Wall 옵션을 줘서 컴파일하면 Warning 하나가 뜨는데 물론 무시해도 관계 없습니다 :) 다음은 gcc를 쓸 경우의 컴파일 방법입니다.

$ gcc whirl.c -o whirl

이 프로그램은 tkbf93과 비슷한 방법으로 사용할 수 있습니다. 인자 하나를 주면 그 파일을 읽어서 실행하고, 아니면 안내 메시지를 출력합니다.

$ whirl
TokigunStudio Whirl Interpreter by Kang Seonghoon <tokigun@gmail.com>

$ whirl helloworld.txt
hello world

revision 2부터는 인자가 -일 경우 표준 입력(stdin)에서 코드를 읽어 오게 되어 있습니다. 단 프로그램 자체가 사용자 입력을 받아 들일 경우 일부 플랫폼에서는 입력값이 EOF로 처리될 수 있습니다. (FreeBSD에서는 동작하지 않고, 윈도우즈에서는 동작하는 것을 확인했습니다.)

$ whirl -
011000001111000011111000001111000011111000001111000
011111000001100100000110011111000111000111100011001
11000000000111110001000111110011001111100010001100
(EOF)
2

2005년 1월 8일에 추가한 유틸리티(whirlutil.c) 역시 같은 방법으로 컴파일하면 됩니다. 그냥 실행할 경우 이 코드는 사용법을 출력하며, 첫 인자가 "e"로 시작하면 표준 입력에서 Whirl 코드를 받아서 바이너리를 출력하고, "d"로 시작하면 거꾸로 표준 입력에서 바이너리를 받아서 Whirl 코드를 출력합니다.

$ whirlutil
Usage:
    whirlutil e < plain > binary
    whirlutil d < binary > plain

$ whirlutil encode
01100001010110010110001001100001011000100111010001110101
(EOF)
aYbabtu

$ whirlutil decode
aYbabtu
(EOF)
01100001010110010110001001100001011000100111010001110101

바이너리를 출력할 때 Whirl 코드의 길이가 8의 배수가 아니면 8의 배수가 되도록 뒤에 1 비트들을 붙입니다. 예를 들어서 "010"이 입력되었을 때는 "01011111"이 입력된 것으로 간주합니다. (0 대신 1을 넣는 이유는 Whirl에서 0이 연속으로 들어 있을 경우 원치 않는 명령이 실행될 수 있기 때문입니다.)

실제로는 whirl과 함께 붙어서 다음과 같이 사용합니다.

$ whirlutil e < code.wr > code.wrb
$ whirlutil d < code.wrb | whirl -

공식 인터프리터와의 차이점

이 인터프리터는 Whirl 홈페이지에 공개된 공식 인터프리터와 거의 완벽하게 똑같이 동작합니다. (단, 앞에 메시지 뜨는 건 없습니다 -_-;) 또한 공식 인터프리터와 마찬가지로 주석문을 지원하지 않으며, (이건 버그 아닙니다 :p) 프로그램 포인터가 프로그램을 벗어날 경우에 대한 처리도 완전히 되어 있습니다.

단, 프로그램과 메모리 크기는 고정되어 있으므로 큰 프로그램을 실행하기 위해서는 숫자를 바꿔서 재컴파일 해야 합니다. (2005년 6월 현재까지 나온 Whirl 프로그램 중에서 그 만큼 큰 프로그램은 없습니다만...)

이 프로그램은 메모리 포인터가 원래 위치보다 더 앞으로 갈 경우를 따로 처리하지 않습니다.


지금은 글을 남길 수 없고 이미 남겨진 걸 볼 수만 있습니다.

Copyright © 2004-2005, Kang Seonghoon (Tokigun). All Rights Reserved.
Permanent Link: http://page.tokigun.net/obfuscation/_/whirl.php
Last updated on June 17, 2005. Valid XHTML 1.1 Valid CSS 2