from ViewMode import * from cemu import * import TextSelection from PyQt5 import QtGui, QtCore, QtWidgets from time import time import sys import threading import re import binascii import capstone import capstone.x86 import ply.lex as lex MNEMONIC_COLUMN = 30 MNEMONIC_WIDTH = 15 Disasm_x86_16bit = (capstone.CS_ARCH_X86, capstone.CS_MODE_16) Disasm_x86_32bit = (capstone.CS_ARCH_X86, capstone.CS_MODE_32) Disasm_x86_64bit = (capstone.CS_ARCH_X86, capstone.CS_MODE_64) #Disasm_ARM = (capstone.CS_ARCH_ARM, capstone.CS_MODE_ARM) Disasm_ARM = (capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) Disasm_ARM_Thumb = (capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) Disasm_ARM64 = (capstone.CS_ARCH_ARM64, capstone.CS_MODE_ARM) class ASMLexer(object): def __init__(self): pass def set(self, name, value): setattr(self, name, value) # Build the lexer def build(self,**kwargs): self._lexer = lex.lex(module=self, **kwargs) def lexer(self): return self._lexer class ARM_Lexer(ASMLexer): def __init__(self): self._loaded = True my_t_REGISTER = r'[pcdr][0-9][1-9]*|sb|sl|fp|ip|sp|lr|pc!?|lsr|lsl|asr|apsr_nzcv' my_t_NUMBER = r'\#?-?(0x)?[0-9a-f]+|[0-9]+' my_t_PTR = r'qword|dword|word|byte|ptr|xmmword|ror' my_t_ignore = r' [],+:*{}^!-' self.set('t_REGISTER', my_t_REGISTER) self.set('t_NUMBER', my_t_NUMBER) self.set('t_PTR', my_t_PTR) self.set('t_ignore', my_t_ignore) self.set('t_error', self.my_t_error) tokens = ('REGISTER', 'NUMBER', 'PTR') def my_t_error(self, t): t.type = t.value[0] t.value = t.value[0] t.lexer.skip(1) #print t.value #print 'LEXER ERROR' return t class ARM64_Lexer(ASMLexer): def __init__(self): self._loaded = True my_t_REGISTER = r'[pcdr][0-9][1-9]+|sb|sl|fp|ip|sp|lr|pc!?|lsr|lsl|asr|wzr|xzr|[xw][0-9]+|v[0-9]+(\.16b)?' my_t_NUMBER = r'\#-?0x[0-9a-f]+|[0-9]+|\#[0-9]+' my_t_PTR = r'qword|dword|word|byte|ptr|xmmword' my_t_ignore = r' [],+:*{}^' self.set('t_REGISTER', my_t_REGISTER) self.set('t_NUMBER', my_t_NUMBER) self.set('t_PTR', my_t_PTR) self.set('t_ignore', my_t_ignore) self.set('t_error', self.my_t_error) tokens = ('REGISTER', 'NUMBER', 'PTR') def my_t_error(self, t): t.type = t.value[0] t.value = t.value[0] t.lexer.skip(1) return t class X86_Lexer(ASMLexer): def __init__(self): self._loaded = True my_t_REGISTER = r'e?r?[abcde]x|ebp|bp|esp|rip|rbp|rsp|r[1-9][0-9]*d?b?w?|si|di|rsi|rdi|edi|esi?|gs|fs|ss|cs|ds|xmm[0-9]|[abcd][lh]|cr[0-9]|ymm[0-9]|dr[0-7]|fp[0-7]|mm[0-7]|dil|st' my_t_NUMBER = r'0x[0-9a-f]+|[0-9]+' my_t_PTR = r'qword|dword|word|byte|ptr|xmmword' my_t_ignore = r' []-,+:*()' self.set('t_REGISTER', my_t_REGISTER) self.set('t_NUMBER', my_t_NUMBER) self.set('t_PTR', my_t_PTR) self.set('t_ignore', my_t_ignore) self.set('t_error', self.my_t_error) tokens = ('REGISTER', 'NUMBER', 'PTR') def my_t_error(self, t): t.type = t.value[0] t.value = t.value[0] t.lexer.skip(1) return t class ASMLine(object): def __init__(self, d, plugin, lexer): self._hexstr = self.hexlify(d.bytes) self._instruction = d.op_str self._operands = d.op_str self._size = d.size self._addr = d.address self._mnemonic = d.mnemonic self._computed = False self.Lexer = lexer self._d = d self._asm = d self._symbol = None self._refString = None self._indexTable = [] self._loaded = False self._plugin = plugin def iterTokens(self): self.full_load() return self.lexer def full_load(self): if self._loaded: return self._lexer = self.Lexer self._lexer.input(self._asm.op_str) self.lexer = list(self._lexer) # compute index table # add hex bytes as tokens H = self.hex.split(b' ') for i, h in enumerate(H): self._indexTable += [(i*3, len(h), h)] # add mnemonic as token self._indexTable += [(MNEMONIC_COLUMN, len(self.mnemonic), self.mnemonic)] # add symbol as token if self.symbol: t = (MNEMONIC_COLUMN + MNEMONIC_WIDTH + 1, len(self.symbol), self.symbol) self._indexTable += [t] else: for tok in self.lexer: t = (tok.lexpos + MNEMONIC_COLUMN + MNEMONIC_WIDTH, len(tok.value), tok.value) self._indexTable += [t] self._loaded = True @property def referencedString(self): raise "not implemented" @property def symbol(self): raise "not implemented" @property def indexTable(self): return self._indexTable def tokens(self): return self._indexTable def getSelectedToken(self, cx): for i, t in enumerate(self.indexTable): idx, length, value = t if cx == idx: return t return self.indexTable[0] def getSelectionWidth(self, cx): self.full_load() for i, t in enumerate(self.indexTable): idx, length, value = t if cx == idx: return length return 0 def getEndCursor(self): self.full_load() idx, length, value = self.indexTable[-1] return idx def getNearestCursor(self, cx): if cx > self.getEndCursor(): return self.getEndCursor() i = len(self.indexTable) - 1 while i > 0: idx, length, value = self.indexTable[i] if cx >= idx: return idx i -= 1 return 0 def getNextCursor(self, cx, direction=''): for i, t in enumerate(self.indexTable): idx, length, value = t if cx == idx: break if direction == Directions.Right: if i < len(self.indexTable) - 1: idx, length, value = self.indexTable[i + 1] else: return 0, 1 if direction == Directions.Left: if i > 0: idx, length, value = self.indexTable[i - 1] else: return 0, -1 return idx, 0 def ingroup(self, group): if len(self._d.groups) > 0: for g in self._d.groups: for x in group: if x == g: return True return False def hexlify(self, sb): hexstr = binascii.hexlify(sb) hexstr = b' '.join([hexstr[i:i+2] for i in range(0, len(hexstr), 2)]) return hexstr @property def size(self): return self._size @property def offset(self): return self._addr @property def hex(self): return self._hexstr @property def mnemonic(self): return self._mnemonic#.upper() @property def operands(self): return self._operands @property def obj(self): return self._d def isBranch(self): raise "not implemented" def branchAddress(self): raise "not implemented" # represents x86 asm line class ASMx86Line(ASMLine): def __init__(self, d, plugin, lexer): super(ASMx86Line, self).__init__(d, plugin, lexer) @property def referencedString(self): # get referenced string if self._refString != None: return self._refString asm = self._asm self._refString = '' # PUSH <imm> if asm.id == capstone.x86.X86_INS_PUSH: if len(asm.operands) == 1: o = asm.operands[0] if o.type == capstone.x86.X86_OP_IMM: value = o.imm self._refString = self._plugin.stringFromVA(value) # [RIP + <imm>] if len(asm.operands) > 1: o = asm.operands[1] if o.type == capstone.x86.X86_OP_MEM: if o.mem.base == capstone.x86.X86_REG_RIP: x = asm.address + asm.size + o.mem.disp self._refString = self._plugin.stringFromVA(x) return self._refString @property def symbol(self): # get symbol from plugin (for API calls for eg.) if self._symbol != None: return self._symbol # get symbol if self.ingroup([capstone.x86.X86_GRP_CALL]): value = None asm = self._asm for o in asm.operands: if o.type == capstone.x86.X86_OP_IMM: value = o.imm if o.type == capstone.x86.X86_OP_MEM: # todo: should we consider other reg relative ?? if o.mem.base == capstone.x86.X86_REG_RIP: value = o.mem.disp + asm.size + asm.address # mainly 32bit if o.mem.base == capstone.x86.X86_REG_INVALID: value = o.mem.disp if value: sym = self._plugin.disasmSymbol(value) if sym: self._symbol = sym return self._symbol def isBranch(self): return self.ingroup([capstone.x86.X86_GRP_JUMP, capstone.x86.X86_GRP_CALL]) def branchAddress(self): if not self.isBranch(): return None asm = self.obj if len(asm.operands) == 1: o = asm.operands[0] if o.type == capstone.x86.X86_OP_MEM: x = asm.address + asm.size + o.mem.disp return x if o.type == capstone.x86.X86_OP_IMM: x = o.imm return x class DisasmViewMode(ViewMode): def __init__(self, width, height, data, cursor, widget=None, plugin=None): super(DisasmViewMode, self).__init__() self.plugin = plugin self.dataModel = data self.addHandler(self.dataModel) self.width = width self.height = height self.cursor = cursor self.widget = widget self.refresh = True self.selector = TextSelection.DefaultSelection(self) # background brush self.backgroundBrush = QtGui.QBrush(QtGui.QColor(0, 0, 128)) # text font self.font = QtGui.QFont('Terminus', 11, QtGui.QFont.Light) # font metrics. assume font is monospaced self.font.setKerning(False) self.font.setFixedPitch(True) fm = QtGui.QFontMetrics(self.font) self._fontWidth = fm.width('a') self._fontHeight = fm.height() self.textPen = QtGui.QPen(QtGui.QColor(192, 192, 192), 0, QtCore.Qt.SolidLine) self.resize(width, height) self.Paints = {} self.Ops = [] self.newPix = None self.FlowHistory = [] self.OPCODES = [] self.selector = TextSelection.DisasmSelection(self) self.init_disassembler_engine() def init_disassembler_engine(self): # init state for disasambler # set capstone, lexer, asmline arch, mode = self.plugin.hintDisasm() self.disasm_engine = capstone.Cs(arch, mode) self.disasm_engine.detail = True if arch == capstone.CS_ARCH_X86: Lexer = X86_Lexer() if arch == capstone.CS_ARCH_ARM and mode in [capstone.CS_MODE_ARM, capstone.CS_MODE_THUMB]: Lexer = ARM_Lexer() if arch == capstone.CS_ARCH_ARM64: Lexer = ARM64_Lexer() # todo: ASM_ARM_Line? self.ASMLine = ASMx86Line Lexer.build() self.lexer = Lexer.lexer() @property def fontWidth(self): return self._fontWidth @property def fontHeight(self): return self._fontHeight def setTransformationEngine(self, engine): self.transformationEngine = engine def resize(self, width, height): self.width = width - width%self.fontWidth self.height = height - height%self.fontHeight self.computeTextArea() self.qpix = self._getNewPixmap(self.width, self.height + self.SPACER) self.refresh = True def computeTextArea(self): self.COLUMNS = self.width//self.fontWidth self.ROWS = self.height//self.fontHeight self.notify(self.ROWS, self.COLUMNS) def getPixmap(self): for t in self.Ops: if len(t) == 1: t[0]() else: t[0](*t[1:]) self.Ops = [] if not self.newPix: self.draw() return self.newPix def getPageOffset(self): return self.dataModel.getOffset() def getGeometry(self): return self.COLUMNS, self.ROWS def getDataModel(self): return self.dataModel def startSelection(self): self.selector.startSelection() def stopSelection(self): self.selector.stopSelection() def getCursorOffsetInPage(self): x, y = self.cursor.getPosition() preY = sum([asm.size for asm in self.OPCODES[:y]]) if len(self.OPCODES) - 1 < y: return 0 asm = self.OPCODES[y] if x < len(asm.hex): postY = x//3 else: postY = asm.size return preY + postY def getCursorAbsolutePosition(self): offset = self.getCursorOffsetInPage() return self.dataModel.getOffset() + offset def drawCursor(self, qp): cursorX, cursorY = self.cursor.getPosition() asm = self.OPCODES[cursorY] xstart = cursorX width = asm.getSelectionWidth(xstart) qp.setBrush(QtGui.QColor(255, 255, 0)) qp.setOpacity(0.5) qp.drawRect(xstart*self.fontWidth, cursorY*self.fontHeight + 1, width*self.fontWidth, self.fontHeight + 1) qp.setOpacity(1) def drawSelected(self, qp): qp.setFont(self.font) cursorX, cursorY = self.cursor.getPosition() if len(self.OPCODES) - 1 < cursorY: return asm = self.OPCODES[cursorY] cx, width, text = asm.getSelectedToken(cursorX) cemu = ConsoleEmulator(qp, self.ROWS, self.COLUMNS) for i, asm in enumerate(self.OPCODES): for idx, length, value in asm.tokens(): # skip current cursor position if cursorY == i and cursorX == idx: continue # check every line, if match, select it if value == text: qp.setOpacity(0.4) brush=QtGui.QBrush(QtGui.QColor(0, 255, 0)) qp.fillRect(idx*self.fontWidth, i*self.fontHeight + 2 , width*self.fontWidth, self.fontHeight, brush) qp.setOpacity(1) def drawBranch(self, qp): qp.fillRect(-50, 0, 50, self.ROWS * self.fontHeight, self.backgroundBrush) cursorX, cursorY = self.cursor.getPosition() if len(self.OPCODES) - 1 < cursorY: return asm = self.OPCODES[cursorY] if asm.isBranch(): tsize = sum([o.size for o in self.OPCODES]) msize = sum([o.size for o in self.OPCODES[:cursorY]]) half = self.fontHeight//2 # branch address target = asm.branchAddress() if target == None: return screenVA = self._getVA(self.dataModel.getOffset()) if target > screenVA and target < self._getVA(self.dataModel.getOffset()) + tsize - self.OPCODES[-1].size: # branch target is in screen sz = 0 for i, t in enumerate(self.OPCODES): sz += t.size if sz+self._getVA(self.dataModel.getOffset()) >= target: break qp.setPen(QtGui.QPen(QtGui.QColor(0, 192, 0), 1, QtCore.Qt.SolidLine)) # draw the three lines qp.drawLine(-5, cursorY*self.fontHeight + self.fontHeight//2, -30, cursorY*self.fontHeight + half) qp.drawLine(-30, cursorY*self.fontHeight + self.fontHeight//2, -30, (i + 1)*self.fontHeight + half) qp.drawLine(-30, (i + 1)*self.fontHeight + half, -15, (i + 1)*self.fontHeight + half) # draw arrow points = [QtCore.QPoint(-15, (i + 1)*self.fontHeight + half - 5), QtCore.QPoint(-15, (i + 1)*self.fontHeight + half + 5), QtCore.QPoint(-5, (i + 1)*self.fontHeight + half), ] needle = QtGui.QPolygon(points) qp.setBrush(QtGui.QBrush(QtGui.QColor(0, 128, 0))) qp.drawPolygon(needle) elif target > screenVA: # branch is at greater address, out of screen qp.setPen(QtGui.QPen(QtGui.QColor(0, 192, 0), 1, QtCore.Qt.DotLine)) # draw the two lines qp.drawLine(-5, cursorY*self.fontHeight + self.fontHeight//2, -30, cursorY*self.fontHeight + half) qp.drawLine(-30, cursorY*self.fontHeight + self.fontHeight//2, -30, (self.ROWS - 2)*self.fontHeight + half) # draw arrow points = [QtCore.QPoint(-25, (self.ROWS - 2)*self.fontHeight + half), QtCore.QPoint(-35, (self.ROWS - 2)*self.fontHeight + half), QtCore.QPoint(-30, (self.ROWS - 2)*self.fontHeight + 2*half), ] needle = QtGui.QPolygon(points) qp.setBrush(QtGui.QBrush(QtGui.QColor(0, 128, 0))) qp.drawPolygon(needle) else: # upper arrow # branch is at lower address, out of screen qp.setPen(QtGui.QPen(QtGui.QColor(0, 192, 0), 1, QtCore.Qt.DotLine)) # draw the two lines qp.drawLine(-5, cursorY*self.fontHeight + self.fontHeight//2, -30, cursorY*self.fontHeight + half) qp.drawLine(-30, cursorY*self.fontHeight + self.fontHeight//2, -30, (1)*self.fontHeight + half) # draw arrow points = [QtCore.QPoint(-25, (1)*self.fontHeight + half), QtCore.QPoint(-35, (1)*self.fontHeight + half), QtCore.QPoint(-30, (1)*self.fontHeight), ] needle = QtGui.QPolygon(points) qp.setBrush(QtGui.QBrush(QtGui.QColor(0, 128, 0))) qp.drawPolygon(needle) def draw(self, refresh=False): if self.dataModel.getOffset() in self.Paints: self.refresh = False self.qpix = QtGui.QPixmap(self.Paints[self.dataModel.getOffset()]) #print 'hit' self.drawAdditionals() return if self.refresh or refresh: qp = QtGui.QPainter() qp.begin(self.qpix) # viewport #qp.fillRect(0, 0, self.COLUMNS * self.fontWidth, self.ROWS * self.fontHeight, self.backgroundBrush) #start = time() self.drawTextMode(qp) #end = time() - start #print 'Time ' + str(end) self.refresh = False qp.end() # self.Paints[self.dataModel.getOffset()] = QtGui.QPixmap(self.qpix) self.drawAdditionals() def drawAdditionals(self): self.newPix = self._getNewPixmap(self.width, self.height + self.SPACER) qp = QtGui.QPainter() qp.begin(self.newPix) qp.setWindow(-50, 0, self.COLUMNS * self.fontWidth, self.ROWS * self.fontHeight) qp.drawPixmap(0, 0, self.qpix) #self.transformationEngine.decorateText() # highlight selected text self.selector.highlightText() # draw other selections self.selector.drawSelections(qp) # draw our cursor self.drawCursor(qp) #qp.setViewport(0, 0, 50, self.ROWS * self.fontHeight) """ self.newPix = self._getNewPixmap(50, self.height) qp = QtGui.QPainter() qp.begin(self.newPix) qp.setViewport(0, 0, 50, self.ROWS * self.fontHeight) """ self.drawBranch(qp) self.drawSelected(qp) qp.end() def _getNewPixmap(self, width, height): return QtGui.QPixmap(width, height) def getColumnsbyRow(self, row): if row < len(self.OPCODES): obj = self.OPCODES[row] return obj.size else: return 0 def _getVA(self, offset): if self.plugin: return self.plugin.hintDisasmVA(offset) return 0 def _getOpcodes(self, ofs, code, dt, count): md = self.disasm_engine cnt = 0 offset = 0 OPCODES = [] # how ugly ... don't like capstone on this one.. while cnt < count and offset < len(code): buffer = bytes(code[offset:]) Disasm = md.disasm(buffer, self._getVA(ofs) + offset, count=count) # disasamble as much as we can for d in Disasm: cnt += 1 offset += d.size OPCODES.append(self.ASMLine(d, self.plugin, self.lexer)) # when we are stopped with errors, inject fake isntructions if cnt < self.ROWS and offset < len(code): class D: mnemonic = 'db' bytes = '' op_str = '' size = 1 id = 0 address = 0 operands = [] groups = [] d = D() d.mnemonic = 'db ' + '0x{:02x}'.format(code[offset]) d.bytes = bytes([code[offset]]) d.address = self._getVA(ofs) + offset OPCODES.append(self.ASMLine(d, self.plugin, self.lexer)) cnt += 1 offset += 1 return OPCODES def _drawRow(self, qp, cemu, row, asm): qp.setPen(QtGui.QPen(QtGui.QColor(192, 192, 192), 1, QtCore.Qt.SolidLine)) # write hexdump cemu.writeAt(0, row, asm.hex) # fill with spaces cemu.write((MNEMONIC_COLUMN - len(asm.hex))*' ') # let's color some branch instr if asm.isBranch(): qp.setPen(QtGui.QPen(QtGui.QColor(255, 80, 0))) else: qp.setPen(QtGui.QPen(QtGui.QColor(192, 192, 192), 1, QtCore.Qt.SolidLine)) cemu.write(asm.mnemonic) # leave some spaces cemu.write((MNEMONIC_WIDTH-len(asm.mnemonic))*' ') if asm.symbol: qp.setPen(QtGui.QPen(QtGui.QColor(192, 192, 192), 1, QtCore.Qt.SolidLine)) cemu.write_c('[') qp.setPen(QtGui.QPen(QtGui.QColor('yellow'), 1, QtCore.Qt.SolidLine)) cemu.write(asm.symbol) qp.setPen(QtGui.QPen(QtGui.QColor(192, 192, 192), 1, QtCore.Qt.SolidLine)) cemu.write_c(']') result = '[' + asm.symbol + ']' else: self._write_instruction(asm, qp, cemu) if len(asm.referencedString) > 4: cemu.write(30*' ') qp.setPen(QtGui.QPen(QtGui.QColor(82, 192, 192), 1, QtCore.Qt.SolidLine)) cemu.write('; "{0}"'.format(asm.referencedString)) def _write_instruction(self, asm, qp, cemu): asm.full_load() s = asm.operands idx = 0 qp.setPen(QtGui.QPen(QtGui.QColor(192, 192, 192), 1, QtCore.Qt.SolidLine)) for tok in asm.lexer: if tok.lexpos > idx: cemu.write(s[idx:tok.lexpos]) idx = tok.lexpos qp.save() if tok.type == 'REGISTER': qp.setPen(QtGui.QPen(QtGui.QColor('white'))) if tok.type == 'NUMBER': qp.setPen(QtGui.QPen(QtGui.QColor('green'))) cemu.write(tok.value) qp.restore() idx = tok.lexpos + len(tok.value) if idx < len(s): cemu.write(s[idx:]) def drawTextMode(self, qp): # draw background qp.fillRect(0, 0, self.COLUMNS * self.fontWidth, self.ROWS * self.fontHeight, self.backgroundBrush) # set text pen&font qp.setFont(self.font) qp.setPen(self.textPen) cemu = ConsoleEmulator(qp, self.ROWS, self.COLUMNS) if len(self.OPCODES) == 0: self.OPCODES = self._getOpcodes(self.dataModel.getOffset(), self.getDisplayablePage(), self.plugin.hintDisasm(), count=self.ROWS) for i in range(self.ROWS): if i < len(self.OPCODES): asm = self.OPCODES[i] self._drawRow(qp, cemu, i, asm) def _getRowInPage(self, offset): offset -= self.dataModel.getOffset() size = 0 for i, asm in enumerate(self.OPCODES): if size + asm.size > offset: return i size += asm.size return None def _getOffsetOfRow(self, row): # of course, it could be done nicely, not like this size = 0 for i, asm in enumerate(self.OPCODES): if i == row: return size size += asm.size return None def goTo(self, offset): tsize = sum([asm.size for asm in self.OPCODES]) if offset < self.dataModel.getOffset() + tsize and offset > self.dataModel.getOffset(): # if in current page, move cursor row = self._getRowInPage(offset) off_row = self._getOffsetOfRow(row) diff = offset - self.dataModel.getOffset() - off_row# self.OPCODES[row].size if row is not None: self.cursor.moveAbsolute((diff)*3, row) self.draw(refresh=False) else: # else, move page self.dataModel.goTo(offset) self.OPCODES = self._getOpcodes(offset, self.getDisplayablePage(), self.plugin.hintDisasm(), count=self.ROWS) self.cursor.moveAbsolute(0, 0) self.draw(refresh=True) #TODO: getDisplayablePage() won't contain what we want to disasm. we will use dataModel # in this view, getDisplayablePage will contain disasm text, because that is what is displayed if self.widget: self.widget.update() def scrollPages(self, number, cachePix=None, pageOffset=None): self.scroll(0, -number*self.ROWS, cachePix=cachePix, pageOffset=pageOffset) def _disassamble_one(self, start, end, count=1): code = self.dataModel.getStream(start, end) Disasm = self._getOpcodes(start, code, 0, count) return Disasm def scroll_v(self, dy, cachePix=None, pageOffset=None): #start = time() RowsToDraw = [] factor = abs(dy) # repeat as many rows we have scrolled for row in range(factor): d = None if dy < 0: tsize = sum([asm.size for asm in self.OPCODES]) #self.dataModel.slide(self.OPCODES[0].size) start = self.dataModel.getOffset() + tsize # let's say we decode maximum 50 bytes end = start + 50 # make sure we won't jump off the limits if start > self.dataModel.getDataSize(): return if end > self.dataModel.getDataSize(): end = self.dataModel.getDataSize() if start == end: return iterable = self._disassamble_one(start, end, count=1) #print iterable if len(iterable) > 0: d = iterable[0] else: #todo: what should we handle here break if dy >= 0: # we try to decode from current offset - 50, in this case even if decoding is incorrect (will overlap), # in 50 (actually 64, arm hack, we have 4bytes/instr and instruction is almoast always decoded in something) # bytes it is possible to correct itself, so hopefully last instruction it's decoded correctly and # followed correctly by current_offset instruction INST = 64 if self.dataModel.getOffset() < INST: start = 0 else: start = self.dataModel.getOffset() - INST end = self.dataModel.getOffset() iterable = self._disassamble_one(start, end, count=INST) #print iterable if len(iterable) > 0: d = iterable[-1] else: #todo: what should we handle here break newasm = d if dy < 0: self.dataModel.slide(self.OPCODES[0].size) del self.OPCODES[0] if dy >= 0: self.dataModel.slide(-d.size) del self.OPCODES[len(self.OPCODES) - 1] if dy < 0: self.OPCODES.append(newasm) if dy > 0: self.OPCODES.insert(0, newasm) if dy < 0: #RowsToDraw.append((self.ROWS - factor + row, newasm)) RowsToDraw.append((self.ROWS + row, newasm)) if dy > 0: #RowsToDraw.append((factor - row - 1, newasm)) RowsToDraw.append((-row - 1, newasm)) # draw if len(RowsToDraw) < abs(dy): # maybe we couldn't draw dy rows (possible we reached the beginning of the data to early), recalculate dy dy = len(RowsToDraw)*dy//abs(dy) factor = abs(dy) if not cachePix: self.qpix.scroll(0, dy*self.fontHeight, self.qpix.rect()) qp = QtGui.QPainter() if cachePix: qp.begin(cachePix) else: qp.begin(self.qpix) qp.setFont(self.font) qp.setPen(self.textPen) # erase rows that will disappear if dy < 0: qp.fillRect(0, (self.ROWS-factor)*self.fontHeight, self.fontWidth*self.COLUMNS, factor * self.fontHeight, self.backgroundBrush) if dy > 0: qp.fillRect(0, 0, self.fontWidth*self.COLUMNS, factor * self.fontHeight, self.backgroundBrush) cemu = ConsoleEmulator(qp, self.ROWS, self.COLUMNS) for row, asm in RowsToDraw: self._drawRow(qp, cemu, dy + row, asm) qp.end() #end = time() - start def scroll(self, dx, dy, cachePix=None, pageOffset=None): if dx != 0: if self.dataModel.inLimits((self.dataModel.getOffset() - dx)): self.dataModel.slide(dx) self.draw(refresh=True) #self.scroll_h(dx) if dy != 0: if dy > 0: if self.dataModel.getOffset() == 0: return if dy < 0: tsize = sum([asm.size for asm in self.OPCODES]) if self.dataModel.getOffset() + tsize == self.dataModel.getDataSize(): return self.scroll_v(dy, cachePix, pageOffset) def moveCursor(self, direction): cursorX, cursorY = self.cursor.getPosition() if direction == Directions.Left: asm = self.OPCODES[cursorY] if cursorX == 0: if cursorY == 0: # if first line, scroll self.scroll(0, 1) self.cursor.moveAbsolute(0, 0) else: # move to last token from previous line asm_prev = self.OPCODES[cursorY - 1] idx = asm_prev.getEndCursor() self.cursor.moveAbsolute(idx, cursorY - 1) else: x, dy = asm.getNextCursor(cursorX, direction=Directions.Left) self.cursor.move(-(cursorX-x), dy) if direction == Directions.Right: asm = self.OPCODES[cursorY] x, dy = asm.getNextCursor(cursorX, direction=Directions.Right) if cursorY == self.ROWS-1 and dy > 0: self.scroll(0, -1) self.cursor.moveAbsolute(0, cursorY) else: if cursorY + dy >= len(self.OPCODES): dy = 0 self.cursor.move(x-cursorX, dy) if direction == Directions.Down: if cursorY == self.ROWS-1: # move cursor to first token self.scroll(0, -1) self.cursor.moveAbsolute(0, cursorY) else: # move next line, to nearest token on columns if cursorY + 1 < len(self.OPCODES): asm = self.OPCODES[cursorY + 1] x = asm.getNearestCursor(cursorX) self.cursor.moveAbsolute(x, cursorY + 1) if direction == Directions.Up: if cursorY == 0: # move cursor to first token self.scroll(0, 1) self.cursor.moveAbsolute(0, cursorY) else: # move next line, to nearest token on columns asm = self.OPCODES[cursorY - 1] x = asm.getNearestCursor(cursorX) self.cursor.moveAbsolute(x, cursorY - 1) if direction == Directions.End: pass if direction == Directions.Home: self.cursor.moveAbsolute(0, 0) if direction == Directions.CtrlHome: self.goTo(0) if direction == Directions.CtrlEnd: self.dataModel.slideToLastPage() self.draw(refresh=True) self.cursor.moveAbsolute(self.COLUMNS-1, self.ROWS-1) def _followBranch(self): cursorX, cursorY = self.cursor.getPosition() asm = self.OPCODES[cursorY] if asm.isBranch(): value = asm.branchAddress() if value: fofs = self.plugin.disasmVAtoFA(value) if fofs != None: rowOfs = self._getOffsetOfRow(cursorY) if rowOfs != None: self.FlowHistory.append(rowOfs + self.dataModel.getOffset()) self.goTo(fofs) def _followBranchHistory(self): if len(self.FlowHistory) > 0: offset = self.FlowHistory[-1] del self.FlowHistory[-1] self.goTo(offset) def handleKeyEvent(self, modifiers, key, event=None): if event.type() == QtCore.QEvent.KeyRelease: if key == QtCore.Qt.Key_Shift: self.stopSelection() return True if event.type() == QtCore.QEvent.KeyPress: if modifiers == QtCore.Qt.ShiftModifier: keys = [QtCore.Qt.Key_Right, QtCore.Qt.Key_Left, QtCore.Qt.Key_Down, QtCore.Qt.Key_Up, QtCore.Qt.Key_End, QtCore.Qt.Key_Home] if key in keys: self.startSelection() if modifiers == QtCore.Qt.ControlModifier: if key == QtCore.Qt.Key_Right: self.dataModel.slide(1) self.addop((self.scroll, -1, 0)) if key == QtCore.Qt.Key_Left: self.dataModel.slide(-1) self.addop((self.scroll, 1, 0)) if key == QtCore.Qt.Key_Down: self.addop((self.scroll, 0, -1)) self.addop((self.draw,)) if key == QtCore.Qt.Key_Up: self.addop((self.scroll, 0, 1)) self.addop((self.draw,)) if key == QtCore.Qt.Key_End: # not supported pass if key == QtCore.Qt.Key_Home: self.moveCursor(Directions.CtrlHome) self.addop((self.draw,)) #self.draw() return True else:#elif modifiers == QtCore.Qt.NoModifier: if key == QtCore.Qt.Key_Escape: self.selector.resetSelections() self.addop((self.draw,)) if key == QtCore.Qt.Key_Left: self.moveCursor(Directions.Left) self.addop((self.draw,)) #self.draw() if key == QtCore.Qt.Key_Right: self.moveCursor(Directions.Right) self.addop((self.draw,)) #self.draw() if key == QtCore.Qt.Key_Down: self.moveCursor(Directions.Down) self.addop((self.draw,)) #self.draw() if key == QtCore.Qt.Key_End: self.moveCursor(Directions.End) self.addop((self.draw,)) #self.draw() if key == QtCore.Qt.Key_Home: self.moveCursor(Directions.Home) self.addop((self.draw,)) #self.draw() if key == QtCore.Qt.Key_Up: self.moveCursor(Directions.Up) self.addop((self.draw,)) #self.draw() if key == QtCore.Qt.Key_PageDown: self.addop((self.scrollPages, 1)) self.addop((self.draw,)) if key == QtCore.Qt.Key_PageUp: self.addop((self.scrollPages, -1)) self.addop((self.draw,)) if key == QtCore.Qt.Key_Return: self.addop((self._followBranch,)) self.addop((self.draw,)) if key == QtCore.Qt.Key_Escape: self.addop((self._followBranchHistory,)) self.addop((self.draw,)) return True return False def addop(self, t): self.Ops.append(t) def getHeaderInfo(self): return 'Disasm listing'