From b33bed4dbf709031b273115f6e15631e673658a9 Mon Sep 17 00:00:00 2001 From: iximeow Date: Thu, 12 Mar 2015 02:16:29 -0700 Subject: get better instruction stream parsing working --- src/Instruction.scala | 77 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 5 deletions(-) (limited to 'src/Instruction.scala') diff --git a/src/Instruction.scala b/src/Instruction.scala index 8a4e018..7723ef7 100644 --- a/src/Instruction.scala +++ b/src/Instruction.scala @@ -3,25 +3,54 @@ package ixee.re.disasm import ixee.cryptopals.utils.ByteUtils._ import ixee.cryptopals.utils.FunctionUtils._ -import scala.collection.mutable.Map +import scala.collection.mutable.{ArrayBuffer, Map} -sealed trait Instruction +sealed trait Instruction { + def isDone: Boolean + def giveWord(word: Int): Unit +} case class NoOpInst(code: NoOpCode, offset: Int) extends Instruction { override def toString: String = { s"$code -> $offset" } + def giveWord(word: Int) = throw new RuntimeException("Too many bytes!") + def isDone = true } case class OneOpInst(code: OneOpCode, byte: Boolean, dest: Int, addressMode: OneOpAddressing) extends Instruction { + private var extraWord: Option[Int] = None + def neededWords = Seq(addressMode).count(_.wide) + def giveWord(word: Int) = + extraWord.fold(extraWord = Some(word))(_ => throw new RuntimeException("Too many bytes!")) + def isDone = neededWords == Seq(extraWord).flatten.length + override def toString: String = { val byteStr = if (byte) ".B" else " " - s"$code$byteStr ${addressMode.stringifyWith(dest)}" + s"$code$byteStr ${addressMode match { + case mode: NormMode => mode.stringifyWith(dest) + case mode: WideMode => mode.stringifyWith(dest, extraWord.get) + }}" } } case class TwoOpInst(code: TwoOpCode, byte: Boolean, src: Int, srcMode: TwoOpSourceAddressing, dest: Int, dstMode: TwoOpDestAddressing) extends Instruction { + private var extraWord1: Option[Int] = None + private var extraWord2: Option[Int] = None + def neededWords = Seq(srcMode, dstMode).count(_.wide) + def giveWord(word: Int) = + extraWord1.fold(extraWord1 = Some(word))(_ => + extraWord2.fold(extraWord2 = Some(word))(_ => throw new RuntimeException("Too many bytes!")) + ) + def isDone = neededWords == Seq(extraWord1, extraWord2).flatten.length + override def toString: String = { val byteStr = if (byte) ".B" else " " - s"$code$byteStr ${srcMode.stringifyWith(src)}, ${dstMode.stringifyWith(dest)}" + s"$code$byteStr ${srcMode match { + case mode: NormMode => mode.stringifyWith(src) + case mode: WideMode => mode.stringifyWith(src, extraWord1.get) + }}, ${dstMode match { + case mode: NormMode => mode.stringifyWith(dest) + case mode: WideMode => mode.stringifyWith(dest, extraWord2.orElse(if (srcMode.wide) None else extraWord1).get) + }}" } } @@ -103,10 +132,48 @@ object Instruction { def twoOp(instruction: (Byte, Byte)): Option[TwoOpInst] = TwoOp.apply(instruction) - def isNoOp(instruction: (Byte ,Byte)) = noOp(instruction).isDefined + def isNoOp(instruction: (Byte, Byte)) = noOp(instruction).isDefined def isOneOp(instruction: (Byte, Byte)) = oneOp(instruction).isDefined def isTwoOp(instruction: (Byte, Byte)) = twoOp(instruction).isDefined } +class InstructionParser(bytes: Seq[Byte]) { + val instructions = scala.collection.mutable.ArrayBuffer[Instruction]() + val bytePair = scala.collection.mutable.ArrayBuffer[Byte]() + + for (byte <- bytes) { + bytePair match { + case ArrayBuffer() => bytePair += byte + case ArrayBuffer(first) => { + val second = byte + def parseInstr = { + instructions += Instruction((second, first)).getOrElse { throw new RuntimeException(s"Parse failure! Bytes = $first, $second") } + bytePair.clear + } + + val lastIsDone = instructions.lastOption.map(_.isDone).getOrElse(true) + + instructions.lastOption.map { instr => + if (instr.isDone) { + parseInstr + } else { + instr.giveWord(second * 256 + first) + bytePair.clear + } + } getOrElse { parseInstr } + } + case buffer @ _ => throw new RuntimeException("Byte array overflow: " + buffer) + } + } + + instructions.lastOption match { + case Some(instr) if !instr.isDone => throw new RuntimeException("Unable to parse instruction stream, last instruction is missing data.") + case _ => Unit + } +} + +object InstructionParser { + def apply(bytes: Seq[String]) = new InstructionParser(bytes.map(java.lang.Integer.parseInt(_, 16).toByte)) +} -- cgit v1.1