JavaScript で Hello,world! に挑戦 (ただし記号だけを使って)という記事をみて、記号だけ(アルファベットを使わない)でプログラムができることを知りました
例えば、こんなhello world!
プログラムが
alert('Hello,world!')
こんな形に変換します
_= ({}+[])[-~-~-~-~-~[]]+({}+[])[-~[]]+([]['']+[])[-~[]]+(![]+[])[-~-~-~[]]+(!![]+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[+[]]+({}+[])[-~-~-~-~-~[]]+(!![]+[])[+[]]+({}+[])[-~[]]+(!![]+[])[-~[]];__= (!![]+[])[-~[]]+({}+[])[-~-~-~-~[]]+(!![]+[])[+[]]+([]['']+[])[+[]]+(!![]+[])[-~[]]+([]['']+[])[-~[]];[][_][_]((![]+[])[-~[]]+(![]+[])[-~-~[]]+(!![] + [])[-~-~-~[]]+(!![] + [])[-~[]]+(!![] + [])[+[]]+'('+"'"+[][_][_](__+' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~[]<<-~[])+(-~-~[]<<-~-~[])+'"')()+(!![] + [])[-~-~-~[]]+(![]+[])[-~-~[]]+(![]+[])[-~-~[]]+({} + [])[-~[]]+','+[][_][_](__+' "\\'+([]['']+[])[+[]]+(+[])+(+[])+(-~-~-~-~-~-~-~[])+(-~-~-~-~-~-~-~[])+'"')()+({} + [])[-~[]]+(!![] + [])[-~[]]+(![]+[])[-~-~[]]+([][[]] + [])[-~-~[]]+'!'+"'"+')')()
ブラウザのコンソールから実行すると、ちゃんとhello world!
が表示されました
変換プログラムの使い方
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'
プログラムソース
/**
* 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));