#!/usr/bin/env python
# encoding: utf-8
import cmd
import collections
import os
import sys
import pfp.fields as fields
import pfp.errors as errors
import pfp.utils as utils
[docs]class PfpDbg(cmd.Cmd, object):
"""The pfp debugger cmd.Cmd class"""
prompt = "pfp> "
def __init__(self, interp):
"""Create the pfp debugger
:scope: The scope in which to start debugging
"""
super(PfpDbg, self).__init__()
self._interp = interp
self._do_print_from_last_cmd = False
def _update_prompt(self):
if fields.NumberBase.endian == fields.BIG_ENDIAN:
self.prompt = "BE pfp> "
else:
self.prompt = "LE pfp> "
def update(self, ctxt, scope):
self._ctxt = ctxt
self._scope = scope
[docs] def preloop(self):
self._update_prompt()
self.print_lines()
[docs] def postcmd(self, stop, line):
self._update_prompt()
return stop
[docs] def default(self, line):
cmd, arg, line = self.parseline(line)
funcs = [
getattr(self, n)
for n in self.get_names()
if n.startswith("do_" + cmd)
]
if len(funcs) > 1:
for func in funcs:
print(func)
elif len(funcs) == 1:
return funcs[0](arg)
elif len(funcs) == 0:
return self.do_eval(line)
[docs] def do_peek(self, args):
"""Peek at the next 16 bytes in the stream::
Example:
The peek command will display the next 16 hex bytes in the input
stream::
pfp> peek
89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 .PNG........IHDR
"""
s = self._interp._stream
# make a copy of it
pos = s.tell()
saved_bits = collections.deque(s._bits)
data = s.read(0x10)
s.seek(pos, 0)
s._bits = saved_bits
parts = [
"{:02x}".format(ord(data[x : x + 1])) for x in range(len(data))
]
if len(parts) != 0x10:
parts += [" "] * (0x10 - len(parts))
hex_line = " ".join(parts)
res = utils.binary("")
for x in range(len(data)):
char = data[x : x + 1]
val = ord(char)
if 0x20 <= val <= 0x7E:
res += char
else:
res += utils.binary(".")
if len(res) < 0x10:
res += utils.binary(" " * (0x10 - len(res)))
res = "{} {}".format(hex_line, utils.string(res))
if len(saved_bits) > 0:
reverse_bits = reversed(list(saved_bits))
print("bits: {}".format(" ".join(str(x) for x in reverse_bits)))
print(res)
[docs] def do_next(self, args):
"""Step over the next statement
"""
self._do_print_from_last_cmd = True
self._interp.step_over()
return True
[docs] def do_step(self, args):
"""Step INTO the next statement
"""
self._do_print_from_last_cmd = True
self._interp.step_into()
return True
[docs] def do_s(self, args):
"""Step into the next statement
"""
return self.do_step(args)
[docs] def do_continue(self, args):
"""Continue the interpreter
"""
self._do_print_from_last_cmd = True
self._interp.cont()
return True
[docs] def do_eval(self, args):
"""Eval the user-supplied statement. Note that you can do anything with
this command that you can do in a template.
The resulting value of your statement will be displayed.
"""
try:
res = self._interp.eval(args)
if res is not None:
if hasattr(res, "_pfp__show"):
print(res._pfp__show())
else:
print(repr(res))
except errors.UnresolvedID as e:
print("ERROR: " + e.message)
except Exception as e:
raise
print("ERROR: " + e.message)
return False
[docs] def do_show(self, args):
"""Show the current structure of __root (no args),
or show the result of the expression (something that can be eval'd).
"""
args = args.strip()
to_show = self._interp._root
if args != "":
try:
to_show = self._interp.eval(args)
except Exception as e:
print("ERROR: " + e.message)
return False
if hasattr(to_show, "_pfp__show"):
print(to_show._pfp__show())
else:
print(repr(to_show))
[docs] def do_x(self, args):
pass
do_x = do_show
[docs] def do_list(self, args):
"""List the current location in the template
"""
self.print_lines()
return False
[docs] def do_quit(self, args):
"""The quit command
"""
self._interp.set_break(self._interp.BREAK_NONE)
return True
[docs] def do_EOF(self, args):
"""The eof command
"""
return True
# ---------------------
def print_lines(self):
curr_line, lines = self._interp.get_curr_lines()
for line_no, line in lines:
prefix = " "
line_no += 1
if line_no == curr_line:
prefix = "--> "
print(
"{}{:3d} {}".format(
prefix, line_no, line.replace("\t", " ")
)
)