diff --git a/README.md b/README.md
index 8cf3770..e30743f 100644
--- a/README.md
+++ b/README.md
@@ -95,9 +95,9 @@ import urllib2; exec(urllib2.urlopen('https://raw.githubusercontent.com/fidgetin
To enable the integrated server, you can choose "Integrated Server" after right-clicking
the IDArling widget located in the status bar.
-The integrated server requires PyQt5, which is integrated into IDA. If you're
+The integrated server requires PySide6, which is integrated into IDA. If you're
using an external Python installation, we recommand using Python 3, which offers
-a pre-built package that can be installed with a simple `pip install PyQt5`.
+a pre-built package that can be installed with a simple `pip install PySide6`.
## Connection to server and usage
diff --git a/idarling/core/core.py b/idarling/core/core.py
index c48a4f1..1bcff3d 100644
--- a/idarling/core/core.py
+++ b/idarling/core/core.py
@@ -21,7 +21,7 @@
import ida_netnode
import ida_typeinf
-from PyQt5.QtCore import QCoreApplication, QFileInfo # noqa: I202
+from PySide6.QtCore import QCoreApplication, QFileInfo # noqa: I202
from .hooks import HexRaysHooks, IDBHooks, IDPHooks, UIHooks
from ..module import Module
@@ -121,7 +121,7 @@ def tick(self, tick):
self.save_netnode()
def update_local_types_map(self):
- for i in range(1, ida_typeinf.get_ordinal_qty(ida_typeinf.get_idati())):
+ for i in range(1, ida_typeinf.get_ordinal_count(ida_typeinf.get_idati())):
t = ImportLocalType(i)
self.local_type_map[i] = t
diff --git a/idarling/core/events.py b/idarling/core/events.py
index 8c1a096..06b4f5c 100644
--- a/idarling/core/events.py
+++ b/idarling/core/events.py
@@ -15,7 +15,6 @@
import sys
import ida_bytes
-import ida_enum
import ida_funcs
import ida_hexrays
import ida_idaapi
@@ -28,7 +27,6 @@
import ida_range
import ida_segment
import ida_segregs
-import ida_struct
import ida_typeinf
import ida_ua
import ida_idc
@@ -115,7 +113,7 @@ def __init__(self, ea, flags, size, sname):
self.sname = sname
def __call__(self):
- ida_bytes.create_data(self.ea, ida_bytes.calc_dflags(self.flags, True), self.size, ida_struct.get_struc_id(self.sname) if self.sname else ida_netnode.BADNODE)
+ ida_bytes.create_data(self.ea, ida_bytes.calc_dflags(self.flags, True), self.size, idc.get_struc_id(self.sname) if self.sname else ida_netnode.BADNODE)
class RenamedEvent(Event):
@@ -133,8 +131,13 @@ def __call__(self):
self.ea, self.new_name, flags | ida_name.SN_NOWARN
)
ida_kernwin.request_refresh(ida_kernwin.IWID_DISASMS)
- ida_kernwin.request_refresh(ida_kernwin.IWID_STRUCTS)
ida_kernwin.request_refresh(ida_kernwin.IWID_STKVIEW)
+
+ if hasattr(ida_kernwin, "IWID_STRUCTS"):
+ ida_kernwin.request_refresh(ida_kernwin.IWID_STRUCTS)
+ else:
+ ida_kernwin.refresh_idaview_anyway()
+
HexRaysEvent.refresh_pseudocode_view(self.ea)
@@ -295,7 +298,7 @@ def __call__(self):
py_type = py_type[1:]
if len(py_type) >= 2:
if self.name:
- r = ida_struct.get_member_by_fullname(self.name)
+ r = idc.get_member_by_fullname(self.name)
if r:
self.ea = r[0].id
ida_typeinf.apply_type(
@@ -404,14 +407,14 @@ def __call__(self):
if self.op == "offset":
ida_offset.op_plain_offset(self.ea, self.n, 0)
if self.op == "enum":
- id = ida_enum.get_enum(self.extra["ename"])
+ id = idc.get_enum(self.extra["ename"])
ida_bytes.op_enum(self.ea, self.n, id, self.extra["serial"])
if self.op == "struct":
path_len = len(self.extra["spath"])
path = ida_pro.tid_array(path_len)
for i in range(path_len):
sname = self.extra["spath"][i]
- path[i] = ida_struct.get_struc_id(sname)
+ path[i] = idc.get_struc_id(sname)
insn = ida_ua.insn_t()
ida_ua.decode_insn(insn, self.ea)
ida_bytes.op_stroff(
@@ -433,7 +436,7 @@ def __init__(self, enum, name):
self.name = name
def __call__(self):
- ida_enum.add_enum(self.enum, self.name, 0)
+ idc.add_enum(self.enum, self.name, 0)
class EnumDeletedEvent(Event):
@@ -444,7 +447,7 @@ def __init__(self, ename):
self.ename = ename
def __call__(self):
- ida_enum.del_enum(ida_enum.get_enum(self.ename))
+ idc.del_enum(idc.get_enum(self.ename))
class EnumRenamedEvent(Event):
@@ -458,11 +461,11 @@ def __init__(self, oldname, newname, is_enum):
def __call__(self):
if self.is_enum:
- enum = ida_enum.get_enum(self.oldname)
- ida_enum.set_enum_name(enum, self.newname)
+ enum = idc.get_enum(self.oldname)
+ idc.set_enum_name(enum, self.newname)
else:
- emem = ida_enum.get_enum_member_by_name(self.oldname)
- ida_enum.set_enum_member_name(emem, self.newname)
+ emem = idc.get_enum_member_by_name(self.oldname)
+ idc.set_enum_member_name(emem, self.newname)
class EnumBfChangedEvent(Event):
@@ -474,8 +477,8 @@ def __init__(self, ename, bf_flag):
self.bf_flag = bf_flag
def __call__(self):
- enum = ida_enum.get_enum(self.ename)
- ida_enum.set_enum_bf(enum, self.bf_flag)
+ enum = idc.get_enum(self.ename)
+ idc.set_enum_bf(enum, self.bf_flag)
class EnumCmtChangedEvent(Event):
@@ -488,9 +491,9 @@ def __init__(self, emname, cmt, repeatable_cmt):
self.repeatable_cmt = repeatable_cmt
def __call__(self):
- emem = ida_enum.get_enum_member_by_name(self.emname)
+ emem = idc.get_enum_member_by_name(self.emname)
cmt = self.cmt if self.cmt else ""
- ida_enum.set_enum_cmt(emem, cmt, self.repeatable_cmt)
+ idc.set_enum_cmt(emem, cmt, self.repeatable_cmt)
class EnumMemberCreatedEvent(Event):
@@ -504,8 +507,8 @@ def __init__(self, ename, name, value, bmask):
self.bmask = bmask
def __call__(self):
- enum = ida_enum.get_enum(self.ename)
- ida_enum.add_enum_member(
+ enum = idc.get_enum(self.ename)
+ idc.add_enum_member(
enum, self.name, self.value, self.bmask
)
@@ -521,8 +524,8 @@ def __init__(self, ename, value, serial, bmask):
self.bmask = bmask
def __call__(self):
- enum = ida_enum.get_enum(self.ename)
- ida_enum.del_enum_member(enum, self.value, self.serial, self.bmask)
+ enum = idc.get_enum(self.ename)
+ idc.del_enum_member(enum, self.value, self.serial, self.bmask)
class StrucCreatedEvent(Event):
@@ -535,7 +538,7 @@ def __init__(self, struc, name, is_union):
self.is_union = is_union
def __call__(self):
- ida_struct.add_struc(
+ idc.add_struc(
ida_idaapi.BADADDR, self.name, self.is_union
)
@@ -548,8 +551,8 @@ def __init__(self, sname):
self.sname = sname
def __call__(self):
- struc = ida_struct.get_struc_id(self.sname)
- ida_struct.del_struc(ida_struct.get_struc(struc))
+ struc = idc.get_struc_id(self.sname)
+ idc.del_struc(idc.get_struc(struc))
class StrucRenamedEvent(Event):
@@ -561,8 +564,8 @@ def __init__(self, oldname, newname):
self.newname = newname
def __call__(self):
- struc = ida_struct.get_struc_id(self.oldname)
- ida_struct.set_struc_name(struc, self.newname)
+ struc = idc.get_struc_id(self.oldname)
+ idc.set_struc_name(struc, self.newname)
class StrucCmtChangedEvent(Event):
@@ -576,16 +579,16 @@ def __init__(self, sname, smname, cmt, repeatable_cmt):
self.repeatable_cmt = repeatable_cmt
def __call__(self):
- struc = ida_struct.get_struc_id(self.sname)
- sptr = ida_struct.get_struc(struc)
+ struc = idc.get_struc_id(self.sname)
+ sptr = idc.get_struc(struc)
cmt = self.cmt if self.cmt else ""
if self.smname:
- mptr = ida_struct.get_member_by_name(
+ mptr = idc.get_member_by_name(
sptr, self.smname
)
- ida_struct.set_member_cmt(mptr, cmt, self.repeatable_cmt)
+ idc.set_member_cmt(mptr, cmt, self.repeatable_cmt)
else:
- ida_struct.set_struc_cmt(sptr.id, cmt, self.repeatable_cmt)
+ idc.set_struc_cmt(sptr.id, cmt, self.repeatable_cmt)
class StrucMemberEvent(Event):
@@ -594,14 +597,14 @@ class StrucMemberEvent(Event):
"""
@staticmethod
def _get_sptr(struct_name):
- struc_id = ida_struct.get_struc_id(struct_name)
- return ida_struct.get_struc(struc_id)
+ struc_id = idc.get_struc_id(struct_name)
+ return idc.get_struc(struc_id)
@staticmethod
def _get_member_type(type_flag, extra):
mt = ida_nalt.opinfo_t()
if ida_bytes.is_struct(type_flag):
- mt.tid = ida_struct.get_struc_id(extra['struc_name'])
+ mt.tid = idc.get_struc_id(extra['struc_name'])
if type_flag & ida_bytes.off_flag():
mt.ri = ida_nalt.refinfo_t()
mt.ri.init(
@@ -635,7 +638,7 @@ def __init__(self, sname, fieldname, offset, flag, nbytes, extra):
def __call__(self):
sptr = self._get_sptr(self.sname)
mt = self._get_member_type(self.flag, self.extra)
- ida_struct.add_struc_member(
+ idc.add_struc_member(
sptr,
self.fieldname,
self.offset,
@@ -659,7 +662,7 @@ def __init__(self, sname, soff, eoff, flag, extra):
def __call__(self):
sptr = self._get_sptr(self.sname)
mt = self._get_member_type(self.flag, self.extra)
- ida_struct.set_member_type(
+ idc.set_member_type(
sptr, self.soff, self.flag, mt, self.eoff - self.soff
)
@@ -674,7 +677,7 @@ def __init__(self, sname, offset):
def __call__(self):
sptr = self._get_sptr(self.sname)
- ida_struct.del_struc_member(sptr, self.offset)
+ idc.del_struc_member(sptr, self.offset)
class StrucMemberRenamedEvent(StrucMemberEvent):
@@ -688,7 +691,7 @@ def __init__(self, sname, offset, newname):
def __call__(self):
sptr = self._get_sptr(self.sname)
- ida_struct.set_member_name(
+ idc.set_member_name(
sptr, self.offset, self.newname
)
@@ -703,9 +706,9 @@ def __init__(self, sname, offset, delta):
self.delta = delta
def __call__(self):
- struc = ida_struct.get_struc_id(self.sname)
- sptr = ida_struct.get_struc(struc)
- ida_struct.expand_struc(sptr, self.offset, self.delta)
+ struc = idc.get_struc_id(self.sname)
+ sptr = idc.get_struc(struc)
+ idc.expand_struc(sptr, self.offset, self.delta)
class SegmAddedEvent(Event):
diff --git a/idarling/core/hooks.py b/idarling/core/hooks.py
index 409b12d..e440f33 100644
--- a/idarling/core/hooks.py
+++ b/idarling/core/hooks.py
@@ -15,17 +15,16 @@
import ida_auto
import ida_bytes
-import ida_enum
import ida_funcs
import ida_hexrays
import ida_idaapi
import ida_idp
import ida_kernwin
import ida_nalt
+import idc
import ida_netnode
import ida_pro
import ida_segment
-import ida_struct
import ida_typeinf
fDebug = False
@@ -77,9 +76,9 @@ def auto_empty(self):
def local_types_changed(self):
changed_types = []
# self._plugin.logger.trace(self._plugin.core.local_type_map)
- for i in range(1, ida_typeinf.get_ordinal_qty(ida_typeinf.get_idati())):
+ for i in range(1, ida_typeinf.get_ordinal_count(ida_typeinf.get_idati())):
t = ImportLocalType(i)
- if t and t.name and ida_struct.get_struc_id(t.name) == ida_idaapi.BADADDR and ida_enum.get_enum(t.name) == ida_idaapi.BADADDR:
+ if t and t.name and idc.get_struc_id(t.name) == ida_idaapi.BADADDR and idc.get_enum(t.name) == ida_idaapi.BADADDR:
if i in self._plugin.core.local_type_map:
t_old = self._plugin.core.local_type_map[i]
if t_old and not t.isEqual(t_old):
@@ -170,8 +169,8 @@ def local_types_changed(self):
def ti_changed(self, ea, type, fname):
self._plugin.logger.debug("ti_changed(ea = 0x%X, type = %s, fname = %s)" % (ea, type, fname))
name = ""
- if ida_struct.is_member_id(ea):
- name = ida_struct.get_struc_name(ea)
+ if idc.is_member_id(ea):
+ name = idc.get_struc_name(ea)
type = ida_typeinf.idc_get_type_raw(ea)
self._send_packet(evt.TiChangedEvent(ea, (ParseTypeString(type[0]) if type else [], type[1] if type else None), name))
return 0
@@ -184,7 +183,7 @@ def op_type_changed(self, ea, n):
self._plugin.logger.debug("op_type_changed(ea = %x, n = %d)" % (ea,n))
def gather_enum_info(ea, n):
id = ida_bytes.get_enum_id(ea, n)[0]
- serial = ida_enum.get_enum_idx(id)
+ serial = idc.get_enum_idx(id)
return id, serial
extra = {}
@@ -209,7 +208,7 @@ def is_flag(type):
elif is_flag(ida_bytes.enum_flag()):
op = "enum"
id, serial = gather_enum_info(ea, n)
- ename = ida_enum.get_enum_name(id)
+ ename = idc.get_enum_name(id)
extra["ename"] = Event.decode(ename)
extra["serial"] = serial
elif flags & ida_bytes.stroff_flag():
@@ -221,7 +220,7 @@ def is_flag(type):
)
spath = []
for i in range(path_len):
- sname = ida_struct.get_struc_name(path[i])
+ sname = idc.get_struc_name(path[i])
spath.append(Event.decode(sname))
extra["delta"] = delta.value()
extra["spath"] = spath
@@ -236,41 +235,41 @@ def is_flag(type):
return 0
def enum_created(self, enum):
- name = ida_enum.get_enum_name(enum)
+ name = idc.get_enum_name(enum)
self._send_packet(evt.EnumCreatedEvent(enum, name))
return 0
# XXX - use enum_deleted(self, id) instead?
def deleting_enum(self, id):
- self._send_packet(evt.EnumDeletedEvent(ida_enum.get_enum_name(id)))
+ self._send_packet(evt.EnumDeletedEvent(idc.get_enum_name(id)))
return 0
# XXX - use enum_renamed(self, id) instead?
def renaming_enum(self, id, is_enum, newname):
if is_enum:
- oldname = ida_enum.get_enum_name(id)
+ oldname = idc.get_enum_name(id)
else:
- oldname = ida_enum.get_enum_member_name(id)
+ oldname = idc.get_enum_member_name(id)
self._send_packet(evt.EnumRenamedEvent(oldname, newname, is_enum))
return 0
def enum_bf_changed(self, id):
- bf_flag = 1 if ida_enum.is_bf(id) else 0
- ename = ida_enum.get_enum_name(id)
+ bf_flag = 1 if idc.is_bf(id) else 0
+ ename = idc.get_enum_name(id)
self._send_packet(evt.EnumBfChangedEvent(ename, bf_flag))
return 0
def enum_cmt_changed(self, tid, repeatable_cmt):
- cmt = ida_enum.get_enum_cmt(tid, repeatable_cmt)
- emname = ida_enum.get_enum_name(tid)
+ cmt = idc.get_enum_cmt(tid, repeatable_cmt)
+ emname = idc.get_enum_name(tid)
self._send_packet(evt.EnumCmtChangedEvent(emname, cmt, repeatable_cmt))
return 0
def enum_member_created(self, id, cid):
- ename = ida_enum.get_enum_name(id)
- name = ida_enum.get_enum_member_name(cid)
- value = ida_enum.get_enum_member_value(cid)
- bmask = ida_enum.get_enum_member_bmask(cid)
+ ename = idc.get_enum_name(id)
+ name = idc.get_enum_member_name(cid)
+ value = idc.get_enum_member_value(cid)
+ bmask = idc.get_enum_member_bmask(cid)
self._send_packet(
evt.EnumMemberCreatedEvent(ename, name, value, bmask)
)
@@ -278,24 +277,24 @@ def enum_member_created(self, id, cid):
# XXX - use enum_member_deleted(self, id, cid) instead?
def deleting_enum_member(self, id, cid):
- ename = ida_enum.get_enum_name(id)
- value = ida_enum.get_enum_member_value(cid)
- serial = ida_enum.get_enum_member_serial(cid)
- bmask = ida_enum.get_enum_member_bmask(cid)
+ ename = idc.get_enum_name(id)
+ value = idc.get_enum_member_value(cid)
+ serial = idc.get_enum_member_serial(cid)
+ bmask = idc.get_enum_member_bmask(cid)
self._send_packet(
evt.EnumMemberDeletedEvent(ename, value, serial, bmask)
)
return 0
def struc_created(self, tid):
- name = ida_struct.get_struc_name(tid)
- is_union = ida_struct.is_union(tid)
+ name = idc.get_struc_name(tid)
+ is_union = idc.is_union(tid)
self._send_packet(evt.StrucCreatedEvent(tid, name, is_union))
return 0
# XXX - use struc_deleted(self, struc_id) instead?
def deleting_struc(self, sptr):
- sname = ida_struct.get_struc_name(sptr.id)
+ sname = idc.get_struc_name(sptr.id)
self._send_packet(evt.StrucDeletedEvent(sname))
return 0
@@ -310,19 +309,19 @@ def renaming_struc(self, id, oldname, newname):
# XXX - use struc_expanded(self, sptr) instead
def expanding_struc(self, sptr, offset, delta):
- sname = ida_struct.get_struc_name(sptr.id)
+ sname = idc.get_struc_name(sptr.id)
self._send_packet(evt.ExpandingStrucEvent(sname, offset, delta))
return 0
def struc_member_created(self, sptr, mptr):
extra = {}
- sname = ida_struct.get_struc_name(sptr.id)
- fieldname = ida_struct.get_member_name(mptr.id)
+ sname = idc.get_struc_name(sptr.id)
+ fieldname = idc.get_member_name(mptr.id)
offset = 0 if mptr.unimem() else mptr.soff
flag = mptr.flag
nbytes = mptr.eoff if mptr.unimem() else mptr.eoff - mptr.soff
mt = ida_nalt.opinfo_t()
- is_not_data = ida_struct.retrieve_member_info(mt, mptr)
+ is_not_data = idc.retrieve_member_info(mt, mptr)
if is_not_data:
if flag & ida_bytes.off_flag():
extra["target"] = mt.ri.target
@@ -343,7 +342,7 @@ def struc_member_created(self, sptr, mptr):
)
)
elif flag & ida_bytes.stru_flag():
- extra["struc_name"] = ida_struct.get_struc_name(mt.tid)
+ extra["struc_name"] = idc.get_struc_name(mt.tid)
if flag & ida_bytes.strlit_flag():
extra["strtype"] = mt.strtype
self._send_packet(
@@ -360,13 +359,13 @@ def struc_member_created(self, sptr, mptr):
return 0
def struc_member_deleted(self, sptr, off1, off2):
- sname = ida_struct.get_struc_name(sptr.id)
+ sname = idc.get_struc_name(sptr.id)
self._send_packet(evt.StrucMemberDeletedEvent(sname, off2))
return 0
# XXX - use struc_member_renamed(self, sptr, mptr) instead?
def renaming_struc_member(self, sptr, mptr, newname):
- sname = ida_struct.get_struc_name(sptr.id)
+ sname = idc.get_struc_name(sptr.id)
offset = mptr.soff
self._send_packet(evt.StrucMemberRenamedEvent(sname, offset, newname))
return 0
@@ -374,10 +373,10 @@ def renaming_struc_member(self, sptr, mptr, newname):
def struc_member_changed(self, sptr, mptr):
extra = {}
- sname = ida_struct.get_struc_name(sptr.id)
+ sname = idc.get_struc_name(sptr.id)
flag = mptr.flag
mt = ida_nalt.opinfo_t()
- is_not_data = ida_struct.retrieve_member_info(mt, mptr)
+ is_not_data = idc.retrieve_member_info(mt, mptr)
if is_not_data:
if flag & ida_bytes.off_flag():
extra["target"] = mt.ri.target
@@ -398,7 +397,7 @@ def struc_member_changed(self, sptr, mptr):
)
)
elif flag & ida_bytes.stru_flag():
- extra["struc_name"] = ida_struct.get_struc_name(mt.tid)
+ extra["struc_name"] = idc.get_struc_name(mt.tid)
if flag & ida_bytes.strlit_flag():
extra["strtype"] = mt.strtype
self._send_packet(
@@ -415,13 +414,13 @@ def struc_member_changed(self, sptr, mptr):
return 0
def struc_cmt_changed(self, id, repeatable_cmt):
- fullname = ida_struct.get_struc_name(id)
+ fullname = idc.get_struc_name(id)
if "." in fullname:
sname, smname = fullname.split(".", 1)
else:
sname = fullname
smname = ""
- cmt = ida_struct.get_struc_cmt(id, repeatable_cmt)
+ cmt = idc.get_struc_cmt(id, repeatable_cmt)
self._send_packet(
evt.StrucCmtChangedEvent(sname, smname, cmt, repeatable_cmt)
)
@@ -529,12 +528,14 @@ def sgr_changed(self, start_ea, end_ea, regnum, value, old_value, tag):
def make_data(self, ea, flags, tid, size):
self._plugin.logger.debug("make_data(ea = %x, flags = %x, tid = %x, size = %x)" % (ea, flags, tid, size))
# Note: MakeDataEvent.sname == '' is convention for BADNODE
- self._send_packet(evt.MakeDataEvent(ea, flags, size, ida_struct.get_struc_name(tid) if tid != ida_netnode.BADNODE else ''))
+ self._send_packet(evt.MakeDataEvent(ea, flags, size, idc.get_struc_name(tid) if tid != ida_netnode.BADNODE else ''))
return 0
def renamed(self, ea, new_name, local_name):
self._plugin.logger.debug("renamed(ea = %x, new_name = %s, local_name = %d)" % (ea, new_name, local_name))
- if ida_struct.is_member_id(ea) or ida_struct.get_struc(ea) or ida_enum.get_enum_name(ea):
+ # `idc.get_struc` was removed in newer IDA Python APIs; avoid calling it.
+ # We only need to detect member-id or enum rename events here.
+ if idc.is_member_id(ea) or idc.get_enum_name(ea):
# Drop hook to avoid duplicate since already handled by the following hooks:
# - renaming_struc_member() -> sends 'StrucMemberRenamedEvent'
# - renaming_struc() -> sends 'StrucRenamedEvent'
diff --git a/idarling/interface/actions.py b/idarling/interface/actions.py
index 9dbb914..3e900ab 100644
--- a/idarling/interface/actions.py
+++ b/idarling/interface/actions.py
@@ -22,9 +22,9 @@
import ida_kernwin
import ida_loader
-from PyQt5.QtCore import QCoreApplication, QFileInfo, Qt # noqa: I202
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QMessageBox, QProgressDialog
+from PySide6.QtCore import QCoreApplication, QFileInfo, Qt # noqa: I202
+from PySide6.QtGui import QIcon
+from PySide6.QtWidgets import QMessageBox, QProgressDialog
from .dialogs import OpenDialog, SaveDialog
from ..shared.commands import DownloadFile, UpdateFile
@@ -207,7 +207,7 @@ def _file_downloaded(self, project, snapshot, progress, reply):
# Get the absolute path of the file
app_path = QCoreApplication.applicationFilePath()
app_name = QFileInfo(app_path).fileName()
- file_ext = "i64" if "64" in app_name else "idb"
+ file_ext = "i64"
file_name = "%s_%s_%s.%s" % (project.name, snapshot.binary, snapshot.name, file_ext)
file_path = os.path.join(self._plugin.config["files_dir"], file_name)
@@ -251,7 +251,7 @@ def _file_downloaded(self, project, snapshot, progress, reply):
# Create a temporary copy of the new database because we cannot use
# the snapshot functionality to restore the currently opened database
- file_ext = ".i64" if "64" in app_name else ".idb"
+ file_ext = ".i64"
tmp_file, tmp_path = tempfile.mkstemp(suffix=file_ext)
shutil.copyfile(file_path, tmp_path)
diff --git a/idarling/interface/dialogs.py b/idarling/interface/dialogs.py
index 2d61912..7611dfa 100644
--- a/idarling/interface/dialogs.py
+++ b/idarling/interface/dialogs.py
@@ -20,9 +20,9 @@
import ida_nalt
import idc
-from PyQt5.QtCore import QRegExp, Qt, QDir # noqa: I202
-from PyQt5.QtGui import QIcon, QRegExpValidator
-from PyQt5.QtWidgets import (
+from PySide6.QtCore import QRegularExpression, Qt, QDir # noqa: I202
+from PySide6.QtGui import QIcon, QRegularExpressionValidator
+from PySide6.QtWidgets import (
QCheckBox,
QColorDialog,
QComboBox,
@@ -44,6 +44,14 @@
QWidget, QSizePolicy, QFileDialog,
)
+# QHeaderView enum compatibility between PyQt5 and PySide6
+try:
+ _HV_Stretch = QHeaderView.ResizeMode.Stretch
+ _HV_ResizeToContents = QHeaderView.ResizeMode.ResizeToContents
+except AttributeError:
+ _HV_Stretch = QHeaderView.Stretch
+ _HV_ResizeToContents = QHeaderView.ResizeToContents
+
from ..shared.commands import (
CreateProject,
CreateBinary,
@@ -164,7 +172,7 @@ def __init__(self, plugin):
self._snapshots_table.setHorizontalHeaderLabels(labels)
horizontal_header = self._snapshots_table.horizontalHeader()
horizontal_header.setSectionsClickable(False)
- horizontal_header.setSectionResizeMode(0, horizontal_header.Stretch)
+ horizontal_header.setSectionResizeMode(0, _HV_Stretch)
self._snapshots_table.verticalHeader().setVisible(False)
self._snapshots_table.setSelectionBehavior(QTableWidget.SelectRows)
self._snapshots_table.setSelectionMode(QTableWidget.SingleSelection)
@@ -685,7 +693,7 @@ def __init__(self, plugin):
self._nameLabel = QLabel("Project Name")
layout.addWidget(self._nameLabel)
self._nameEdit = QLineEdit()
- self._nameEdit.setValidator(QRegExpValidator(QRegExp("[a-zA-Z0-9-]+")))
+ self._nameEdit.setValidator(QRegularExpressionValidator(QRegularExpression("[a-zA-Z0-9-]+")))
layout.addWidget(self._nameEdit)
buttons = QWidget(self)
@@ -897,9 +905,9 @@ def state_changed(state):
"Auto"))
horizontal_header = self._servers_table.horizontalHeader()
horizontal_header.setSectionsClickable(False)
- horizontal_header.setSectionResizeMode(0, QHeaderView.Stretch)
- horizontal_header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
- horizontal_header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
+ horizontal_header.setSectionResizeMode(0, _HV_Stretch)
+ horizontal_header.setSectionResizeMode(1, _HV_ResizeToContents)
+ horizontal_header.setSectionResizeMode(2, _HV_ResizeToContents)
self._servers_table.verticalHeader().setVisible(False)
self._servers_table.setSelectionBehavior(QTableWidget.SelectRows)
self._servers_table.setSelectionMode(QTableWidget.SingleSelection)
diff --git a/idarling/interface/filter.py b/idarling/interface/filter.py
index 9ae9c9f..c0efaeb 100644
--- a/idarling/interface/filter.py
+++ b/idarling/interface/filter.py
@@ -13,11 +13,10 @@
import ida_funcs
import ida_kernwin
-from PyQt5.QtCore import QEvent, QObject, Qt # noqa: I202
-from PyQt5.QtGui import QContextMenuEvent, QIcon, QImage, QPixmap, QShowEvent
-from PyQt5.QtWidgets import (
- QAction,
- qApp,
+from PySide6.QtCore import QEvent, QObject, Qt # noqa: I202
+from PySide6.QtGui import QAction, QContextMenuEvent, QIcon, QImage, QPixmap, QShowEvent
+from PySide6.QtWidgets import (
+ QApplication,
QDialog,
QGroupBox,
QLabel,
@@ -26,6 +25,7 @@
QWidget,
)
+
from .widget import StatusWidget
from ..shared.commands import InviteToLocation
@@ -43,11 +43,11 @@ def __init__(self, plugin, parent=None):
def install(self):
self._plugin.logger.debug("Installing the event filter")
- qApp.instance().installEventFilter(self)
+ QApplication.instance().installEventFilter(self)
def uninstall(self):
self._plugin.logger.debug("Uninstalling the event filter")
- qApp.instance().removeEventFilter(self)
+ QApplication.instance().removeEventFilter(self)
def _replace_icon(self, label):
pixmap = QPixmap(self._plugin.plugin_resource("idarling.png"))
diff --git a/idarling/interface/interface.py b/idarling/interface/interface.py
index eab53da..bb460a7 100644
--- a/idarling/interface/interface.py
+++ b/idarling/interface/interface.py
@@ -12,8 +12,8 @@
# along with this program. If not, see .
import time
-from PyQt5.QtGui import QPixmap
-from PyQt5.QtWidgets import qApp, QMainWindow
+from PySide6.QtGui import QPixmap
+from PySide6.QtWidgets import QApplication, QMainWindow
from .actions import OpenAction, SaveAction
from .filter import EventFilter
@@ -37,7 +37,8 @@ def __init__(self, plugin):
# Find the QMainWindow instance
self._plugin.logger.debug("Searching for the main window")
- for widget in qApp.topLevelWidgets():
+ app = QApplication.instance()
+ for widget in app.topLevelWidgets():
if isinstance(widget, QMainWindow):
self._window = widget
break
diff --git a/idarling/interface/invites.py b/idarling/interface/invites.py
index f67ee0c..19d1a89 100644
--- a/idarling/interface/invites.py
+++ b/idarling/interface/invites.py
@@ -10,16 +10,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-from PyQt5.QtCore import (
- pyqtProperty,
+from PySide6.QtCore import (
+ Property,
QPoint,
QPropertyAnimation,
QRect,
Qt,
QTimer,
)
-from PyQt5.QtGui import QBrush, QColor, QPainter
-from PyQt5.QtWidgets import QHBoxLayout, QLabel, QWidget
+from PySide6.QtGui import QBrush, QColor, QPainter
+from PySide6.QtWidgets import QHBoxLayout, QLabel, QWidget
class Invite(QWidget):
@@ -186,7 +186,7 @@ def hide_animation(self):
self._animation.setEndValue(0.0)
self._animation.start()
- @pyqtProperty(float)
+ @Property(float)
def popup_opacity(self):
return self._popup_opacity
diff --git a/idarling/interface/painter.py b/idarling/interface/painter.py
index 799d3a2..369fdad 100644
--- a/idarling/interface/painter.py
+++ b/idarling/interface/painter.py
@@ -15,15 +15,15 @@
import ida_funcs
import ida_kernwin
-from PyQt5.QtCore import ( # noqa: I202
+from PySide6.QtCore import ( # noqa: I202
QAbstractItemModel,
QModelIndex,
QObject,
Qt,
)
-from PyQt5.QtGui import QColor
-from PyQt5.QtWidgets import QStyledItemDelegate, QWidget
-import sip
+from PySide6.QtGui import QColor
+from PySide6.QtWidgets import QStyledItemDelegate, QWidget
+from shiboken6 import wrapInstance
from .widget import StatusWidget
@@ -81,7 +81,7 @@ def __init__(self, plugin):
self._ida_nav_colorizer = None
self._nbytes = 0
- # XXX - unused since moved to Python3 in IDA due to errors in PyQt5
+ # XXX - unused since moved to Python3 in IDA due to errors in PySide6
# "OverflowError: Python int too large to convert to C long"
# See https://www.hex-rays.com/products/ida/support/idapython_docs/ida_kernwin-module.html#set_nav_colorizer
def nav_colorizer(self, ea, nbytes):
@@ -138,7 +138,7 @@ def get_bg_color(self, ea):
return None
def widget_visible(self, twidget):
- widget = sip.wrapinstance(long(twidget), QWidget)
+ widget = wrapInstance(int(twidget), QWidget)
if widget.windowTitle() != "Functions window":
return
table = widget.layout().itemAt(0).widget()
diff --git a/idarling/interface/widget.py b/idarling/interface/widget.py
index 64552db..be66be2 100644
--- a/idarling/interface/widget.py
+++ b/idarling/interface/widget.py
@@ -14,9 +14,9 @@
from functools import partial, lru_cache
import time
-from PyQt5.QtCore import QPoint, QRect, QSize, Qt, QTimer
-from PyQt5.QtGui import QIcon, QImage, QPainter, QPixmap, QRegion
-from PyQt5.QtWidgets import QAction, QActionGroup, QLabel, QMenu, QWidget
+from PySide6.QtCore import QPoint, QRect, QSize, Qt, QTimer
+from PySide6.QtGui import QAction, QActionGroup, QIcon, QImage, QPainter, QPixmap, QRegion
+from PySide6.QtWidgets import QLabel, QMenu, QWidget
from .dialogs import SettingsDialog
diff --git a/idarling/network/client.py b/idarling/network/client.py
index d3bfd9e..e5c1dc7 100644
--- a/idarling/network/client.py
+++ b/idarling/network/client.py
@@ -13,7 +13,7 @@
import ida_auto
import ida_kernwin
-from PyQt5.QtGui import QImage, QPixmap # noqa: I202
+from PySide6.QtGui import QImage, QPixmap # noqa: I202
from ..interface.widget import StatusWidget
from ..shared.commands import (
@@ -102,8 +102,8 @@ def send_packet(self, packet):
packet.tick = self._plugin.core.tick
return ClientSocket.send_packet(self, packet)
- def disconnect(self, err=None):
- ret = ClientSocket.disconnect(self, err)
+ def terminate(self, err=None):
+ ret = ClientSocket.close_connection(self, err)
self._plugin.network._client = None
self._plugin.network._server = None
@@ -188,12 +188,12 @@ def _handle_download_file(self, query):
def _handle_delete_project(self, packet):
# TODO: Handle situation then user snapshot in deleted project
- self.disconnect()
+ self.terminate()
def _handle_delete_binary(self, packet):
# TODO: Handle situation then user snapshot in deleted binary
- self.disconnect()
+ self.terminate()
def _handle_delete_snapshot(self, packet):
# TODO: Handle situation then user snapshot deleted
- self.disconnect()
\ No newline at end of file
+ self.terminate()
\ No newline at end of file
diff --git a/idarling/network/network.py b/idarling/network/network.py
index 3044a6a..e6359f2 100644
--- a/idarling/network/network.py
+++ b/idarling/network/network.py
@@ -117,7 +117,7 @@ def connect(self, server):
raise OSError(err, os.strerror(err), '')
except OSError as e:
self._plugin._logger.exception(e)
- self._client.disconnect()
+ self._client.terminate()
def disconnect(self):
"""Disconnect from the current server."""
@@ -126,7 +126,7 @@ def disconnect(self):
return
self._plugin.logger.info("Disconnecting...")
- self._client.disconnect()
+ self._client.terminate()
def send_packet(self, packet):
"""Send a packet to the server."""
diff --git a/idarling/plugin.py b/idarling/plugin.py
index 7104ef5..dd65f05 100644
--- a/idarling/plugin.py
+++ b/idarling/plugin.py
@@ -28,7 +28,7 @@
-class Plugin(ida_idaapi.plugin_t):
+class IdarlingPlugin(ida_idaapi.plugin_t):
"""
This is the main class of the plugin. It subclasses plugin_t as required
by IDA. It holds the modules of plugin, which themselves provides the
@@ -51,7 +51,7 @@ class Plugin(ida_idaapi.plugin_t):
@staticmethod
def description():
"""Return the description displayed in the console."""
- return "{} v{}".format(Plugin.PLUGIN_NAME, Plugin.PLUGIN_VERSION)
+ return "{} v{}".format(IdarlingPlugin.PLUGIN_NAME, IdarlingPlugin.PLUGIN_VERSION)
@staticmethod
def plugin_resource(filename):
@@ -87,7 +87,7 @@ def default_config():
"""
r, g, b = colorsys.hls_to_rgb(random.random(), 0.5, 1.0)
color = int(b * 255) << 16 | int(g * 255) << 8 | int(r * 255)
- file_path = Plugin.user_resource("files", "")
+ file_path = IdarlingPlugin.user_resource("files", "")
return {
"level": logging.INFO,
"servers": [],
@@ -107,7 +107,7 @@ def __init__(self):
# Then setup the default logger
log_path = self.user_resource("logs", "idarling.%s.log" % os.getpid())
level = self.config["level"]
- self._logger = start_logging(log_path, "IDArling.Plugin", level)
+ self._logger = start_logging(log_path, "IDArling.IdarlingPlugin", level)
self._core = Core(self)
self._interface = Interface(self)
diff --git a/idarling/server.py b/idarling/server.py
index 11b36d8..bcda617 100644
--- a/idarling/server.py
+++ b/idarling/server.py
@@ -16,7 +16,7 @@
import sys
import traceback
-from PyQt5.QtCore import QCoreApplication, QTimer
+from PySide6.QtCore import QCoreApplication, QTimer
from .shared.server import Server
from .shared.utils import start_logging
@@ -25,7 +25,7 @@
class DedicatedServer(Server):
"""
This is the dedicated/standalone server. It can be invoked from the command line. It
- requires only PyQt5 and should be invoked from Python 3. The dedicated
+ requires only PySide6 and should be invoked from Python 3. The dedicated
server should be used when the integrated doesn't fulfil the user's needs.
"""
diff --git a/idarling/shared/discovery.py b/idarling/shared/discovery.py
index 0c7ff57..151f553 100644
--- a/idarling/shared/discovery.py
+++ b/idarling/shared/discovery.py
@@ -13,8 +13,9 @@
import platform
import socket
import time
+import errno
-from PyQt5.QtCore import QObject, QSocketNotifier, QTimer
+from PySide6.QtCore import QObject, QSocketNotifier, QTimer
DISCOVERY_REQUEST = "IDARLING_DISCOVERY_REQUEST"
@@ -92,10 +93,15 @@ def _send_request(self):
)
request = request[sent:]
except socket.error as e:
- self._logger.warning("Couldn't send discovery request: {}".format(e))
+ # Some systems may not be able to broadcast on all interfaces
+ # ("No route to host" / network unreachable). Don't spam
+ # warnings for those expected transient errors.
+ err_no = getattr(e, "errno", None)
+ if err_no in (errno.EHOSTUNREACH, errno.ENETUNREACH):
+ self._logger.debug("Couldn't send discovery request (network unreachable): {}".format(e))
+ else:
+ self._logger.warning("Couldn't send discovery request: {}".format(e))
# Force return, otherwise the while loop will halt IDA
- # This is a temporary fix, and it's gonna yield the above
- # warning every every n seconds..
return
def _notify_read(self):
diff --git a/idarling/shared/forms.py b/idarling/shared/forms.py
index 6685a34..856eda8 100644
--- a/idarling/shared/forms.py
+++ b/idarling/shared/forms.py
@@ -11,10 +11,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
import idaapi
-from PyQt5.QtWidgets import *
-from PyQt5.QtGui import *
-from PyQt5.QtCore import *
-from PyQt5 import QtGui, QtCore, QtWidgets
+from PySide6.QtWidgets import *
+from PySide6.QtGui import *
+from PySide6.QtCore import *
+from PySide6 import QtGui, QtCore, QtWidgets
from difflib import *
diff --git a/idarling/shared/server.py b/idarling/shared/server.py
index 843eeef..34c3ebe 100644
--- a/idarling/shared/server.py
+++ b/idarling/shared/server.py
@@ -125,12 +125,12 @@ def process(self, msg, kwargs):
self._logger = CustomAdapter(self._logger, {})
- def disconnect(self, err=None, notify=True):
+ def close_client(self, err=None, notify=True):
# Notify other users that we disconnected
self.parent().reject(self)
if self._project and self._binary and self._snapshot and notify:
self.parent().forward_users(self, LeaveSession(self.name, False))
- ClientSocket.disconnect(self, err)
+ ClientSocket.close_connection(self, err)
def recv_packet(self, packet):
if isinstance(packet, Command):
@@ -162,7 +162,7 @@ def recv_packet(self, packet):
if packet.tick and interval and packet.tick % interval == 0:
def file_downloaded(reply):
- file_name = "%s_%s_%s.idb" % (self._project, self._binary, self._snapshot)
+ file_name = "%s_%s_%s.i64" % (self._project, self._binary, self._snapshot)
file_path = self.parent().server_file(file_name)
# Write the file to disk
@@ -206,8 +206,8 @@ def _handle_rename_binary(self, query):
# for queries
snapshots = self.parent().storage.select_snapshots(query.project, query.new_name)
for snapshot in snapshots:
- old_file_name = "%s_%s_%s.idb" % (query.project, query.old_name, snapshot.name)
- new_file_name = "%s_%s_%s.idb" % (query.project, query.new_name, snapshot.name)
+ old_file_name = "%s_%s_%s.i64" % (query.project, query.old_name, snapshot.name)
+ new_file_name = "%s_%s_%s.i64" % (query.project, query.new_name, snapshot.name)
old_file_path = self.parent().server_file(old_file_name)
new_file_path = self.parent().server_file(new_file_name)
# If a rename happens before a file is uploaded, the
@@ -241,7 +241,7 @@ def _handle_list_snapshots(self, query):
snapshots = self.parent().storage.select_snapshots(query.project, query.binary)
for snapshot in snapshots:
snapshot_info = snapshot.project, snapshot.binary, snapshot.name
- file_name = "%s_%s_%s.idb" % (snapshot_info)
+ file_name = "%s_%s_%s.i64" % (snapshot_info)
file_path = self.parent().server_file(file_name)
if os.path.isfile(file_path):
snapshot.tick = self.parent().storage.last_tick(*snapshot_info)
@@ -265,7 +265,7 @@ def _handle_upload_file(self, query):
snapshot = self.parent().storage.select_snapshot(
query.project, query.binary, query.snapshot
)
- file_name = "%s_%s_%s.idb" % (query.project, snapshot.binary, snapshot.name)
+ file_name = "%s_%s_%s.i64" % (query.project, snapshot.binary, snapshot.name)
file_path = self.parent().server_file(file_name)
# Write the file received to disk
@@ -279,7 +279,7 @@ def _handle_download_file(self, query):
snapshot = self.parent().storage.select_snapshot(
query.project, query.binary, query.snapshot
)
- file_name = "%s_%s_%s.idb" % (query.project, snapshot.binary, snapshot.name)
+ file_name = "%s_%s_%s.i64" % (query.project, snapshot.binary, snapshot.name)
file_path = self.parent().server_file(file_name)
# Read file from disk and sent it
@@ -365,7 +365,7 @@ def _delete_binary_files(self, project, binary):
self._delete_snapshot_files(project, binary, db.name)
def _delete_snapshot_files(self, project, binary, snapshot):
- file_name = "%s_%s_%s.idb" % (project, binary, snapshot)
+ file_name = "%s_%s_%s.i64" % (project, binary, snapshot)
file_path = self.parent().server_file(file_name)
try:
os.remove(file_path)
@@ -587,7 +587,7 @@ def start(self, host, port=0, ssl_=None):
sock.settimeout(0) # No timeout
sock.setblocking(0) # No blocking
sock.listen(5)
- self.connect(sock)
+ self.set_socket(sock)
# Start discovering clients
host, port = sock.getsockname()
@@ -600,8 +600,8 @@ def stop(self):
self._discovery.stop()
# Disconnect all clients
for client in list(self._clients):
- client.disconnect(notify=False)
- self.disconnect()
+ client.close_client(notify=False)
+ self.close_listener()
try:
self.db_update_lock.release()
except RuntimeError:
diff --git a/idarling/shared/sockets.py b/idarling/shared/sockets.py
index d83c4c4..19572e7 100644
--- a/idarling/shared/sockets.py
+++ b/idarling/shared/sockets.py
@@ -18,7 +18,7 @@
import ssl
import sys
-from PyQt5.QtCore import QCoreApplication, QEvent, QObject, QSocketNotifier
+from PySide6.QtCore import QCoreApplication, QEvent, QObject, QSocketNotifier
from .packets import Container, Packet, PacketDeferred, Query, Reply
from ..shared.commands import (
@@ -86,7 +86,7 @@ def wrap_socket(self, sock):
self._socket = sock
- def disconnect(self, err=None):
+ def close_connection(self, err=None):
"""Terminates the current connection."""
if not self._socket:
return
@@ -151,7 +151,7 @@ def _check_socket(self):
ret = self._socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
if ret != 0:
if ret != errno.EINPROGRESS and ret != errno.EWOULDBLOCK:
- self.disconnect(socket.error(ret, os.strerror(ret)))
+ self.close_connection(socket.error(ret, os.strerror(ret)))
return False
else:
self._connected = True
@@ -167,7 +167,7 @@ def _notify_read(self):
try:
data = self._socket.recv(ClientSocket.MAX_DATA_SIZE)
if not data:
- self.disconnect()
+ self.close_connection()
return
except socket.error as e:
if (
@@ -175,7 +175,7 @@ def _notify_read(self):
and not isinstance(e, ssl.SSLWantReadError)
and not isinstance(e, ssl.SSLWantWriteError)
):
- self.disconnect(e)
+ self.close_connection(e)
return # No more data available
self._read_buffer.extend(data)
@@ -191,10 +191,22 @@ def _notify_read(self):
# Try to parse the line (= packet)
try:
- dct = json.loads(line.decode("utf-8"))
- self._read_packet = Packet.parse_packet(
- dct, self._server
- )
+ try:
+ decoded = line.decode("utf-8")
+ except UnicodeDecodeError as e:
+ # Received binary data (for example an SSL/TLS
+ # handshake) on a plaintext socket. Close the
+ # connection to avoid trying to parse arbitrary
+ # binary data as JSON.
+ self._logger.warning(
+ "Invalid (non-UTF8) packet received: %s", line
+ )
+ self._logger.debug("Closing connection")
+ self.close_connection()
+ return
+
+ dct = json.loads(decoded)
+ self._read_packet = Packet.parse_packet(dct, self._server)
except Exception as e:
msg = "Invalid packet received: %s" % line
self._logger.warning(msg)
@@ -267,7 +279,7 @@ def _notify_write(self):
and not isinstance(e, ssl.SSLWantReadError)
and not isinstance(e, ssl.SSLWantWriteError)
):
- self.disconnect(e)
+ self.close_connection(e)
return # Can't write anything
# Trigger the upback
@@ -356,7 +368,7 @@ def connected(self):
"""Is the underlying socket connected?"""
return self._connected
- def connect(self, sock):
+ def set_socket(self, sock):
"""Sets the underlying socket to utilize."""
self._accept_notifier = QSocketNotifier(
sock.fileno(), QSocketNotifier.Read, self
@@ -367,7 +379,7 @@ def connect(self, sock):
self._socket = sock
self._connected = True
- def disconnect(self, err=None):
+ def close_listener(self, err=None):
"""Terminates the current connection."""
if not self._socket:
return
@@ -390,7 +402,7 @@ def _notify_accept(self):
except socket.error as e:
if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
break
- self.disconnect(e)
+ self.close_listener(e)
break
self._accept(sock)
diff --git a/idarling_plugin.py b/idarling_plugin.py
index f847e75..fa48794 100644
--- a/idarling_plugin.py
+++ b/idarling_plugin.py
@@ -10,9 +10,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-from idarling.plugin import Plugin
+from idarling.plugin import IdarlingPlugin
def PLUGIN_ENTRY(): # noqa: N802
"""Mandatory entry point for IDAPython plugins."""
- return Plugin()
+ return IdarlingPlugin()
diff --git a/setup.py b/setup.py
index fb4bb2d..43a6ea5 100755
--- a/setup.py
+++ b/setup.py
@@ -8,10 +8,10 @@
description="Collaborative Reverse Engineering plugin for IDA Pro",
url="https://github.com/IDArlingTeam/IDArling",
packages=find_packages(),
- install_requires=["PyQt5; python_version >= '3.0'"],
+ install_requires=["PySide6; python_version >= '3.9'"],
include_package_data=True,
entry_points={
- "idapython_plugins": ["idarling=idarling.plugin:Plugin"],
+ "idapython_plugins": ["idarling=idarling.plugin:IdarlingPlugin"],
"console_scripts": ["idarling_server=idarling.server:main"],
},
)