Skip to content

Commit fd87973

Browse files
author
hackcatml
committed
add list pid checkbox, so it can attach to the process. update requirements.txt
1 parent 4a6d32e commit fd87973

File tree

8 files changed

+134
-109
lines changed

8 files changed

+134
-109
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ __pycache__
55
.idea
66
test
77
ui_test.py
8+
ui_test.ui
89
ui_win-test.py
910
ui_win-test.ui

code.py

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
class Instrument(QObject):
1717
messagesig = QtCore.pyqtSignal(str)
1818

19-
def __init__(self, script_text, isremote, remoteaddr, spawntargetid):
19+
def __init__(self, script_text, isremote, remoteaddr, target, isspawn):
2020
super().__init__()
2121
self.name = None
2222
self.sessions = []
2323
self.script = None
2424
self.script_text = script_text
2525
self.device = None
26+
self.isspawn = isspawn
27+
self.attachtarget = None
2628
self.spawntarget = None
2729
self.remoteaddr = ''
2830
if isremote is True and remoteaddr != '':
@@ -33,8 +35,12 @@ def __init__(self, script_text, isremote, remoteaddr, spawntargetid):
3335
else:
3436
self.device = frida.get_usb_device(1)
3537

36-
if spawntargetid is not None:
37-
self.spawntarget = spawntargetid
38+
# spawn mode
39+
if target is not None and isspawn:
40+
self.spawntarget = target
41+
# list pid mode
42+
else:
43+
self.attachtarget = target
3844

3945
def __del__(self):
4046
for session in self.sessions:
@@ -44,14 +50,14 @@ def __del__(self):
4450
def on_message(self, message, data):
4551
# print(message)
4652
global MESSAGE
47-
if 'payload' in message:
48-
if message['payload'] is not None and 'scancompletedratio' in message['payload']:
53+
if 'payload' in message and message['payload'] is not None:
54+
if 'scancompletedratio' in message['payload']:
4955
globvar.scanProgressRatio = floor(message['payload']['scancompletedratio'])
5056
# print(globvar.scanProgressRatio)
51-
if message['payload'] is not None and 'watchArgs' in message['payload']:
57+
if 'watchArgs' in message['payload']:
5258
self.messagesig.emit(message['payload']['watchArgs'])
5359
return
54-
if message['payload'] is not None and 'watchRegs' in message['payload']:
60+
if 'watchRegs' in message['payload']:
5561
self.messagesig.emit(message['payload']['watchRegs'])
5662
return
5763
MESSAGE = message['payload']
@@ -67,20 +73,24 @@ def read_frida_js_source(self):
6773
return f.read()
6874

6975
def instrument(self):
70-
if self.spawntarget is None and self.device.get_frontmost_application() is None:
71-
msg = "Launch the target app first"
72-
return msg
76+
if not any([self.spawntarget, self.attachtarget, self.device.get_frontmost_application()]):
77+
return "Launch the target app first"
7378

74-
if self.spawntarget is not None:
75-
pid = self.device.spawn([self.spawntarget])
79+
if self.attachtarget: # list pid mode
80+
session = self.device.attach(self.attachtarget)
81+
self.name = self.attachtarget
7682
else:
77-
pid = self.device.get_frontmost_application().pid
78-
session = self.device.attach(pid)
79-
self.sessions.append(session)
80-
# print("[hackcatml] fridaInstrument sessions: ", self.sessions)
81-
if self.spawntarget is not None:
83+
if self.spawntarget: # spawn mode
84+
pid = self.device.spawn([self.spawntarget])
85+
else: # attach frontmost application
86+
pid = self.device.get_frontmost_application().pid
87+
88+
session = self.device.attach(pid)
8289
self.device.resume(pid)
83-
self.name = self.device.get_frontmost_application().name
90+
if self.device.get_frontmost_application():
91+
self.name = self.device.get_frontmost_application().name
92+
93+
self.sessions.append(session)
8494
self.script = session.create_script(self.read_frida_js_source())
8595
self.script.on('message', self.on_message)
8696
self.script.load()

hexviewer.py

Lines changed: 21 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ def setScrollBarPos(self, value):
2626

2727
# wheelevent https://spec.tistory.com/449
2828
def wheelEvent(self, e: QtGui.QWheelEvent) -> None:
29+
delta = e.angleDelta().y()
2930
# wheel down
30-
if e.angleDelta().y() < 0:
31-
globvar.currentFrameBlockNumber += -1 * e.angleDelta().y() / 120 * 4
31+
if delta < 0:
32+
globvar.currentFrameBlockNumber += -1 * delta / 120 * 4
3233
# wheel up
33-
elif e.angleDelta().y() > 0 and globvar.currentFrameBlockNumber > 0:
34-
globvar.currentFrameBlockNumber -= e.angleDelta().y() / 120 * 4
34+
elif delta > 0 and globvar.currentFrameBlockNumber > 0:
35+
globvar.currentFrameBlockNumber -= delta / 120 * 4
3536

3637
tc = self.textCursor()
3738
tc.movePosition(QTextCursor.MoveOperation.Start, QTextCursor.MoveMode.MoveAnchor, 1)
@@ -40,7 +41,7 @@ def wheelEvent(self, e: QtGui.QWheelEvent) -> None:
4041

4142
if tc.blockNumber() == 0 and re.search(r"\d+\. 0x[0-9a-f]+, module:", tc.block().text()) is None:
4243
self.hitcount += 1
43-
if self.hitcount > 0:
44+
if self.hitcount > 0 and delta > 0:
4445
self.wheelupsig.emit(globvar.currentFrameStartAddress)
4546
self.hitcount = 0
4647

@@ -390,47 +391,21 @@ def contextMenuEvent(self, e: QtGui.QContextMenuEvent) -> None:
390391
check_action.setDefaultWidget(on_leave_check)
391392
menu.insertAction(select_all_action, check_action)
392393

393-
read_pointer_action = QAction("readPointer", self)
394-
if match is not None:
395-
read_pointer_action.setEnabled(True)
396-
read_pointer_action.triggered.connect(self.read_pointer)
397-
menu.insertAction(select_all_action, read_pointer_action)
398-
399-
read_utf8_action = QAction("readUtf8String", self)
400-
if match is not None:
401-
read_utf8_action.setEnabled(True)
402-
read_utf8_action.triggered.connect(self.read_utf8_string)
403-
menu.insertAction(select_all_action, read_utf8_action)
404-
405-
read_utf16_action = QAction("readUtf16String", self)
406-
if match is not None:
407-
read_utf16_action.setEnabled(True)
408-
read_utf16_action.triggered.connect(self.read_utf16_string)
409-
menu.insertAction(select_all_action, read_utf16_action)
410-
411-
read_float_action = QAction("readFloat", self)
412-
if match is not None:
413-
read_float_action.setEnabled(True)
414-
read_float_action.triggered.connect(self.read_float)
415-
menu.insertAction(select_all_action, read_float_action)
416-
417-
read_double_action = QAction("readDouble", self)
418-
if match is not None:
419-
read_double_action.setEnabled(True)
420-
read_double_action.triggered.connect(self.read_double)
421-
menu.insertAction(select_all_action, read_double_action)
422-
423-
read_bytearray_action = QAction("readByteArray", self)
424-
if match is not None:
425-
read_bytearray_action.setEnabled(True)
426-
read_bytearray_action.triggered.connect(self.read_bytearray)
427-
menu.insertAction(select_all_action, read_bytearray_action)
428-
429-
read_reset_action = QAction("reset", self)
430-
if match is not None:
431-
read_reset_action.setEnabled(True)
432-
read_reset_action.triggered.connect(self.reset)
433-
menu.insertAction(select_all_action, read_reset_action)
394+
actions = [
395+
("readPointer", self.read_pointer),
396+
("readUtf8String", self.read_utf8_string),
397+
("readUtf16String", self.read_utf16_string),
398+
("readFloat", self.read_float),
399+
("readDouble", self.read_double),
400+
("readByteArray", self.read_bytearray),
401+
("reset", self.reset)
402+
]
403+
404+
for text, method in actions:
405+
action = QAction(text, self)
406+
action.setEnabled(True)
407+
action.triggered.connect(method)
408+
menu.insertAction(select_all_action, action)
434409

435410
# Show the context menu.
436411
menu.exec(e.globalPos())

main.py

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ def __init__(self):
190190
self.arrangedresult = None
191191
self.arrangedresult2 = None
192192
self.platform = None
193-
self.spawntargetid = None
193+
self.islistpidchecked = False
194+
self.attachtargetname = None # name to attach. need to provide on the AppList widget
195+
self.attachedname = None # main module name after frida attached successfully
196+
self.spawntargetid = None # target identifier to do frida spawn. need to provide on the AppList widget
194197
self.remoteaddr = ''
195198

196199
self.attachBtn.clicked.connect(self.attach_frida)
@@ -210,6 +213,7 @@ def __init__(self):
210213
self.memReplaceBtn.clicked.connect(self.mem_search_replace_func)
211214
self.memSearchTargetImgCheckBox.stateChanged.connect(self.mem_search_with_img_checkbox)
212215
self.memScanPatternTypeCheckBox.stateChanged.connect(self.mem_scan_pattern_checkbox)
216+
self.listPIDCheckBox.stateChanged.connect(self.list_pid)
213217
self.attachTypeCheckBox.stateChanged.connect(self.remote_attach)
214218
self.spawnModeCheckBox.stateChanged.connect(self.spawn_mode)
215219
self.memSearchReplaceCheckBox.stateChanged.connect(self.mem_search_replace_checkbox)
@@ -256,17 +260,22 @@ def searchresultaddrsig_func(self, searchresultaddrsig: str):
256260
self.addr_btn_func()
257261

258262
@pyqtSlot(str)
259-
def spawntargetsig_func(self, spawntargetidsig: str):
260-
self.spawntargetid = spawntargetidsig
263+
def targetsig_func(self, targetsig: str):
264+
if self.isspawnchecked:
265+
self.spawntargetid = targetsig
266+
else:
267+
self.attachtargetname = targetsig
261268
if self.isremoteattachchecked is True:
262269
if re.search(r"^\d+\.\d+\.\d+\.\d+:\d+$", self.spawndialog.spawnui.remoteAddrInput.text()) is None:
263270
QMessageBox.information(self, "info", "Enter IP:PORT")
264271
self.spawntargetid = None
272+
self.attachtargetname = None
265273
return
266274
self.remoteaddr = self.spawndialog.spawnui.remoteAddrInput.text()
267275
self.attach_frida()
268276
self.spawndialog = None
269277
self.spawntargetid = None
278+
self.attachtargetname = None
270279
self.remoteaddr = ''
271280

272281
@pyqtSlot(str)
@@ -290,17 +299,14 @@ def adjust_label_pos(self):
290299
else:
291300
self.label_3.setIndent(28 - (77 - text_length) * 7)
292301

302+
def list_pid(self, state):
303+
self.islistpidchecked = state == Qt.CheckState.Checked.value
304+
293305
def remote_attach(self, state):
294-
if state == Qt.CheckState.Checked.value:
295-
self.isremoteattachchecked = True
296-
else:
297-
self.isremoteattachchecked = False
306+
self.isremoteattachchecked = state == Qt.CheckState.Checked.value
298307

299308
def spawn_mode(self, state):
300-
if state == Qt.CheckState.Checked.value:
301-
self.isspawnchecked = True
302-
else:
303-
self.isspawnchecked = False
309+
self.isspawnchecked = state == Qt.CheckState.Checked.value
304310

305311
def attach_frida(self):
306312
if globvar.isFridaAttached is True:
@@ -314,10 +320,18 @@ def attach_frida(self):
314320
return
315321

316322
try:
317-
if self.isspawnchecked and self.spawntargetid is None:
323+
if (self.islistpidchecked and not self.isspawnchecked and self.attachtargetname is None) or \
324+
(self.isspawnchecked and self.spawntargetid is None):
318325
self.spawndialog = spawn.SpawnDialogClass() if (
319326
platform.system() == 'Darwin') else spawn_win.SpawnDialogClass()
320-
self.spawndialog.spawntargetidsig.connect(self.spawntargetsig_func)
327+
if self.islistpidchecked and not self.isspawnchecked:
328+
self.spawndialog.ispidlistchecked = True
329+
self.spawndialog.spawnui.spawnTargetIdInput.setPlaceholderText("AppStore")
330+
self.spawndialog.spawnui.appListLabel.setText("PID Name")
331+
self.spawndialog.spawnui.spawnBtn.setText("Attach")
332+
self.spawndialog.attachtargetnamesig.connect(self.targetsig_func)
333+
334+
self.spawndialog.spawntargetidsig.connect(self.targetsig_func)
321335

322336
if self.isremoteattachchecked is False:
323337
self.spawndialog.spawnui.remoteAddrInput.setEnabled(False)
@@ -332,7 +346,8 @@ def attach_frida(self):
332346
return
333347

334348
globvar.fridaInstrument = code.Instrument("scripts/default.js", self.isremoteattachchecked, self.remoteaddr,
335-
self.spawntargetid)
349+
self.attachtargetname if (self.islistpidchecked and not self.isspawnchecked) else self.spawntargetid,
350+
self.isspawnchecked)
336351
msg = globvar.fridaInstrument.instrument()
337352
self.remoteaddr = ''
338353
except Exception as e:
@@ -348,6 +363,7 @@ def attach_frida(self):
348363

349364
self.platform = globvar.fridaInstrument.platform()
350365
name = globvar.fridaInstrument.list_modules()[0]['name']
366+
self.attachedname = name
351367
self.set_status(name)
352368

353369
def detach_frida(self):
@@ -889,36 +905,23 @@ def search_mem_search_result(self):
889905
self.memSearchFoundCount.setText(str(len(searchresult)) + ' found')
890906

891907
def mem_search_with_img_checkbox(self, state):
892-
if state == Qt.CheckState.Checked.value:
893-
self.ismemsearchwithimgchecked = True
894-
self.memSearchTargetImgInput.setEnabled(True)
895-
else:
896-
self.ismemsearchwithimgchecked = False
897-
self.memSearchTargetImgInput.setEnabled(False)
908+
isChecked = state == Qt.CheckState.Checked.value
909+
self.ismemsearchwithimgchecked = isChecked
910+
self.memSearchTargetImgInput.setEnabled(isChecked)
898911

899912
def mem_scan_pattern_checkbox(self, state):
900-
if state == Qt.CheckState.Checked.value:
901-
self.ismemscanstrchecked = True
902-
else:
903-
self.ismemscanstrchecked = False
913+
self.ismemscanstrchecked = state == Qt.CheckState.Checked.value
904914

905915
def mem_search_replace_checkbox(self, state):
906-
if state == Qt.CheckState.Checked.value:
907-
self.memReplaceBtn.setEnabled(True)
908-
self.memReplacePattern.setEnabled(True)
909-
self.ismemsearchreplacechecked = True
910-
else:
911-
self.memReplaceBtn.setEnabled(False)
912-
self.memReplacePattern.setEnabled(False)
913-
self.ismemsearchreplacechecked = False
916+
isChecked = state == Qt.CheckState.Checked.value
917+
self.memReplaceBtn.setEnabled(isChecked)
918+
self.memReplacePattern.setEnabled(isChecked)
919+
self.ismemsearchreplacechecked = isChecked
914920

915921
def il2cpp_checkbox(self, state):
916-
if state == Qt.CheckState.Checked.value:
917-
self.isil2cppchecked = True
918-
self.memDumpModuleName.setEnabled(False)
919-
else:
920-
self.isil2cppchecked = False
921-
self.memDumpModuleName.setEnabled(True)
922+
isChecked = state == Qt.CheckState.Checked.value
923+
self.isil2cppchecked = isChecked
924+
self.memDumpModuleName.setEnabled(not isChecked)
922925

923926
def dump_module(self):
924927
# il2cpp dump
@@ -938,7 +941,7 @@ def dump_module(self):
938941
# print("[hackcatml] il2cppFridaInstrument: ", self.il2cppFridaInstrument)
939942
if self.il2cppFridaInstrument is None or len(self.il2cppFridaInstrument.sessions) == 0:
940943
self.il2cppFridaInstrument = code.Instrument("scripts/il2cppdump.js", self.isremoteattachchecked,
941-
globvar.fridaInstrument.remoteaddr, None)
944+
globvar.fridaInstrument.remoteaddr, self.attachedname, False)
942945
msg = self.il2cppFridaInstrument.instrument()
943946
if msg is not None:
944947
QMessageBox.information(self, "info", msg)

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
attrs==22.2.0
22
certifi==2023.5.7
33
charset-normalizer==2.1.1
4-
frida==16.0.8
4+
frida==16.1.1
55
frozenlist==1.3.3
66
idna==3.4
77
multidict==6.0.4

0 commit comments

Comments
 (0)