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

[WIP] implement match backend #22

Merged
merged 68 commits into from
Nov 8, 2016
Merged
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
a6db5b2
[WIP] implement match backend
nirizr Aug 18, 2016
bd3f58c
add celery and empty tasks file
nirizr Aug 25, 2016
a878d5d
Try my multi-table inheritence idea
nirizr Aug 25, 2016
09db5cd
Merge Task model with async tasks and create proper views
nirizr Aug 25, 2016
ad24f3a
Create task when function data upload is done
nirizr Aug 26, 2016
7eb3470
Create base MatchAction class both MatchAll and MatchFunction inherit
nirizr Aug 26, 2016
ccf5a83
call get_functions() in MatchAllAction.get_functions_count
nirizr Aug 27, 2016
34a4280
initialize MatchAction params in __init__
nirizr Aug 27, 2016
36126ae
add match dialog
nirizr Aug 28, 2016
25243e0
fix minor codacy issues
nirizr Aug 29, 2016
3122721
fix flake8 issues
nirizr Aug 29, 2016
8d863f8
Implement simple match sources
nirizr Aug 29, 2016
a290903
Fix single source function match
nirizr Aug 29, 2016
f2ec53e
Add file_id to vectors and an empty loop that pulls the vectors
nirizr Aug 31, 2016
25ddae6
Add progress fields to task
nirizr Aug 31, 2016
1401ac1
fix spaces around '+' in tasks.py
nirizr Aug 31, 2016
e202739
Fix vector file field population
nirizr Aug 31, 2016
1d95efb
Add match task exection's progress bar
nirizr Sep 1, 2016
35f2a4a
Fix target_vectors filter
nirizr Sep 2, 2016
19cffe4
Add task execution progress report and progress bar
nirizr Sep 2, 2016
69b48b6
use BaseDialog's bottom layout
nirizr Sep 2, 2016
d7d17af
explicitly catch all exceptions and raise on MatchAction.perform_task
nirizr Sep 2, 2016
d7b4100
Add task to admin panel
nirizr Sep 2, 2016
eac0bea
fix two base_layout issues
nirizr Sep 5, 2016
a7f6de7
accept null project when creating task
nirizr Sep 5, 2016
f73894d
Accept progress bar when task is complete
nirizr Sep 5, 2016
b02ddd8
Bulk delayed instance requests to speed up the collection phase
nirizr Sep 6, 2016
f2244cd
simplify get_functions_count
nirizr Sep 7, 2016
94a7d75
Create serverside vector classes to reduce boilerplate
nirizr Sep 7, 2016
b05bf87
safer timer use in match action
nirizr Sep 7, 2016
4df4f20
fix codacy minor issues
nirizr Sep 7, 2016
4578c45
add type and score to Match Model
nirizr Sep 13, 2016
975b184
implement match for identity functions
nirizr Sep 15, 2016
4ad58b4
make Vector class inherit and remove VectorHash from list of avaiable…
nirizr Sep 15, 2016
b8958ee
Add space
nirizr Sep 16, 2016
d341729
HistogramVector skeleton
nirizr Sep 16, 2016
042a105
create Match objects when successfully matching
nirizr Sep 16, 2016
8915bfe
Split id to match_type and vector_type, to allow several matches usin…
nirizr Sep 17, 2016
3b4a9d8
Begining of histogram matching
nirizr Sep 18, 2016
9c71fc5
Add scipy and sklearn to requirements
nirizr Sep 20, 2016
85b690e
Mark task as failed on exception (and reraise it)
nirizr Sep 20, 2016
f3e9aac
move Match object creation to tasks.py to avoid cyclic import
nirizr Sep 21, 2016
eb3320e
Add miniconda install to travis when testing server
nirizr Sep 21, 2016
5e24543
finish HistogramVector.match
nirizr Sep 22, 2016
60b8e11
Remove python 3.2 from travis, fix codacy issues
nirizr Sep 22, 2016
afd5b63
Better seperation between server matching algos and database vectors
nirizr Sep 23, 2016
144e51a
Fix new action/dialog related issues introduced in 6913865
nirizr Sep 23, 2016
547b62f
Ditch my through replacement attempt, make matching flow work
nirizr Sep 25, 2016
f529d39
make task progress bar visible without requiring actual progress
nirizr Sep 25, 2016
db2b09b
more progress on making histogram matches work
nirizr Sep 28, 2016
775bd41
Time each match type
nirizr Oct 5, 2016
fbc8078
set Task model progress_max nullable and handle optional None value i…
nirizr Oct 5, 2016
072ccc9
more fixes to histogram match backend
nirizr Oct 5, 2016
d8edd71
Another attempt at speeding up the matches process
nirizr Oct 16, 2016
6633342
more robust status handling in task progress query
nirizr Oct 17, 2016
e1bb374
send more parameters in a match task creation
nirizr Oct 19, 2016
7718f39
More adjustments to the source/target/methods parameters
nirizr Oct 21, 2016
13b696f
Make item selection combobox a dialog.Base method
nirizr Oct 22, 2016
e489af6
Add optional choice-specific additional input to the radio group crea…
nirizr Oct 22, 2016
27da9ff
Send selected target_project and target_file to server
nirizr Oct 22, 2016
90ceb59
Handle target_project and target_file match parameters on backend
nirizr Oct 22, 2016
2c7bf08
Allow excuding values in create_item_select and exclude current file …
nirizr Oct 26, 2016
1b80518
import qt from idasix
nirizr Oct 26, 2016
2d4521a
Add support for range and single function sources
nirizr Oct 27, 2016
b8ef12f
self review fixes part one
nirizr Oct 28, 2016
c31224f
Prevent editing target and source info in existing task
nirizr Oct 28, 2016
6cb948c
Replace create_item_select and create_radio_select with QItemSelect a…
nirizr Oct 30, 2016
41ccc63
Split matches.py to entire directory of match type definitions
nirizr Nov 2, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[flake8]
ignore = E111, E113, E114, E121, E125
ignore = E111, E113, E114, E121, E125, E127
16 changes: 13 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: python
python:
- "2.7"
- "3.2"
- "3.4"
- "3.5"
env:
@@ -10,8 +9,6 @@ env:
matrix:
# idaplugin is run with python2.7, we'll check against python3 to assert compatibility
exclude:
- python: "3.2"
env: PROJECT=idaplugin
- python: "3.4"
env: PROJECT=idaplugin

@@ -28,6 +25,19 @@ before_install:
- git submodule update --init --recursive

install:
- if [ ${PROJECT} = "server" ]; then
set +vx;
if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh;
else
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
fi;
bash ./miniconda.sh -b -p ${HOME}/miniconda;
export PATH=${HOME}/miniconda/bin:$PATH;
conda info -a;
conda update --yes conda;
conda install --yes python=${TRAVIS_PYTHON_VERSION} atlas numpy scipy scikit-learn;
fi;
- if [ -f ${PROJECT}/requirements.txt ]; then pip install -r ${PROJECT}/requirements.txt ; fi
- pip install flake8

195 changes: 155 additions & 40 deletions idaplugin/rematch/actions/match.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,178 @@
import idaapi
import idc
from idautils import Functions

from ..idasix import QtCore, QtWidgets
import idautils

from ..dialogs.match import MatchDialog

from .. import instances
from .. import network, netnode
from . import base


class MatchAllAction(base.BoundFileAction):
name = "&Match all"
group = "Match"
class MatchAction(base.BoundFileAction):
name = "&Match"
dialog = MatchDialog

def __init__(self, *args, **kwargs):
super(MatchAction, self).__init__(*args, **kwargs)
self.function_gen = None
self.pbar = None
self.timer = None
self.task_id = None
self.instance_set = []

self.source = None
self.source_single = None
self.source_range = None
self.target = None
self.target_project = None
self.target_file = None
self.methods = None

def get_functions(self):
if self.source == 'idb':
return set(idautils.Functions())
elif self.source == 'user':
Copy link
Owner Author

Choose a reason for hiding this comment

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

Should cleanup source='user' residue code if we don't support it for now

Copy link
Owner Author

@nirizr nirizr Oct 30, 2016

Choose a reason for hiding this comment

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

All methods and source=user cleanups are implemented at #96 to make reverting this easier

raise NotImplementedError("All user functions are not currently "
"supported as source value.")
elif self.source == 'single':
return [self.source_single]
elif self.source == 'range':
return set(idautils.Functions(self.source_range[0],
self.source_range[1]))

raise ValueError("Invalid source value received from MatchDialog: {}"
"".format(self.source))

def submit_handler(self, source, source_single, source_range, target,
target_project, target_file, methods):
self.source = source
self.source_single = source_single
self.source_range = source_range
self.target = target
self.target_project = target_project if target == 'project' else None
self.target_file = target_file if target == 'file' else None
self.methods = methods

def activate(self, ctx):
self.file_id = netnode.bound_file_id
functions = self.get_functions()
if not functions:
return False

self.function_gen = enumerate(functions)
self.pbar = QtWidgets.QProgressDialog()
self.pbar.setLabelText("Processing IDB... You may continue working,\nbut "
"please avoid making any ground-breaking changes.")
self.pbar.setRange(0, len(functions))
self.pbar.setValue(0)
self.pbar.canceled.connect(self.cancel_upload)
self.pbar.rejected.connect(self.reject_upload)
self.pbar.accepted.connect(self.accept_upload)

self.function_gen = enumerate(Functions())
pd = QtWidgets.QProgressDialog(labelText="Processing functions...",
minimum=0, maximum=len(list(Functions())))
self.progress = pd
self.progress.canceled.connect(self.cancel)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.perform)
self.timer.start()
self.timer.timeout.connect(self.perform_upload)
self.timer.start(0)

return True

def perform(self):
def perform_upload(self):
try:
i, offset = self.function_gen.next()
except StopIteration:
self.timer.stop()
return

func = instances.FunctionInstance(self.file_id, offset)
network.query("POST", "collab/instances/", params=func.serialize(),
json=True)
try:
func = instances.FunctionInstance(netnode.bound_file_id, offset)
self.instance_set.append(func.serialize())

i = i + 1
self.progress.setValue(i)
if (i >= self.progress.maximum()):
self.timer.stop()
except:
self.timer.stop()
if len(self.instance_set) >= 100:
network.delayed_query("POST", "collab/instances/",
params=self.instance_set, json=True,
callback=self.progress_advance)
self.instance_set = []
self.pbar.setMaximum(self.pbar.maximum() + 1)
self.progress_advance()
except Exception:
self.cancel_upload()
raise

def cancel(self):
def progress_advance(self, result=None):
del result
new_value = self.pbar.value() + 1
self.pbar.setValue(new_value)
if new_value >= self.pbar.maximum():
self.pbar.accept()

def cancel_upload(self):
self.timer.stop()
self.timer = None
self.pbar = None

def reject_upload(self):
self.cancel_upload()

class MatchFunctionAction(base.BoundFileAction):
name = "Match &Function"
group = "Match"
def accept_upload(self):
self.cancel_upload()

@staticmethod
def activate(ctx):
file_id = netnode.bound_file_id
if self.source == 'idb':
self.source_range = [None, None]
elif self.source == 'single':
self.source_range = [self.source_single, self.source_single]
elif self.source == 'range':
pass
else:
raise NotImplementedError("Unsupported source type encountered in task "
"creation")

function = idaapi.choose_func("Choose function to match with database",
idc.ScreenEA())
if function is None:
return
params = {'source_file': netnode.bound_file_id,
'source_start': self.source_range[0],
'source_end': self.source_range[1],
'target_project': self.target_project,
'target_file': self.target_file,
'source': self.source, 'methods': self.methods}
r = network.query("POST", "collab/tasks/", params=params, json=True)
self.task_id = r['id']

self.pbar = QtWidgets.QProgressDialog()
self.pbar.setLabelText("Waiting for remote matching... You may continue "
"working without any limitations.")
self.pbar.setRange(0, int(r['progress_max']) if r['progress_max'] else 0)
self.pbar.setValue(int(r['progress']))
self.pbar.canceled.connect(self.cancel_task)
self.pbar.rejected.connect(self.reject_task)
self.pbar.accepted.connect(self.accept_task)
self.pbar.show()
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not create a class for this one ?
and moving it to dialogs

Copy link
Owner Author

Choose a reason for hiding this comment

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

A class for what? pbar? there's nothing more than a QProgressDialog there.
I'm not quite sure what do you suggest exactly?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yup.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Then I'm not quite sure I understand why. Other Q* widgets had extra functionality built into them, what's the extra functionality in this progress bar dialog?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's more of a cosmetic thing, rather than having all this code written there, create a class, move it to the proper place, connect it later


self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.perform_task)
self.timer.start(1000)

def perform_task(self):
try:
r = network.query("GET", "collab/tasks/{}/".format(self.task_id),
json=True)

progress_max = int(r['progress_max']) if r['progress_max'] else None
progress = int(r['progress'])
status = r['status']
if status == 'failed':
self.pbar.reject()
elif progress_max:
self.pbar.setMaximum(progress_max)
if progress >= progress_max:
self.pbar.accept()
else:
self.pbar.setValue(progress)
except Exception:
self.cancel_task()
raise

def cancel_task(self):
self.timer.stop()
self.timer = None
self.pbar = None

def reject_task(self):
self.cancel_task()

data = instances.FunctionInstance(file_id, function.startEA)
network.query("POST", "collab/instances/", params=data.serialize(),
json=True)
def accept_task(self):
self.cancel_task()
3 changes: 2 additions & 1 deletion idaplugin/rematch/dialogs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import base
import login
import match
import project
import settings

__all__ = [base, login, project, settings]
__all__ = [base, login, match, project, settings]
Loading