从0到1学JSFuck:用!+实现第一个Hello World
你是否曾想过,只使用[]()!+这6个字符就能编写完整的JavaScript程序?当所有字母、数字和符号都被禁用时,JSFuck技术让这一切成为可能。本文将带你揭开这种"极端编程"的神秘面纱,从基础原理到实战开发,最终完成你的第一个JSFuck版本的"Hello World"。
读完本文你将掌握:
- JSFuck核心原理与字符映射规则
- 从布尔值到字符串的完整构建路径
- 手动编码与自动化工具的使用方法
- 实现任意文本输出的关键技术
什么是JSFuck?
JSFuck是一种深奥且富有教育意义的编程风格,它仅使用[]()!+这六个字符来编写可执行的JavaScript代码。这种技术不依赖浏览器环境,可在Node.js中直接运行。其核心思想是通过JavaScript语言的特性,从最基础的原子部分构建出所有必要的语法元素。
项目源码位于gh_mirrors/js/jsfuck,核心实现文件为jsfuck.js,命令行工具为fuck.js。
核心字符集解析
JSFuck的强大之处在于仅用6个字符就能表达所有JavaScript概念:
| 字符 | 作用 | 基础应用 |
|---|---|---|
[] | 数组字面量与属性访问 | 创建数组、获取属性 |
() | 函数调用与分组 | 执行函数、控制运算优先级 |
! | 逻辑非运算符 | 创建布尔值、类型转换 |
+ | 加法与类型转换 | 数值运算、转为字符串/数字 |
第一个示例:alert(1)的JSFuck实现
以下代码仅使用上述6个字符,却能实现alert(1)的功能:
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()
这串看似杂乱的字符背后,隐藏着精妙的类型转换和属性访问逻辑,我们将在后续章节逐步解析其构造过程。
JSFuck基础构建模块
从布尔值到基本数据类型
JSFuck的构建始于最基础的数据类型,通过简单的表达式创造出各种原始值:
// 布尔值
![] // false - 空数组的逻辑非
!![] // true - 双重否定
// 特殊值
[][[]] // undefined - 访问空数组的空属性
+[![]] // NaN - 将false转为数字
+[] // 0 - 空数组转为数字
+!+[] // 1 - true转为数字后取正
这些基础值在jsfuck.js的SIMPLE对象中定义:
const SIMPLE = {
'false': '![]',
'true': '!![]',
'undefined': '[][[]]',
'NaN': '+[![]]',
'Infinity': '+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])'
};
数值系统构建
通过基础数字的组合,可以构建出任意整数:
0 → +[]
1 → +!+[]
2 → !+[]+!+[]
3 → !+[]+!+[]+!+[]
10 → +[[+!+[]]+[+[]]] // 等价于 Number([1,0].join(''))
更复杂的数字可通过进制转换实现,例如在jsfuck.js中定义的映射:
'h': '(+(101))["to"+String["name"]](21)[1]', // 101的21进制表示为"4h"
'k': '(+(20))["to"+String["name"]](21)', // 20的21进制表示为"k"
字符串构建原理
字符串构建是JSFuck的核心挑战,主要通过以下三种方式实现:
- 布尔值转字符串
![]+[] // "false" - false转为字符串
!![]+[] // "true" - true转为字符串
- 特殊值转字符串
[][[]]+[] // "undefined" - undefined转为字符串
+[![]]+[] // "NaN" - NaN转为字符串
- 字符提取技术 从上述字符串中提取单个字符:
// 获取"false"中的字符
(false+"")[0] // "f" - 等价于 (![]+[])[+[]]
(false+"")[1] // "a" - 等价于 (![]+[])[+!+[]]
// 获取"undefined"中的字符
(undefined+"")[2] // "d" - 等价于 ([][[]]+[])[!+[]+!+[]]
从字符到字符串:构建"Hello"
字符映射表
jsfuck.js中定义了完整的字符映射规则(MAPPING对象),部分常用字符映射如下:
| 字符 | JSFuck表达式 | 解析 |
|---|---|---|
| 'a' | (false+"")[1] | "false"的第2个字符 |
| 'e' | (true+"")[3] | "true"的第4个字符 |
| 'l' | (false+"")[2] | "false"的第3个字符 |
| 'o' | (true+[]["flat"])[10] | 复杂表达式组合 |
| 'H' | null | 需通过其他方式构建 |
"Hello"的分步构建
让我们逐步构建"Hello"中的每个字符:
- 构建"H" 由于H在基础映射中为null,需要通过Date对象构建:
(NaN+Function("return Date")()())[30] // "H"
- 构建"e"
(true+"")[3] // "e" → 等价于 (!![]+[])[+!+[]+!+[]+!+[]]
- 构建"l"
(false+"")[2] // "l" → 等价于 (![]+[])[!+[]+!+[]]
- 构建"o"
(true+[]["flat"])[10] // "o" → 复杂组合表达式
组合以上字符得到"Hello"的JSFuck表达式:
(NaN+Function("return Date")()())[30]+(!![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+(true+[]["flat"])[10]
实现Hello World:完整流程
构建console.log函数
要输出文本,需先获取console.log函数。通过以下步骤实现:
- 获取Function构造函数
[]["flat"]["constructor"] // Function - 数组flat方法的构造函数
- 创建eval函数
[]["flat"]"constructor"() // 等价于eval函数
- 获取全局对象
[]["flat"]"constructor"() // window/global对象
- 获取console.log
[]["flat"]"constructor"()["console"]["log"]
完整的Hello World实现
结合上述技术,完整的"Hello World"实现如下:
// 获取console.log
var log = []["flat"]"constructor"()["console"]["log"];
// 构建"Hello World"字符串
var str = (NaN+Function("return Date")()())[30]+(!![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+(true+[]["flat"])[10]+(NaN+[]["flat"])[11]+(+[]+[]["flat"])[10]+(true+[]["flat"])[10]+(![]+[])[!+[]+!+[]]+(true+[]["flat"])[10]+(+(20))["to"+String["name"]](21);
// 输出结果
log(str);
转换为纯JSFuck字符后:
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+[]]+(+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[+!+[]+!+[]+!+[]])( (NaN+Function("return Date")()())[30]+(!![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+(true+[]["flat"])[10]+(NaN+[]["flat"])[11]+(+[]+[]["flat"])[10]+(true+[]["flat"])[10]+(![]+[])[!+[]+!+[]]+(true+[]["flat"])[10]+(+(20))["to"+String["name"]](21) )
JSFuck工具使用指南
命令行工具快速上手
项目提供fuck.js命令行工具,可将普通JS代码转换为JSFuck格式:
- 安装依赖
npm install
- 转换文件
node fuck.js input.js > output.js
- 交互式REPL
node fuck.js
FUCK> console.log("Hello")
在线转换工具
官方提供在线转换网站(需自行搭建本地服务):
- 打开index.html
- 输入JavaScript代码
- 点击转换按钮获取JSFuck代码
自动化编码流程
在jsfuck.js中,encode函数实现了完整的转换逻辑:
function encode(input, wrapWithEval, runInParentScope){
var output = [];
// ... 编码逻辑 ...
return output;
}
核心转换步骤包括:
- 字符映射替换
- 未映射字符的转义序列处理
- 构造函数调用包装
- 代码执行上下文处理
高级应用与安全考量
实际应用场景
JSFuck虽看似玩具,却有实际应用价值:
- 代码混淆:将敏感代码转换为难以阅读的形式
- XSS攻击:在输入过滤严格的环境中注入代码
- 教育研究:深入理解JavaScript类型系统和隐式转换
安全风险与防御
由于JSFuck可绕过字符过滤,存在潜在安全风险:
-
防御策略:
- 实施严格的CSP策略
- 限制eval及类似函数的使用
- 监控异常代码执行模式
-
检测方法:
- 识别大量重复的
[]()!+字符模式 - 监控不寻常的类型转换序列
- 使用AST分析检测可疑代码结构
- 识别大量重复的
性能优化技巧
JSFuck代码通常比原始代码大100倍以上,可通过以下方式优化:
- 公共子表达式提取:复用已构建的字符和函数
- 批量字符处理:优先使用内置方法处理字符串
- 避免嵌套过深:控制表达式复杂度以提高执行效率
总结与进阶学习
核心知识点回顾
本文从基础到实践,带你掌握了:
- JSFuck原理:6字符构建所有JS概念的核心技术
- 字符映射:从布尔值到复杂对象的完整构建路径
- 工具使用:fuck.js与index.html的应用
- 实战开发:"Hello World"的手动编码与自动化实现
进阶学习资源
-
源码研究:
- jsfuck.js中的MAPPING与CONSTRUCTORS对象
- test/jsfuck_test.js中的测试用例
-
扩展技术:
- 使用其他受限字符集(如仅用括号)
- JSFuck与其他深奥语言的结合
- 浏览器环境与Node.js环境的差异处理
-
社区贡献:
- 参与项目开发:gh_mirrors/js/jsfuck
- 提交新字符映射方案
- 优化转换算法性能
JSFuck不仅是一种编程技巧,更是理解JavaScript本质的绝佳途径。通过这种极端约束下的编程练习,你将获得对JavaScript类型系统、隐式转换和函数式编程的深刻认识。现在,轮到你用这6个字符创造自己的JS奇迹了!
如果你觉得本文对你有帮助,请点赞收藏,并关注后续关于高级JSFuck技巧的文章。下次我们将探讨如何用JSFuck实现复杂数据结构和算法!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



