4 ms·
> JS minification is fairly mechanical and comparably simple, so the inversion should be relatively easy. Just because a task is simple doesn't mean its invers
by panda-giddiness 2y ago
> JS minification is fairly mechanical and comparably simple, so the inversion should be relatively easy.
Just because a task is simple doesn't mean its inverse need be. Examples:
- multiplication / prime factorization
- deriving / integrating
- remembering the past / predicting the future
Code unobfuscation is clearly one of those difficult inverse problems, as it can be easily exacerbated by any of the following problems:
- bugs
- unused or irrelevant routines
- incorrect implementations that incidentally give the right results
In that sense, it would be fortunate if chatGPT could give decent results at unobfuscating code, as there is no a priori expectation that it should be able to do so. It's good that you've also checked chatGPT's code unobfuscation capabilities on a more difficult problem, but I think you've only discovered an upper limit. I wouldn't consider the example in the OP to be trivial.
- lifthrasiir 2y agoOf course, it is not generalizable! In my experience though, most minifiers do only the following: - Whitespace removal, which is trivially invertible. - Comment removal, which we never expect to recover via unminification. - Renaming to shorter names, which is tedious to track but still mechanical. And most minifiers have little understanding of underlying types anyway, so they are usually very conservative and rarely reuse the same mangled identifier for multiple uses. (Google Closure Compiler is a significant counterexample here, but it is also known to be much slower.) - Constant folding and inlining, which is annoying but can be still tracked. Again, most minifiers are limited in their reasoning to do extensive constant folding and inlining. - Language-specific transformations, like turning `a; b; c;` into `a, b, c;` and `if (a) b;` into `a && b;` whenever possible. They will be hard to understand if you don't know in advance, but there aren't too many of them anyway. As a result, minified code still remains comparably human-readable with some note taking and perseverance. And since these transformations are mostly local, I would expect LLMs can pick them up by their own as well. (But why? Because I do inspect such programs fairly regularly, for example for comments like https://news.ycombinator.com/item?id=39066262 https://news.ycombinator.com/item?id=39066262)
- mgkimsal 2y ago> As a result, minified code still remains comparably human-readable with some note taking and perseverance. At least some of the time, simply taking it and reformatting to be unfolded and on multiple lines is useful enough to be readable/debuggable. FIXING that bug is likely more complex, because you have to find where it is in the original code, which, to my eyes, isn't always easy to spot.
- cal85 2y agoI feel you’re downplaying the obfuscatory power of name-mangling. Reversing that (giving everything meaningful names) is surely a difficult problem?
- lifthrasiir 2y agoI would say the actual difficulty greatly varies. It is generally easy if you have a good guess about what the code would actually do. It would be much harder if you have nothing to guess, but usually you should have something to start with. Much like debugging, you need a detective mindset to be good at reverse engineering, and name mangling is a relatively easy obstacle to handle in this scale. Let me give some concrete example from my old comment [1]. The full code in question was as follows, with only whitespaces added: function smb(){ var a,b,c,d,e,h,l; return t(function(m){ a=new aj; b=document.createElement("ytd-player"); try{ document.body.prepend(b) }catch(p){ return m.return(4) } c=function(){ b.parentElement&&b.parentElement.removeChild(b) }; 0<b.getElementsByTagName("div").length? d=b.getElementsByTagName("div")[0]: (d=document.createElement("div"),b.appendChild(d)); e=document.createElement("div"); d.appendChild(e); h=document.createElement("video"); l=new Blob([new Uint8Array([/* snip */])],{type:"video/webm"}); h.src=lc(Mia(l)); h.ontimeupdate=function(){ c(); a.resolve(0) }; e.appendChild(h); h.classList.add("html5-main-video"); setTimeout(function(){ e.classList.add("ad-interrupting") },200); setTimeout(function(){ c(); a.resolve(1) },5E3); return m.return(a.promise) }) } Many local variables should be easy to reconstruct: b -> player, c -> removePlayer, d -> playerDiv1, e -> playerDiv2, h -> playerVideo, l -> blob (we don't know which blob it is yet though). We still don't know about non-local names including t, aj, lc, Mia and m, but we are reasonably sure that it builds some DOM tree that looks like `<ytd-player><div></div><div class="ad-interrupting"><video class="html5-main-video"></div></ytd-player>`. We can also infer that `removePlayer` would be some sort of a cleanup function, as it gets eventually called in any possible control flow visible here. Given that `a.resolve` is the final function to be executed, even later than `removePlayer`, it will be some sort of "returning" function. You will need some information about how async functions are desugared to fully understand that (and also `m.return`), but such information is not strictly necessary here. In fact, you can safely ignore `lc` and `Mia` because it eventually sets `playerVideo.src` and we are not that interested in the exact contents here. (Actually, you will fall into a rabbit hole if you are going to dissect `Mia`. Better to assume first and verify later.) And from there you can conclude that this function constructs a certain DOM tree, sets some class after 200 ms, and then "returns" 0 if the video "ticks" or 1 on timeout, giving my initial hypothesis. I then hardened my hypothesis by looking at the blob itself, which turned out to be a 3-second-long placeholder video and fits with the supposed timeout of 5 seconds. If it were something else, then I would look further to see what I might have missed. [1] https://news.ycombinator.com/item?id=38346602 https://news.ycombinator.com/item?id=38346602
- drakythe 2y agoAs a point of order Code Minification != Code Obfuscation. Minification does tend to obfuscate as as side effect, but it is not the goal, so reversing minification becomes much easier. Obfuscation on the other hand can minify code, but crucially that isn't the place it starts from. As the goal is different between minificaiton and obfuscation reversing them takes different efforts and I'd much rather attempt to reverse minification than I would obfuscation. I'd also readily believe there are hundreds/thousands of examples online of reverse code minification (or here is code X, here is code X _after_ minifcation) that LLMs have ingested in their training data.
- jmb99 2y agoYeah, having run some state of the art obfuscated code through ChatGPT, it still fails miserably. Even what was state of the art 20 years ago it can't make heads or tails of.