Improve behavior without IDA Pro (#442)

* Improve behavior without IDA Pro

* Fix import order

* Improved IDA Pro behaviour more

* Added only_after_first_prompt decorator

* Removed newline after import

* Added documentation

* Improved docstring
pull/448/head
Benedikt Werner 8 years ago committed by Disconnect3d
parent 0736dbd1c7
commit 399eb3137c

@ -18,6 +18,8 @@ Feel free to update the list below!
* We have a caching mechanism (["memoization"](https://en.wikipedia.org/wiki/Memoization)) which we use through Python's decorators - those are defined in `pwndbg/memoize.py` - just check its usages
* To block a function before the first prompt was displayed use the `pwndbg.decorators.only_after_first_prompt` decorator.
* Memory accesses should be done through `pwndbg/memory.py` functions
* Process properties can be retrieved thx to `pwndbg/proc.py` - e.g. using `pwndbg.proc.pid` will give us current process pid
@ -35,4 +37,3 @@ Feel free to update the list below!
Our tests are written using [pytest](https://docs.pytest.org/en/latest/). It uses some magic so that Python's `assert` can be used for asserting things in tests and it injects dependencies which are called fixtures, into test functions.
The fixtures should be defined in [tests/conftest.py](tests/conftest.py). If you need help with writing tests, feel free to reach out on gitub issues/pr or on our irc channel on freenode.

@ -32,6 +32,8 @@ from functools import total_ordering
import gdb
import six
import pwndbg.decorators
TYPES = collections.OrderedDict()
# The value is a plain boolean.
@ -165,6 +167,9 @@ class Parameter(gdb.Parameter):
for trigger in triggers[self.name]:
trigger()
if not pwndbg.decorators.first_prompt:
# Remove the newline that gdb adds automatically
return '\b'
return 'Set %s to %r' % (self.docstring, self.value)
def get_show_string(self, svalue):

@ -0,0 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import functools
first_prompt = False
def only_after_first_prompt(value_before=None):
"""
Decorator to prevent a function from running before the first prompt was displayed.
The 'value_before' parameter can be used to specify the value that is
returned if the function is called before the first prompt was displayed.
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if first_prompt:
return func(*args, **kwargs)
else:
return value_before
return wrapper
return decorator

@ -14,6 +14,7 @@ import errno
import functools
import socket
import sys
import time
import traceback
import gdb
@ -21,6 +22,7 @@ import gdb
import pwndbg.arch
import pwndbg.compat
import pwndbg.config
import pwndbg.decorators
import pwndbg.elf
import pwndbg.events
import pwndbg.memoize
@ -37,6 +39,7 @@ except:
ida_rpc_host = pwndbg.config.Parameter('ida-rpc-host', '127.0.0.1', 'ida xmlrpc server address')
ida_rpc_port = pwndbg.config.Parameter('ida-rpc-port', 8888, 'ida xmlrpc server port')
ida_enabled = pwndbg.config.Parameter('ida-enabled', True, 'whether to enable ida integration')
ida_timeout = pwndbg.config.Parameter('ida-timeout', 2, 'time to wait for ida xmlrpc in seconds')
xmlrpclib.Marshaller.dispatch[int] = lambda _, v, w: w("<value><i8>%d</i8></value>" % v)
@ -50,14 +53,26 @@ _ida = None
# to avoid printing the same exception multiple times, we store the last exception here
_ida_last_exception = None
# to avoid checking the connection multiple times with no delay, we store the last time we checked it
_ida_last_connection_check = 0
@pwndbg.config.Trigger([ida_rpc_host, ida_rpc_port])
@pwndbg.decorators.only_after_first_prompt()
@pwndbg.config.Trigger([ida_rpc_host, ida_rpc_port, ida_timeout])
def init_ida_rpc_client():
global _ida, _ida_last_exception
global _ida, _ida_last_exception, _ida_last_connection_check
if not ida_enabled:
return
now = time.time()
if _ida is None and (now - _ida_last_connection_check) < int(ida_timeout) + 5:
return
addr = 'http://{host}:{port}'.format(host=ida_rpc_host, port=ida_rpc_port)
_ida = xmlrpclib.ServerProxy(addr)
socket.setdefaulttimeout(3)
socket.setdefaulttimeout(int(ida_timeout))
exception = None # (type, value, traceback)
try:
@ -76,10 +91,20 @@ def init_ida_rpc_client():
if exception:
if not isinstance(_ida_last_exception, exception[0]) or _ida_last_exception.args != exception[1].args:
if hasattr(pwndbg.config, "exception_verbose") and pwndbg.config.exception_verbose:
print(message.error("[!] Ida Pro xmlrpc error"))
traceback.print_exception(*exception)
else:
exc_type, exc_value, _ = exception
print(message.error('Failed to connect to IDA Pro ({}: {})'.format(exc_type.__qualname__, exc_value)))
if exc_type is socket.timeout:
print(message.notice('To increase the time to wait for IDA Pro use `') + message.hint('set ida-timeout <new-timeout-in-seconds>') + message.notice('`'))
else:
print(message.notice('For more info invoke `') + message.hint('set exception-verbose on') + message.notice('`'))
print(message.notice('To disable IDA Pro integration invoke `') + message.hint('set ida-enabled off') + message.notice('`'))
_ida_last_exception = exception and exception[1]
_ida_last_connection_check = now
class withIDA(object):
@ -88,8 +113,6 @@ class withIDA(object):
functools.update_wrapper(self, fn)
def __call__(self, *args, **kwargs):
if not ida_enabled:
return None
if _ida is None:
init_ida_rpc_client()
if _ida is not None:

@ -7,6 +7,7 @@ from __future__ import unicode_literals
import gdb
import pwndbg.decorators
import pwndbg.events
import pwndbg.gdbutils
import pwndbg.memoize
@ -28,6 +29,8 @@ cur = (gdb.selected_inferior(), gdb.selected_thread())
def prompt_hook(*a):
global cur
pwndbg.decorators.first_prompt = True
new = (gdb.selected_inferior(), gdb.selected_thread())
if cur != new:

Loading…
Cancel
Save