Repository: pwndbg/pwndbg

Issue:
Display IDA commands in pwndbg commands list when IDA rpc server is offline
Currently IDA Pro commands are hidden if IDA is not available. Because of that, those commands are not displayed when someone is looking for features/commands through `pwndbg ida`.

As @zachriggle suggested in https://github.com/pwndbg/pwndbg/pull/223#issuecomment-294612888 we should just re-load the module when the connection to IDA succeeded.


Discussion:

Resolving patch:
diff --git a/pwndbg/commands/ida.py b/pwndbg/commands/ida.py
--- a/pwndbg/commands/ida.py
+++ b/pwndbg/commands/ida.py
@@ -32,87 +32,89 @@ def j(*args):
         pass
 
 
-if pwndbg.ida.available():
-    @pwndbg.commands.Command
-    @pwndbg.commands.OnlyWhenRunning
-    def up(n=1):
-        """
-        Select and print stack frame that called this one.
-        An argument says how many frames up to go.
-        """
-        f = gdb.selected_frame()
 
-        for i in range(n):
-            o = f.older()
-            if o:
-                o.select()
+@pwndbg.commands.Command
+@pwndbg.commands.OnlyWhenRunning
+def up(n=1):
+    """
+    Select and print stack frame that called this one.
+    An argument says how many frames up to go.
+    """
+    f = gdb.selected_frame()
+
+    for i in range(n):
+        o = f.older()
+        if o:
+            o.select()
 
-        bt = pwndbg.commands.context.context_backtrace(with_banner=False)
-        print('\n'.join(bt))
+    bt = pwndbg.commands.context.context_backtrace(with_banner=False)
+    print('\n'.join(bt))
 
-        j()
+    j()
 
 
-    @pwndbg.commands.Command
-    @pwndbg.commands.OnlyWhenRunning
-    def down(n=1):
-        """
-        Select and print stack frame called by this one.
-        An argument says how many frames down to go.
-        """
-        f = gdb.selected_frame()
+@pwndbg.commands.Command
+@pwndbg.commands.OnlyWhenRunning
+def down(n=1):
+    """
+    Select and print stack frame called by this one.
+    An argument says how many frames down to go.
+    """
+    f = gdb.selected_frame()
 
-        for i in range(n):
-            o = f.newer()
-            if o:
-                o.select()
+    for i in range(n):
+        o = f.newer()
+        if o:
+            o.select()
 
-        bt = pwndbg.commands.context.context_backtrace(with_banner=False)
-        print('\n'.join(bt))
+    bt = pwndbg.commands.context.context_backtrace(with_banner=False)
+    print('\n'.join(bt))
 
-        j()
+    j()
 
 
-    @pwndbg.commands.Command
-    def save_ida():
-        if not pwndbg.ida.available():
-            return
+@pwndbg.commands.Command
+@pwndbg.ida.withIDA
+def save_ida():
+    """Save the IDA database"""
+    if not pwndbg.ida.available():
+        return
 
-        path = pwndbg.ida.GetIdbPath()
+    path = pwndbg.ida.GetIdbPath()
 
-        # Need to handle emulated paths for Wine
-        if path.startswith('Z:'):
-            path = path[2:].replace('\\', '/')
-            pwndbg.ida.SaveBase(path)
+    # Need to handle emulated paths for Wine
+    if path.startswith('Z:'):
+        path = path[2:].replace('\\', '/')
+        pwndbg.ida.SaveBase(path)
 
-        basename = os.path.basename(path)
-        dirname = os.path.dirname(path)
-        backups = os.path.join(dirname, 'ida-backup')
+    basename = os.path.basename(path)
+    dirname = os.path.dirname(path)
+    backups = os.path.join(dirname, 'ida-backup')
 
-        if not os.path.isdir(backups):
-            os.mkdir(backups)
+    if not os.path.isdir(backups):
+        os.mkdir(backups)
 
-        basename, ext = os.path.splitext(basename)
-        basename += '-%s' % datetime.datetime.now().isoformat()
-        basename += ext
+    basename, ext = os.path.splitext(basename)
+    basename += '-%s' % datetime.datetime.now().isoformat()
+    basename += ext
 
-        # Windows doesn't like colons in paths
-        basename = basename.replace(':', '_')
+    # Windows doesn't like colons in paths
+    basename = basename.replace(':', '_')
 
-        full_path = os.path.join(backups, basename)
+    full_path = os.path.join(backups, basename)
 
-        pwndbg.ida.SaveBase(full_path)
+    pwndbg.ida.SaveBase(full_path)
 
-        data = open(full_path, 'rb').read()
+    data = open(full_path, 'rb').read()
 
-        # Compress!
-        full_path_compressed = full_path + '.bz2'
-        bz2.BZ2File(full_path_compressed, 'w').write(data)
+    # Compress!
+    full_path_compressed = full_path + '.bz2'
+    bz2.BZ2File(full_path_compressed, 'w').write(data)
 
-        # Remove old version
-        os.unlink(full_path)
+    # Remove old version
+    os.unlink(full_path)
 
-    save_ida()
+save_ida()
 
 
 class ida(gdb.Function):


Test patch: