JSFuck是如何编码的

JSFuck []()!+

SFuck 是一种基于 JavaScript 原子部分的深奥且具有教育意义的编程风格。它仅使用六个不同的字符来编写和执行代码。
By @aemkei
它不依赖于浏览器,因此您甚至可以在 Node.js 上运行它。

加密: jsfuck.com

解密:de4js

Example

以下代码将执行alert(1)

[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[
]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]
])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+
(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+
!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![
]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]
+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[
+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!!
[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![
]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[
]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![
]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(!
[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])
[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(
!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[
])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()

Basics

false       =>  ![]
true        =>  !![]
undefined   =>  [][[]]
NaN         =>  +[![]]
0           =>  +[]
1           =>  +!+[]
2           =>  !+[]+!+[]
10          =>  +[[+!+[]]+[+[]]]
Array       =>  []
Number      =>  +[]
String      =>  []+[]
Boolean     =>  ![]
Function    =>  []["filter"]
run         =>  []["filter"]["constructor"]( CODE )()
eval        =>  []["filter"]["constructor"]("return eval")()( CODE )
window      =>  []["filter"]["constructor"]("return this")()

参阅完整列表 here.

How it Works

[] – 中括号

让我们从左括号和右括号开始,看看这里可以做什么。它们对于这个项目非常有用,并被视为核心元素,因为它们提供了一种方法:

  1. 处理数组
  2. 访问属性和方法。

[] – 数组文字

创建新数组:

[]   // 一个空数组
[[]] // 包含一个元素(另一个数组)的数组

[X][i] – 数组/对象访问

[][[]] // undefined, 与[][""]一样

稍后我们将能够这样做:

"abc"[0]     // 获取单个字母
[]["length"] // 获取属性
[]["fill"]   // 获取方法

[X][0] - 数组环绕技巧

通过将表达式包装在数组中,然后获取索引零处的元素,我们可以对一个表达式应用多个运算符。这意味着方括号“[]”可以代替圆括号“()”来隔离表达式:

          [X][0]           // X
++[ ++[ ++[X][0] ][0] ][0] // X + 3

+ – 加号

这个符号很有用,因为它允许我们:

  1. 创建数字
  2. 两个value相加
  3. 连接字符串
  4. 创建字符串

前版本的 JSFuck 经常使用它,但我们不确定它们是否是基础的。

转换为Number

+[] // 0 - the number 0

数字自增

使用上面提到的数组包装技巧:

++[ 0  ][  0  ] // 1
++[ [] ][ +[] ] // 1

获取undefined字符串

通过空数组中的索引获取元素将返回“undefined”:

[][   0 ] // undefined
[][ +[] ] // 获取空数组第一个元素 (undefined)
[][  [] ] // look for property ""

获取NaN

undefined 转换为 Number 将导致非数字:

+[][[]]    // +undefined = NaN

数值相加

          1 +           1 // 2
++[[]][+[]] + ++[[]][+[]] // 2

更短的方式使用 ++:

++[          1][  0] // 2
++[++[[]][  0]][  0] // 2
++[++[[]][+[]]][+[]] // 2

使用这种技术,我们能够访问所有数字:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

+[] – 转换成String

组合加号和括号会将其他值转换为字符串:

  []        +[] // "" - empty string
 +[]        +[] // "0"
  [][[]]    +[] // "undefined"
++[][[]]    +[] // "NaN"
++[[]][+[]] +[] // "1"

"word"[i] – 获取单个字符

由于我们有字符串,我们也可以获得单个字符:

  "undefined"          [  0] // "u"
[ "undefined"    ][  0][  0] // "u"
[  undefined +[] ][+[]][+[]] // "u"
[  [][[]]    +[] ][+[]][+[]] // "u"
  "undefined"   [           1 ] // "n"
[[][[]]+[]][+[]][ ++[[]][+[]] ] // "n"

由于我们有“NaN”和“undefined”,因此我们得到以下字符:

N,a,d,e,f,i,n,u.

+ – 组合字符

现在我们可以将字符连接到新单词。

// can be written using []+ only:
"undefined"[4] // "f"
"undefined"[5] // "i"
"undefined"[6] // "n"
"undefined"[3] // "d"

// combine using +
"f"+"i"+"n"+"d" // "find"

"e" – 指数表示法中的数字

由于我们有来自undefined的字符e,我们可以使用指数表示法来构造非常大的数字并获得对Infinity的引用:

+("1e309")         //  Infinity
+("1e309")     +[] // "Infinity"
+("11e100")        //  1.1e+101
+("11e100")    +[] // "1.1e+101"   (gives us `.` and `+`)
+("0.0000001")     //  1e-7
+("0.0000001") +[] // "1e-7"       (gives us `-`)

结果字符:

I,f,i,n,t,y,.,+,-.

[]["method"] – 访问方法

新组合的字符可以形成方法名称。可以使用方括号表示法来访问它们:

[]["f"+"i"+"n"+"d"] // 其中“f”是“false”的第一个字符,依此类推
[]["find"]          // 和.find语法一样:
[] .find

Note: 对于"undefined", “NaN” and "Infinity"的字符, 我们能够在现有对象中找到的唯一方法是 Array.prototype.find.

method+[] – 获取方法定义

我们可以将一个方法转换为String并获取其定义:

[]["find"] +[]

返回下面String:

"function find() { [native code] }"

Note: native functions字符串表示形式不是 ECMAScript 标准的一部分,并且在浏览器之间有所不同。 例如,Firefox 将输出稍微不同的字符串,并使用附加换行符\n.

结果字符:

  • a,c,d,e,f,i,n,o,t,u,v
  • , {, }, (, ), [,]

由此产生的方法:

  • .concat
  • .find.

! – 逻辑非运算符

这是原始 JSFuck 集中的第四个字符,用于创建布尔值。

Note: 该符号也可以替换为其他符号,例如< or =. 请参阅下面的“替代方案”部分。

!X – 转换为 Boolean

逻辑非运算符可用于创建布尔值false and true:

 ![] // false
!![] // true

!X+[] – 获取"true" and “false”

Booleans 可以转换成 string:

 ![] +[] // "false"
!![] +[] // "true"

这将使我们能够访问更多字符:

a, e, f, l, r, s, t, u.

与上面的集合一起,我们将有{}()[]+. INacdefilnorstuvy 可以访问这些方法:

  • call
  • concat
  • constructor
  • entries
  • every
  • fill
  • filter
  • find
  • fontcolor
  • includes
  • italics
  • reduce
  • reverse
  • slice
  • sort

Important: 我们可能会使用其他符号,例如 = 创建布尔值,因为它们更强大 (请参阅下面的“替代方案”部分。).

X["constructor"] – 原始包装名称

通过 .constructor 我们可以引用创建实例的函数。 对于原始值,它返回相应的内置包装器:

0       ["constructor"] // Number
""      ["constructor"] // String
[]      ["constructor"] // Array
false   ["constructor"] // Boolean
[].find ["constructor"] // Function

使用 +[] 将它们转换为字符串并检索它们的函数名称以获得更多字符:

0["constructor"]+[] // "function Number() { ... }"

New chars available :
m, b, S, g, B, A, F.

…以及更多方法和属性

  • arguments
  • big
  • bind
  • bold
  • name
  • small
  • some
  • sub
  • substr
  • substring
  • toString
  • trim

() – 小括号

调用方法

由于我们可以访问方法,因此我们可以调用它们来获得更多功能。为此,我们需要引入另外两个符号 ( and ) .

没有参数的例子:

""["fontcolor"]()   // "<font color="undefined"></font>"
[]["entries"]() +[] // "[object Array Iterator]"

结果字符:

j, <, >, =, ", /

使用多个参数调用方法

调用具有多个参数的方法并非易事 - 为此,您可以使用以下命令
technique (discovered by trincot) - 例如:

调用字符串方法 "truefalse".replace("true","1") 可以写成 ["true", "1"].reduce("".replace.bind("truefalse")) 最后:

["true"]["concat"]("1")["reduce"](""["replace"]["bind"]("truefalse"))

调用数组方法 [1,2,3].slice(1,2) 可以写成 [1,2].reduce([].slice.bind([1,2,3])) 最后:

[1]["concat"](2)["reduce"]([]["slice"]["bind"]([1,2,3]))

在“流程”中调用具有多个参数的字符串方法

为了能够在上一个方法的结果的右侧调用方法(具有多个参数),您可以使用此方法
technique (discovered by trincot) - 例如: "truefalse".replace("true","1").replace("false","0") can be written as

"truefalse"
    .split().concat([["true", "1"]]).reduce("".replace.apply.bind("".replace))
    .split().concat([["false", "0"]]).reduce("".replace.apply.bind("".replace))

最后:

"truefalse"
  ["split"]()["concat"]([["true"]["concat"]("1")])["reduce"](""["replace"]["apply"]["bind"](""["replace"]))
  ["split"]()["concat"]([["false"]["concat"]("0")])["reduce"](""["replace"]["apply"]["bind"](""["replace"]))

在“流程”中调用具有多个参数的数组方法

要以右侧(流)方式调用数组方法”,我们使用与字符串类似的技术,但有额外的技巧(details here) presented in following example: [3,4,5].slice(1,2).concat(6) 可以写成 [[3,4,5]].concat([[1,2]]).reduce([].slice.apply.bind([].slice)).concat(6) (类似于字符串) 但现在我们需要找到右手边的方式来包装数组 [3,4,5] 和获取 [[3,4,5]] 和按照以下方法 [3,4,5].map([].constructor).concat([[[]]])[0].slice(-1) 所以我们得到

[3,4,5]
    // call: slice(1,2) 
    .map([].constructor).concat([[[]]])[0].slice(-1)
    .concat([[1,2]]).reduce([].slice.apply.bind([].slice))
    // call next method (in flow)
    .concat(6) 

最后(删除点和逗号后)

[3]["concat"](4)["concat"](5)
    ["map"]([]["constructor"])["concat"]([[[]]])[0]["slice"](-1)
    ["concat"]([[1]["concat"](2)])["reduce"]([]["slice"]["apply"]["bind"]([]["slice"]))
    ["concat"](6) 

number.toString(x) – 获取任意小写字母

Number的 toString 方法有一个可选参数,指定要使用的基数 (between 2 and 36). 使用基数 36 我们可以获取 lowercase

10["toString"](36) // "a"
11["toString"](36) // "b"
...
34["toString"](36) // "y"
35["toString"](36) // "z"

暴露出的字符: abcdefghijklmnopqrstuvwxyz

Function("code")() – 执行代码

Function 构造函数是 JSFuck 中的万能钥匙: 它接受一个字符串作为参数,并返回一个新的匿名函数,并以此字符串作为函数体。所以它基本上可以让你将任何代码作为字符串进行评估。这就像 eval, 无需引用全局范围 (a.k.a. window). 我们能得到 Function constructor e.g. with []["find"]["constructor"].

这是第一个主要步骤,也是 JS-to-JSFuck 编译器的重要组成部分.

Function("return this")() – window

当执行 function anonymous() { return this }, 我们在这里获得调用上下文,它是对全局范围的引用: window!

获得对window的引用是 JSFuck 向前迈出的又一大步。 使用括号字符,我们只能挖掘可用的对象:数字、数组、一些函数…以及对全局范围的引用, 我们现在可以访问任何全局变量以及这些全局变量的内部属性。

创建正则表达式对象

您可以创建正则表达式,例如 /pattern/g 如下

[]["fill"]["constructor"]("return RegExp")()("pattern","g")

删除逗号后 (通过使用 multi-arguments technique without binding) 看起来如下:

["pattern"]["concat"]("g")["reduce"]([]["fill"]["constructor"]("return RegExp")())

替代方案

组合字符

代替 + 我们可以使用 .concat 组合字符:

"f"["concat"]("i")["concat"]("l")["concat"]("l") // fill

问题:我们需要组合“c”、“o”、“n”、“c”、“a”和“t”来得到“concat”。

布尔值

! 可能会被替换为具有多种用途的更“强大”的字符。

= – Boolean + 赋值

X == X // true
X == Y // false
X = Y  // assign a new value

> – Boolean + 创建 Numbers

X > Y  // true
X > X  // false
X >> Y // number

一个更复杂的例子是仅使用 []>+ 来获取字符“f”:

[[ []>[] ] + [] ] [[]>>[]] [[]>>[]]
[[ false ] + [] ] [     0] [     0]
[ "false"       ] [     0] [     0]
  "false"                  [     0]

Numbers

代替 + 我们可以使用布尔值和位移运算符来创建数字:

true >> false         // 1
true << true          // 2
true << true << true  // 4

问题: 一些数字 (like 5) 更难得到。. 但使用字符串时是可能的,例如 "11" >> true.

执行函数

除了使用 () 之外的执行函数的方式:

  1. 使用反引号: `
  2. 处理事件: on...
  3. 构造函数: new ...
  4. 类型转换: toString|valueOf
  5. 符号数据类型: [Symbol...]

使用反引号

代替左括号和右括号, 我们可以使用反引号 ` 执行方法. 在 ES6 中,它们可用于插入字符串并为标记模板文字提供表达式。

([]["entries"]``).constructor // Object

这将为我们提供“Object”中的字符并访问其方法。

不幸的是,我们只能传递一个字符串 (从我们的基本字母表中,例如 []!+) 作为参数.无法调用具有多个参数或预编译字符串的方法。 为此,我们必须使用“${}”进行表达式插值,这会引入新字符。

详细讨论了反引号的可能性 in the Gitter chat room.

映射类型转换

另一种执行不带括号的函数的方法是映射.toString.valueOf方法并隐式调用它们。

A = []
A["toString"] = A["pop"]
A+"" // will execute A.pop

Note: 没有办法传递参数,它需要 = 出现在我们的基本字母表中. 它仅适用于返回基本类型的方法。

到目前为止,唯一的用例是在 Firefox 中连接 .toSource 以获得特殊字符,例如反斜杠 \.

触发事件处理程序

函数或方法也可以通过将它们分配给事件处理程序来执行。有几种方法可以做到这一点,例如:

// 启动时覆盖 onload 事件
onload = f

// 写入图像标签
document.body.innerHTML = '<img οnerrοr=f src=X />'

// 抛出并处理错误
onerror=f; throw 'x'

// 触发事件
onhashchange = f; location.hash = 1;

Note: 我们需要 = 来分配处理程序。

Problem: 我们无法访问windowDOM 元素来附加事件处理程序。

Constructor

我们还可以使用new运算符将函数作为伪对象类型调用:

new f

Problem: 我们的基本符号集还不能使用new运算符。

Symbol

Symbol是唯一且不可变的数据类型,可用作对象属性的标识符。这可用于隐式调用函数。

f[Symbol.toPrimitive] = f;  f++;
f[Symbol.iterator]    = f; [...f];

Note: 我们需要“=”来分配函数。

Problem: 我们无法使用简化的字符集访问“Symbol”。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值