List of issuesList of issues

#1145 consecutive NOT(!)s are being merged, altering the code behavior at times
Author: user ilufang
Date created:
Type: bug
Visibility: Everybody
Assigned to:
Labels: Decompilation
State: upgraded Help

> What steps will reproduce the problem? What is the expected output? What do you see instead? Create an swf containing code like this trace(!!"Hello"); // Evaluates to true Decompile that with ffdec, it becomes something like trace("Hello"); // Evaluates to "Hello" Basically the double-NOTs are automatically removed in the decompiled actionscript. (They are still present in pcode though) In cases like above when logical-NOTs are not used against boolean values, simply removing them can cause the code to behave differently. > What version of the product are you using? Is it "nightly build"? Which operating system do you have? I guess this is platform independent and the issue appears in all recent versions. Just in case, I am running MacOS 10.11.2, JavaSE 64 bit build 1.8.0_71-b15 and FFDec 7.1.2 Release and FFDec 7.1.2 Nightly 1243. > Please provide any additional information below. Here's the case for me (a bit irrelevant): Although using !! is illogical and redundant, the scenario actually happened in a block of manually obfuscated actionscript. The developer used the implicit toString to hide code, eg, /./(!!{}) for the letter "t". Of course this is hardly deobfuscatable through a general approach, but can you fix this so that the decompiled code can still produce the same results, making further analysis possible? Anyway, thank you very much for this wonderful project!
admin
Ad "this is hardly deobfuscatable through a general approach" Challenge accepted! I already seen a swf with this kind of things but not realized that it's actual obfuscation. Thanks for the idea. Will definitely try to get past this in the decompiler :-)
user
Haha, here is the swf containing the obfuscated code. It's located in scene.sally.__abcde__. For __abcde__ object foo, calling foo._(foo.o) gets the "protected" value (for verification), which is typically an integer between 1 and 99. Good luck! XD
DownloadSallyMain.swf (2,176 KiB)
admin
uhh, this one looks pretty harder that ones I have seen earlier, something tells me that you were a bit right with the statement "hadly deobfuscatable" :-D But thanks for the sample SWF file, maybe I will figure out something with it, chances are low, but it is definitely challenging :-). I will surely fix the !! problem.
admin
Do you have an idea which tool (obfuscator?) produces such code? You said it's "manually obfuscated". I don't think anybody will do this manually, it's a lot of work. Or could you provide some related links? Google is not helpful with this or I do not provide useful search keywords. I have seen one other SWF with similar obsfuscation so I wonder how can someone use it in his own SWF.
user
I'm sorry I am not related to any part of the creation of that swf. It is a part of the online game Kantai Collection. I also have no idea about how these crazy stuff came out. I also tried a lot of web search and got no clues : ( Here's why I think it is manually obfuscated: throughout all swf components of the game, only 7 methods are obfuscated in that way. These methods just produce meaningless parameters used in sending HTTP API requests to prevent automated bot requests. All other code is perfectly readable, without even hiding a method name. Moreover, the techniques themselves varied a lot and are so complicated in structure that I think it might just not apply to any generic function easily. The principle of its obfuscation is basically "leave NO character of the source code as-is". It also became more and more complicated as the developer updated the game. That is why I guess those developers obfuscated only these key functions by hand. In case you might need, here are the 7 main methods: mainD2.swf: mainD2.___(bytesIn, bytesOut, key) decodes Core.swf as binary data for Loader.loadBytes Core_decoded.swf: common.util.Util.$$ (display as method_61 in source code by a bug?) generate one parameter Core_decoded.swf: common.util.Util.f generate one filename Core_decoded.swf: common.util.Util.s (not yet found) Core_decoded.swf: connection.api_port.PortAPI.__ generate one parameter DutyMain.swf: scene.duty.utils.__ generate one parameter SallyMain.swf: scene.sally.__abcde__ generate one parameter (the previous one) They might call one or two supporting functions, but not these are obfuscated (or partially obfuscated)
DownloadmainD2.swf (5 KiB)DownloadCore_decoded.swf (185 KiB)DownloadDutyMain.swf (237 KiB)
admin
Thanks. It looks like the other similar file I was talking about is from #792 and it's probably the Core_decoded, or a past version of it. So it is unique to this game, you're right. It is amazing since it looks like actual obfuscation, not like one of these "just insert if 56==4 to make decompiler crash" tools. I'll try to improve our deobfuscator-simplifier, but probably won't get anything super readable.
user
Thanks a lot. I tried the latest nightly and it works just right. The code is much shorter and more readable now! However, as I tried to analyze these functions, I pasted the code into Flash Builder and the script did not work exactly as the SWF did. I looked into the code and found a few bugs that you might also want to fix: 1. (P-Code->AS decompilation, no deobfuscation) Missing parenthesis when doing rshift and urshift Compile something like a>>(b>>>c), the decompiler will generate a>>b>>>c, giving wrong results 2. (P-Code->AS decompilation, no deobfuscation) P-Code casting instruction like convert_s is ignored `String(this).length` should return 15 because `this` becomes `"[object Object]"`. But the casting is missing in the decompiled script, giving `this.length`, causing an error to generate as this.length is not defined. 3. (AS deobfuscation, P-Code to AS OK) values inside square brackets are probably assumed to be numbers, therefore using string indexes against objects results in a NaN in the deobfuscated code. /./(/.. /({})) + /.$/(/../({})) + /./(/./([])) + /./(/..$/(!{})) + /.../(!!{}) + /../(/.. /({})) + /.$/(/../({})) + /.$/(/../(!!{})) evaluates to "constructor", thus 1[/./(/.. /({})) + /.$/(/../({})) + /./(/./([])) + /./(/..$/(!{})) + /.../(!!{}) + /../(/.. /({})) + /.$/(/../({})) + /.$/(/../(!!{}))] evaluates to [class Number] Without deobfuscation the code works exactly as intended when pasted, but after auto-deobfuscation, it becomes 1["NaN"] which gives an error. In the attached file please find the three specific scenarios above. Also for the deobfuscation part, I am just guessing, is it possible to evaluate constant expressions to simplify the code? Thank you!
DownloadASTest.swf (1 KiB)
admin
Hi, I'll try to implement fix for the points 1,2,3. Ad "evaluate constant expressions to simplify the code"., I already implemented something like this (forgot to tell), you can enable it as experimental feature in Advanced settings / Other / (Internal) _simplifyExpressions. It makes your code a bit more readable. Only exception is when there is function argument involved.
developer
Now there are a lot of unnecessary parentheses in the following format: if (a && (b && (c && d)) and a * (b * (c * d)) earlier it was: if (a && b && c && d) and a * b * c * d I know that it is correct now, but it causes a lot of differences with the previous decompilations, so hard to compare the result and find the real differences. Could you please fix it?
developer
Forgot to add examples. 100000.swf\scripts\__Packages\JSON.as current: if(this.next() == "r" && (this.next() == "u" && this.next() == "e")) before: if(this.next() == "r" && this.next() == "u" && this.next() == "e") 2.swf (71401 bytes)\scripts\DefineSprite_27\frame_1\DoAction.as current: _loc5_ = 100 * (_loc2_ * _loc8_) / _loc9_; before: _loc5_ = 100 * _loc2_ * _loc8_ / _loc9_;
admin
I am afraid it is not a bug, it is feature :-). The decompiled parentheses show exactly how the code will be executed - order of execution. Order of execution sometimes matter. It does not matter for numbers or local registers, but it is important for calling methods. If you compile following code, the decompiler will detect where the parentheses are and correctly displays them. Previous version of decompiler ignored order of operations in many cases which can lead to incorrectly decompiled code. public function main() { if(this.tst("a") && this.tst("b") && this.tst("c")) { trace("first"); } if(this.tst("a") && (this.tst("b") && this.tst("c"))) { trace("second"); } var a:* = 5; var b:* = 6; var c:* = 2; var d1:* = 100 * (a * b) / c; var d2:* = 100 * a * b / c; } public function tst(k:String) : Boolean { return true; }
admin
ilufang: the three scenarios should be fixed in 8.0.1 (but better try nightly build). Just check "Simplify expressions" in settings. The third one results in trace(1["constructor"]), because 1["constructor"] cannot be simplified more - you cannot create Number constructor manually, but... If you convert it some way to string, like var a:String = 1["constructor"] or String(1["constructor"]), then it will be simplified correctly I guess.
State: new→upgraded
developer
Thanks for the explanation, I'd like to investigate it a little bit more, because for me it seems that the original code was (without parenthesis), and i don't think that any programmer puts parenthesis in this case:) if (this.next() == 'r' && this.next() == 'u' && this.next() == 'e') { https://github.com/earth911/search-api-sample-code-as2/blob/master/JSON.as line 319 Maybe it is different for AS2. But it is not important for now, thanks.
State: upgraded→new
developer
Sorry it was accidentally set to new again.... probably because i wrote the comment in the same time as you...
State: new→upgraded
 
Google Translate: Translate to Czech Translate to Slovak Translate to Russian Translate to Hungarian Translate to Swedish Translate to French Translate to German Translate to Spanish Translate to Italian
Change style: oceanic classic