Skip to content

Commit

Permalink
Add Hexdump Command (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephen-f0 authored Aug 15, 2024
1 parent 8bccf5c commit 42015fc
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 3 deletions.
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ set Set LLEF color settings
Supported colors: BLUE, GREEN, YELLOW, RED, PINK, CYAN, GREY

| Color |
|-------------------------------|
| ----------------------------- |
| register_color |
| modified_register_color |
| code_color |
Expand All @@ -109,7 +109,38 @@ Supported colors: BLUE, GREEN, YELLOW, RED, PINK, CYAN, GREY
| dereferenced_value_color |
| dereferenced_register_color |
| frame_argument_name_color |

| read_memory_address_color |

#### Hexdump
View memory contents with:
```
(lldb) hexdump type address [--size SIZE] [--reverse]
```
e.g.
```
(lldb) hexdump byte 0x7fffffffecc8 --size 0x38
0x7fffffffecc8 3d 2f 75 73 72 2f 6c 6f 63 61 6c 2f 73 62 69 6e =/usr/local/sbin
0x7fffffffecd8 3a 2f 75 73 72 2f 6c 6f 63 61 6c 2f 62 69 6e 3a :/usr/local/bin:
0x7fffffffece8 2f 75 73 72 2f 73 62 69 6e 3a 2f 75 73 72 2f 62 /usr/sbin:/usr/b
0x7fffffffecf8 69 6e 3a 2f 73 62 69 6e in:/sbin
(lldb) hexdump word 0x7fffffffecc8 --reverse
0x7fffffffece6│+001e: 0x4654
0x7fffffffece4│+001c: 0x4361
0x7fffffffece2│+001a: 0x746f
0x7fffffffece0│+0018: 0x4e23
0x7fffffffecde│+0016: 0x3f73
0x7fffffffecdc│+0014: 0x6968
0x7fffffffecda│+0012: 0x742d
0x7fffffffecd8│+0010: 0x6564
0x7fffffffecd6│+000e: 0x6f63
0x7fffffffecd4│+000c: 0x6564
0x7fffffffecd2│+000a: 0x2d75
0x7fffffffecd0│+0008: 0x6f79
0x7fffffffecce│+0006: 0x2d64
0x7fffffffeccc│+0004: 0x6964
0x7fffffffecca│+0002: 0x2d79
0x7fffffffecc8│+0000: 0x6857
```

#### Context

Expand Down
102 changes: 102 additions & 0 deletions commands/hexdump.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""Hexdump command class."""
import argparse
import shlex
from typing import Any, Dict

from lldb import SBCommandReturnObject, SBDebugger, SBExecutionContext

from commands.base_command import BaseCommand
from common.context_handler import ContextHandler
from common.constants import SIZES


class HexdumpCommand(BaseCommand):
"""Implements the hexdump command"""

program: str = "hexdump"
container = None
context_handler = None

def __init__(self, debugger: SBDebugger, __: Dict[Any, Any]) -> None:
super().__init__()
self.parser = self.get_command_parser()
self.context_handler = ContextHandler(debugger)

@classmethod
def get_command_parser(cls) -> argparse.ArgumentParser:
"""Get the command parser."""
parser = argparse.ArgumentParser()
parser.add_argument(
"type",
choices=["qword", "dword", "word", "byte"],
default="byte",
help="The format for presenting data"
)
parser.add_argument(
"--reverse",
action="store_true",
help="The direction of output lines. Low to high by default"
)
parser.add_argument("--size", type=positive_int, default=16, help="The number of qword/dword/word/bytes to display")
parser.add_argument(
"address",
type=hex_int,
help="A value/address/symbol used as the location to print the hexdump from"
)
return parser

@staticmethod
def get_short_help() -> str:
"""Return a short help message"""
return "Usage: hexdump (qword|dword|word|byte) [-h] [--reverse] [--size SIZE] [address]"

@staticmethod
def get_long_help() -> str:
"""Return a longer help message"""
return HexdumpCommand.get_command_parser().format_help()

def __call__(
self,
debugger: SBDebugger,
command: str,
exe_ctx: SBExecutionContext,
result: SBCommandReturnObject,
) -> None:
"""Handles the invocation of the hexdump command"""
args = self.parser.parse_args(shlex.split(command))

divisions = SIZES[args.type.upper()].value
address = args.address
size = args.size

self.context_handler.refresh(exe_ctx)

start = (size-1) * divisions if args.reverse else 0
end = -divisions if args.reverse else size * divisions
step = -divisions if args.reverse else divisions

if divisions == SIZES.BYTE.value:
if args.reverse:
self.context_handler.print_bytes(address + size - (size % 16), size % 16)
start = size - (size % 16) - 16
end = -1
step = -16

for i in range(start, end, -16 if args.reverse else 16):
self.context_handler.print_bytes(address + i, min(16, size - abs(start - i)))
else:
for i in range(start, end, step):
self.context_handler.print_memory_address(address + i, i, divisions)


def hex_int(x):
"""A converter for input arguments in different bases to ints"""
return int(x, 0)


def positive_int(x):
"""A converter for input arguments in different bases to positive ints"""
x = int(x, 0)
if x <= 0:
raise argparse.ArgumentTypeError("Must be positive")
return x
4 changes: 4 additions & 0 deletions common/color_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ def dereferenced_register_color(self):
@property
def frame_argument_name_color(self):
return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "frame_argument_name_color", fallback="YELLOW").upper()

@property
def read_memory_address_color(self):
return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "read_memory_address_color", fallback="CYAN").upper()

def __init__(self):
self.supported_colors = [color.name for color in TERM_COLORS]
Expand Down
9 changes: 9 additions & 0 deletions common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,12 @@ class ALIGN(Enum):
LEFT = 1
CENTRE = 2
RIGHT = 3


class SIZES(Enum):
"""Size of data types"""

QWORD = 8
DWORD = 4
WORD = 2
BYTE = 1
50 changes: 50 additions & 0 deletions common/context_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os

from typing import Dict, Type, Optional
from string import printable

from lldb import (
SBAddress,
Expand Down Expand Up @@ -153,6 +154,55 @@ def print_stack_addr(self, addr: SBValue, offset: int) -> None:
)
output_line(line)

def print_memory_address(self, addr: int, offset: int, size: int) -> None:
"""Print a line containing information about @size bytes at @addr displaying @offset"""
# Add address to line
line = (
f"{TERM_COLORS[self.color_settings.read_memory_address_color].value}{hex(addr)}"
+ f"{TERM_COLORS.ENDC.value}{GLYPHS.VERTICAL_LINE.value}"
)
# Add offset to line
line += f"+{offset:04x}: "

# Add value to line
err = SBError()
memory_value = int.from_bytes(self.process.ReadMemory(addr, size, err), 'little')
if err.Success():
line += f"0x{memory_value:0{size * 2}x}"
else:
line += str(err)

output_line(line)

def print_bytes(self, addr: int, size: int) -> None:
"""Print a line containing information about @size individual bytes at @addr"""
if size > 0:
# Add address to line
line = (
f"{TERM_COLORS[self.color_settings.read_memory_address_color].value}{hex(addr)}"
+ f"{TERM_COLORS.ENDC.value} "
)

# Add value to line
err = SBError()
memory_value: bytes = self.process.ReadMemory(addr, size, err)
if err.Success():
line += f"{memory_value.hex(' '):47} "

# Add characters to line
characters = ""
for byte in memory_value:
if chr(byte) in printable.strip():
characters += chr(byte)
else:
characters += "."

line += characters
else:
line += str(err)

output_line(line)

def print_register(self, register: SBValue) -> None:
"""Print details of a @register"""
reg_name = register.GetName()
Expand Down
4 changes: 3 additions & 1 deletion llef.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from commands.context import ContextCommand
from commands.settings import SettingsCommand
from commands.color_settings import ColorSettingsCommand
from commands.hexdump import HexdumpCommand
from handlers.stop_hook import StopHookHandler


Expand All @@ -34,7 +35,8 @@ def __lldb_init_module(debugger: SBDebugger, _: Dict[Any, Any]) -> None:
PatternSearchCommand,
ContextCommand,
SettingsCommand,
ColorSettingsCommand
ColorSettingsCommand,
HexdumpCommand
]

handlers = [StopHookHandler]
Expand Down

0 comments on commit 42015fc

Please sign in to comment.