Menu

[r11]: / trunk / SWFInvestigator / src / decompiler / ASDecompiler.as  Maximize  Restore  History

Download this file

497 lines (426 with data), 15.6 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
/****************************************************************************
* ADOBE SYSTEMS INCORPORATED
* Copyright 2012 Adobe Systems Incorporated and it’s licensors
* All Rights Reserved.
*
* NOTICE: Adobe permits you to use, modify, and distribute this file
* in accordance with the terms of the license agreement accompanying it.
* ****************************************************************************/
package decompiler
{
import decompiler.Logging.StringWriter;
import decompiler.Swf;
import decompiler.swfdump.SWFFormatError;
import decompiler.swfdump.TagDecoder;
import decompiler.tamarin.abcdump.Abc;
import decompiler.tamarin.abcdump.Tag;
import decompiler.tamarin.abcdump.Traits;
import flash.filesystem.FileStream;
import flash.utils.ByteArray;
/**
* The ASDecompiler is the class that wraps the swfutils library for decompiling AS2 and AS3 SWFs.
*
* This is the core class that maintains all information regarding the SWF's decompilation for all of SWF Investigator
*/
public class ASDecompiler
{
private var swfFile:Swf;
private var swfInfo:String;
private var fileTagsArray:Array;
private var logger:StringWriter = new StringWriter("Array");
public var swfData:Object = {};
public var fileName:String;
/**
* @public
* Constructor
*
* @param bytes The ByteArray from loader.loadBytes
*/
public function ASDecompiler(bytes:ByteArray)
{
this.swfFile = new Swf(bytes,this.logger);
this.swfData.size = this.swfFile.length;
this.swfData.version = this.swfFile.version;
this.swfData.compressed = this.swfFile.compressed;
}
/**
* @public
* Once the constructor is initialized, this will get additional info for the class and the main SWF Investigator tab.
*
* @return A string containing a basic summary of the SWF
*/
public function getInfo():String {
this.swfData.avm2 = this.swfFile.avm2;
this.swfData.hasMetadata = this.swfFile.hasMetadata;
this.swfData.useGPU = this.swfFile.useGPU;
this.swfData.useDirectBlit = this.swfFile.useDirectBlit;
this.swfData.swfRelativeURLs = this.swfFile.swfRelativeURLs;
this.swfData.suppressCrossDomainCaching = this.swfFile.suppressXDomainCaching;
this.swfData.useNetwork = this.swfFile.useNetwork;
this.swfData.frameRate = this.swfFile.frameRate;
this.swfData.frameCount = this.swfFile.frameCount;
this.swfData.movieRect = this.swfFile.rect.toString();
this.fileTagsArray = this.swfFile.tags;
this.swfInfo = this.logger.outArray[0];
this.swfFile.log.clear();
var outputString:String = "";
outputString += "Total # of File Tags: " + this.fileTagsArray.length + "\r";
var swfTag:Tag;
for (var s:String in this.fileTagsArray) {
swfTag = this.fileTagsArray[s][0];
outputString += " * " + this.swfFile.getTagName(swfTag.type) + " (" + swfTag.type +") -- total: " + this.fileTagsArray[s].length + "\r";
}
this.swfData.fileTags = outputString;
outputString = "";
try {
if (this.swfFile.avm2) {
this.swfFile.dumpABC();
} else if (this.swfFile.hasActions) {
this.swfFile.dumpAllActions();
}
this.swfData.byteCode = this.logger.outArray;
} catch (e:SWFFormatError) {
this.logger.print("\rError during processing! - pos=" + this.swfFile.data.position + "\r");
this.logger.print(e.message + "\r");
this.swfData.byteCode = this.logger.outArray;
return (e.message);
} catch (e:Error) {
this.logger.print("\rError during processing! - pos=" + this.swfFile.data.position + "\r");
this.logger.print(e.message + "\r");
this.swfData.byteCode = this.logger.outArray;
return (e.message);
}
if (this.swfFile.avm2 && this.swfFile.abc[0].classes != null && this.swfFile.abc[0].classes.length > 0) {
for (var i:int = 0; i < this.swfFile.abc.length; i++) {
outputString += "ABC Block " + i + " Classes\r";
for (var j:String in this.swfFile.abc[i].classes) {
outputString += " - " + this.swfFile.abc[i].classes[j] + "\r";
}
}
} else {
outputString += "No exports\r";
}
this.swfData.exportClasses = outputString;
if (swfFile.hasActions) {
this.swfData.hasActions = true;
} else {
this.swfData.hasActions = false;
}
return this.logger.errorOutput;
}
/**
* @private
* Used to collect the instance information for a specific class when called by the dumpABCClasses function
*
* This collects information such as whether the class is dynamic or final, its interfaces, its traits, etc.
*
* @param abc An ABC class object
* @param inst An instance of the Traits found in the SWF
* @param sWriter The StringWriter object where the output is written
*/
private function dumpInstances(abc:Abc, inst:Traits, sWriter:StringWriter):void {
var line:String;
if (inst.flags & abc.CLASS_FLAG_interface)
line = "interface"
else {
line = "class";
if ( !(inst.flags & abc.CLASS_FLAG_sealed) )
line = "dynamic " + line;
if ( inst.flags & abc.CLASS_FLAG_final )
line = "final " + line;
};
line += " " + inst.name + " extends " + inst.base;
if ( inst.interfaces )
{
line += " implements " + inst.interfaces[0];
for ( var i:int = 1; i < inst.interfaces.length; i += 1)
line += ", " + inst.interfaces[i];
};
sWriter.print( " " + line + " { " );
if ( (inst.flags & 0x08 ) ) sWriter.print(" /* protected namespace : "+ inst.protectedNs + " */" );
if (inst.init) {
inst.init.dump(abc," ");
} else {
sWriter.print("/* no constructor code */");
}
inst.dump(abc, " ");
}
/**
* @public
* Used by the AS3Navigator function to obtain an ABCXML description of the SWF for the tree navigator
*
* @return An ABCXML description of the packages and classes, null if error
*/
public function dumpABCClasses():ABCXML {
var swfXML:ABCXML = new ABCXML();
var tr:Traits;
var sw:StringWriter = new StringWriter("String");
if (this.swfFile.abc == null || this.swfFile.abc.length == 0) {
return (null);
}
var abc:Abc;
var pckgName:String;
var className:String;
for (var i:int = 0; i < this.swfFile.abc.length; i++) {
abc = this.swfFile.abc[i];
for (var c:* in abc.classes) {
sw.clear();
abc.log = sw;
pckgName = "(default)";
className = abc.classes[c].name;
if (abc.classes[c].name.indexOf("::") > 0) {
var info:Array = className.split("::");
pckgName = info[0];
className = info[1];
swfXML.addPackage(pckgName);
}
dumpInstances(abc, abc.instances[c],sw);
abc.classes[c].dump(abc, " ");
abc.classes[c].init.dump(abc, " ");
sw.print(" }");
swfXML.addClass(pckgName,className,sw.output);
}
}
swfXML.createCollection();
return (swfXML);
}
/**
* @private
* Used (via proxy) by the Decompiler Tab to dump the source code for an AS3 SWF.
*
* An array is returned because the dump decompilation can be rather lengthy.
*
* @param i An int describing which ABC Block it is currently disassesmbling.
* @return An Array of strings with the dumped source code.
*/
private function dumpABC (i:int):Array {
var logger:StringWriter = new StringWriter("Array");
var outputString:String = "-------- Dumping ABC Block #" + i + " --------\r";
var position:int = this.swfFile.tags[82][i].position;
swfFile.data.position = position;
outputString += "position: " + position + "\r";
var abc:Abc = this.swfFile.abc[i];
abc.log = logger;
abc.dump();
logger.outArray[0] = outputString + logger.outArray[0];
return (logger.outArray);
}
/**
* Return the strings array for the String Viewer tab
*
* @return The Array of each string within the SWF
*/
public function dumpStrings():Array {
if (this.swfFile.abc == null || this.swfFile.abc.length == 0) {
var empty:Array = new Array();
return (empty);
}
var masterStr:Array = new Array();
var abc:Abc;
for (var i:int = 0; i < this.swfFile.abc.length; i++) {
abc = this.swfFile.abc[i];
if (abc.strings != null) {
masterStr = masterStr.concat(abc.strings);
}
}
return (masterStr);
}
/**
* @public
* Return a decompressed version of the SWF bytes for the Hex Viewer
*
* @param bytes The bytes of the SWF to be cast as a SWFFile
* @return A ByteArray of the decompressed SWF
*/
public function decompressSWF(bytes:ByteArray):ByteArray {
//Get Header info
var header:ByteArray = new ByteArray();
bytes.readBytes(header,0,8);
header[0] = 70; // Reset to non-compressed :-)
var dataArray:ByteArray = new ByteArray();
bytes.readBytes(dataArray);
dataArray.position = 0;
dataArray.uncompress();
dataArray.position = 0;
//Put together in pretty package
var tmpArray:ByteArray = new ByteArray();
tmpArray.writeBytes(header);
tmpArray.writeBytes(dataArray);
return (tmpArray);
}
/**
* @public
* Return an Array of the File Tags from the SWF for the Hex Viewer Tab
*
* @return An array of the File Tags from the SWF
*/
public function getFileTagArray():Array {
var tmpArray:Array = new Array;
var swfTag:Tag;
var tmpObject:Object;
for (var j:String in this.fileTagsArray) {
swfTag = this.fileTagsArray[j][0];
tmpObject = new Object;
tmpObject.label = this.swfFile.getTagName(swfTag.type);
tmpObject.data = swfTag.type;
tmpArray.push(tmpObject);
}
return (tmpArray);
}
/**
* @public
* Find the position of the next file tag based on the current position for the HexViewer Tab
*
* @param tagType A String representing the tag type
* @param position An unsigned int representing the current position
* @return A uint of the next position within the SWF of the current file tag.
*/
public function getNextFileTag(tagType:String, position:uint):Tag {
var tmpArray:Array;
var swfTag:Tag;
for (var j:String in this.fileTagsArray) {
if (j == tagType) {
tmpArray = this.fileTagsArray[j];
if (tmpArray.length == 1) {
return (Tag(tmpArray[0]));
}
for (var str:String in this.fileTagsArray[j]) {
swfTag = this.fileTagsArray[j][str];
if (swfTag.position > position) {
return (swfTag);
}
}
//Search Wrapped
return (Tag(tmpArray[0]));
}
}
return (null);
}
/**
* @private
* Used to sort the Tags array
*
* @param a The first tag for the comparison
* @param b The second tag for the comparison
*/
private function sortOnPosition(a:Tag, b:Tag):Number {
if(a.position > b.position) {
return 1;
} else if(a.position < b.position) {
return -1;
} else {
//aPrice == bPrice
return 0;
}
}
/**
* @private
* Converts a byte into a hex string
*
* @param byte An unsigned int of the byte that will be converted to a hex String
* @return A string representation of the hex value of the supplied byte.
*/
private function convertToHex(byte:uint):String {
var hex:String = byte.toString(16);
if (hex.length == 1) {
hex = "0" + hex;
}
return (hex)
}
/**
* @public
* Used by the TagPanel to generate the XML
*
* @return The TagXML representing the Tag
*/
public function produceTagXML():TagXML {
var tagXML:TagXML = new TagXML();
var tmpArray:Array = new Array();
for each (var arr:Array in this.fileTagsArray) {
for (var j:int=0; j < arr.length; j++) {
tmpArray.push(arr[j]);
}
}
tmpArray.sort(sortOnPosition);
for (var i:int=0; i < tmpArray.length; i++) {
var t:* = tmpArray[i];
tagXML.addTag(t);
/**
if (t.type == 4 || t.type == 26 || t.type == 70) {
tagXML.addField(t.position);
}
*/
}
tagXML.createCollection();
return (tagXML);
}
/**
* @public
* Used by TagPanel to get the hex for the selected tag
*
* @param pos The start position in the SWF file
* @param size The size of the tag
* @return The hex encoded section of the SWF file as a String
*/
public function getTagHex(pos:uint, size:uint):String {
var hex:String = new String();
var str:String = new String();
str = "\t\t| ";
for (var i:int=0; i < size; i++) {
if (i > 0 && i % 16 == 0) {
hex += str + " |\r";
str = "\t\t| ";
}
hex += convertToHex(this.swfFile.data[pos + i]) + " ";
if (this.swfFile.data[pos + i] > 31 && this.swfFile.data[pos + i] < 128) {
str += String.fromCharCode(this.swfFile.data[pos + i].toString());
} else {
str += "*";
}
}
hex += str + " |\r";
return (hex);
}
/**
* @public
* Returns the string representation of a Tag
*
* @param type The type of the tag to print
* @param pos The position of the tag in the SWF
* @param dumpFile Used to indicate that binary data in the tag will be saved to a file
* @return The string representing the tag
*/
public function printTagInfo(type:uint, pos:uint, dumpFile:Boolean=false):String {
var t:Tag;
for each (t in this.swfFile.tags[type]) {
if (t.position == pos) {
break;
}
}
return (this.swfFile.printTag(t,dumpFile,this.fileName));
}
/**
* @public
* Used to retrive tag list sorted by position
*
* @param The Function for sending the data.
* @param returnChar the character to use for carriage returns
*/
public function printSortedFileTagArray(fStream:FileStream, returnChar:String="\r\n"):void {
var tmpVector:Vector.<Tag> = new Vector.<Tag>;
for each (var arr:Array in this.fileTagsArray) {
for (var j:int=0; j < arr.length; j++) {
tmpVector.push(arr[j]);
}
}
tmpVector.sort(sortOnPosition);
var myPattern:RegExp = /[\r]/g;
var tmpString:String;
for (var i:int = 0; i < tmpVector.length; i++) {
tmpString = printTagInfo(tmpVector[i].type, tmpVector[i].position);
fStream.writeUTFBytes(tmpString.replace(myPattern,returnChar));
}
return;
}
}
}
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.