CyberShadow / RABCDAsm

Robust ABC (ActionScript Bytecode) [Dis-]Assembler
http://blog.thecybershadow.net/2010/05/05/announcing-rabcdasm/
GNU General Public License v3.0
430 stars 92 forks source link

.swf is wierd after reassembling by rabcdasm #22

Closed Mixaill closed 11 years ago

Mixaill commented 11 years ago

Application.swf from World of Tanks game is wierd after reassembling by rabcdasm with this patch.

As @PavelMaca says, some parts are changed, but we don't know what that code means... diff: https://gist.github.com/PavelMaca/f9213c8be54413a2ea23

pavelmaca commented 11 years ago

using v1.14

CyberShadow commented 11 years ago

These changes are inconsequential. The changes are due to the non-deterministic method RABCDAsm uses to assign unique text IDs to entities that do not have an unique name. These strings do not end up in the final file. If the resulting file does not behave as expected, your problem is elsewhere. I would try to reassemble the file without any changes, to determine if the problem is caused by your patch, or the process of disassembling/reassembling with RABCDAsm.

M-r-A commented 11 years ago

Reassemling w/o any patch causes same problem - crash game.

Here is no patched version after RABCDAsm: http://www.koreanrandom.com/forum/index.php?app=core&module=attach&section=attach&attach_id=30570

CyberShadow commented 11 years ago

The file works fine as far as I can test, and I can find no fault in RABCDAsm. If the file does not behave as expected after reassembly, the application is probably protected from modification, using a mechanism outside of the scope of RABCDAsm.

efournival commented 11 years ago

The crashs caused by the disasm/reasm SWF file are random, so it's definitely not a protection or whatever. I just ran SWFDump on the original SWF, it works fine, all other SWFs previously patched with RABCDAsm under older game versions are working fine too. But when you launch SWFDump on the disasm/reasm last SWF (with no patch, using RABCDAsm of course) it throws a reference error and refuse to dump. Considering the diff @PavelMaca did, the disassembling process is clearly messing up with something, there is apparently no differences except texts IDs between the original SWF and the disasm/reasm/disasm one. Last thing, the disasm/reasm raw SWF is SMALLER than the original even if we aren't modifying anything. The compressed SWF is slighty bigger but it might be caused by zLib limits regarding binary format.

Please consider this issue.

CyberShadow commented 11 years ago

The crashs caused by the disasm/reasm SWF file are random

I don't understand what you mean by this. Random, how?

But when you launch SWFDump on the disasm/reasm last SWF (with no patch, using RABCDAsm of course) it throws a reference error and refuse to dump.

If I can reproduce this, I'll look into this.

Last thing, the disasm/reasm raw SWF is SMALLER than the original even if we aren't modifying anything.

Size differences are expected. ABC entries can be written in a different order, which is further affected by compression (LZMA/deflate) which could even use different compression settings than the program that created the original SWF. None of this affects how the SWF works, of course.

ChEeSyBoOfS commented 11 years ago

[The crashs caused by the disasm/reasm SWF file are random]

If I may. I think he means that the crashes are random and not reproducible. The only thing that can be guaranteed is that it will happen at the end of a 'battle' but not how often it will happen. Sometimes it will happen three times in a row sometimes it will not happen for 13 'battles'. If it was a protection issue would it not do it every time?

If one removes/disables the "disasm/reasm SWF" file the game/application does not crash.

efournival commented 11 years ago

Yeah @ChEeSyBoOfS explained it well, I think it's a wrong ABC in a block that is not executed everytimes, that would explain the "random" part. Regarding the smaller file issue, I was speaking about the raw SWF size, without any compression (original and disasm/reasm SWF raw sizes differ by ~80 kB).

It'll be hard for you to reproduce the issue as you need to download the game and play a lot. But you can reproduce the error in SWFDump (from SWFTools package: http://www.swftools.org/download.html) by running: swfdump -a Application.swf

Here is the original file (it can be disassembled by SWFDump flawlessly): http://www.mediafire.com/download/bc6lbd3pqupay8z/Application.swf

We are making the patched file with this script: https://code.google.com/p/wot-xvm/source/browse/trunk/src/xvm/swf/1.make-patched-swfs.bat

With RABCDasm 1.14 pre-compiled binaries under Windows 7 x64, using 1.make-patched-swfs.bat with no patch: http://i.imgur.com/44GqYRZ.png (latest SWFTools dev snapshot for Windows: swftools-2013-04-09-1007.exe)

CyberShadow commented 11 years ago

If it was a protection issue would it not do it every time?

No. The best software protection frustrates reverse-engineers by not behaving reliably. This makes it more difficult to find the exact conditions when it is triggered.

I will investigate the SWFDump crash when I have time.

4lCapwn commented 11 years ago

for now you could add logging to every line of code you encounter, and "pipe" it out using dokan/your own python logging system... but that is gonna be ugly.

did you try to contact the dev of swfdump yet? maybe swfdump crashes because of a different error (since decompilers like sothink and ffdec can cope (?) with the patched file)

CyberShadow commented 11 years ago

I have investigated the SWFDump crash. SWFDump does not implement parsing typenames correctly when reading the multiname constant pool. Because the fields are written to the new file in a different order, the crash occurs in only one file. However, it is a coincidence that SWFDump doesn't crash on the original file, and crashes on the new one (SWFDump can't handle forward typename references). As further evidence that SWFDump does not correctly implement ABC parsing, it does not parse typenames with more than one parameter correctly:

        int v1 = swf_GetU30(tag); //multiname
        int v2 = swf_GetU30(tag); //counter?
        int v3 = swf_GetU30(tag); //multiname
        // e.g. Vector<int> ... we only store the parent object
        m = *(multiname_t*)array_getkey(pool->x_multinames, v1);

The last integer (v3) is actually an array of integers, with the length v2. It is only by luck that the file did not contain any typenames with more than one type parameter - otherwise, SWFDump would crash almost as soon as it ran into the first one.

I firmly believe that the SWFDump crash is not related to the problematic behavior you're describing, on the grounds that Adobe's SWF player is unable to load a file if it fails to load the constant pools. Unless the randomness originates from the application's use of (pseudo)random decisions, malformed SWF files cannot cause random errors.

Mixaill commented 11 years ago

I firmly believe that the SWFDump crash is not related to the problematic behavior you're describing, on the grounds that Adobe's SWF player is unable to load a file if it fails to load the constant pools.

WoT uses Autodesk Scaleform SWF player, which can be slightly different compare to Adobe player.

efournival commented 11 years ago

Maybe Scaleform doesn't support forward typename references too? Would not it be better for your tool to keep the same elements order when re-assembling?

Do you know which element/function name/class field is causing SWFDump to crash? If we know which one, we should be able to find a workaround.

Thanks in advance for your consideration.

Mixaill commented 11 years ago

Also, this file (0.8.8 release) works OK both in game and in swfdump Problem starts with this version (0.8.9 CT1)

CyberShadow commented 11 years ago

Please try latest master. efada3d5c10993905a5e6bcf2416becd5904d477 should fix the SWFDump crash.

Mixaill commented 11 years ago

Problem with compilation. Ubuntu 13.10 amd64, dmd 2.064

mixaill@Mixaill-K56CB:~/repo/RABCDAsm$ dmd build_rabcdasm.d 
mixaill@Mixaill-K56CB:~/repo/RABCDAsm$ ./build_rabcdasm 
* Checking for working compiler...
 >>> OK
* Checking for LZMA...
 >>> OK
* Building rabcasm
asprogram.d(879): Error: cannot implicitly convert expression (1LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (1LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (1LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (1LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (1LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (1LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (1LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (0LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (0LU + this.pool[this.toKey(value)].index) of type ulong to uint
asprogram.d(879): Error: cannot implicitly convert expression (0LU + this.pool[this.toKey(value)].index) of type ulong to uint
Error: Compilation of rabcasm failed
efournival commented 11 years ago

I'm a noob with Linux so I compiled under Windows using git clone, and it worked without any error. Is the last master commit taken into account when cloning the git dir?

CyberShadow commented 11 years ago

Problem with compilation.

Sorry about that. Fixed.

Is the last master commit taken into account when cloning the git dir?

Yes, master is the default branch.

I'm a noob with Linux so I compiled under Windows using git clone, and it worked without any error.

So these random crashes are gone?

efournival commented 11 years ago

Okay I just checked the source, I was able to compile RABCDAsm using DMD for Windows 2.063.2 and the commit was taken into account. However, the crashes are still occuring despite your fix.

I'm currently writing an injection utility to perform our changes into the file without modifying its whole structure. For the moment, I can add a string into the CPool and rewrite the entire file, and it runs into the game flawlessly as far as I tested. Tonight I'll add our changes into the corresponding method body and send it to our users, I'll keep you posted if it worked or not but imho the Scaleform player doesn't handle well RABCDAsm's file structure modifications. But, now, I don't understand why it worked with the previous game version... This is a mystery.

efournival commented 11 years ago

Also, Application.swf without any patch (only disassembling/reassembling) is still making SWFDump to crash.

CyberShadow commented 11 years ago

Also, Application.swf without any patch (only disassembling/reassembling) is still making SWFDump to crash.

Ouch, you're right! I forgot to pass -a to SWFDump when testing.

Mixaill commented 11 years ago

Crashes on .asasm file assembling

mixaill@Mixaill-K56CB:~/repo/wot-xvm/utils/build-system-linux/bin/rabcdasm$ ./rabcasm Application-0/Application-0.main.asasm 
object.Exception@assembler.d(1242): Application-0/net/wg/app/impl/base/AbstractApplication.class.asasm(194,10): 
    (included from Application-0/net/wg/app/impl/base/AbstractApplication.script.asasm(55,45))
    (included from Application-0/Application-0.main.asasm(9,66))
----------------
./rabcasm(_Dmain+0x10d) [0x4c63dd]
./rabcasm(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll().void __lambda1()+0x18) [0x52b1f4]
./rabcasm(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate())+0x2a) [0x52b14e]
./rabcasm(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll()+0x30) [0x52b1b4]
./rabcasm(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate())+0x2a) [0x52b14e]
./rabcasm(_d_run_main+0x1a3) [0x52b0cf]
./rabcasm(main+0x17) [0x4eda7f]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7fbfc391ede5]
----------------
object.Exception@assembler.d(441): Unknown flag NEED
----------------
./rabcasm(asprogram.ASProgram.Method assembler.Assembler.readMethod()+0x149) [0x520689]
./rabcasm(asprogram.ASProgram.Trait assembler.Assembler.readTrait()+0x69d) [0x52003d]
./rabcasm(asprogram.ASProgram.Instance assembler.Assembler.readInstance()+0x14e) [0x52097e]
./rabcasm(asprogram.ASProgram.Class assembler.Assembler.readClass()+0xae) [0x520b76]
./rabcasm(asprogram.ASProgram.Trait assembler.Assembler.readTrait()+0x383) [0x51fd23]
./rabcasm(asprogram.ASProgram.Script assembler.Assembler.readScript()+0x9a) [0x520d9a]
./rabcasm(void assembler.Assembler.readProgram()+0xa2) [0x522fc2]
./rabcasm(void assembler.Assembler.assemble(immutable(char)[])+0x1f2) [0x523372]
./rabcasm(_Dmain+0x10d) [0x4c63dd]
./rabcasm(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll().void __lambda1()+0x18) [0x52b1f4]
./rabcasm(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate())+0x2a) [0x52b14e]
./rabcasm(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll()+0x30) [0x52b1b4]
./rabcasm(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate())+0x2a) [0x52b14e]
./rabcasm(_d_run_main+0x1a3) [0x52b0cf]
./rabcasm(main+0x17) [0x4eda7f]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7fbfc391ede5]
----------------
mixaill@Mixaill-K56CB:~/repo/wot-xvm/utils/build-system-linux/bin/rabcdasm$ 
efournival commented 11 years ago

Try with an older DMD version like 2.063.2, no trouble for me.

CyberShadow commented 11 years ago

Crashes on .asasm file assembling

What is on line 194 of Application-0/net/wg/app/impl/base/AbstractApplication.class.asasm?

Try with an older DMD version like 2.063.2, no trouble for me.

I've tested with latest DMD, no problem here.

CyberShadow commented 11 years ago

Also, Application.swf without any patch (only disassembling/reassembling) is still making SWFDump to crash.

OK, really fixed in 4251e409a2eb7a2acce55ff529c9e221ad6fc324.

efournival commented 11 years ago

Thanks for your hard work, I've compiled RABCDAsm with your last commits and made a patched Application.swf. I'm waiting for users' reports.

PS: SWFDump is working fine now, good job.

Mixaill commented 11 years ago

What is on line 194 of Application-0/net/wg/app/impl/base/AbstractApplication.class.asasm?

    flag NEED_ACTIVATION
CyberShadow commented 11 years ago

I can't reproduce that problem.

Since when did it start happening? What is your distribution, bitness and D compiler version?

Mixaill commented 11 years ago

Since when did it start happening?

RABCDAsm 1.14 already has this bug. Before I used Ubuntu 13.04 amd64 and all were OK.

What is your distribution, bitness and D compiler version?

Ubuntu 13.10 and 14.04 nightly, amd64, dmd 2.063.2, 2.064.2

CyberShadow commented 11 years ago

Crashes on .asasm file assembling

Reproduced with 64-bit DMD 2.064. Does not happen with 32-bit DMD 2.064.

CyberShadow commented 11 years ago

Reduced to https://d.puremagic.com/issues/show_bug.cgi?id=11508

CyberShadow commented 11 years ago

Ubuntu 13.10 and 14.04 nightly, amd64, dmd 2.063.2, 2.064.2

It doesn't happen with 2.063.2 here.

Mixaill commented 11 years ago

It doesn't happen with 2.063.2 here.

Sorry, I recompiled RABCDasm with 2.063.2 again and it works fine.

Mixaill commented 11 years ago

It seems to be working fine. Thank you!

efournival commented 11 years ago

Thanks!