Skip to content

Commit ecb2583

Browse files
author
hackcatml
committed
add watching args onLeave feature
1 parent 489ae00 commit ecb2583

3 files changed

Lines changed: 85 additions & 18 deletions

File tree

code.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ def set_watch(self, addr, is_reg_watch):
203203
def detach_all(self):
204204
self.script.exports.detachall()
205205

206-
def set_read_args_options(self, addr, index, option):
207-
self.script.exports.setreadargsoptions(addr, index, option)
206+
def set_read_args_options(self, addr, index, option, on_leave):
207+
self.script.exports.setreadargsoptions(addr, index, option, on_leave)
208208

209209
def set_read_retval_options(self, addr, option):
210210
self.script.exports.setreadretvalsoptions(addr, option)

hexviewer.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from PyQt6.QtCore import Qt, pyqtSlot
66
from PyQt6.QtGui import QTextCursor, QAction, QCursor
77
from PyQt6.QtWidgets import QTextEdit, QApplication, QWidget, QVBoxLayout, QSlider, QLabel, QHBoxLayout, QListWidget, \
8-
QPushButton, QMenu
8+
QPushButton, QMenu, QCheckBox, QWidgetAction
99

1010
import globvar
1111

@@ -336,9 +336,35 @@ def __init__(self, text):
336336

337337
# Custom TextEdit class for NewWatchWidget
338338
class CustomTextEdit(QTextEdit):
339+
def __init__(self, parent=None):
340+
super(CustomTextEdit, self).__init__(parent)
341+
self.key = None
342+
self.args_index = None
343+
self.addr = None
344+
self.checkedStates = {}
345+
346+
def closeEvent(self, e: QtGui.QCloseEvent) -> None:
347+
self.checkedStates.clear()
348+
349+
def get_args_index_and_addr_from_selected_text(self):
350+
# Get the currently selected text
351+
tc = self.textCursor()
352+
selected_text = self.textCursor().selectedText()
353+
if ":" in selected_text:
354+
self.args_index = selected_text[4:selected_text.index(":")]
355+
else:
356+
self.args_index = selected_text[4:]
357+
358+
while True:
359+
tc.movePosition(QTextCursor.MoveOperation.Up, QTextCursor.MoveMode.MoveAnchor)
360+
if re.search(r"] 0x[a-f0-9]+", tc.block().text()):
361+
self.addr = tc.block().text()[4:].strip()
362+
break
363+
339364
def contextMenuEvent(self, e: QtGui.QContextMenuEvent) -> None:
340365
menu = super(CustomTextEdit, self).createStandardContextMenu() # Get the default context menu
341366
select_all_action = None
367+
self.get_args_index_and_addr_from_selected_text()
342368

343369
for action in menu.actions(): # loop over the existing actions
344370
if action.text() == "Select All":
@@ -349,6 +375,21 @@ def contextMenuEvent(self, e: QtGui.QContextMenuEvent) -> None:
349375
args_regx = re.compile(r'(\bargs\d+\b|\breturn\b)')
350376
match = args_regx.match(self.textCursor().selectedText())
351377

378+
on_leave_check = QCheckBox("OnLeave", self)
379+
if match is not None:
380+
self.key = (self.addr, self.args_index) # Use a tuple as the key
381+
# If the key is not in the checkedStates dictionary, add it with a default state of unchecked
382+
if self.key not in self.checkedStates:
383+
self.checkedStates[self.key] = Qt.CheckState.Unchecked
384+
# Set the checked state based on the checkedStates dictionary
385+
on_leave_check.setCheckState(Qt.CheckState(self.checkedStates[self.key]))
386+
# Connect the checkbox's stateChanged signal to a function
387+
on_leave_check.stateChanged.connect(lambda state: self.on_leave_check_state_changed(state, self.key))
388+
389+
check_action = QWidgetAction(self)
390+
check_action.setDefaultWidget(on_leave_check)
391+
menu.insertAction(select_all_action, check_action)
392+
352393
read_pointer_action = QAction("readPointer", self)
353394
if match is not None:
354395
read_pointer_action.setEnabled(True)
@@ -394,6 +435,11 @@ def contextMenuEvent(self, e: QtGui.QContextMenuEvent) -> None:
394435
# Show the context menu.
395436
menu.exec(e.globalPos())
396437

438+
# handle the checkbox state change
439+
def on_leave_check_state_changed(self, state, key):
440+
# Update the checked state in the checkedStates dictionary
441+
self.checkedStates[key] = state
442+
397443
def read_pointer(self):
398444
self.read_args_with_options("readPointer")
399445

@@ -416,18 +462,12 @@ def reset(self):
416462
self.read_args_with_options("")
417463

418464
def read_args_with_options(self, option):
419-
tc = self.textCursor()
420-
args_index = self.textCursor().selectedText()[4:]
421-
while True:
422-
tc.movePosition(QTextCursor.MoveOperation.Up, QTextCursor.MoveMode.MoveAnchor)
423-
if re.search(r"] 0x[a-f0-9]+", tc.block().text()):
424-
addr = tc.block().text()[4:].strip()
425-
break
426465
try:
427466
if self.textCursor().selectedText() == "return":
428-
globvar.fridaInstrument.set_read_retval_options(addr, option)
467+
globvar.fridaInstrument.set_read_retval_options(self.addr, option)
429468
else:
430-
globvar.fridaInstrument.set_read_args_options(addr, args_index, option)
469+
globvar.fridaInstrument.set_read_args_options(self.addr, self.args_index, option,
470+
self.checkedStates[self.key])
431471
except Exception as e:
432472
print(f"{inspect.currentframe().f_code.co_name}: {e}")
433473
return
@@ -470,6 +510,7 @@ def __init__(self):
470510

471511
def closeEvent(self, e: QtGui.QCloseEvent) -> None:
472512
self.text_edit.clear()
513+
self.text_edit.closeEvent(e)
473514
self.watch_list.clear()
474515
try:
475516
globvar.fridaInstrument.detach_all()

scripts/default.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@ function readargs(args, index, addr) {
3737
// if the argument's index is in the read_args_options for the current address
3838
if (read_args_options[addr] && read_args_options[addr][index]) {
3939
// get the option from the read_args_options
40-
var option = read_args_options[addr][index];
40+
var option = read_args_options[addr][index]["readOption"];
4141
// read the argument based on the option
4242
if(option === "readByteArray") {
4343
let data = new Uint8Array(args[option](32))
4444
return Object.keys(data).map(key => data[key].toString(16).padStart(2, '0')).join(' ');
4545
}
46+
if(option === '') {
47+
return args
48+
}
4649
return args[option]();
4750
} else {
4851
// just log the argument if it's not in the read_args_options
@@ -278,13 +281,15 @@ rpc.exports = {
278281
count = 0
279282
modules.length = 0
280283
},
281-
setreadargsoptions: (addr, index, option) => {
284+
setreadargsoptions: (addr, index, option, onleave) => {
282285
// if the address is not in the read_args_options yet, add it
283286
if (!read_args_options[addr]) {
284287
read_args_options[addr] = {};
285288
}
286-
// add/update the index and option for the address
287-
read_args_options[addr][index] = option;
289+
// add/update the index and read options for the address
290+
read_args_options[addr][index] = {};
291+
read_args_options[addr][index]["readOption"] = option
292+
read_args_options[addr][index]["onLeave"] = onleave !== 0;
288293
},
289294
setreadretvalsoptions: (addr, option) => {
290295
// if the address is not in the read_args_options yet, add it
@@ -300,6 +305,11 @@ rpc.exports = {
300305
setwatch: (addr, is_reg_watch) => {
301306
Interceptor.attach(ptr(addr), {
302307
onEnter: function (args) {
308+
this.argv = []
309+
for (let index = 0; index < num_args_to_watch; index++) {
310+
this.argv[index] = args[index]
311+
}
312+
303313
if(is_reg_watch) {
304314
let count = 0
305315
let log = `[+] ${ptr(addr)}:\n`
@@ -317,10 +327,17 @@ rpc.exports = {
317327
} else {
318328
let log = `[+] ${ptr(addr)}\n`
319329
for (let index = 0; index < num_args_to_watch - 1; index++) {
330+
if(read_args_options[ptr(addr)] && read_args_options[ptr(addr)][index] && read_args_options[ptr(addr)][index]["onLeave"]) {
331+
continue;
332+
}
320333
log += `args${index}: ${readargs(args[index], index, ptr(addr))}, `
321334
}
322-
log += `args${num_args_to_watch - 1}: ${readargs(args[num_args_to_watch - 1], num_args_to_watch - 1, ptr(addr))}`
323-
send({'watchArgs':log})
335+
if(read_args_options[ptr(addr)] && read_args_options[ptr(addr)][num_args_to_watch - 1] && read_args_options[ptr(addr)][num_args_to_watch - 1]["onLeave"]) {
336+
send({'watchArgs':log})
337+
} else {
338+
log += `args${num_args_to_watch - 1}: ${readargs(args[num_args_to_watch - 1], num_args_to_watch - 1, ptr(addr))}`
339+
send({'watchArgs':log})
340+
}
324341
}
325342
},
326343
onLeave: function(retval) {
@@ -340,6 +357,15 @@ rpc.exports = {
340357
send({'watchRegs':log})
341358
} else {
342359
let log = `return: ${readretval(retval, ptr(addr))}\n`
360+
for (let index = 0; index < num_args_to_watch; index++) {
361+
if(read_args_options[ptr(addr)] && read_args_options[ptr(addr)][index] && read_args_options[ptr(addr)][index]["onLeave"]){
362+
log += `args${index}: ${readargs(this.argv[index], index, ptr(addr))}, `
363+
this.onleave = true
364+
}
365+
}
366+
if (this.onleave) {
367+
log += '\n'
368+
}
343369
log += `[-] ${ptr(addr)}`
344370
send({'watchArgs':log})
345371
}

0 commit comments

Comments
 (0)