@awayfl/avm1
Version:
Virtual machine for executing AS1 and AS2 code
455 lines (454 loc) • 18.8 kB
JavaScript
/*
* Copyright 2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ActionsDataStream } from './stream';
import { AVM1ActionsData } from './context';
var IS_INVALID_NAME = /[^A-Za-z0-9_]+/g;
var ParsedPushRegisterAction = /** @class */ (function () {
function ParsedPushRegisterAction(registerNumber) {
this.registerNumber = registerNumber;
}
return ParsedPushRegisterAction;
}());
export { ParsedPushRegisterAction };
var ParsedPushConstantAction = /** @class */ (function () {
function ParsedPushConstantAction(constantIndex) {
this.constantIndex = constantIndex;
}
return ParsedPushConstantAction;
}());
export { ParsedPushConstantAction };
var ActionsDataParser = /** @class */ (function () {
function ActionsDataParser(actionsData, swfVersion) {
this.swfVersion = swfVersion;
this._lastPushedValue = null;
this._lastDefinedConstantPool = null;
this._initialPosition = 0;
this._initialLen = 0;
this._actionsData = actionsData;
this.dataId = actionsData.id;
var bytes = actionsData.bytes;
var buffer = new Uint8Array(bytes.buffer, 0);
this._initialPosition = bytes.byteOffset;
this._initialLen = bytes.length;
/**
* This is important, because apps with encryption is problem,
* We should parse ALL bytes of SWF, because tag253 contains part of bytecodes
*/
this._stream = new ActionsDataStream(buffer, swfVersion);
this._stream.position = this._initialPosition;
}
Object.defineProperty(ActionsDataParser.prototype, "position", {
get: function () {
return this._stream.position - this._initialPosition;
},
set: function (value) {
this._stream.position = value + this._initialPosition;
},
enumerable: false,
configurable: true
});
Object.defineProperty(ActionsDataParser.prototype, "eof", {
get: function () {
return this.position >= this._initialLen;
},
enumerable: false,
configurable: true
});
Object.defineProperty(ActionsDataParser.prototype, "length", {
get: function () {
return this._initialLen;
},
enumerable: false,
configurable: true
});
ActionsDataParser.prototype.readNext = function () {
var stream = this._stream;
var currentPosition = stream.position;
var actionCode = stream.readUI8();
var length = actionCode >= 0x80 ? stream.readUI16() : 0;
var nextPosition = stream.position + length;
var args = null;
if (actionCode !== 155 /* ActionCode.ActionDefineFunction */ &&
actionCode !== 142 /* ActionCode.ActionDefineFunction2 */) {
this._lastPushedValue = null;
}
switch (actionCode | 0) {
case 129 /* ActionCode.ActionGotoFrame */:
var frame = stream.readUI16();
var nextActionCode = stream.readUI8();
var play = false;
if (nextActionCode !== 0x06 && nextActionCode !== 0x07) {
stream.position--;
}
else {
nextPosition++;
play = nextActionCode === 0x06;
}
args = [frame, play];
break;
case 131 /* ActionCode.ActionGetURL */:
var urlString = stream.readString();
var targetString = stream.readString();
args = [urlString, targetString];
break;
case 138 /* ActionCode.ActionWaitForFrame */:
var frame = stream.readUI16();
var count = stream.readUI8();
args = [frame, count];
break;
case 139 /* ActionCode.ActionSetTarget */:
var targetName = stream.readString();
args = [targetName];
break;
case 140 /* ActionCode.ActionGoToLabel */:
var label = stream.readString();
var nextActionCode = stream.readUI8();
var play = false;
if (nextActionCode !== 0x06 && nextActionCode !== 0x07) {
stream.position--;
}
else {
nextPosition++;
play = nextActionCode === 0x06;
}
args = [label, play];
break;
case 150 /* ActionCode.ActionPush */:
var type, value;
args = [];
while (stream.position < nextPosition) {
type = stream.readUI8();
switch (type | 0) {
case 0: // STRING
value = stream.readString();
this._lastPushedValue = value;
break;
case 1: // FLOAT
value = stream.readFloat();
break;
case 2: // null
value = null;
break;
case 3: // undefined
value = void (0);
break;
case 4: // Register number
value = new ParsedPushRegisterAction(stream.readUI8());
break;
case 5: // Boolean
value = stream.readBoolean();
break;
case 6: // Double
value = stream.readDouble();
break;
case 7: // Integer
value = stream.readInteger();
break;
case 8: // Constant8
value = new ParsedPushConstantAction(stream.readUI8());
break;
case 9: // Constant16
value = new ParsedPushConstantAction(stream.readUI16());
break;
default:
console.error('Unknown value type: ' + type, stream.position);
stream.position = nextPosition;
continue;
}
this._lastPushedValue = value;
args.push(value);
}
break;
case 153 /* ActionCode.ActionJump */:
var offset = stream.readSI16();
args = [offset];
break;
case 157 /* ActionCode.ActionIf */:
var offset = stream.readSI16();
args = [offset];
break;
case 154 /* ActionCode.ActionGetURL2 */:
var flags = stream.readUI8();
args = [flags];
break;
case 159 /* ActionCode.ActionGotoFrame2 */:
var flags = stream.readUI8();
args = [flags];
if (flags & 2) {
args.push(stream.readUI16());
}
break;
case 141 /* ActionCode.ActionWaitForFrame2 */:
var count = stream.readUI8();
args = [count];
break;
case 136 /* ActionCode.ActionConstantPool */:
var count = stream.readUI16();
var constantPool = [];
for (var i = 0; i < count; i++) {
constantPool.push(stream.readString());
}
this._lastDefinedConstantPool = constantPool;
args = [constantPool];
break;
case 155 /* ActionCode.ActionDefineFunction */:
var functionName = stream.readString();
var count = stream.readUI16();
var functionParams = [];
for (var i = 0; i < count; i++) {
functionParams.push(stream.readString());
}
var codeSize = stream.readUI16();
nextPosition += codeSize;
var functionBody = new AVM1ActionsData(stream.readBytes(codeSize), this.dataId + '_f' + stream.position, this._actionsData);
args = [functionBody, functionName, functionParams];
break;
case 148 /* ActionCode.ActionWith */:
var codeSize = stream.readUI16();
nextPosition += codeSize;
var withBody = new AVM1ActionsData(stream.readBytes(codeSize), this.dataId + '_w' + stream.position, this._actionsData);
args = [withBody];
break;
case 135 /* ActionCode.ActionStoreRegister */:
var register = stream.readUI8();
args = [register];
break;
case 142 /* ActionCode.ActionDefineFunction2 */:
var methodName = null;
if (this._lastPushedValue instanceof ParsedPushConstantAction
&& this._lastDefinedConstantPool) {
methodName = this._lastDefinedConstantPool[this._lastPushedValue.constantIndex];
}
if (typeof this._lastPushedValue === 'string') {
methodName = this._lastPushedValue;
}
if (methodName && IS_INVALID_NAME.test(methodName)) {
methodName = null;
}
var functionName = stream.readString();
var count = stream.readUI16();
var registerCount = stream.readUI8();
var flags = stream.readUI16();
var registerAllocation = [];
var functionParams = [];
for (var i = 0; i < count; i++) {
var register = stream.readUI8();
var paramName = stream.readString();
functionParams.push(paramName);
if (register) {
registerAllocation[register] = {
type: 1 /* ArgumentAssignmentType.Argument */,
name: paramName,
index: i
};
}
}
var j = 1;
// order this, arguments, super, _root, _parent, and _global
if (flags & 0x0001) { // preloadThis
registerAllocation[j++] = { type: 2 /* ArgumentAssignmentType.This */ };
}
if (flags & 0x0004) { // preloadArguments
registerAllocation[j++] = { type: 4 /* ArgumentAssignmentType.Arguments */ };
}
if (flags & 0x0010) { // preloadSuper
registerAllocation[j++] = { type: 8 /* ArgumentAssignmentType.Super */ };
}
if (flags & 0x0040) { // preloadRoot
registerAllocation[j++] = { type: 64 /* ArgumentAssignmentType.Root */ };
}
if (flags & 0x0080) { // preloadParent
registerAllocation[j++] = { type: 32 /* ArgumentAssignmentType.Parent */ };
}
if (flags & 0x0100) { // preloadGlobal
registerAllocation[j++] = { type: 16 /* ArgumentAssignmentType.Global */ };
}
var suppressArguments = 0;
if (flags & 0x0002) { // suppressThis
suppressArguments |= 2 /* ArgumentAssignmentType.This */;
}
if (flags & 0x0008) { // suppressArguments
suppressArguments |= 4 /* ArgumentAssignmentType.Arguments */;
}
if (flags & 0x0020) { // suppressSuper
suppressArguments |= 8 /* ArgumentAssignmentType.Super */;
}
var codeSize = stream.readUI16();
nextPosition += codeSize;
var name_1 = this.dataId + '_f' + stream.position;
if (methodName) {
name_1 = methodName + '__' + name_1;
}
var functionBody = new AVM1ActionsData(stream.readBytes(codeSize), name_1, this._actionsData);
functionBody.debugPath = this.dataId + '/' + (methodName ? methodName : ('f' + stream.position));
args = [functionBody, functionName, functionParams, registerCount,
registerAllocation, suppressArguments];
break;
case 143 /* ActionCode.ActionTry */:
var flags = stream.readUI8();
var catchIsRegisterFlag = !!(flags & 4);
var finallyBlockFlag = !!(flags & 2);
var catchBlockFlag = !!(flags & 1);
var trySize = stream.readUI16();
var catchSize = stream.readUI16();
var finallySize = stream.readUI16();
var catchTarget = catchIsRegisterFlag ? stream.readUI8() : stream.readString();
nextPosition += trySize + catchSize + finallySize;
var thisPath = this._actionsData.debugPath;
var tryBody = new AVM1ActionsData(stream.readBytes(trySize), this.dataId + '_t' + stream.position, this._actionsData);
if (thisPath)
tryBody.debugPath = thisPath + '/try_' + this._stream.position;
var catchBody = new AVM1ActionsData(stream.readBytes(catchSize), this.dataId + '_c' + stream.position, this._actionsData);
if (thisPath) {
catchBody.debugPath = thisPath + '/catch_' + this._stream.position;
}
var finallyBody = new AVM1ActionsData(stream.readBytes(finallySize), this.dataId + '_z' + stream.position, this._actionsData);
if (thisPath) {
finallyBody.debugPath = thisPath + '/finaly_' + this._stream.position;
}
args = [catchIsRegisterFlag, catchTarget, tryBody,
catchBlockFlag, catchBody, finallyBlockFlag, finallyBody];
break;
case 137 /* ActionCode.ActionStrictMode */:
var mode = stream.readUI8();
args = [mode];
break;
}
stream.position = nextPosition;
return {
position: currentPosition - this._initialPosition,
actionCode: actionCode,
actionName: ActionNamesMap[actionCode],
knownAction: !!(ActionNamesMap[actionCode]),
args: args
};
};
ActionsDataParser.prototype.skip = function (count) {
var stream = this._stream;
while (count > 0 && this.position < this._initialLen) {
var actionCode = stream.readUI8();
var length_1 = actionCode >= 0x80 ? stream.readUI16() : 0;
stream.position += length_1;
count--;
}
};
return ActionsDataParser;
}());
export { ActionsDataParser };
var ActionNamesMap = {
0x00: 'EOA',
0x04: 'ActionNextFrame',
0x05: 'ActionPreviousFrame',
0x06: 'ActionPlay',
0x07: 'ActionStop',
0x08: 'ActionToggleQuality',
0x09: 'ActionStopSounds',
0x0A: 'ActionAdd',
0x0B: 'ActionSubtract',
0x0C: 'ActionMultiply',
0x0D: 'ActionDivide',
0x0E: 'ActionEquals',
0x0F: 'ActionLess',
0x10: 'ActionAnd',
0x11: 'ActionOr',
0x12: 'ActionNot',
0x13: 'ActionStringEquals',
0x14: 'ActionStringLength',
0x15: 'ActionStringExtract',
0x17: 'ActionPop',
0x18: 'ActionToInteger',
0x1C: 'ActionGetVariable',
0x1D: 'ActionSetVariable',
0x20: 'ActionSetTarget2',
0x21: 'ActionStringAdd',
0x22: 'ActionGetProperty',
0x23: 'ActionSetProperty',
0x24: 'ActionCloneSprite',
0x25: 'ActionRemoveSprite',
0x26: 'ActionTrace',
0x27: 'ActionStartDrag',
0x28: 'ActionEndDrag',
0x29: 'ActionStringLess',
0x2A: 'ActionThrow',
0x2B: 'ActionCastOp',
0x2C: 'ActionImplementsOp',
0x2D: 'ActionFSCommand2',
0x30: 'ActionRandomNumber',
0x31: 'ActionMBStringLength',
0x32: 'ActionCharToAscii',
0x33: 'ActionAsciiToChar',
0x34: 'ActionGetTime',
0x35: 'ActionMBStringExtract',
0x36: 'ActionMBCharToAscii',
0x37: 'ActionMBAsciiToChar',
0x3A: 'ActionDelete',
0x3B: 'ActionDelete2',
0x3C: 'ActionDefineLocal',
0x3D: 'ActionCallFunction',
0x3E: 'ActionReturn',
0x3F: 'ActionModulo',
0x40: 'ActionNewObject',
0x41: 'ActionDefineLocal2',
0x42: 'ActionInitArray',
0x43: 'ActionInitObject',
0x44: 'ActionTypeOf',
0x45: 'ActionTargetPath',
0x46: 'ActionEnumerate',
0x47: 'ActionAdd2',
0x48: 'ActionLess2',
0x49: 'ActionEquals2',
0x4A: 'ActionToNumber',
0x4B: 'ActionToString',
0x4C: 'ActionPushDuplicate',
0x4D: 'ActionStackSwap',
0x4E: 'ActionGetMember',
0x4F: 'ActionSetMember',
0x50: 'ActionIncrement',
0x51: 'ActionDecrement',
0x52: 'ActionCallMethod',
0x53: 'ActionNewMethod',
0x54: 'ActionInstanceOf',
0x55: 'ActionEnumerate2',
0x60: 'ActionBitAnd',
0x61: 'ActionBitOr',
0x62: 'ActionBitXor',
0x63: 'ActionBitLShift',
0x64: 'ActionBitRShift',
0x65: 'ActionBitURShift',
0x66: 'ActionStrictEquals',
0x67: 'ActionGreater',
0x68: 'ActionStringGreater',
0x69: 'ActionExtends',
0x81: 'ActionGotoFrame',
0x83: 'ActionGetURL',
0x87: 'ActionStoreRegister',
0x88: 'ActionConstantPool',
0x89: 'ActionStrictMode',
0x8A: 'ActionWaitForFrame',
0x8B: 'ActionSetTarget',
0x8C: 'ActionGoToLabel',
0x8D: 'ActionWaitForFrame2',
0x8E: 'ActionDefineFunction2',
0x8F: 'ActionTry',
0x94: 'ActionWith',
0x96: 'ActionPush',
0x99: 'ActionJump',
0x9A: 'ActionGetURL2',
0x9B: 'ActionDefineFunction',
0x9D: 'ActionIf',
0x9E: 'ActionCall',
0x9F: 'ActionGotoFrame2'
};