This repo contains some thoughts by maps aka msramalho on the problems of the ninja challenge 2018, by jscrambler
Write code that executes the same as the following code in the least amount of characters:
function seq(ΘΆΘ){
var Λ γ=0,Λ‘Ι=Λ γ,γΙ='',ΙΚΆ=10,Σ·γ=Λ‘Ι+1,Θ±Λ='length',ΘΏΟ§=ΘΆΘ&&Λ‘Ι+ΙΚΆ;
function Κ²γ(){Λ γ=Σ·γ;;}
function γΚ±(){Σ·γ=ΘΏΟ§;}
function γΉΟΌ(){Λ‘Ι+=γγ²(Κ²γ,γΙ)[Θ±Λ]-γγ²(γΙ,γΚ±)[Θ±Λ]}
function Ιγ΅(){return Λ γ+Σ·γ}
function ΚΉΟ‘(Σ·γ){return γγ²(ΙΚΆ*(ΙΚΆ),Ιγ΅())}
function ΣΏΡ©(ΟΎγ±){return Λ‘Ι===ΙΚΆ*ΟΎγ±}
function Λ’γΊ(ΟΎγ±){γΉΟΌ();ΘΏΟ§=ΣΏΡ©(ΟΎγ±)?ΚΉΟ‘(Λ‘Ι):Ιγ΅();(Κ²γ(),γΚ±());}
function γγ²(Κ²γ,γΚ±,γΉΟΌ){return γΉΟΌ?Κ²γ||γΚ±:(γΚ±)+Κ²γ;}
while(Λ‘Ι<ΘΆΘ){Λ’γΊ(Θ±Λ[(Θ±Λ)]-γγ²(γΙ,ΙΚΆ)[Θ±Λ]);}
return γγ²(ΘΏΟ§,γγ²(γΚ±,γΙ)[Θ±Λ]-γγ²(γΙ,γΚ±)[Θ±Λ],γγ²);
}
After some testing and simplification I came to understand that this function is essentially a fibonacci sequence where the 40th element had an increment of 100 and this change propagated for the consequent numbers. The function also had a few peculiarities for return values of other values (negatives, non-numeric, ...).
- (You only need to submit the body of the function and the header given is:
function sequence(max){...}
) - Notice that weird characters are representing names of variables (
Λ γ
) and names of functions (ΚΉΟ‘
). - Replace all occurrences of each of these patterns with friendly characters, like letters from
a
toq
and work from there... βthis is not so simpleβ if you analyse the code properly, you'll notice that, in certain points, thelength
property is used on functions (the number of bytes of that function when converted to text), which means that ifΚΉΟ‘
is replaced bya
then 3 bytes are lost and this leads to a function that does something else!! However, if you replace carefully (respecting byte length) this will produce a more readable piece of code. (The same type of thing would happen if you tried to replace the;;
by;
) - Next steps are essentially simplifying, here are some of the more obvious things:
- replace function
length
s by their value - remove unused functions
- keep on removing variables
- simplify operators (a lot can be done on this topic):
===
for==
where possible,a+=b
instead ofa = a + b
, ...
- replace function
- You will eventually be able to remove all functions from the code and end up with something like (of course all unnecessary whitespace can also be removed):
var n = 0,
e = 0,
f = 1,
o = !!max;
for (; e < max;) o = 40 == ++e ? 100 + n + f : n + f, n = f, f = o;
return o || 0
- At this stage I might have tried to use something like a js minifier and learn some new tactics like declaring the variables inside the loop, something like [79 bytes]:
for(var a=0,r=0,n=1,f=!!max;r<max;)f=40==(r+=1)?100+a+n:a+n,a=n,n=f;return f||0
- Now a silver lining: if you can understand and apply JS operator precedence you can start doing simultaneous updates of the variables inside the loop, instead of comma separated expressions, here is an intermediary example [74 bytes]:
for(var n=0,e=0,f=1,o=!!max;e<max;)o=n+(n=f)+100*(40==++e),f=o;return o||0
- I also did some attempts at this simultaneous assignment using lists, but this approach was to costly in terms of bytes [76 bytes]:
for(var n=0,e=0,f=1,o=!!max;e<max;)n=[f,o=f+=n+100*(40==++e)][0];return o||0
- Next, there was more simultaneous assignment simplification to be done [67 bytes]:
for(var n=0,e=0,f=1,o=max;e<max;f=o=n+(n=f)+100*(40==++e));return o
- At this stage, there was still an unnecessary variable [62 bytes]:
for(var n=0,e=0,f=1;e<max;f=n+(n=f)+100*(40==++e));return e&&f
- At this point there seemed little hope of improval, but a counter intuitive approach actually worked: reverting the cycle so that we remove the counter variable and decrease the value of
max
instead, like so [62 bytes] as well (we need to compare to39!=102334155
instead of40
), but...:
for(var a=0,d=1;max--;d+=a+100*((a=d)==102334155));return a&&d
- At this point, I dare the reader to think of approaches, because it was not that obvious...
- The click happened when an alternative to
((a=d)==102334155)
was actually to use the%
module operator, given that((a=d)%1000==155)
was true and there was no collision in the tests... [61 bytes]
for(var a=0,d=1;max--;d+=a+100*((a=d)%1000==155));return a&&d
- Also, in JS
1000 == 1e3
, so another byte... - But then... what if the module operator could be taken further... this required a
for
loop to look forx
such that(a=d)%x==y
in order to makey
have a single digit (y<10
)... and this was possible, for example forx=61,y=6
[57 bytes] (notice thatx
andy
had to be such that there was no collision in the previous factorials... this also took some time to test!!):
for(var a=0,d=1;max--;d+=a+100*((a=d)%61==6));return a&&d
- You can't go any further, right? wrong, always wrong... What if there was an
x
so that(a=d)%x==0
? that would be the same amount of bytes asx=61,y=6
... yes, but ify=0
, we could still go further... Meet magic number 77!! [55 bytes]:
for(var a=0,d=1;max--;d+=a+100*!((a=d)%77));return a&&d
- This is as far as I went: 55 f**king bytes. Nevertheless, someone was able to take it further (I estimate by one byte), but you may still do better so any PR is welcome and kudos will be widespread if you can improve this answer(I suspect a different approach would be needed)!!
- Don't waste time, simple copy and paste the orignal code into a minifier which would output [273 bytes] (without using
max
- that must be done!) not a bad start :
var t=0,u=t,r="",f=10,o=u+1,c="length",i=n&&u+f;function e(){t=o}function a(){o=i}function g(){return t+o}function h(n){u+=l(e,r)[c]-l(r,a)[c],i=u===f*n?l(f*f,g()):g(),e(),a()}function l(n,t,u){return u?n||t:t+n}for(;u<n;)h(c[c]-l(r,f)[c]);return l(i,l(a,r)[c]-l(r,a)[c],l)
- This would save steps 1. and 2., which is a lot!! The other steps are essentially the same.
var r=0,t=0,u=1,e=!!max;function f(){return r+u}for(;t<max;)e=40==(t+=1)?100+f():f(),r=u,u=e;return e||0
var n=0,e=0,f=1,o=!!max;for(;e<max;)o=40==(e+=1)?100+n+f:n+f,n=f,f=o;return o||0
var n=0,e=0,f=1,o=!!max;for(;e<max;)o=40==++e?100+n+f:n+f,n=f,f=o;return o||0
for(var n=0,e=0,f=1,o=!!max;e<max;)o=f+n+100*(40==++e),n=[f,f=o][0];return o||0
for(var n=0,e=0,f=1,o=!!max;e<max;)n=[f,o=f+=n+100*(40==++e)][0];return o||0
for(var n=0,e=0,f=1,o=!!max;e<max;)o=n+f+100*(40==++e),n=f,f=o;return o||0
for(var n=0,e=0,f=1,o=!!max;e<max;)o=n+(n=f)+100*(40==++e),f=o;return o||0
for(var n=0,e=0,f=1,o=!!max;e<max;)f=o=n+(n=f)+100*(40==++e);return o||0
for(var n=0,e=0,f=1,o=!!max;e<max;)f=o=n+(n=f)+100*(40==++e);return +o
for(var n=0,e=0,f=1,o=+max;e<max;)f=o=n+(n=f)+100*(40==++e);return +o
for(var n=0,e=0,f=1,o=+max;e<max;)f=o=n+(n=f)+100*(40==++e);return o
for(var n=0,e=0,f=1,o=max;e<max;)f=o=n+(n=f)+100*(40==++e);return o
for(var n=0,e=0,f=1,o=max;e<max;f=o=n+(n=f)+100*(40==++e));return o
for(var n=0,e=0,f=1;e<max;f=n+(n=f)+100*(40==++e));return e&&f
for(var a=0,d=1;max--;d+=a+100*((a=d)==102334155));return a&&d
for(var a=0,d=1;max--;d+=a+100*((a=d)%1000==155));return a&&d
for(var a=0,d=1;max--;d+=a+100*((a=d)%1e3==155));return a&&d
for(var a=0,d=1;max--;d=a+100*(a%1e2==86)+(a=d));return a&&d
for(var a=0,d=1;max--;d=a+100*(a%86==38)+(a=d));return a&&d
for(var a=0,d=1;max--;d=a+100*(a%35==6)+(a=d));return a&&d
for(var a=0,d=1;max--;d+=a+100*((a=d)%61==6));return a&&d
for(var a=0,d=1;max--;d+=a+100*((a=d)%77==0));return a&&d
for(var a=0,d=1;max--;d+=a+100*!((a=d)%77));return a&&d
for(var a=1,g=0;max--;)g+=a+!((a=g||1)%77)*100;return g
Replace <OUR CODE GOES HERE>
by a condition that allows the ninja to detect that there was some sabotage. The ninja will perform 5 weapon attacks, reload, and attack 5 times once more, so as to kill both enemies (DrunkenFist
and FlyingPunch
) after 10 throws. I advise you to look at the code and to understand what is going on.
'use strict';
var EnemyNinja = function(name) {
return (function() {
var _health = 100;
var _name = name;
return Object.freeze({ // methods and properties of this object cannot be changed
getName: function() {
return '' + _name;
},
getHealth: function() {
return _health;
},
takesDamage: function(damage) {
_health -= damage;
return _health;
}
});
})();
};
var DrunkenFist = new EnemyNinja('Drunken Fist');
var FlyingPunch = new EnemyNinja('Flying Punch');
var Shuriken = (function() {
var _energy = 100;
var ENERGY_UNIT = 1,
ENERGY_ATTACK_COST = ENERGY_UNIT * 20;
var DAMAGE_ON_HIT = 20;
return Object.freeze({
recharge: function fn() {
if (
// <OUR CODE GOES HERE>
) {
throw 'Weapon-tampering-detected-o-jutsu!';
}
if (_energy <= 100) {
_energy += ENERGY_UNIT;
}
},
'throw': function(enemyTarget) {
var _weaponThrown = false;
if (_energy > 0) {
_energy -= ENERGY_ATTACK_COST;
enemyTarget.takesDamage(DAMAGE_ON_HIT);
_weaponThrown = true;
}
return _weaponThrown;
}
});
})();
My first few approaches were a bit overcomplicated, I was trying to assert multiple conditions at once, but given the script and the assumption that the ninja would alwyas attack 5 times (5 * 20 == 100
) recharge for 100 times (one energy point per loop) and then attack 5 more, things got easier.
I need a counter to make sure that the number of times the functions is called match the value of _energy
the ninja weapon should have.
But we could only write code inside the condition, there is no way to do a var x = 0;
in there... nor would we be able to keep the value of that variable over the next iterations...
Remember, everything in JS is an object, and so are functions! What I did was add a property to the fn
function so that it could be incremented on every call of itself. How to do this? checking for its precious instantiation with typeof
and ternary operators, incrementing if it is defined and setting to 0 if it is not:
fn.called = (typeof fn.called === "undefined") ? 0 : fn.called + 1
So the only thing to do now would be to compare it to the expected value of energy:
if (
(fn.called = (typeof fn.called === "undefined") ? 0 : fn.called + 1) != _energy
) {
throw 'Weapon-tampering-detected-o-jutsu!';
}
Thre you go, it works!
However, there was another approach I was latter told to work, if I recall correctly it was...
_energy < 0
Yes, just that... this was partly due to the simplicity of the so-called tampering they performed... Many other conditions could be used, but it was just a matter of trying some simpler things before overdoing it.
fn !== this.recharge
There is a super secure vault protected by a powerful security mechanism, that holds priceless jewels, only accessible to those wit a valid. However, the code for validating keys has been leaked and you need to be fastest at finding a valid key!!! Here is the leaked code:
(function(p) {
p[0].parseValue = p[1][4](p[1]);
}([
window, [
function(s) {
return s[0] = s[1].join(''), typeof eval(s[0]) === 'function' && s[0] === 'alert';
},
function(H) {
for (H[1] = 0, H[2] = H[0].length; H[1] < H[2]; ++H[1]) {
H[0][H[1]] = String.fromCharCode(H[0][H[1]].charCodeAt(0) + 1);
}
},
function(g) {
return String.fromCharCode(g[0]) === g[1];
},
function(U) {
while (U[1] != 0) {
U[2] = U[0] & U[1], U[0] = U[0] ^ U[1], U[1] = U[2] << 1;
}
return U[0];
},
function(l) {
return function(j) {
var m;
return m = [], j.match(/([2-70-18-9]){1,}([^6-90-5])/g).forEach(l[5]([l, m])), l[1]([m]), l[0]([j, m]);
};
},
function(A) {
return function(y) {
var r = [0];
var len = y.length - 1;
var i = 0;
for (; i < len; ++i) {
r[0] = A[0][3]([r[0], y[i]]);
}
A[0][2]([r[0], y[i]]) && A[1].push(y[i]);
};
}
]
]));
This was designed to make you despair over what function calling what function with which function returned whoose function, function. Although a good look at the code was essential to understant that it is setting the following function window.parseValue
and that that should be the function to call on your tests to check for a valid key, the best approach here would be debugging! So using a debugger or just some console logs (my approach)...
I inserted a console.log("this is function XXX being called with argumetns YYY");
in every function. What I first understood was that the key should be, in some way compatile with the regex /([2-70-18-9]){1,}([^6-90-5])/g
and so I started using some patterns that matched it like 341a
. At this stage the code would go through at stop at another point, I then realized that the pattern should be so that the sum of each digit in the key (in 341a
it is 8
) plus 1 should match the charCode value of the last char of the key. At this stage I used a key like 9999999999997s
meaning that the sum of the digits in 9999999999997
would match an r
and so the last char should be s
.
At this point, the program flow would continue and reach the eval
on the first function of the array, but it crashed inside it. After inspecting what happened to the string I supplied and the eval
called, I understood that the the key should be such that foreach instance of the pattern in it, the last chars of each instance would be concatenated and the eval
would be called. There was also this:
typeof eval(s[0]) === 'function' && s[0] === 'alert';
which led me to conclude that the chars should match the word alert
and that would evaluate to the JS alert("something");
function. After constructing it, I got something like:
99999999996`999999999998k999999999991d9999999999995q9999999999997s
This would spell alert
and the vault was open!! Other combinations of numbers and spaces could be used, but every regex match must lead to the addition of another letter in alert
!
You need to equip your messenger with the skills necessary to deliver a Message to the Empero:
From: Ninja
To: Emperor
Highness,
My spying mission was a success and I was able to gather valuable information.
The rival clan will attack the capital tomorrow at 9PM.
Ninja
However, many messengers had died on their way due to poisoning. And, in some cities, poison is also known as monkey patching. The could would be poisoned in several undisclosed places and we needed to build the antidotes for the messenger trip. This was the code we saw:
'use strict';
// <OUR CODE GOES HERE 1>
var messenger = new Messenger({
from: '',
to: '',
text: ''
});
// <OUR CODE GOES HERE 2>
var timer = setInterval(function() {
nextLocation();
if (location === CAPITAL) {
var message = messenger.deliverMessage();
// <OUR CODE GOES HERE 3>
clearInterval(timer);
}
}, 1000);
A lot to be said on this one, I will not reveal the whole solution, just give some tips and remarks on what I tried and latter learned:
- The first
<OUR CODE GOES HERE 1>
was the simplest, you needed only to create an object-returning function (aka class):
function Messenger(info) {
this.from = info.from;
this.to = info.to;
this.text = info.text;
}
This would display the next hint: You never left the castle
...
2. Many things were tried but I will only give an example. By defining my on setInterval
function I would have access to the poisoned code and I was able to do a lot of inspection to it using if
s and while(true);
s (if the code reached an infinite-loop it would take significantly longer in the server so we could due this kind of blind inspection).
3. As a matter of fact, I managed to modified their poisoned version and run a valid eval
on it, successfully entering the if
and running the third
An example for the code in <OUR CODE GOES HERE 2>
that did this, would be:
window.setInterval = function(f, time){
var s = ""+f; // convert to string
s = "var f = " + s; // cast to f variable
s = s.replace("===", "!="); // override their code
eval(s); // eval it
f(); // call f and get inside <OUR CODE GOES HERE 3>
return 1;
}
I knew I had gotten in, because my <OUR CODE GOES HERE 3>
was while(true);
π
- The error you got when doing this reverse-monkey-patching was
You tried to freeze but it is always sunny
. This was a strong enough hint, but I was too locked inside my head and could not make sense out of it (one needs to learn how to think). You needed to use some machiavelic stuff to be able to overcome it, and that is the mind challenge I leave you with... Of course I tried dozens of other things even before I got here, but these problems, in the end, just need a clear approach in which you don't sidetrack. However, if in hindsight it looks easy, let me tell you it is not! at all!
// Part 1
function Messenger (message) {
this.message = message;
this.secretMessage = message.text;
}
Messenger.prototype.deliverMessage = function () {
return this.message;
};
delete window.setInterval;
// Part 2
delete Object.freeze;
var iframe = document.body.appendChild(document.createElement('frame'));
var newWindow = iframe.contentWindow
Object.prototype.freeze = newWindow.Object.prototype.freeze;
// Part 3
message.text = messenger.secretMessage;