Search code examples
javascriptencodingcharacter-encodingminify

Minifying javascript via unicode


on dwitter.net i often see dweets that are encoded interestingly to minify the JS to character count.

for example https://www.dwitter.net/d/22372 (or https://www.dwitter.net/d/11506)

eval(unescape(escape`𮀮𩡯𫡴🐧𜡥𫐠𨐧𛁸𛡦𪑬𫁔𩑸𭀨𙱜𭐲𝠲𜀠𙰬𜰬𜠵𚐊𭀿𜀺𩀽𮀮𩱥𭁉𫑡𩱥𡁡𭁡𚀰𛀰𛁶🐳𝠬𭠩𛡤𨑴𨐊𩡯𬠨𨰮𭱩𩁴𪁼👷👩🐹𜰶𞱩𛐭𞰩𩐽𪐥𭠪𝠬𩁛𪐪𝀫𜱝🠵𜁼𯁸𛡦𪑬𫁒𩑣𭀨𦀽𩐫𩐯𜠪𤰨𭀭𪐯𭰩𚱷𛁩𛰳𛑥𚡃𚁴𛑘𛰹𞐩𚱥𚰵𜀬𞐬𪐼𜐿𭰺𞐩`.replace(/u../g,'')))

Now I understand how to decode this and read the javascript, it's pretty trivial

unescape(escape`𮀮𩡯𫡴🐧𜡥𫐠𨐧𛁸𛡦𪑬𫁔𩑸𭀨𙱜𭐲𝠲𜀠𙰬𜰬𜠵𚐊𭀿𜀺𩀽𮀮𩱥𭁉𫑡𩱥𡁡𭁡𚀰𛀰𛁶🐳𝠬𭠩𛡤𨑴𨐊𩡯𬠨𨰮𭱩𩁴𪁼👷👩🐹𜰶𞱩𛐭𞰩𩐽𪐥𭠪𝠬𩁛𪐪𝀫𜱝🠵𜁼𯁸𛡦𪑬𫁒𩑣𭀨𦀽𩐫𩐯𜠪𤰨𭀭𪐯𭰩𚱷𛁩𛰳𛑥𚡃𚁴𛑘𛰹𞐩𚱥𚰵𜀬𞐬𪐼𜐿𭰺𞐩`.replace(/u../g,''))

returns

x.font='2em a',x.fillText('\u2620 ',3,25)
t?0:d=x.getImageData(0,0,v=36,v).data
for(c.width|=w=i=936;i--;)e=i%v*6,d[i*4+3]>50||x.fillRect(X=e+e/2*S(t-i/w)+w,i/3-e*C(t-X/99)+e+50,9,i<1?w:9)

but what I don't understand is how to encode js like this.

I noticed there is an intermediary step in this process

running:

escape`𮀮𩡯𫡴🐧𜡥𫐠𨐧𛁸𛡦𪑬𫁔𩑸𭀨𙱜𭐲𝠲𜀠𙰬𜰬𜠵𚐊𭀿𜀺𩀽𮀮𩱥𭁉𫑡𩱥𡁡𭁡𚀰𛀰𛁶🐳𝠬𭠩𛡤𨑴𨐊𩡯𬠨𨰮𭱩𩁴𪁼👷👩🐹𜰶𞱩𛐭𞰩𩐽𪐥𭠪𝠬𩁛𪐪𝀫𜱝🠵𜁼𯁸𛡦𪑬𫁒𩑣𭀨𦀽𩐫𩐯𜠪𤰨𭀭𪐯𭰩𚱷𛁩𛰳𛑥𚡃𚁴𛑘𛰹𞐩𚱥𚰵𜀬𞐬𪐼𜐿𭰺𞐩`

returns

%uD878%uDC2E%uD866%uDC6F%uD86E%uDC74%uD83D%uDC27%uD832%uDC65%uD86D%uDC20%uD861%uDC27%uD82C%uDC78%uD82E%uDC66%uD869%uDC6C%uD86C%uDC54%uD865%uDC78%uD874%uDC28%uD827%uDC5C%uD875%uDC32%uD836%uDC32%uD830%uDC20%uD827%uDC2C%uD833%uDC2C%uD832%uDC35%uD829%uDC0A%uD874%uDC3F%uD830%uDC3A%uD864%uDC3D%uD878%uDC2E%uD867%uDC65%uD874%uDC49%uD86D%uDC61%uD867%uDC65%uD844%uDC61%uD874%uDC61%uD828%uDC30%uD82C%uDC30%uD82C%uDC76%uD83D%uDC33%uD836%uDC2C%uD876%uDC29%uD82E%uDC64%uD861%uDC74%uD861%uDC0A%uD866%uDC6F%uD872%uDC28%uD863%uDC2E%uD877%uDC69%uD864%uDC74%uD868%uDC7C%uD83D%uDC77%uD83D%uDC69%uD83D%uDC39%uD833%uDC36%uD83B%uDC69%uD82D%uDC2D%uD83B%uDC29%uD865%uDC3D%uD869%uDC25%uD876%uDC2A%uD836%uDC2C%uD864%uDC5B%uD869%uDC2A%uD834%uDC2B%uD833%uDC5D%uD83E%uDC35%uD830%uDC7C%uD87C%uDC78%uD82E%uDC66%uD869%uDC6C%uD86C%uDC52%uD865%uDC63%uD874%uDC28%uD858%uDC3D%uD865%uDC2B%uD865%uDC2F%uD832%uDC2A%uD853%uDC28%uD874%uDC2D%uD869%uDC2F%uD877%uDC29%uD82B%uDC77%uD82C%uDC69%uD82F%uDC33%uD82D%uDC65%uD82A%uDC43%uD828%uDC74%uD82D%uDC58%uD82F%uDC39%uD839%uDC29%uD82B%uDC65%uD82B%uDC35%uD830%uDC2C%uD839%uDC2C%uD869%uDC3C%uD831%uDC3F%uD877%uDC3A%uD839%uDC29

which then gets regex replaced with .replace(/u../g,''), but getting this string from minified javascript isn't easy for me.

simply running encodeURIComponent() or escape() doesn't get you quite there, though it gets you part of the way there.

So how do I get the string of my javascript converted into a string containing %uD then the character code for each?


Solution

  • I am also on dwitter. The code compressor actually began with a dweet (https://www.dwitter.net/d/23092). It was made so people could add more bytes into their demos by going right up to 194 chars instead of having the limit of 140.

    Note this does not reduce the byte size. Even though this reduces the amount of characters, the size stays the same

    There is also an uncompressor at https://www.dwitter.net/d/14246

    The simplified code for this is a simple unpack function:

    function unpack(strange_blocky_code) {
        const index = code.toLowerCase().search(/eval\(unescape\(escape`/g)
                    
        if (index >= 0) {
            const start = strange_blocky_code.slice(0, index)
            const end = strange_blocky_code.slice(index)
            const result = eval(end.slice(4))
    
            if (result) return start + result // returns readable (but trivial) code
        }
    }
    

    The simplified compressing code is:

    function compress(readable_code) {
        const value = [...readable_code.trim()]
        let code = ''
    
        for (let character of value) {
            const char = character.charCodeAt(0)
            if (char > 255) character = escape(character).replace(/%u/g, "\\u")
            code += character
        }
    
        const compressed =
        String.fromCharCode(...[...code.length % 2 ? code + ";" : code]
            .map((item, index) =>
                item.charCodeAt() | (index % 2 ? 0xDF00 : 0xDB00)
            )
        )
    
        return `eval(unescape(escape\`${compressed}\`.replace(/u../g,'')))`
    }
    

    If you're looking for editors, these are two that I like to use:

    I hope this helps at all.