Skip to content

Commit

Permalink
Variable Size Buffer Fill (#806)
Browse files Browse the repository at this point in the history
Support custom buffer-read sizes for `tube` objects via their internal `Buffer` object.  Default buffer size is 4096.  Buffer size can be set per-object at instantiation-time (`tube(buffer_fill_size=...)`), by attribute (`tube.buffer_fill_size=...`), or in a context (`context.buffer_size=...`).
  • Loading branch information
Owlz authored and zachriggle committed Dec 5, 2016
1 parent 1c54e34 commit 5d9792f
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 47 deletions.
2 changes: 1 addition & 1 deletion pwnlib/commandline/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
from . import shellcraft
from . import unhex
from . import update
from .common import parser
from ..context import context
from .common import parser

commands = {
'asm': asm.main,
Expand Down
9 changes: 9 additions & 0 deletions pwnlib/context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ class ContextType(object):
'aslr': True,
'binary': None,
'bits': 32,
'buffer_size': 4096,
'device': os.getenv('ANDROID_SERIAL', None) or None,
'endian': 'little',
'kernel': None,
Expand Down Expand Up @@ -1140,6 +1141,14 @@ def adb(self):

return command

@_validator
def buffer_size(self, size):
"""Internal buffer size to use for ``tube`` objects.
This is not the maximum size of the buffer, but this is the amount of data
which is passed to each raw ``read`` syscall (or equivalent).
"""
return int(size)

#*************************************************************************
# ALIASES
Expand Down
1 change: 1 addition & 0 deletions pwnlib/data/useragents/download-useragents.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import urllib

from bs4 import BeautifulSoup

from pwn import *

uas = set()
Expand Down
2 changes: 1 addition & 1 deletion pwnlib/term/term.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
import threading
import traceback

from ..context import ContextType
from . import termcap
from ..context import ContextType

settings = None
_graphics_mode = False
Expand Down
23 changes: 21 additions & 2 deletions pwnlib/tubes/buffer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/usr/bin/env python2

from ..context import context


class Buffer(Exception):
"""
List of strings with some helper routines.
Expand Down Expand Up @@ -28,10 +31,10 @@ class Buffer(Exception):
The ``0th`` item in the buffer is the oldest item, and
will be received first.
"""
def __init__(self):
def __init__(self, buffer_fill_size = None):
self.data = [] # Buffer
self.size = 0 # Length

self.buffer_fill_size = buffer_fill_size

def __len__(self):
"""
Expand Down Expand Up @@ -169,3 +172,19 @@ def get(self, want=float('inf')):
self.size -= len(data)

return data

def get_fill_size(self,size=None):
"""
Retrieves the default fill size for this buffer class.
Arguments:
size (int): (Optional) If set and not None, returns the size variable back.
Returns:
Fill size as integer if size == None, else size.
"""
if size:
return size

with context.local(buffer_size = self.buffer_fill_size):
return context.buffer_size
6 changes: 2 additions & 4 deletions pwnlib/tubes/listen.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@ class listen(sock):
bindaddr(str): The address to bind to.
fam: The string "any", "ipv4" or "ipv6" or an integer to pass to :func:`socket.getaddrinfo`.
typ: The string "tcp" or "udp" or an integer to pass to :func:`socket.getaddrinfo`.
timeout: A positive number, None
"""

def __init__(self, port=0, bindaddr = "0.0.0.0",
fam = "any", typ = "tcp",
timeout = Timeout.default, level = None):
super(listen, self).__init__(timeout, level = level)
fam = "any", typ = "tcp", *args, **kwargs):
super(listen, self).__init__(*args, **kwargs)

port = int(port)
fam = {socket.AF_INET: 'ipv4',
Expand Down
11 changes: 5 additions & 6 deletions pwnlib/tubes/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ class process(tube):
Working directory. Uses the current working directory by default.
env(dict):
Environment variables. By default, inherits from Python's environment.
timeout(int):
Timeout to use on ``tube`` ``recv`` operations.
stdin(int):
File object or file descriptor number to use for ``stdin``.
By default, a pipe is used. A pty can be used instead by setting
Expand Down Expand Up @@ -202,20 +200,21 @@ def __init__(self, argv = None,
executable = None,
cwd = None,
env = None,
timeout = Timeout.default,
stdin = PIPE,
stdout = PTY,
stderr = STDOUT,
level = None,
close_fds = True,
preexec_fn = lambda: None,
raw = True,
aslr = None,
setuid = None,
where = 'local',
display = None,
alarm = None):
super(process, self).__init__(timeout, level = level)
alarm = None,
*args,
**kwargs
):
super(process, self).__init__(*args,**kwargs)

# Permit using context.binary
if argv is None:
Expand Down
16 changes: 6 additions & 10 deletions pwnlib/tubes/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ class remote(sock):

def __init__(self, host, port,
fam = "any", typ = "tcp",
timeout = Timeout.default, ssl=False, sock=None, level = None):
super(remote, self).__init__(timeout, level = level)
ssl=False, sock=None, *args, **kwargs):
super(remote, self).__init__(*args, **kwargs)

self.rport = int(port)
self.rhost = host
Expand Down Expand Up @@ -158,17 +158,13 @@ def fromsocket(cls, socket):

class tcp(remote):
__doc__ = remote.__doc__
def __init__(self, host, port,
fam = "any", typ = "tcp",
timeout = Timeout.default, ssl=False, sock=None, level = None):
return super(tcp, self).__init__(host, port, fam, typ, timeout, ssl, sock, level)
def __init__(self, host, port, *a, **kw):
return super(tcp, self).__init__(host, port, typ="tcp", *a, **kw)

class udp(remote):
__doc__ = remote.__doc__
def __init__(self, host, port,
fam = "any", typ = "udp",
timeout = Timeout.default, ssl=False, sock=None, level = None):
return super(udp, self).__init__(host, port, fam, typ, timeout, ssl, sock, level)
def __init__(self, host, port, *a, **kw):
return super(udp, self).__init__(host, port, typ="udp", *a, **kw)

class connect(remote):
__doc__ = remote.__doc__
6 changes: 2 additions & 4 deletions pwnlib/tubes/serialtube.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ def __init__(
self, port = None, baudrate = 115200,
convert_newlines = True,
bytesize = 8, parity='N', stopbits=1, xonxoff = False,
rtscts = False, dsrdtr = False,
timeout = Timeout.default,
level = None):
super(serialtube, self).__init__(timeout, level = level)
rtscts = False, dsrdtr = False):
super(serialtube, self).__init__(*a, **kw)

if port is None:
if platform.system() == 'Darwin':
Expand Down
4 changes: 2 additions & 2 deletions pwnlib/tubes/sock.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
class sock(tube):
"""Methods available exclusively to sockets."""

def __init__(self, timeout, level = None):
super(sock, self).__init__(timeout, level = level)
def __init__(self, *args, **kwargs):
super(sock, self).__init__(*args, **kwargs)
self.closed = {"recv": False, "send": False}

# Overwritten for better usability
Expand Down
26 changes: 14 additions & 12 deletions pwnlib/tubes/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@ class ssh_channel(sock):
#: Only valid when instantiated through :meth:`ssh.process`
pid = None

#: Executable of the process
#: Executable of the procesks
#: Only valid when instantiated through :meth:`ssh.process`
executable = None

#: Arguments passed to the process
#: Only valid when instantiated through :meth:`ssh.process`
argv = None

def __init__(self, parent, process = None, tty = False, wd = None, env = None, timeout = Timeout.default, level = 0, raw = True):
super(ssh_channel, self).__init__(timeout, level=level)
def __init__(self, parent, process = None, tty = False, wd = None, env = None, raw = True, *args, **kwargs):
super(ssh_channel, self).__init__(*args, **kwargs)

# keep the parent from being garbage collected in some cases
self.parent = parent
Expand Down Expand Up @@ -366,8 +366,8 @@ def libc(self):
return e

class ssh_connecter(sock):
def __init__(self, parent, host, port, timeout = Timeout.default, level = None):
super(ssh_connecter, self).__init__(timeout, level = level)
def __init__(self, parent, host, port, *a, **kw):
super(ssh_connecter, self).__init__(*a, **kw)

# keep the parent from being garbage collected in some cases
self.parent = parent
Expand Down Expand Up @@ -398,8 +398,8 @@ def _close_msg(self):


class ssh_listener(sock):
def __init__(self, parent, bind_address, port, timeout = Timeout.default, level = None):
super(ssh_listener, self).__init__(timeout, level = level)
def __init__(self, parent, bind_address, port, *a, **kw):
super(ssh_listener, self).__init__(*a, **kw)

# keep the parent from being garbage collected in some cases
self.parent = parent
Expand Down Expand Up @@ -478,8 +478,7 @@ class ssh(Timeout, Logger):

def __init__(self, user, host, port = 22, password = None, key = None,
keyfile = None, proxy_command = None, proxy_sock = None,
timeout = Timeout.default, level = None, cache = True,
ssh_agent = False):
level = None, cache = True, ssh_agent = False, *a, **kw):
"""Creates a new ssh connection.
Arguments:
Expand All @@ -498,7 +497,7 @@ def __init__(self, user, host, port = 22, password = None, key = None,
NOTE: The proxy_command and proxy_sock arguments is only available if a
fairly new version of paramiko is used."""
super(ssh, self).__init__(timeout)
super(ssh, self).__init__(*a, **kw)

Logger.__init__(self)
if level is not None:
Expand Down Expand Up @@ -952,7 +951,7 @@ def which(self, program):

return result

def system(self, process, tty = True, wd = None, env = None, timeout = Timeout.default, raw = True):
def system(self, process, tty = True, wd = None, env = None, timeout = None, raw = True):
r"""system(process, tty = True, wd = None, env = None, timeout = Timeout.default, raw = True) -> ssh_channel
Open a new channel with a specific process inside. If `tty` is True,
Expand All @@ -978,7 +977,10 @@ def system(self, process, tty = True, wd = None, env = None, timeout = Timeout.d
if wd is None:
wd = self.cwd

return ssh_channel(self, process, tty, wd, env, timeout, level = self.level, raw = raw)
if timeout is None:
timeout = self.timeout

return ssh_channel(self, process, tty, wd, env, timeout = timeout, level = self.level, raw = raw)

#: Backward compatibility. Use :meth:`system`
run = system
Expand Down
12 changes: 7 additions & 5 deletions pwnlib/tubes/tube.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ class tube(Timeout, Logger):
#: and related functions.
newline = '\n'

def __init__(self, timeout = default, level = None):
def __init__(self, timeout = default, level = None, *a, **kw):
super(tube, self).__init__(timeout)

Logger.__init__(self, None)
if level is not None:
self.setLevel(level)

self.buffer = Buffer()
self.buffer = Buffer(*a, **kw)
atexit.register(self.close)

# Functions based on functions from subclasses
def recv(self, numb = 4096, timeout = default):
def recv(self, numb = None, timeout = default):
r"""recv(numb = 4096, timeout = default) -> str
Receives up to `numb` bytes of data from the tube, and returns
Expand Down Expand Up @@ -72,6 +72,7 @@ def recv(self, numb = 4096, timeout = default):
[...] Received 0xc bytes:
'Hello, world'
"""
numb = self.buffer.get_fill_size(numb)
return self._recv(numb, timeout) or ''

def unrecv(self, data):
Expand Down Expand Up @@ -122,7 +123,7 @@ def _fillbuffer(self, timeout = default):
data = ''

with self.local(timeout):
data = self.recv_raw(4096)
data = self.recv_raw(self.buffer.get_fill_size())

if data and self.isEnabledFor(logging.DEBUG):
self.debug('Received %#x bytes:' % len(data))
Expand All @@ -141,12 +142,13 @@ def _fillbuffer(self, timeout = default):
return data


def _recv(self, numb = 4096, timeout = default):
def _recv(self, numb = None, timeout = default):
"""_recv(numb = 4096, timeout = default) -> str
Receives one chunk of from the internal buffer or from the OS if the
buffer is empty.
"""
numb = self.buffer.get_fill_size(numb)
data = ''

# No buffered data, could not put anything in the buffer
Expand Down

0 comments on commit 5d9792f

Please sign in to comment.