UNPKG

disassembler-x86-intel

Version:

An Intel x86 disassembler, generates assembly from Opcode

668 lines (667 loc) 31.4 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports._Disassembler = void 0; var _32bit_adressing_table_modrm_1 = require("32bit-adressing-table-modrm"); var tables_1 = require("./constants/tables"); var isNotANumber_1 = require("./helper/isNotANumber"); var register_set_1 = require("./constants/sets/register.set"); require("../utils/string.extensions"); var rotate_1 = require("./helper/rotate"); var removeTrailingZero_1 = require("./helper/removeTrailingZero"); var object_extensions_1 = require("../utils/object.extensions"); var Types_table_1 = require("./constants/tables/Types.table"); var Jump_table_1 = require("./constants/tables/Jump.table"); var twosComplement_1 = require("./helper/twosComplement"); var makeValueToByte_1 = require("./helper/makeValueToByte"); var _Disassembler = /** @class */ (function () { function _Disassembler() { this.prefixMap = new Map().set('66', 'registerMode').set('67', 'addressMode'); this.opCodeTable = tables_1.ALL_TABLES; this.position = 0; } _Disassembler.prototype.byteIterator = function (s) { var i; return __generator(this, function (_a) { switch (_a.label) { case 0: i = 0; _a.label = 1; case 1: if (!(i < s.length)) return [3 /*break*/, 4]; return [4 /*yield*/, { byte: s[i] + s[i + 1], position: i === 0 ? 0 : i / 2 }]; case 2: _a.sent(); _a.label = 3; case 3: i += 2; return [3 /*break*/, 1]; case 4: return [2 /*return*/]; } }); }; _Disassembler.prototype.generateInstructions = function (code) { var _this = this; code = code.toUpperCase().trim(); var result = []; var iterator = this.byteIterator(code); var next = function () { var val = iterator.next(); _this.position = val.value.position; return val.value.byte; }; while (true) { var currentByte = iterator.next(); if (currentByte.done) { return result; } var position = currentByte.value.position; var instruction = this.getInstruction(currentByte.value.byte, next); instruction = object_extensions_1.removeFalsy(__assign(__assign({}, instruction), { operand1: object_extensions_1.removeFalsy(instruction.operand1), operand2: object_extensions_1.removeFalsy(instruction.operand2) })); result.push(__assign(__assign({}, instruction), { position: position })); } }; _Disassembler.prototype.getInstruction = function (currentByte, next) { var _a; var is16Bit = false; var isAddress = false; var prefix1 = this.fetchPrefix(currentByte); var prefix2; if (prefix1) { // deal with prefixes currentByte = next(); switch (prefix1) { case 'addressMode': isAddress = true; break; case 'registerMode': is16Bit = true; } prefix2 = this.fetchPrefix(currentByte); if (prefix2) { currentByte = next(); switch (prefix2) { case 'addressMode': isAddress = true; break; case 'registerMode': is16Bit = true; } } } var operation; var tableResult; if (tables_1.MOD_RM_SET.has(currentByte)) { var nextByte = next(); tableResult = _32bit_adressing_table_modrm_1.Table.getReverseValueFromTable(nextByte, isAddress ? '16rm' : '32rm'); if (tableResult) { var modRm = tableResult[1].filter(function (s) { return !isNotANumber_1.isNotANumber(s); })[0]; operation = this.opCodeTable.get({ opcode: currentByte, modRm: modRm }); } } else { operation = this.opCodeTable.get({ opcode: currentByte }); if (this.operationIsJump(operation === null || operation === void 0 ? void 0 : operation.operation)) { return this.processJump(operation, next); } if (operation && !(operation === null || operation === void 0 ? void 0 : operation.op1)) { // if instruction does not have any operand if (operation === null || operation === void 0 ? void 0 : operation.constant) { //eg int 03 return { instruction: operation.operation, operand1: { value: operation.constant } }; } return { instruction: operation.operation }; } var registerCode = 0; if (!operation) { // op might contain a register code for (var i = 1; i <= 7; i++) { var substracted = (parseInt(currentByte, 16) - i).toString(16).toUpperCase(); operation = this.opCodeTable.get({ opcode: substracted }); if (operation) { registerCode = i; break; } } } if (!operation) { // check for 2 bytes operation = this.opCodeTable.get({ opcode: currentByte + next() }); if (this.operationIsJump(operation === null || operation === void 0 ? void 0 : operation.operation)) { return this.processJump(operation, next); } } if (operation === null || operation === void 0 ? void 0 : operation.type) { var type = is16Bit ? 'rw' : operation.type; var reg = (_a = Types_table_1.typeTable.get(type)) === null || _a === void 0 ? void 0 : _a.get(registerCode); switch (type) { case 'rb': tableResult = [[], [reg]]; break; case 'rw': tableResult = [[], ['', reg]]; break; case 'rd': tableResult = [[], ['', '', reg]]; break; } } else { if (!(operation === null || operation === void 0 ? void 0 : operation.isRegisterIncluded) && (operation === null || operation === void 0 ? void 0 : operation.op2)) { var nextByte = next(); tableResult = _32bit_adressing_table_modrm_1.Table.getReverseValueFromTable(nextByte, isAddress ? '16rm' : '32rm'); } } } if (!operation) { throw Error('invalid code'); } var operands = this.processOperand(operation.op1, operation.op2, next, tableResult, { is16Bit: is16Bit, isAddress: isAddress, isSignExtended: operation.isSignExtended, }); return __assign({ instruction: operation.operation }, operands); }; _Disassembler.prototype.operationIsJump = function (operation) { var isJump = false; if (operation) { Jump_table_1.JMP_TABLE.forEach(function (_, v) { if (v.operation === operation) { isJump = true; } }); } return isJump; }; _Disassembler.prototype.fetchPrefix = function (b) { return this.prefixMap.get(b); }; _Disassembler.prototype.checkRegister = function (op1, op2, options) { var isRegister = function (op) { if (register_set_1.ALL_REGISTERS.has(op)) { if (options === null || options === void 0 ? void 0 : options.is16Bit) { // convert eax to ax return op.replace('e', ''); } return op; } return undefined; }; return [isRegister(op1), isRegister(op2)]; }; _Disassembler.prototype.processOperand = function (op1, op2, next, tableResult, options) { var _a, _b; var _c, _d; var operand1; var operand2; //check register _a = this.checkRegister(op1, op2, options), operand1 = _a[0], operand2 = _a[1]; if (operand1 || operand2) { if (operand1) { // e.g 04 if (!op2) { return { operand1: { register: operand1 } }; } if (op2 === 'imm8') { operand2 = next(); return { operand1: { register: operand1 }, operand2: { value: operand2 } }; } if (op2 === 'imm32') { operand2 = next() + next(); if (!(options === null || options === void 0 ? void 0 : options.is16Bit)) { // if 32 bit operand2 = operand2 + next() + next(); } operand2 = removeTrailingZero_1.removeTrailingZero(rotate_1.rotate(operand2)); return { operand1: { register: operand1 }, operand2: { value: operand2 } }; } if (op2.includes('r')) { operand2 = tableResult[1][(options === null || options === void 0 ? void 0 : options.is16Bit) ? 1 : 2]; return { operand1: { register: operand1 }, operand2: { register: operand2 } }; } } if (operand2) { // e.g 04 if (op1 === 'imm8') { operand1 = next(); return { operand1: { register: operand1 }, operand2: { value: operand2 } }; } if (op1 === 'imm32') { operand1 = next() + next(); if (!(options === null || options === void 0 ? void 0 : options.is16Bit)) { // if 32 bit operand1 = operand1 + next() + next(); } return { operand1: { register: operand1 }, operand2: { value: rotate_1.rotate(operand1) } }; } } return { operand1: { register: operand1 }, operand2: { register: operand2 } }; } if (op1 === 'm8') { operand1 = tableResult[0][0]; var operandObj = void 0; var isMemory = operand1.includes('['); var secondRegister = void 0; if (operand1.includes('disp')) { var disp = this.processDisp(operand1, next, options); operandObj = __assign(__assign({}, disp === null || disp === void 0 ? void 0 : disp.operand), { pointer: 'b' }); } else { if (operand1.includes('[sib]')) { operandObj = this.processSIB(next, options); operandObj.pointer = 'b'; } else { if (isMemory) { if (operand1.includes('+')) { _b = operand1.removeBrackets().split('+'), operand1 = _b[0], secondRegister = _b[1]; operandObj = { register: operand1, register2: secondRegister, pointer: 'b' }; } else { operand1 = operand1.removeBrackets(); operandObj = { register: operand1, pointer: 'b' }; } } } } if (!op2) { return { operand1: operandObj }; } if (op2.includes('imm')) { var operand2_1 = this.processImm(op2, next, options); return { operand1: (_c = operandObj) !== null && _c !== void 0 ? _c : { register: operand1.removeBrackets(), pointer: isMemory ? 'b' : undefined, }, operand2: __assign({}, operand2_1), }; } if (op2 === 'r8') { operand2 = tableResult[1][0]; return { operand1: (_d = operandObj) !== null && _d !== void 0 ? _d : { register: operand1.removeBrackets(), pointer: isMemory ? 'b' : undefined, }, operand2: { register: operand2 }, }; } } if (op1 === 'm32') { var opObj = void 0; var tableResultForOp1 = tableResult[0][0]; if (tableResultForOp1.includes('disp')) { var disp = this.processDisp(tableResultForOp1, next, options); opObj = { operand1: __assign(__assign({}, disp === null || disp === void 0 ? void 0 : disp.operand), { pointer: (options === null || options === void 0 ? void 0 : options.is16Bit) ? 'w' : 'd' }) }; } else { if (tableResultForOp1.includes('[sib]')) { //check sib var sib = this.processSIB(next, options); opObj = { operand1: __assign(__assign({}, sib), { pointer: (options === null || options === void 0 ? void 0 : options.is16Bit) ? 'w' : 'd' }) }; } else { if (tableResultForOp1.includes('[')) { // if op1 is memory address if (tableResultForOp1.includes('+')) { var temp = tableResultForOp1.removeBrackets().split('+'); opObj = { operand1: { register: temp[0], register2: temp[1], pointer: (options === null || options === void 0 ? void 0 : options.is16Bit) ? 'w' : 'd', }, }; } else { operand1 = tableResult[0][0].removeBrackets(); opObj = { operand1: { register: operand1, pointer: (options === null || options === void 0 ? void 0 : options.is16Bit) ? 'w' : 'd' } }; } } else { operand1 = tableResult[0][(options === null || options === void 0 ? void 0 : options.is16Bit) ? 1 : 2]; opObj = { operand1: { register: operand1 } }; } } } if (!op2) { return opObj; } if (op2 === 'r32') { operand2 = tableResult[1][(options === null || options === void 0 ? void 0 : options.is16Bit) ? 1 : 2]; return { operand1: opObj.operand1, operand2: { register: operand2 }, }; } if (op2.includes('imm')) { operand2 = this.processImm(op2, next, options); return { operand1: opObj.operand1, operand2: operand2, }; } } if (op1.includes('r')) { //register and displacement if (op1 === 'r8') { operand1 = tableResult[1][0]; if (!op2) { return { operand1: { register: operand1 } }; } if (op2.includes('imm')) { return { operand1: { register: operand1 }, operand2: this.processImm(op2, next, options) }; } if (op2 === 'm8') { var result = tableResult[0][0]; if (options === null || options === void 0 ? void 0 : options.isAddress) { return this.processAddressMode(operand1, result, next, 'b'); } if (result.includes('disp')) { var operand2_2 = this.processDisp(result, next, options); return { operand1: { register: operand1 }, operand2: __assign(__assign({}, operand2_2 === null || operand2_2 === void 0 ? void 0 : operand2_2.operand), { pointer: 'b' }), }; } if (result === '[sib]') { var sib = this.processSIB(next, options); return { operand1: { register: operand1 }, operand2: { register: sib.register, constant: sib.constant ? sib.constant : '1', register2: sib.register2, displacement: sib.displacement, pointer: 'b', }, }; } } } if (op1 === 'r32') { operand1 = tableResult[1][(options === null || options === void 0 ? void 0 : options.is16Bit) ? 1 : 2]; if (op2.includes('imm')) { operand2 = this.processImm(op2, next, options); return { operand1: { register: operand1 }, operand2: operand2, }; } if (op2.includes('m')) { var result = tableResult[0].length > 1 ? tableResult[0][(options === null || options === void 0 ? void 0 : options.is16Bit) ? 1 : 2] : tableResult[0][0]; if (options === null || options === void 0 ? void 0 : options.isAddress) { return this.processAddressMode(operand1, result, next, options.is16Bit ? 'w' : 'd'); } if (result.includes('disp')) { var operand2_3 = this.processDisp(result, next, options); return { operand1: { register: operand1 }, operand2: __assign(__assign({}, operand2_3 === null || operand2_3 === void 0 ? void 0 : operand2_3.operand), { pointer: op2 === 'm32' ? ((options === null || options === void 0 ? void 0 : options.is16Bit) ? 'w' : 'd') : 'b' }), }; } if (result === '[sib]') { var sib = this.processSIB(next, options); return { operand1: { register: operand1 }, operand2: { register: sib.register, constant: sib.constant ? sib.constant : '1', register2: sib.register2, displacement: sib.displacement, pointer: (options === null || options === void 0 ? void 0 : options.is16Bit) ? 'w' : 'd', }, }; } operand2 = this.processM(result, op2, next, options); return { operand1: { register: operand1 }, operand2: operand2, }; } } } if (op1.includes('imm')) { if (op1 === 'imm32') { return { operand1: { value: this.nextByteXtimes(next, 4) } }; } if (options === null || options === void 0 ? void 0 : options.is16Bit) { return { operand1: { value: this.nextByteXtimes(next, 2) } }; } return { operand1: { value: this.nextByteXtimes(next) } }; } return { operand1: { register: operand1 } }; }; _Disassembler.prototype.nextByteXtimes = function (next, x) { var result = ''; var _x = x !== null && x !== void 0 ? x : 1; for (var i = 0; i < _x; i++) { result += next(); } return rotate_1.rotate(result); }; _Disassembler.prototype.processM = function (op, opType, next, options) { if (op.includes('[')) { return { register: op.removeBrackets(), pointer: opType === 'm32' ? ((options === null || options === void 0 ? void 0 : options.is16Bit) ? 'w' : 'd') : 'b' }; } return { register: op }; }; _Disassembler.prototype.getPointer = function (s) { switch (s.length) { case 2: return 'b'; case 4: return 'w'; case 8: return 'd'; default: throw new Error('byte pointer error'); } }; _Disassembler.prototype.processAddressMode = function (op1, op2, next, type) { var _a, _b; var tableResult = op2.removeBrackets().split('+'); if ((_a = tableResult[2]) === null || _a === void 0 ? void 0 : _a.includes('disp')) { // e.g bx+si+disp var disp = tableResult[2]; var displacement = next(); if (disp.includes('16')) { displacement = displacement + next(); } return { operand1: { register: op1 }, operand2: { register: tableResult[0], register2: tableResult[1], displacement: displacement, pointer: type }, }; } if ((_b = tableResult[1]) === null || _b === void 0 ? void 0 : _b.includes('disp')) { // e.g bx+disp var disp = tableResult[1]; var displacement = next(); if (disp.includes('16')) { displacement = displacement + next(); if (this.isNegative(displacement, 4)) { displacement = '-' + twosComplement_1.convertToTwosComp(displacement); } } else { if (this.isNegative(displacement, 2)) { displacement = '-' + twosComplement_1.convertToTwosComp(displacement); } } return { operand1: { register: op1 }, operand2: { register: tableResult[0], displacement: displacement, pointer: type }, }; } return { operand1: { register: op1 }, operand2: { register: tableResult[0], register2: tableResult[1] ? tableResult[1] : undefined, pointer: type, }, }; }; _Disassembler.prototype.processSIB = function (next, options) { var n = next(); var sib = _32bit_adressing_table_modrm_1.Table.getReverseValueFromTable(n, '32sib'); var _a = sib[0][0].removeBrackets('[').split('*'), register = _a[0], constant = _a[1]; var displacement; var register2; if (sib[1][0] !== '[*]') { register2 = sib[1][0]; } else { displacement = removeTrailingZero_1.removeTrailingZero(rotate_1.rotate(next() + next() + next() + next())); } var result = { displacement: displacement, register: register2 !== null && register2 !== void 0 ? register2 : register, register2: register2 ? register : undefined, constant: constant, }; return result; }; _Disassembler.prototype.processDisp = function (op, next, options) { var displacement; var result; if (op === 'disp32') { displacement = removeTrailingZero_1.removeTrailingZero(rotate_1.rotate(next() + next() + next() + next())); return { operand: { displacement: displacement } }; } if (op.includes('disp')) { if (op.includes('sib')) { result = this.processSIB(next, options); } if (op.includes('disp32')) { displacement = removeTrailingZero_1.removeTrailingZero(rotate_1.rotate(next() + next() + next() + next())); if (this.isNegative(displacement, 8)) { displacement = '-' + twosComplement_1.convertToTwosComp(displacement); } } else if (op.includes('disp16')) { // disp16 displacement = removeTrailingZero_1.removeTrailingZero(rotate_1.rotate(next() + next())); if (this.isNegative(displacement, 4)) { displacement = '-' + twosComplement_1.convertToTwosComp(displacement); } } else { // disp8 displacement = removeTrailingZero_1.removeTrailingZero(rotate_1.rotate(next())); if (this.isNegative(displacement, 2)) { displacement = '-' + twosComplement_1.convertToTwosComp(displacement); } } if (result) { return { operand: __assign(__assign({}, result), { displacement: displacement }) }; } } var split = op.removeBrackets().split('+'); if (split.length > 2) { //[bx+si]+disp var register_1 = split[0], register2 = split[1]; return { operand: { register: register_1, register2: register2, displacement: displacement } }; } else { // eg [eax + disp32] var register_2 = split[0]; return { operand: { register: register_2, displacement: displacement } }; } }; _Disassembler.prototype.processImm = function (op2, next, options) { switch (op2) { case 'imm8': var nextByte = next(); if ((options === null || options === void 0 ? void 0 : options.isSignExtended) && this.isNegative(nextByte)) { nextByte = 'FF' + nextByte; if (!options.is16Bit) { nextByte = 'FFFF' + nextByte; } } return { value: nextByte }; case 'imm32': var operand2 = removeTrailingZero_1.removeTrailingZero(rotate_1.rotate(next() + next() + (!(options === null || options === void 0 ? void 0 : options.is16Bit) ? next() + next() : ''))); return { value: operand2 }; } }; _Disassembler.prototype.isNegative = function (s, length) { if (length === 2) { return parseInt(s, 16) > 127; } if (length === 4) { return parseInt(s, 16) > 32767; } if (length === 8) { return parseInt(s, 16) > 2147483647; } return parseInt(s, 16) > 127; }; _Disassembler.prototype.processJump = function (operation, next) { var _this = this; var getDestination = function (value, length) { var rotatedVal = rotate_1.rotate(value); var _value; if (_this.isNegative(rotatedVal)) { _value = -parseInt(twosComplement_1.convertToTwosComp(rotatedVal), 16); } else { _value = parseInt(rotatedVal, 16); } return makeValueToByte_1.makeValueToByte((_this.position + 1 + _value).toString(16), length); }; switch (operation === null || operation === void 0 ? void 0 : operation.op1) { case 'imm8': var value = next(); return { instruction: operation === null || operation === void 0 ? void 0 : operation.operation, operand1: { value: getDestination(value, 2) } }; case 'imm16': var value2 = next() + next(); return { instruction: operation === null || operation === void 0 ? void 0 : operation.operation, operand1: { value: getDestination(value2, 4) } }; default: //imm32 var value3 = next() + next() + next() + next(); return { instruction: operation === null || operation === void 0 ? void 0 : operation.operation, operand1: { value: getDestination(value3, 8) } }; } }; return _Disassembler; }()); exports._Disassembler = _Disassembler;