-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
===== | ||
Dwarf | ||
===== | ||
|
||
Dwarf | ||
===== | ||
|
||
.. autoclass:: revenge.plugins.dwarf.Dwarf | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
===== | ||
DWARF | ||
===== | ||
|
||
DWARF is a format for debugging info relating to ELF files. Standard | ||
compilations of binaries do not contain DWARF info. However, when you compile | ||
binaries with this info (generally with the `-g` flag), much more useful | ||
inforamtion is available. This plugin attempts to expose that information. | ||
|
||
General Interaction | ||
=================== | ||
|
||
General interaction with the DWARF plugin is via the `modules`. For instance: | ||
|
||
.. code-block:: python | ||
bin = process.modules['bin'] | ||
dwarf = bin.dwarf | ||
Functions | ||
========= | ||
|
||
Functions are enumerated and exposed via the | ||
:attr:`~revenge.plugins.dwarf.Dwarf.functions` property. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
|
||
from revenge.plugins.dwarf import Dwarf as DwarfBase | ||
|
||
class Dwarf(DwarfBase): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
from .dwarf import Dwarf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
|
||
import logging | ||
|
||
from prettytable import PrettyTable | ||
from ... import common | ||
from .. import Plugin | ||
|
||
class Dwarf(Plugin): | ||
_MODULE_PLUGIN_REGISTERED = False | ||
|
||
def __init__(self, process, module=None): | ||
"""Lookup Dwarf debugging information from the file. | ||
Examples: | ||
.. code-block:: python | ||
dwarf = process.modules['*libc'].dwarf | ||
# Show all known function names and their address and size | ||
print(dwarf.functions) | ||
# Print the first instruction block in main | ||
print(dwarf.functions[b'main'].instruction_block) | ||
""" | ||
self._process = process | ||
self._module = module | ||
|
||
# Register this in modules | ||
if not Dwarf._MODULE_PLUGIN_REGISTERED: | ||
self._process.modules._register_plugin(Dwarf._modules_plugin, "dwarf") | ||
Dwarf._MODULE_PLUGIN_REGISTERED = True | ||
|
||
if self._dwarffile is not None: | ||
self.__init_functions() | ||
|
||
def __init_functions(self): | ||
|
||
for CU in self._dwarffile.iter_CUs(): | ||
for DIE in CU.iter_DIEs(): | ||
try: | ||
if DIE.tag == 'DW_TAG_subprogram': | ||
lowpc = DIE.attributes['DW_AT_low_pc'].value | ||
|
||
# DWARF v4 in section 2.17 describes how to interpret the | ||
# DW_AT_high_pc attribute based on the class of its form. | ||
# For class 'address' it's taken as an absolute address | ||
# (similarly to DW_AT_low_pc); for class 'constant', it's | ||
# an offset from DW_AT_low_pc. | ||
highpc_attr = DIE.attributes['DW_AT_high_pc'] | ||
highpc_attr_class = describe_form_class(highpc_attr.form) | ||
if highpc_attr_class == 'address': | ||
highpc = highpc_attr.value | ||
elif highpc_attr_class == 'constant': | ||
highpc = lowpc + highpc_attr.value | ||
else: | ||
print('Error: invalid DW_AT_high_pc class:', | ||
highpc_attr_class) | ||
continue | ||
|
||
self.functions[DIE.attributes['DW_AT_name'].value] = self._process.memory[self._module.base + lowpc - self.base_address : self._module.base + highpc - self.base_address] | ||
except KeyError: | ||
continue | ||
|
||
@classmethod | ||
def _modules_plugin(klass, module): | ||
self = klass(module._process, module) | ||
|
||
# ELF parsing error | ||
if self._elffile is None: | ||
return | ||
|
||
# No point in having Dwarf object with no dwarf... | ||
if not self._elffile.has_dwarf_info(): | ||
return | ||
|
||
return self | ||
|
||
@property | ||
def _elffile(self): | ||
try: | ||
return self.__elffile | ||
except AttributeError: | ||
if self._module is None: | ||
self.__elffile = None | ||
else: | ||
try: | ||
self.__elffile = ELFFile(common.load_file(self._process, self._module.path)) | ||
except elftools.common.exceptions.ELFError: | ||
self.__elffile = None | ||
|
||
return self.__elffile | ||
|
||
@property | ||
def _dwarffile(self): | ||
try: | ||
return self.__dwarffile | ||
except AttributeError: | ||
if self._elffile is None: | ||
self.__dwarffile = None | ||
else: | ||
if not self._elffile.has_dwarf_info(): | ||
self.__dwarffile = None | ||
else: | ||
self.__dwarffile = self._elffile.get_dwarf_info() | ||
|
||
return self.__dwarffile | ||
|
||
@property | ||
def has_debug_info(self): | ||
"""bool: Does this module actually have debugging info?""" | ||
try: | ||
return self.__has_debug_info | ||
except AttributeError: | ||
if self._dwarffile is not None: | ||
self.__has_debug_info = self._dwarffile.has_debug_info | ||
else: | ||
self.__has_debug_info = False | ||
|
||
return self.__has_debug_info | ||
|
||
@property | ||
def _is_valid(self): | ||
# Not bothering to load this under process | ||
return False | ||
|
||
@property | ||
def base_address(self): | ||
"""int: What is the binary's defined base address.""" | ||
return next(x.header["p_vaddr"] for x in self._elffile.iter_segments() if x.header['p_type'] == "PT_LOAD" and x.header["p_offset"] == 0) | ||
|
||
@property | ||
def functions(self): | ||
"""dict: Dictionary of function_name -> MemoryBytes.""" | ||
try: | ||
return self.__functions | ||
except AttributeError: | ||
self.__functions = {} | ||
|
||
return self.__functions | ||
|
||
from elftools.elf.elffile import ELFFile | ||
from elftools.common.py3compat import maxint, bytes2str | ||
from elftools.dwarf.descriptions import describe_form_class | ||
import elftools.common.exceptions | ||
|
||
# Doc fixup | ||
Dwarf.__doc__ = Dwarf.__init__.__doc__ | ||
#Dwarf._modules_plugin.__doc__ = Dwarf.__init__.__doc__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#include <stdio.h> | ||
#include <string.h> | ||
|
||
void func1() { | ||
puts("func1"); | ||
} | ||
|
||
int func2(char *thing) { | ||
puts(thing); | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
|
||
if ( argc < 2 ) { | ||
puts("Try more args."); | ||
} else { | ||
if ( ! strcmp( argv[1], "win" ) ) { | ||
puts("Win."); | ||
} | ||
} | ||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
|
||
import logging | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
import os | ||
import pytest | ||
import revenge | ||
types = revenge.types | ||
|
||
here = os.path.dirname(os.path.abspath(__file__)) | ||
bin_location = os.path.join(here, "..", "..", "bins") | ||
|
||
basic_dwarf_x64_path = os.path.join(bin_location, "basic_dwarf_x64") | ||
basic_dwarf_i686_path = os.path.join(bin_location, "basic_dwarf_i686") | ||
basic_dwarf_nopie_i686_path = os.path.join(bin_location, "basic_dwarf_nopie_i686") | ||
|
||
def dwarf_basic(process): | ||
"""Same tests for different archs.""" | ||
basic = process.modules['basic_dwarf*'] | ||
libc = process.modules['*libc*'] | ||
|
||
assert basic.dwarf.has_debug_info == True | ||
assert libc.dwarf.has_debug_info == False | ||
|
||
funcs = ["main", "func1", "func2"] | ||
for func in funcs: | ||
# Check that the dwarf info matches up with what we resolved for symbols | ||
assert basic.dwarf.functions[func.encode()].address == basic.symbols[func].address | ||
|
||
def test_dwarf_x64_basic(): | ||
process = revenge.Process(basic_dwarf_x64_path, resume=False, verbose=False) | ||
dwarf_basic(process) | ||
process.quit() | ||
|
||
def test_dwarf_i686_basic(): | ||
process = revenge.Process(basic_dwarf_i686_path, resume=False, verbose=False) | ||
dwarf_basic(process) | ||
process.quit() | ||
|
||
def test_dwarf_i686_nopie_basic(): | ||
process = revenge.Process(basic_dwarf_nopie_i686_path, resume=False, verbose=False) | ||
dwarf_basic(process) | ||
process.quit() |