JSFuck

JSFuck
홈페이지

1 개요

난해한 프로그래밍 언어 가 아닌, 난해한 프로그래밍 스타일. JavaScript 코드를 JavaScript 문법에서 사용되는 문자 중 단 6가지인 [, ], (, ),  !, +만으로 구현할 수 있음에 착안하여 고안된 프로그래밍 스타일이다. 어?

위 6가지 문자 중 단독으로 써서 멀쩡한 건 [] (어레이 리터럴) 뿐인데, 여기에서 모든 것을 시작하게 된다. 예를 들어 알파벳 문자열 "a" 는 다음의 과정에 의해 얻어오게 된다.

  • ![] = false
배열 자체는 truthy한 값으로 인정되므로, 여기에 부정을 의미하는 !(느낌표)를 사용하게 되면 false가 나온다.
  • []+[] = ""
자바스크립트에서 오브젝트 간에 +(더하기) 연산은 정의되어 있지 않으므로 오브젝트끼리 더하게 되면 .toString()(문자열로 변환) 처리가 되어 문자열 간의 더하기로 처리된다. 오브젝트로 취급되는 배열을 이용하여 [] + [] 를 하게 되면 [].toString() = ""이므로, 결과적으로 "" + "" = ""(빈 문자열)이 생성된다.
  • false + "" = "false"
부울 값인 false에 빈 문자열을 더하게 되면 false.toString() = "false"이므로 "false" 라는 문자열이 나오게 된다. 앞서 확인한 내용을 조합해보면 ![]+([]+[]) = false + "" = "false"가 된다.
  • !![] = true
false를 부정하면 true가 된다.
  • +true = 1
true 앞에 unary plus를 쓰면 숫자형 1로 변환이 된다. # 앞서 얻어온 !![] = true의 앞에 + 를 넣은 +!![] 는 숫자 1이 된다.
  • "false"[1] = "a"
앞서 얻어온 문자열 "false"의 두 번째 알파벳이 a이므로 "false"[1]은 "a"가 된다. 0부터 세는 것에 주의.
  • 지금까지 파악한 내용들을 모두 이으면 (![]+([]+[]))[+!![]] = (false + "")[+true] = "false"[1] = "a" 가 된다. 해냈다!!

이런 식으로 알파벳/숫자를 얻어오고, 각종 내장 오브젝트 등을 이용하여 함수 객체 등에 접근하여 함수를 만들고 또 호출하는 코드를 작성, 최종적으로 자바스크립트 프로그램을 작성하는 것이 가능하다.

2 예시

다음은 JSFuck 스타일로 작성된 alert(1) 호출 프로그램이다. 아래 텍스트를 복사하여 웹 브라우저의 개발자 도구 콘솔 등에 붙여넣기를 하면 alert 창으로 1이 출력되는 것을 볼 수 있다. 코드 맨 뒤의 ()를 생략하고 개발자 도구 콘솔에 아래 코드를 입력하게 되면 함수가 만들어진 것을 확인할 수 있다.


<syntaxhighlight lang="javascript" line="1">
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[
]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]
])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+
(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+
!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![
]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]
+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[
+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!!
[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![
]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[
]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![
]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(!
[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])
[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(
!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[
])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()
</syntaxhighlight>

무려 1,227글자에 달하는 거대한 코드...인 것 같지만, 사실 잘 뜯어보면 []["filter"]["constructor"]("alert(1)")()로 줄어드는 것을 알 수 있고, 이는 [].filter.constructor("alert(1)")() = Function("alert(1)")()으로 해석되어 최종적으로는 alert(1)이 실행된다. 1227글자 중에 무려 1186글자를 문자열 만드는 데 썼다