Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filtered views #237

Closed
wants to merge 12 commits into from
Closed
16 changes: 16 additions & 0 deletions vivisect/qt/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ def _hotkey_nav_prevundef(self):
@vq_hotkey.hotkey('viv:make:code')
def _hotkey_make_code(self):
if self._canv_curva != None:
loctup = self.vw.getLocation(self._canv_curva)
if loctup is not None:
return

self.vw.makeCode(self._canv_curva)

@vq_hotkey.hotkey('viv:make:function')
Expand All @@ -181,16 +185,28 @@ def _hotkey_make_function(self):
@vq_hotkey.hotkey('viv:make:string')
def _hotkey_make_string(self):
if self._canv_curva != None:
loctup = self.vw.getLocation(self._canv_curva)
if loctup is not None:
return

self.vw.makeString(self._canv_curva)

@vq_hotkey.hotkey('viv:make:pointer')
def _hotkey_make_pointer(self):
if self._canv_curva != None:
loctup = self.vw.getLocation(self._canv_curva)
if loctup is not None:
return

self.vw.makePointer(self._canv_curva)

@vq_hotkey.hotkey('viv:make:unicode')
def _hotkey_make_unicode(self):
if self._canv_curva != None:
loctup = self.vw.getLocation(self._canv_curva)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these are good catches.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow, these should be applied before we get back to this PR. i'll probably put these in a smaller PR

if loctup is not None:
return

self.vw.makeUnicode(self._canv_curva)

@vq_hotkey.hotkey('viv:undefine')
Expand Down
194 changes: 175 additions & 19 deletions vivisect/qt/views.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@

import vqt.tree as vq_tree
import vqt.basics as vq_basics
import vivisect.base as viv_base
import envi.qt.memory as e_q_memory
import visgraph.pathcore as vg_path
import envi.qt.memcanvas as e_q_memcanvas
import vivisect.qt.ctxmenu as v_q_ctxmenu

try:
from PyQt5 import QtGui
from PyQt5.QtWidgets import QMenu
from PyQt5.QtCore import QSortFilterProxyModel
except:
from PyQt4 import QtGui
from PyQt4.QtGui import QMenu
from PyQt4.QtCore import QSortFilterProxyModel
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least for PyQt4 QSortFilterProxyModel should be imported from PyQt4.QtGui. Not sure about PyQt5.


from vqt.main import *
from vqt.common import *
from vivisect.const import *

class VivFilterModel(QSortFilterProxyModel):
def __init__(self, parent=None):
QSortFilterProxyModel.__init__(self, parent=parent)
self.setDynamicSortFilter(True)
self.setFilterKeyColumn(-1)

def __getattr__(self, name):
return getattr(self.sourceModel(), name)

class VivNavModel(e_q_memory.EnviNavModel):
pass

Expand Down Expand Up @@ -86,8 +100,8 @@ def __init__(self, vw=None, vwqgui=None, **kwargs):

def doubleClickedSignal(self, idx):
if idx.isValid() and self._viv_navcol != None:
pnode = idx.internalPointer()
expr = pnode.rowdata[self._viv_navcol]
# we need to access the selected navcol, but the current index will be the cell double-clicked
expr = idx.sibling(idx.row(), self._viv_navcol).data()
vqtevent('envi:nav:expr', ('viv',expr,None))
return True

Expand Down Expand Up @@ -130,24 +144,56 @@ def vivSetData(self, va, col, val):
if not pnode:
return

idx = self.model().createIndex(pnode.row(), col, pnode)
idx = self.model().sourceModel().createIndex(pnode.row(), col, pnode)
# We are *not* the edit role...
self.model().setData(idx, val, role=None)
self.model().sourceModel().setData(idx, val, role=QtCore.Qt.DisplayRole)

def vivGetData(self, va, col):
pnode = self._viv_va_nodes.get(va)
if not pnode:
return None
return pnode.rowdata[col]

class VivFilterView(QWidget):
'''
This is the primary window for the VQViv*Views if they want to include filtering
'''
window_title = '__undefined__'
view_type = None

def __init__(self, vw, vwqgui, *args, **kwargs):
QWidget.__init__(self)

self.view = self.view_type(vw, vwqgui, *args, **kwargs)
self.ffilt = VQFilterWidget(self)

layout = vq_basics.VBox(self.view, self.ffilt)
self.setLayout(layout)

self.ffilt.filterChanged.connect(self.textFilterChanged)
self.setWindowTitle(self.view.window_title)

def textFilterChanged(self):
regExp = QtCore.QRegExp(self.ffilt.text(),
self.ffilt.caseSensitivity(),
self.ffilt.patternSyntax())

self.view.filterModel.setFilterRegExp(regExp)

def __getattr__(self, name):
return getattr(self.view, name)

class VQVivLocView(VQVivTreeView):

loctypes = ()

def __init__(self, vw, vwqgui):
VQVivTreeView.__init__(self, vw, vwqgui)
model = VivNavModel(self._viv_navcol, parent=self, columns=self.columns)
self.setModel(model)
# whether we use it or not, include a sort/filter proxy model
self.navModel = VivNavModel(self._viv_navcol, parent=self, columns=self.columns)
self.filterModel = VivFilterModel()
self.filterModel.setSourceModel(self.navModel)
self.setModel(self.filterModel)
self.vqLoad()
self.vqSizeColumns()

Expand All @@ -169,7 +215,7 @@ def VWE_ADDLOCATION(self, vw, event, einfo):
def vivAddLocation(self, lva, lsize, ltype, linfo):
print "FIXME OVERRIDE"

class VQVivStringsView(VQVivLocView):
class VQVivStringsViewPart(VQVivLocView):

columns = ('Address','String')
loctypes = (LOC_STRING, LOC_UNI)
Expand All @@ -181,7 +227,7 @@ def vivAddLocation(self, lva, lsize, ltype, linfo):
s = s.decode('utf-16le', 'ignore')
self.vivAddRow(lva, '0x%.8x' % lva, repr(s))

class VQVivImportsView(VQVivLocView):
class VQVivImportsViewPart(VQVivLocView):

columns = ('Address', 'Library', 'Function')
loctypes = (LOC_IMPORT,)
Expand All @@ -191,7 +237,7 @@ def vivAddLocation(self, lva, lsize, ltype, linfo):
libname, funcname = linfo.split('.', 1)
self.vivAddRow(lva, '0x%.8x' % lva, libname, funcname)

class VQVivStructsView(VQVivLocView):
class VQVivStructsViewPart(VQVivLocView):
columns = ('Address', 'Structure', 'Loc Name')
loctypes = (LOC_STRUCT,)
window_title = 'Structures'
Expand All @@ -200,14 +246,17 @@ def vivAddLocation(self, lva, lsize, ltype, linfo):
sym = self.vw.getSymByAddr(lva)
self.vivAddRow(lva, '0x%.8x' % lva, linfo, str(sym))

class VQVivExportsView(VQVivTreeView):
class VQVivExportsViewPart(VQVivTreeView):

window_title = 'Exports'
columns = ('Address', 'File', 'Export')

def __init__(self, vw, vwqgui):
VQVivTreeView.__init__(self, vw, vwqgui)
self.setModel( VivNavModel(self._viv_navcol, self, columns=self.columns) )
self.navModel = VivNavModel(self._viv_navcol, self, columns=self.columns)
self.filterModel = VivFilterModel()
self.filterModel.setSourceModel(self.navModel)
self.setModel(self.filterModel)
self.vqLoad()
self.vqSizeColumns()

Expand Down Expand Up @@ -238,15 +287,89 @@ def vqLoad(self):
for va, size, sname, fname in self.vw.getSegments():
self.vivAddRow(va, fname, sname, '0x%.8x' % va, str(size))

class VQVivFunctionsView(VQVivTreeView):

class VQFilterWidget(QLineEdit):
filterChanged = QtCore.pyqtSignal()

def __init__(self, parent=None):
QLineEdit.__init__(self, parent=parent)

self.setClearButtonEnabled(True)
self.m_patternGroup = QActionGroup(self)
self.textChanged.connect(self.filterChanged)

self.menu = QMenu(self)
self.m_caseSensitivityAction = self.menu.addAction("Case Sensitive")
self.m_caseSensitivityAction.setCheckable(True)
self.m_caseSensitivityAction.toggled.connect(self.filterChanged)


self.menu.addSeparator();
self.m_patternGroup.setExclusive(True);

self.patternAction = self.menu.addAction("Fixed String");
self.patternAction.setData(QtCore.QVariant(int(QtCore.QRegExp.FixedString)));
self.patternAction.setCheckable(True);
self.patternAction.setChecked(True);
self.m_patternGroup.addAction(self.patternAction);

self.patternAction = self.menu.addAction("Regular Expression");
self.patternAction.setCheckable(True);
self.patternAction.setData(QtCore.QVariant(int(QtCore.QRegExp.RegExp2)));
self.m_patternGroup.addAction(self.patternAction);

self.patternAction = self.menu.addAction("Wildcard");
self.patternAction.setCheckable(True);
self.patternAction.setData(QtCore.QVariant(int(QtCore.QRegExp.Wildcard)));
self.m_patternGroup.addAction(self.patternAction);

self.m_patternGroup.triggered.connect(self.filterChanged);

self.icon = QtGui.QIcon(QtGui.QPixmap(":/images/find.png"))
self.optionsButton = QToolButton()
self.optionsButton.setCursor(QtCore.Qt.ArrowCursor)
self.optionsButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.optionsButton.setStyleSheet("* { border: none; }")
self.optionsButton.setIcon(self.icon)
self.optionsButton.setMenu(self.menu)
self.optionsButton.setPopupMode(QToolButton.InstantPopup)

self.optionsAction = QWidgetAction(self)
self.optionsAction.setDefaultWidget(self.optionsButton)
self.addAction(self.optionsAction, QLineEdit.LeadingPosition)

def caseSensitivity(self):
return (QtCore.Qt.CaseInsensitive, QtCore.Qt.CaseSensitive)[self.m_caseSensitivityAction.isChecked()]

def setCaseSensitivity(self, cs):
self.m_caseSensitivityAction.setChecked(cs == QtCore.Qt.CaseSensitive)

def patternSyntax(self):
return self.patternSyntaxFromAction(self.m_patternGroup.checkedAction())

def setPatternSyntax(self, s):
for a in self.m_patternGroup.actions():
if (self.patternSyntaxFromAction(a) == s):
a.setChecked(True)
break

def patternSyntaxFromAction(self, a):
return int(a.data())

class VQVivFunctionsViewPart(VQVivTreeView):

_viv_navcol = 0
window_title = 'Functions'
columns = ('Name','Address', 'Size', 'Ref Count')

def __init__(self, vw, vwqgui):
VQVivTreeView.__init__(self, vw, vwqgui)
self.setModel( VivNavModel(self._viv_navcol, self, columns=self.columns) )
VQVivTreeView.__init__(self, vw, vwqgui, withfilter=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to open the function listing

Traceback (most recent call last):
  File "/home/james/work/vivisect/vqt/menubuilder.py", line 87, in __call__
    return self.callback(*self.args, **self.kwargs)
  File "/home/james/work/vivisect/vivisect/qt/main.py", line 396, in _menuViewFunctions
    self.vqBuildDockWidget('VQVivFunctionsView', area=QtCore.Qt.RightDockWidgetArea)
  File "/home/james/work/vivisect/vqt/application.py", line 130, in vqBuildDockWidget
    obj = cls(*args)
  File "/home/james/work/vivisect/vivisect/qt/views.py", line 166, in __init__
    self.view = self.view_type(vw, vwqgui, *args, **kwargs)
  File "/home/james/work/vivisect/vivisect/qt/views.py", line 365, in __init__
    VQVivTreeView.__init__(self, vw, vwqgui, withfilter=True)
  File "/home/james/work/vivisect/vivisect/qt/views.py", line 85, in __init__
    vq_tree.VQTreeView.__init__(self, parent=vwqgui, **kwargs)
  File "/home/james/work/vivisect/vqt/tree.py", line 218, in __init__
    QTreeView.__init__(self, parent=parent, **kwargs)
TypeError: 'withfilter' is an unknown keyword argument

Might need to pop the withfilter kwarg in VQVivTreeView.


self.navModel = VivNavModel(self._viv_navcol, self, columns=self.columns)
self.filterModel = VivFilterModel()
self.filterModel.setSourceModel(self.navModel)
self.setModel(self.filterModel)

self.vqLoad()
self.vqSizeColumns()

Expand Down Expand Up @@ -347,12 +470,14 @@ def reprComplex(vw, item):
VASET_COMPLEX: reprComplex,
}

class VQVivVaSetView(VQVivTreeView):
class VQVivVaSetViewPart(VQVivTreeView):

window_title = 'Va Set'
_viv_navcol = 0

def __init__(self, vw, vwqgui, setname):
self._va_setname = setname
self.window_title = 'Va Set: %s' % setname

setdef = vw.getVaSetDef( setname )
cols = [ cname for (cname,ctype) in setdef ]
Expand All @@ -362,7 +487,6 @@ def __init__(self, vw, vwqgui, setname):
self.setModel( VivNavModel(self._viv_navcol, self, columns=cols) )
self.vqLoad()
self.vqSizeColumns()
self.setWindowTitle('Va Set: %s' % setname)

def VWE_SETVASETROW(self, vw, event, einfo):
setname, row = einfo
Expand Down Expand Up @@ -395,7 +519,7 @@ def reprRow(self, row):

return row

class VQXrefView(VQVivTreeView):
class VQXrefViewPart(VQVivTreeView):

_viv_navcol = 0

Expand All @@ -416,15 +540,18 @@ def __init__(self, vw, vwqgui, xrefs=(), title='Xrefs'):

self.vqSizeColumns()

class VQVivNamesView(VQVivTreeView):
class VQVivNamesViewPart(VQVivTreeView):

_viv_navcol = 0
window_title = 'Workspace Names'
columns = ('Address', 'Name')

def __init__(self, vw, vwqgui):
VQVivTreeView.__init__(self, vw, vwqgui)
self.setModel( VivNavModel(self._viv_navcol, self, columns=self.columns) )
self.navModel = VivNavModel(self._viv_navcol, self, columns=self.columns)
self.filterModel = VivFilterModel()
self.filterModel.setSourceModel(self.navModel)
self.setModel(self.filterModel)
self.vqLoad()
self.vqSizeColumns()

Expand All @@ -444,3 +571,32 @@ def vivAddName(self, nifo):
else:
self.vivSetData(va, 1, name)


#FIXME: is this a good time to use a @decorator?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could introduce a wrapper that sets an instance attribute on every instance of these classes (not sure how you'd do it at the class attribute level), but you'd have to wrap the every instance of the class you want, so it adds a layer of indirection, and the actual wrapping introduces more complexity.

I'd say pass for now and remove the FIXME. If we every have to add more of these, it might be worth adding, but right now it's more complexity than what is necessary, and adds a fair bit of overhead.

# Filtering VQTreeViews
class VQVivFunctionsView(VivFilterView):
view_type = VQVivFunctionsViewPart

class VQVivNamesView(VivFilterView):
view_type = VQVivNamesViewPart

class VQVivExportsView(VivFilterView):
view_type = VQVivExportsViewPart

class VQVivVaSetView(VivFilterView):
view_type = VQVivVaSetViewPart

class VQXrefView(VivFilterView):
view_type = VQXrefViewPart


# Filtering VQVivLocViews
class VQVivStringsView(VivFilterView):
view_type = VQVivStringsViewPart

class VQVivImportsView(VivFilterView):
view_type = VQVivImportsViewPart

class VQVivStructsView(VivFilterView):
view_type = VQVivStructsViewPart

2 changes: 1 addition & 1 deletion vqt/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def __init__(self, parent=None, cols=None, **kwargs):
self.setSortingEnabled(True)
self.setAlternatingRowColors(True)

if cols != None:
if cols != None: # FIXME: doesn't account for filtering
model = VQTreeModel(parent=self, columns=cols)
self.setModel( model )

Expand Down