2
3

JavaScriptを記号だけに(関数名やキーワードも)変換するプログラム

Last updated at Posted at 2024-09-08

JavaScript で Hello,world! に挑戦 (ただし記号だけを使って)という記事をみて、記号だけ(アルファベットを使わない)でプログラムができることを知りました

例えば、こんなhello world!プログラムが

alert('Hello,world!')

こんな形に変換します

_= ({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]];__= (!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[-~[]];[][_][_]((![]+[])[-~[]]+(![]+[])[-~-~[]]+(!![] + [])[-~-~-~[]]+(!![] + [])[-~[]]+(!![] + [])[+[]]+'('+"'"+[][_][_](__+' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~[]<<-~-~[])+'"')()+(!![] + [])[-~-~-~[]]+(![]+[])[-~-~[]]+(![]+[])[-~-~[]]+({} + [])[-~[]]+','+[][_][_](__+' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~-~[])+(-~-~-~-~-~-~-~[])+'"')()+({} + [])[-~[]]+(!![] + [])[-~[]]+(![]+[])[-~-~[]]+([][[]] + [])[-~-~[]]+'!'+"'"+')')()

ブラウザのコンソールから実行すると、ちゃんとhello world!が表示されました

image.png

変換プログラムの使い方

node.js のプログラムです

  • 数値、アルファベットだけ対応しています(漢字等は変換できません)

converter.mjs

標準出力からJavaScriptソースを受け取り、変換結果を標準出力へ返します

$ echo 'alert("Hello,world!")' | node converter.mjs
_= ({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]];__= (!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]
+([]['']+[])[-~[]];[][_][_]((![]+[])[-~[]]+(![]+[])[-~-~[]]+(!![] + [])[-~-~-~[]]+(!![] + [])[-~[]]+(!![] + [])[+[]]+'('+'"'+[][_][_](__+' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~[]<<-~-~[])+'"')()+(!![] + [])[-~-~-~[]]+(![]+[])[-~-~[]]+(![]+[])[-~-~[]]+({} + [])[
-~[]]+','+[][_][_](__+' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~-~[])+(-~-~-~-~-~-~-~[])+'"')()+({} + [])[-~[]]+(!![] + [])[-~[]]+(![]+[])[-~-~[]]+([][[]] + [])[-~-~[]]+'!'+'"'+')')()

※Windows環境(git bash)でstdin is not a ttyというエラーが出る場合は、nodeに拡張子をつけて実行してください

$ echo 'alert("Hello,world!")' | node.exe converter.mjs

変換プログラムの説明

仕組みはアイディアの元になったこちらのページをご参照ください

(ちょっとだけ)補足

全体の仕組み

JavaScriptには文字列を評価するeval()関数がありますが、文字を使えないためこのまま実行するわけにはいきません

eval("alert('Hello,world!')");

まず、関数コンストラクタを利用して文字列(式)から関数を作成し、それを実行する形に変形します

Function("alert('Hello,world!')")();

で、関数コンストラクタはどうすればいいかというと、空配列のconstructorから調達できるそうです

> [].constructor.constructor
{ [native code] }[Function: Function]

なので下記の形に書き換えることができます

[].constructor.constructor("alert('Hello,world!')")()

さらにプロパティーへのアクセスを.から[]に変更することでキーワードをなくして文字列と記号だけにすることができました

[]['constructor']['constructor']("alert('Hello,world!')")()

あとは文字(アルファベットと数字)記号のみで作成した(評価結果が文字になる)式に全て置き換えることができれば、記号のみで構成されたプログラムにすることができます

数字の作り方

  • 評価結果が0になる式を作る(空配列に+をつけることで数値として評価される)
> +[]
0
  • 0の補数は-1になる
> ~+[]
-1
  • -1の符号を逆転
> -~+[]
1
  • さらに補数をとると-2になる(以降繰り返すことで大きな数字を作ることができる)
> ~-~+[]
-2

大きな数を作る場合は式が長くなるので、シフト演算で短縮したりできます(2を2bit左シフトすれば8になります)

> -~-~[]<<-~-~[]
8

次に文字の作り方ですが、大きく分けて2つあります

文字の作り方①JavaScriptで評価した結果を文字列化して、そこから[index]で切り取る方法

評価結果がfalseになる式を作成して、それを文字列化+[]して、最初の1文字を切り取る[0]fを得ることができます

  • 評価結果がfalseになる式を作る
> ![]
false
  • 評価結果falseを文字列化(+[])する
> ![]+[]
'false'
  • 評価結果'false'から1文字取り出す
> (![]+[])[0]
'f'
  • 0になる式と合わせると、記号のみでfを作成することができました
> (![]+[])[+[]]
`f`

※ 評価結果から文字を切り出す際に使える式(すべてのアルファベットを作ることはできません)

![]+[]; // 'false'
!![]+[]; // 'true'
{}+[]; // '[object Object]'
[][[]]+[]; // 'undefined'
-~[]/[]+[]; // 'Infinity'
+{}+[]; // 'NaN'

文字の作り方②文字エスケープ(文字コード)から文字を生成する方法

Aという文字は、①の切り出し法では生成できないので、文字エスケープ(\uHHHH)から作ります

> Function('return "\u0041"')()
'A'
  • Function()はこれで調達します
> [].constructor.constructor
{ [native code] }[Function: Function]
  • そこから、下記の形に書き換えます
> [].constructor.constructor('return "\u0041"')()
'A'
  • さらにプロパティーへのアクセスを.から[]に変更することで、文字列('constructor', 'return')に置き換えることができます
> []['constructor']['constructor']('return "\u0041"')()
'A'
  • 'constructor''return''\u0041'も、①で作った文字の組み合わせで生成できるので・・・・
> ({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]
'constructor'
>  (!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[-~[]]
'return'
> ' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~[])+'"'
' "\\u0041"'
  • 組み合わせることで、記号からAを作り出すことができました
> [][(({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]])][(({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[]
)[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]])]((!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[-~[]]+' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~[])+'"')()
'A'

プログラムソース

converter.mjs
/**
 * JavaScript obfuscator program
 *  @license MIT
 */
import * as readline from 'readline';

const chars = {
  ' ': `({} + [])[-~-~-~-~-~-~-~[]]`,
  0: `(+[])+[]`,
  1: `-~[]`,
  2: `-~-~[]`,
  3:` -~-~-~[]`,
  4:` (-~-~[]<<-~[])+[]`,
  5:` -~-~-~-~-~[]`,
  6:` -~-~-~-~-~-~[]`,
  7:` -~-~-~-~-~-~-~[]`,
  8:` (-~-~[]<<-~-~[])+[]`,
  9:` (-~-~-~[]*-~-~-~[])+[]`,
  a: `(![]+[])[-~[]]`,
  b: `({}+[])[-~-~[]]`,
  c: `({}+[])[-~-~-~-~-~[]]`,
  d: `([][[]] + [])[-~-~[]]`,
  e: `(!![] + [])[-~-~-~[]]`,
  f: `(![] + [])[+[]]`,
  g: `(([]+[])[({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]]+[])[-~-~-~-~-~-~-~-~-~-~-~-~-~-~[]]`,
  h: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~[])+(-~-~-~-~-~-~-~-~[])+'"')()`,
  i: `([][[]] + [])[-~-~-~-~-~[]]`,
  j: `({} + [])[-~-~-~[]]`,
  k: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~[])+({}+[])[-~-~[]]+'"')()`,
  l: `(![]+[])[-~-~[]]`,
  m: `((+[])[({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]]+[])[-~-~-~-~-~-~-~-~-~-~-~[]]`,
  n: `([][[]] + [])[-~[]]`,
  o: `({} + [])[-~[]]`,
  p: `(/ /[({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]]+[])[-~-~-~-~-~-~-~-~-~-~-~-~-~-~[]]`,
  q: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~-~[])+(-~[])+'"')()`,
  r: `(!![] + [])[-~[]]`,
  s: `(![] + [])[-~-~-~[]]`,
  t: `(!![] + [])[+[]]`,
  u: `([][[]] + [])[+[]]`,
  v: `([][({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]]+[])[-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~[]]`,
  w: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~-~[])+(-~-~-~-~-~-~-~[])+'"')()`,
  x: `(/ /[({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]]+[])[-~-~-~-~-~-~-~-~-~-~-~-~-~[]]`,
  y: `(-~[] / [] + [])[-~-~-~-~-~-~-~[]]`,
  z: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~-~[])+(![]+[])[-~[]]+'"')()`,
  A: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~[])+'"')()`,
  B: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~[])+'"')()`,
  C: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~-~[])+'"')()`,
  D: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~[]<<-~[])+'"')()`,
  E: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~-~-~-~[])+'"')()`,
  F: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~-~-~-~-~[])+'"')()`,
  G: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~-~-~-~-~-~[])+'"')()`,
  H: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~[]<<-~-~[])+'"')()`,
  I: `(-~[]/[]+[])[+[]]`,
  J: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+((![]+[])[-~[]])+'"')()`,
  K: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(({}+[]+[])[-~-~[]])+'"')()`,
  L: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(({}+[])[-~-~-~-~-~[]])+'"')()`,
  M: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(([][[]] + [])[-~-~[]])+'"')()`,
  N: `(+{}+[])[+[]]`,
  O: `({}+[])[-~-~[]<<-~-~[]]`,
  P: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+(+[])+'"')()`,
  Q: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+(-~[])+'"')()`,
  R: `(/ /[({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]]+[])[-~-~-~[]*-~-~-~[]]`,
  S: `(([]+[])[({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]]]+[])[-~-~-~[]*-~-~-~[]]`,
  T: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+(-~-~[]<<-~[])+'"')()`,
  U: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+(-~-~-~-~-~[])+'"')()`,
  V: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+(-~-~-~-~-~-~[])+'"')()`,
  W: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+(-~-~-~-~-~-~-~[])+'"')()`,
  X: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+(-~-~[]<<-~-~[])+'"')()`,
  Y: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+(-~-~-~[]*-~-~-~[])+'"')()`,
  Z: `[][_][_](__+' "\\\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~[])+((![]+[])[-~[]])+'"')()`,
};



const stdin = readline.createInterface({
  input: process.stdin,
});

stdin.on('line', (line) => {
    console.log(obfuscator(line));
});

export function obfuscator(src) {
  let header = '';
  // 'constructor'
  header += "_= ({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]];";
  // 'return'
  header += "__= (!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[-~[]];";
  header += "[][_][_](";
  return `${header}${encode(src)})()`;
}

function encode(src) {
  let body = '';
  for (var c of src) {
    if (c in chars) {
      body += `${chars[c]}+`;
    }else if (c === "'") {
      body += `"${c}"+`;
    } else {
      body += `'${c}'+`;
    }
  }

  return body.slice(0, -1);
}

// const source_code = 'console.log("Hello world!")';
// console.log(obfuscator(source_code));


参考ページ

JavaScript で Hello,world! に挑戦 (ただし記号だけを使って)

2
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3