Skip to content

Commit

Permalink
Add pwnlib.config module and documentation (#893)
Browse files Browse the repository at this point in the history
This adds functionality for user configuration files at ~/.pwn.conf
and /etc/pwn.conf.  Previously this was only used by the pwnlib.log
module, and was entirely undocumented.

This is now documented, and offers an easy mechanism for other parts
of the code to have extension points.
  • Loading branch information
zachriggle authored Feb 13, 2017
1 parent 71855eb commit 6585897
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 16 deletions.
4 changes: 4 additions & 0 deletions docs/source/config.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:mod:`pwnlib.config` --- Pwntools Configuration File
====================================================

.. automodule:: pwnlib.config
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Each of the ``pwntools`` modules is documented here.
atexception
atexit
constants
config
context
dynelf
encoders
Expand Down
1 change: 1 addition & 0 deletions pwn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

pwnlib.args.initialize()
pwnlib.log.install_default_handler()
pwnlib.config.initialize()

log = pwnlib.log.getLogger('pwnlib.exploit')
args = pwnlib.args.args
Expand Down
60 changes: 60 additions & 0 deletions pwnlib/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
"""Allows per-user and per-host configuration of Pwntools settings.
The list of configurable options includes all of the logging symbols
and colors, as well as all of the default values on the global context
object.
The configuration file is read from ``~/.pwn.conf`` and ``/etc/pwn.conf``.
The configuration file is only read in ``from pwn import *`` mode, and not
when used in library mode (``import pwnlib``). To read the configuration
file in library mode, invoke :func:`.config.initialize`.
The ``context`` section supports complex types, at least as far as is
supported by ``pwnlib.util.safeeval.expr``.
::
[log]
success.symbol=😎
error.symbol=☠
info.color=blue
[context]
adb_port=4141
randomize=1
timeout=60
terminal=['x-terminal-emulator', '-e']
"""
from __future__ import absolute_import

import ConfigParser
import os

registered_configs = {}

def register_config(section, function):
"""Registers a configuration section.
Arguments:
section(str): Named configuration section
function(callable): Function invoked with a dictionary of
``{option: value}`` for the entries in the section.
"""
registered_configs[section] = function

def initialize():
"""Read the configuration files."""
from pwnlib.log import getLogger
log = getLogger(__name__)

c = ConfigParser.ConfigParser()
c.read(['/etc/pwn.conf', os.path.expanduser('~/.pwn.conf')])

for section in c.sections():
if section not in registered_configs:
log.warn("Unknown configuration section %r" % section)
continue
settings = dict(c.items(section))
registered_configs[section](settings)
19 changes: 19 additions & 0 deletions pwnlib/context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@

import socks

from pwnlib.config import register_config
from pwnlib.device import Device
from pwnlib.timeout import Timeout


_original_socket = socket.socket

class _devnull(object):
Expand Down Expand Up @@ -1343,3 +1345,20 @@ def setter(*a, **kw):
with context.local(**{k:kw.pop(k) for k,v in kw.items() if isinstance(getattr(ContextType, k, None), property)}):
return function(*a, **kw)
return setter

# Read configuration options from the context section
def update_context_defaults(section):
# Circular imports FTW!
from pwnlib.util import safeeval
from pwnlib.log import getLogger
log = getLogger(__name__)
for key, value in section.items():
if key not in ContextType.defaults:
log.warn("Unknown configuration option %r in section %r" % (key, 'context'))
continue
if isinstance(ContextType.defaults[key], (str, unicode)):
value = safeeval.expr(value)

ContextType.defaults[key] = value

register_config('context', update_context_defaults)
33 changes: 17 additions & 16 deletions pwnlib/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
import time

from pwnlib import term
from pwnlib.config import register_config
from pwnlib.context import Thread
from pwnlib.context import context
from pwnlib.exception import PwnlibException
Expand Down Expand Up @@ -132,26 +133,26 @@
}


# permit setting logging colors from a configuration file
config = os.path.expanduser('~/.pwn.conf')
if os.path.exists(config):
c = ConfigParser.ConfigParser()
c.read([config])

for section in c.sections():
if section not in _msgtype_prefixes:
def read_log_config(settings):
log = getLogger(__name__)
for key, value in settings.items():
if '.' not in key:
log.warn("Invalid configuration option %r in section %r" % (key, 'log'))
continue

for key, value in c.items(section):
if key == 'color':
try:
_msgtype_prefixes[section][0] = getattr(text, value)
except AttributeError:
pass
msgtype, key = key.split('.', 1)

if key == 'color':
current = _msgtype_prefixes[msgtype][0]
_msgtype_prefixes[msgtype][0] = getattr(text, value, current)

elif key == 'symbol':
_msgtype_prefixes[section][1] = value
elif key == 'symbol':
_msgtype_prefixes[msgtype][1] = value

else:
log.warn("Unknown configuration option %r in section %r" % (key, 'log'))

register_config('log', read_log_config)

# the text decoration to use for spinners. the spinners themselves can be found
# in the `pwnlib.term.spinners` module
Expand Down

0 comments on commit 6585897

Please sign in to comment.