-
Notifications
You must be signed in to change notification settings - Fork 22
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
Changes from all commits
a6db5b2
bd3f58c
a878d5d
09db5cd
ad24f3a
7eb3470
ccf5a83
34a4280
36126ae
25243e0
3122721
8d863f8
a290903
f2ec53e
25ddae6
1401ac1
e202739
1d95efb
35f2a4a
19cffe4
69b48b6
d7d17af
d7b4100
eac0bea
a7f6de7
f73894d
b02ddd8
f2244cd
94a7d75
b05bf87
4df4f20
4578c45
975b184
4ad58b4
b8958ee
d341729
042a105
8915bfe
3b4a9d8
9c71fc5
85b690e
f3e9aac
eb3320e
5e24543
60b8e11
afd5b63
144e51a
547b62f
f529d39
db2b09b
775bd41
fbc8078
072ccc9
d8edd71
6633342
e1bb374
7718f39
13b696f
e489af6
27da9ff
90ceb59
2c7bf08
1b80518
2d4521a
b8ef12f
c31224f
6cb948c
41ccc63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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': | ||
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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not create a class for this one ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A class for what? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then I'm not quite sure I understand why. Other There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() |
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] |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All
methods
andsource=user
cleanups are implemented at #96 to make reverting this easier