It is possible to call function with 2 params or more using JSFuck convention?

Viewed 352

Suppose I have some string and I want to use function "replace" (ignore console.log and white chars in your head)

console.log( 
    "truefalse"["replace"]("true") 
)

And I want to reduce used characters of this code using jsfuck (more info here) which I do by hand as follows (ignore log, comments, white chars, new lines )

console.log(
    // "truefalse"[
    ([]+!![]+![])[

    // long 'replace' word:
    []+(!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]

    // ]("true") 
    ]([]+!![])
);

As you can see I convert first code, to code which use only 6 characters: []{}!+ (this is idea of jsfuck)

Question: It is possible to call function replace with 2 arguments - Like that: "truefalse"["replace"]("true","1") and write it in similar way in JSF (without any kind of 'eval' which interpret string as code)? I have/see problem with comma which separate arguments... :(

PS: I give link to my fork of jsfuck (which is up to date - and produce much smaller output than jsfuck.com old site)

2 Answers

You can pass 2 arguments to a function by playing with Array.prototype.reduce.

The idea is to call reduce on an array with 2 values, and pass only one argument (callback) to it (i.e. no argument for the reducing starting value). That way the first and second value of that array will serve as arguments to the callback, which will only be called once.

In your example, the array with 2 values would have to be:

["true", "1"]

which can be formed using concat:

["true"]["concat"]("1")

Then call reduce on that, and pass replace as the callback function:

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

The only issue to resolve now is to ensure that the callback is called with the right this-binding, because "truefalse" does not play a role in the actual invocation of the callback call now. We can solve this with a call of .bind:

So the expression to evaluate is this one

console.log(
    ["true"]["concat"]("1")["reduce"](""["replace"]["bind"]("truefalse"))
);

Question 2 has a simple solution: prefer the new RegExp constructor; e.g. /e/g becomes new RegExp('e', 'g');

The following would resolve question 1 if it didn't rely on the ... operator:

We can replace:

console.log('a', 'b', 'c');

With:

console.log(...[ 'a' ].concat([ 'b' ]).concat([ 'c' ]));

Using .concat allows us to group values together without using commas.

I am close to saying that question 1 cannot be properly solved without interpreting a string as js code (edit: refuted by trincot!):

  • Arguments will need to be held in an array (direct arguments cannot be used since they always require commas; Array is the only datatype that can be converted into arguments)
  • You will need to use Function.prototype.apply in order to consume Array arguments
  • Function.prototype.apply will always require the use of a comma, to separate the this value from the array of arguments
  • The only way to avoid multiple arguments to Function.prototype.apply is to apply some form of currying
  • All currying will be facilitated by Function.prototype.(apply|bind|call)
  • All three of these currying methods require multiple arguments, and therefore necessitate commas!
Related