Skip to content

Commit a5d6c15

Browse files
committed
Add SWFIntrospector
1 parent ab92993 commit a5d6c15

12 files changed

Lines changed: 536 additions & 271 deletions

File tree

AS3/src/com/cff/anebe/BytecodeEditor.as

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ package com.cff.anebe
88
import flash.events.StatusEvent;
99
import flash.external.ExtensionContext;
1010
import flash.utils.ByteArray;
11-
import flash.utils.CompressionAlgorithm;
12-
import com.cff.anebe.ir.ASTrait;
1311
import com.cff.anebe.ir.ASScript;
1412

1513
/**
@@ -20,41 +18,8 @@ package com.cff.anebe
2018
{
2119
private var extContext:ExtensionContext;
2220

23-
private function decompressAndSetSWF(replaceSWF:ByteArray):void
21+
private function setSWF(replaceSWF:ByteArray):void
2422
{
25-
replaceSWF.position = 0;
26-
var decompressor:ByteArray = new ByteArray();
27-
switch (replaceSWF.readUTFBytes(1))
28-
{
29-
case "F":
30-
replaceSWF.position = 0;
31-
break;
32-
case "C":
33-
replaceSWF.position = 8;
34-
replaceSWF.readBytes(decompressor);
35-
replaceSWF.position = 8;
36-
decompressor.uncompress(CompressionAlgorithm.ZLIB);
37-
replaceSWF.writeBytes(decompressor);
38-
replaceSWF.position = 0;
39-
replaceSWF.writeUTFBytes("F");
40-
replaceSWF.position = 0;
41-
decompressor = null;
42-
break;
43-
case "Z":
44-
replaceSWF.position = 8;
45-
replaceSWF.readBytes(decompressor, 8);
46-
replaceSWF.position = 8;
47-
decompressor.uncompress(CompressionAlgorithm.LZMA);
48-
replaceSWF.writeBytes(decompressor);
49-
replaceSWF.position = 0;
50-
replaceSWF.writeUTFBytes("F");
51-
replaceSWF.position = 0;
52-
decompressor = null;
53-
break;
54-
default:
55-
throw new Error("Unrecognized compression scheme for SWF");
56-
}
57-
5823
var ret:Object = extContext.call("SetCurrentSWF", replaceSWF);
5924

6025
if (ret is String)
@@ -84,7 +49,7 @@ package com.cff.anebe
8449
*/
8550
public function Disassemble(swf:ByteArray):Object
8651
{
87-
decompressAndSetSWF(swf);
52+
setSWF(Utils.decompressSWF(swf));
8853

8954
var ret:Object = extContext.call("Disassemble");
9055

@@ -118,7 +83,7 @@ package com.cff.anebe
11883

11984
if (replaceSWF != null)
12085
{
121-
decompressAndSetSWF(replaceSWF);
86+
setSWF(Utils.decompressSWF(replaceSWF));
12287
}
12388

12489
var vec:Vector.<String> = new <String>[];
@@ -149,7 +114,7 @@ package com.cff.anebe
149114
*/
150115
public function DisassembleAsync(swf:ByteArray):void
151116
{
152-
decompressAndSetSWF(swf);
117+
setSWF(Utils.decompressSWF(swf));
153118

154119
var ret:Object = extContext.call("DisassembleAsync");
155120

@@ -182,7 +147,7 @@ package com.cff.anebe
182147
}
183148
if (replaceSWF != null)
184149
{
185-
decompressAndSetSWF(replaceSWF);
150+
setSWF(Utils.decompressSWF(replaceSWF));
186151
}
187152

188153
var vec:Vector.<String> = new <String>[];
@@ -230,7 +195,7 @@ package com.cff.anebe
230195
}
231196
if (replaceSWF != null)
232197
{
233-
decompressAndSetSWF(replaceSWF);
198+
setSWF(Utils.decompressSWF(replaceSWF));
234199
}
235200

236201
var vec:Vector.<String> = new <String>[];
@@ -270,7 +235,7 @@ package com.cff.anebe
270235
}
271236
if (replaceSWF != null)
272237
{
273-
decompressAndSetSWF(replaceSWF);
238+
setSWF(Utils.decompressSWF(replaceSWF));
274239
}
275240

276241
var vec:Vector.<String> = new <String>[];
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package com.cff.anebe
2+
{
3+
import flash.external.ExtensionContext;
4+
import flash.utils.ByteArray;
5+
import com.cff.anebe.ir.ASMultiname;
6+
import com.cff.anebe.ir.ASReadOnlyClass;
7+
import com.cff.anebe.ir.ASReadOnlyScript;
8+
9+
/**
10+
* Partially disassembles an SWF so that high-level information can be pulled out of it. Does not allow rebuilding and editing.
11+
* @param swf SWF data to disassemble
12+
* @return Introspector for this SWF
13+
*/
14+
public class SWFIntrospector
15+
{
16+
private var extContext:ExtensionContext;
17+
18+
private function setSWF(replaceSWF:ByteArray):void
19+
{
20+
var ret:Object = extContext.call("SetCurrentSWF", replaceSWF);
21+
22+
if (ret is String)
23+
{
24+
throw new Error(ret);
25+
}
26+
else if (ret == null || !(ret is Boolean))
27+
{
28+
throw new Error("Unknown error occurred while setting SWF");
29+
}
30+
else if (!(ret as Boolean))
31+
{
32+
throw new Error("SetCurrentSWF returned false somehow");
33+
}
34+
}
35+
36+
private function beginIntrospection():void
37+
{
38+
var ret:Object = extContext.call("BeginIntrospection");
39+
40+
if (ret is String)
41+
{
42+
throw new Error(ret);
43+
}
44+
else if (ret == null || !(ret is Boolean))
45+
{
46+
throw new Error("Unknown error occurred while setting SWF");
47+
}
48+
else if (!(ret as Boolean))
49+
{
50+
throw new Error("BeginIntrospection returned false somehow");
51+
}
52+
}
53+
54+
public function SWFIntrospector(swf:ByteArray)
55+
{
56+
extContext = ExtensionContext.createExtensionContext("com.cff.anebe.ANEBytecodeEditor", "BytecodeEditor");
57+
58+
setSWF(Utils.decompressSWF(swf));
59+
60+
beginIntrospection();
61+
}
62+
63+
/**
64+
* Gets a class after partial assembly.
65+
* This is essentially a shortcut for GetScript(name).GetTrait(name).clazz, but is more efficient and should likely be used instead.
66+
* @param className Name of class to be patched. Must be a QName.
67+
* @return The class requested.
68+
*/
69+
public function GetClass(name:ASMultiname):ASReadOnlyClass
70+
{
71+
if (name == null || name.type != ASMultiname.TYPE_QNAME)
72+
{
73+
throw new ArgumentError("Class name must be a QName");
74+
}
75+
76+
var ret:Object = extContext.call("GetClass", name);
77+
78+
if (ret is String)
79+
{
80+
throw new Error(ret);
81+
}
82+
else if (ret == null || !(ret is ASReadOnlyClass))
83+
{
84+
throw new Error("Unknown error occurred");
85+
}
86+
else
87+
{
88+
return ret as ASReadOnlyClass;
89+
}
90+
}
91+
92+
/**
93+
* Gets a script after partial assembly.
94+
* Scripts are the top-level building block of AS3 programs, but are never referenced in-engine.
95+
* They can contain functions, classes, and variables, and have a single initializer method that sets them up.
96+
* Note that direct editing of script is discouraged; using GetClass is generally a better idea.
97+
* This is mostly provided for the case of top-level-scope functions and variables, which can only be edited as script traits.
98+
* @param identifier Name of an entity in the script. Must be a QName.
99+
* @return The script requested.
100+
*/
101+
public function GetScript(identifier:ASMultiname):ASReadOnlyScript
102+
{
103+
if (identifier == null || !(identifier is ASMultiname))
104+
{
105+
throw new Error("script identifier must be a QName");
106+
}
107+
108+
var ret:Object = extContext.call("GetScript", identifier);
109+
110+
if (ret is String)
111+
{
112+
throw new Error(ret);
113+
}
114+
else if (ret == null || !(ret is ASReadOnlyScript))
115+
{
116+
throw new Error("Unknown error occurred");
117+
}
118+
else
119+
{
120+
return ret as ASReadOnlyScript;
121+
}
122+
}
123+
}
124+
}

AS3/src/com/cff/anebe/Utils.as

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.cff.anebe
2+
{
3+
import flash.utils.ByteArray;
4+
import flash.utils.CompressionAlgorithm;
5+
6+
/**
7+
* Internal utilities
8+
* @author Chris
9+
*/
10+
11+
internal class Utils
12+
{
13+
public static function decompressSWF(replaceSWF:ByteArray):ByteArray
14+
{
15+
replaceSWF.position = 0;
16+
var decompressor:ByteArray = new ByteArray();
17+
switch (replaceSWF.readUTFBytes(1))
18+
{
19+
case "F":
20+
replaceSWF.position = 0;
21+
break;
22+
case "C":
23+
replaceSWF.position = 8;
24+
replaceSWF.readBytes(decompressor);
25+
replaceSWF.position = 8;
26+
decompressor.uncompress(CompressionAlgorithm.ZLIB);
27+
replaceSWF.writeBytes(decompressor);
28+
replaceSWF.position = 0;
29+
replaceSWF.writeUTFBytes("F");
30+
replaceSWF.position = 0;
31+
decompressor = null;
32+
break;
33+
case "Z":
34+
replaceSWF.position = 8;
35+
replaceSWF.readBytes(decompressor, 8);
36+
replaceSWF.position = 8;
37+
decompressor.uncompress(CompressionAlgorithm.LZMA);
38+
replaceSWF.writeBytes(decompressor);
39+
replaceSWF.position = 0;
40+
replaceSWF.writeUTFBytes("F");
41+
replaceSWF.position = 0;
42+
decompressor = null;
43+
break;
44+
default:
45+
throw new Error("Unrecognized compression scheme for SWF");
46+
}
47+
48+
return replaceSWF;
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)