Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
peace-maker committed Feb 2, 2025
1 parent 798d867 commit f530b0d
Show file tree
Hide file tree
Showing 12 changed files with 468 additions and 99 deletions.
16 changes: 16 additions & 0 deletions bof.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// cl /GS- /Febof64.exe /Fdbof64.pdb /DEBUG:FULL bof.c /link /DYNAMICBASE:NO /DEBUG:FULL
#include <stdio.h>

void win(void) {
puts("You win!\n");
exit(0);
}

int main(int argc, char* argv[]) {
if (argc == 1337) {
win();
}
char buf[32];
gets(buf);
return 0;
}
109 changes: 109 additions & 0 deletions pwnlib/binary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
Common interface for accessing executable files.
This includes :class:`pwnlib.elf.ELF` and :class:`pwnlib.pe.PE`.
"""

from pwnlib.asm import disasm
from pwnlib.tubes.process import process

class dotdict(dict):
"""Wrapper to allow dotted access to dictionary elements.
Is a real :class:`dict` object, but also serves up keys as attributes
when reading attributes.
Supports recursive instantiation for keys which contain dots.
Example:
>>> x = pwnlib.elf.elf.dotdict()
>>> isinstance(x, dict)
True
>>> x['foo'] = 3
>>> x.foo
3
>>> x['bar.baz'] = 4
>>> x.bar.baz
4
"""
def __missing__(self, name):
if isinstance(name, (bytes, bytearray)):
name = packing._decode(name)
return self[name]
raise KeyError(name)

def __getattr__(self, name):
if name in self:
return self[name]

name_dot = name + '.'
name_len = len(name_dot)
subkeys = {k[name_len:]: self[k] for k in self if k.startswith(name_dot)}

if subkeys:
return dotdict(subkeys)
raise AttributeError(name)

class Binary:
@staticmethod
def from_path(path, *k, **kw):
"""
Returns an ELF or an PE object depending on the file type.
"""
# Avoid cyclic imports :(
from pwnlib.elf import ELF
from pwnlib.pe import PE
try:
# Try loading it as an ELF first.
return ELF(path, *k, **kw)
except:
return PE(path, *k, **kw)

def _get_machine_arch(self):
raise NotImplementedError

@property
def entry(self):
raise NotImplementedError
entrypoint = entry
start = entry

@property
def address(self):
raise NotImplementedError

@address.setter
def address(self, new):
raise NotImplementedError

def process(self, argv=[], *a, **kw):
"""process(argv=[], *a, **kw) -> process
Execute the binary with :class:`.process`. Note that ``argv``
is a list of arguments, and should not include ``argv[0]``.
Arguments:
argv(list): List of arguments to the binary
*args: Extra arguments to :class:`.process`
**kwargs: Extra arguments to :class:`.process`
Returns:
:class:`.process`
"""

return process([self.path] + argv, *a, **kw)

def disasm(self, address, n_bytes):
"""disasm(address, n_bytes) -> str
Returns a string of disassembled instructions at
the specified virtual memory address"""
arch = self.arch
if self.arch == 'arm' and address & 1:
arch = 'thumb'
address -= 1

return disasm(self.read(address, n_bytes), vma=address, arch=arch, endian=self.endian)

def search(self, needle, writable = False, executable = False):
raise NotImplementedError
6 changes: 3 additions & 3 deletions pwnlib/context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,10 +892,10 @@ def binary(self, binary):
"""
# Cyclic imports... sorry Idolf.
from pwnlib.elf import ELF
from pwnlib.binary import Binary

if not isinstance(binary, ELF):
binary = ELF(binary)
if not isinstance(binary, Binary):
binary = Binary.from_path(binary)

self.arch = binary.arch
self.bits = binary.bits
Expand Down
45 changes: 4 additions & 41 deletions pwnlib/elf/elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
from pwnlib import adb
from pwnlib import qemu
from pwnlib.asm import *
from pwnlib.binary import Binary, dotdict
from pwnlib.context import LocalContext
from pwnlib.context import context
from pwnlib.elf.config import kernel_configuration
Expand Down Expand Up @@ -138,45 +139,7 @@ def load(*args, **kwargs):
"""Compatibility wrapper for pwntools v1"""
return ELF(*args, **kwargs)

class dotdict(dict):
"""Wrapper to allow dotted access to dictionary elements.
Is a real :class:`dict` object, but also serves up keys as attributes
when reading attributes.
Supports recursive instantiation for keys which contain dots.
Example:
>>> x = pwnlib.elf.elf.dotdict()
>>> isinstance(x, dict)
True
>>> x['foo'] = 3
>>> x.foo
3
>>> x['bar.baz'] = 4
>>> x.bar.baz
4
"""
def __missing__(self, name):
if isinstance(name, (bytes, bytearray)):
name = packing._decode(name)
return self[name]
raise KeyError(name)

def __getattr__(self, name):
if name in self:
return self[name]

name_dot = name + '.'
name_len = len(name_dot)
subkeys = {k[name_len:]: self[k] for k in self if k.startswith(name_dot)}

if subkeys:
return dotdict(subkeys)
raise AttributeError(name)

class ELF(ELFFile):
class ELF(ELFFile, Binary):
"""Encapsulates information about an ELF file.
Example:
Expand Down Expand Up @@ -265,7 +228,7 @@ def __init__(self, path, checksec=True):
#: :class:`str`: Architecture of the file (e.g. ``'i386'``, ``'arm'``).
#:
#: See: :attr:`.ContextType.arch`
self.arch = self.get_machine_arch()
self.arch = self._get_machine_arch()
if isinstance(self.arch, (bytes, six.text_type)):
self.arch = self.arch.lower()

Expand Down Expand Up @@ -467,7 +430,7 @@ def _describe(self, *a, **kw):
self.checksec(*a, **kw)
)

def get_machine_arch(self):
def _get_machine_arch(self):
return {
('EM_X86_64', 64): 'amd64',
('EM_X86_64', 32): 'amd64', # x32 ABI
Expand Down
45 changes: 41 additions & 4 deletions pwnlib/gdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -931,8 +931,6 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
target: The target to attach to.
gdbscript(:obj:`str` or :obj:`file`): GDB script to run after attaching.
exe(str): The path of the target binary.
arch(str): Architechture of the target binary. If `exe` known GDB will
detect the architechture automatically (if it is supported).
gdb_args(list): List of additional arguments to pass to GDB.
sysroot(str): Set an alternate system root. The system root is used to
load absolute shared library symbol files. This is useful to instruct
Expand Down Expand Up @@ -1335,7 +1333,46 @@ def preexec_fn():
return gdb_pid, Gdb(conn)


def ssh_gdb(ssh, argv, gdbscript = None, arch = None, **kwargs):
def ssh_gdb(ssh, argv, gdbscript = None, **kwargs):
r"""ssh_gdb(ssh, argv, gdbscript = None, **kwargs) -> ssh_process
Start a remote process via SSH and attach to it with the local GDB
using a reverse SSH tunnel.
This starts a ``gdbserver`` on the remote end, and connects to it
using the local GDB. This is in contrast to :func:`attach` with
a :class:`.ssh_channel` which uses the GDB on the remote end.
Arguments:
ssh(pwnlib.tubes.ssh.ssh): SSH connection to use for the process
argv(list): Program and arguments to run
gdbscript(str): GDB script to run after attaching
**kwargs: Additional arguments to :func:`pwnlib.tubes.ssh.ssh.process`
Returns:
A :class:`pwnlib.tubes.ssh.process` object
Example:
>>> ssh_io = ssh('travis', 'example.pwnme', password='demopass')
>>> io = ssh_gdb(ssh_io, ['/bin/bash'], gdbscript='''
... tbreak main
... commands
... call puts("Hello from remote debugger!")
... detach
... quit
... end
... continue
... ''')
>>> io.recvline_contains(b'Hello')
b'Hello from remote debugger!'
>>> io.sendline(b'echo Hello from bash && exit')
>>> io.recvline_contains(b'Hello')
b'Hello from bash'
>>> io.close()
>>> ssh_io.close()
"""

if not isinstance(argv, (list, tuple)):
argv = [argv]

Expand All @@ -1359,7 +1396,7 @@ def ssh_gdb(ssh, argv, gdbscript = None, arch = None, **kwargs):
l = tubes.listen.listen(0)
forwardport = l.lport

attach(('127.0.0.1', forwardport), gdbscript, local_exe, arch, ssh=ssh)
attach(('127.0.0.1', forwardport), gdbscript, local_exe, ssh=ssh)
l.wait_for_connection().connect_both(ssh.connect_remote('127.0.0.1', gdbport))
return c

Expand Down
Loading

0 comments on commit f530b0d

Please sign in to comment.