Repository: pwndbg/pwndbg

Issue:
Exception with ELF MIPSII
Hey, when I set a breakpoint in an ELF MIPSII using remote debugging, pwndbg throws an exception (binary is attached):

[bender.gz](https://github.com/pwndbg/pwndbg/files/742558/bender.gz)

pwndbg> file bender_safe 
Reading symbols from bender_safe...(no debugging symbols found)...done.
pwndbg> b validate 
Breakpoint 1 at 0x401c84
pwndbg> c
Continuing.
Traceback (most recent call last):
  File "/home/user/pwndbg/pwndbg/events.py", line 111, in caller
    func()
  File "/home/user/pwndbg/pwndbg/vmmap.py", line 107, in explore_registers
    find(pwndbg.regs[regname])
...
gdb.MemoryError: Cannot access memory at address 0x34400000
Python Exception <class 'gdb.MemoryError'> Cannot access memory at address 0x34400000: 
Traceback (most recent call last):


Discussion:
Is this a remote debugging session?

You probably have to set proper architecture, e.g.:
```
set arch mips
```
you must have the gdb installed with this architecture, or you can try gdb-multiarch.

However I am not sure the pwndbg works with MIPS.
When I have a remotely debuged process and I try to read the local symbols (file path/to/bin) pwndbg doesn't manage it successfully and an exception is thrown.
The issue is that the internal ELF structures declared via `ctypes` parse the data from the ELF files in-memory in Little Endian format, despite the actual contents being Big Endian.

There's some hackery we can probably do with metaclasses or __getattribute__ hooks.
I've added some code to the `dev` branch which prevents all of these error messages.

However, it looks like Capstone's disassembler is broken for this architecture.
Filed aquynh/capstone#849 and added a work-around to `dev`
Looks like most things work.  The branch detection via Unicorn is busted, but that's it.
<img width="1039" alt="screen shot 2017-01-31 at 9 58 40 pm" src="https://cloud.githubusercontent.com/assets/111640/22493831/7983bfae-e800-11e6-917e-665bde7b3523.png">

Merged to `master`
Thanks, this issue is fixed
Is this really fixed?

I still have those memory errors. I launch the binary in one console by:
```
$ ./qemu-mips -g 7777 ./bender_safe
```

And then in another one:
```
[dc@dc:challenge]$ gdb ./bender_safe --quiet 
Loaded 104 commands.  Type pwndbg [filter] for a list.
Reading symbols from ./bender_safe...(no debugging symbols found)...done.
pwndbg> set arch mips
The target architecture is assumed to be mips
pwndbg> target remote localhost:7777
Remote debugging using localhost:7777
Traceback (most recent call last):
  File "/home/dc/installed/pwndbg/pwndbg/events.py", line 120, in caller
    func()
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/stack.py", line 79, in update
    page  = pwndbg.memory.Page(start, stop-start, 6 if not is_executable() else 7, 0, '[stack]')
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/stack.py", line 127, in is_executable
    for phdr in pwndbg.elf.iter_phdrs(ehdr):
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 176, in iter_phdrs
    phnum, phentsize, phdr = get_phdrs(ehdr.address)
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 169, in get_phdrs
    x = (phnum, phentsize, read(Phdr, Elfhdr.address + phoff))
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 59, in read
    data = pwndbg.memory.read(address, size)
  File "/home/dc/installed/pwndbg/pwndbg/memory.py", line 43, in read
    result = gdb.selected_inferior().read_memory(addr, count)
gdb.MemoryError: Cannot access memory at address 0x34400000
Python Exception <class 'gdb.MemoryError'> Cannot access memory at address 0x34400000: 
Traceback (most recent call last):
  File "/home/dc/installed/pwndbg/pwndbg/events.py", line 120, in caller
    func()
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/stack.py", line 127, in is_executable
    for phdr in pwndbg.elf.iter_phdrs(ehdr):
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 176, in iter_phdrs
    phnum, phentsize, phdr = get_phdrs(ehdr.address)
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 169, in get_phdrs
    x = (phnum, phentsize, read(Phdr, Elfhdr.address + phoff))
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 59, in read
    data = pwndbg.memory.read(address, size)
  File "/home/dc/installed/pwndbg/pwndbg/memory.py", line 43, in read
    result = gdb.selected_inferior().read_memory(addr, count)
gdb.MemoryError: Cannot access memory at address 0x34400000
Python Exception <class 'gdb.MemoryError'> Cannot access memory at address 0x34400000: 
Traceback (most recent call last):
  File "/home/dc/installed/pwndbg/pwndbg/events.py", line 120, in caller
    func()
  File "/home/dc/installed/pwndbg/pwndbg/vmmap.py", line 107, in explore_registers
    find(pwndbg.regs[regname])
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/vmmap.py", line 63, in find
    for page in get():
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/vmmap.py", line 47, in get
    else:     pages.extend(info_files())
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/vmmap.py", line 290, in info_files
    pages.extend(pwndbg.elf.map(vaddr, objfile))
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 210, in map
    return map_inner(ei_class, ehdr, objfile)
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 226, in map_inner
    for phdr in iter_phdrs(ehdr):
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 176, in iter_phdrs
    phnum, phentsize, phdr = get_phdrs(ehdr.address)
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 169, in get_phdrs
    x = (phnum, phentsize, read(Phdr, Elfhdr.address + phoff))
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 59, in read
    data = pwndbg.memory.read(address, size)
  File "/home/dc/installed/pwndbg/pwndbg/memory.py", line 43, in read
    result = gdb.selected_inferior().read_memory(addr, count)
gdb.MemoryError: Cannot access memory at address 0x34400000
Python Exception <class 'gdb.MemoryError'> Cannot access memory at address 0x34400000: 
Traceback (most recent call last):
  File "/home/dc/installed/pwndbg/pwndbg/events.py", line 120, in caller
    func()
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/symbol.py", line 244, in add_main_exe_to_symbols
    mmap = pwndbg.vmmap.find(addr)
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/vmmap.py", line 63, in find
    for page in get():
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/vmmap.py", line 47, in get
    else:     pages.extend(info_files())
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/vmmap.py", line 290, in info_files
    pages.extend(pwndbg.elf.map(vaddr, objfile))
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 210, in map
    return map_inner(ei_class, ehdr, objfile)
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 226, in map_inner
    for phdr in iter_phdrs(ehdr):
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 176, in iter_phdrs
    phnum, phentsize, phdr = get_phdrs(ehdr.address)
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 169, in get_phdrs
    x = (phnum, phentsize, read(Phdr, Elfhdr.address + phoff))
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 59, in read
    data = pwndbg.memory.read(address, size)
  File "/home/dc/installed/pwndbg/pwndbg/memory.py", line 43, in read
    result = gdb.selected_inferior().read_memory(addr, count)
gdb.MemoryError: Cannot access memory at address 0x34400000
Python Exception <class 'gdb.MemoryError'> Cannot access memory at address 0x34400000: 
0x00401160 in __start ()
Traceback (most recent call last):
  File "/home/dc/installed/pwndbg/pwndbg/events.py", line 120, in caller
    func()
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/stack.py", line 79, in update
    page  = pwndbg.memory.Page(start, stop-start, 6 if not is_executable() else 7, 0, '[stack]')
  File "/home/dc/installed/pwndbg/pwndbg/memoize.py", line 47, in __call__
    value = self.func(*args, **kwargs)
  File "/home/dc/installed/pwndbg/pwndbg/stack.py", line 127, in is_executable
    for phdr in pwndbg.elf.iter_phdrs(ehdr):
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 176, in iter_phdrs
    phnum, phentsize, phdr = get_phdrs(ehdr.address)
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 169, in get_phdrs
    x = (phnum, phentsize, read(Phdr, Elfhdr.address + phoff))
  File "/home/dc/installed/pwndbg/pwndbg/elf.py", line 59, in read
    data = pwndbg.memory.read(address, size)
  File "/home/dc/installed/pwndbg/pwndbg/memory.py", line 43, in read
    result = gdb.selected_inferior().read_memory(addr, count)
gdb.MemoryError: Cannot access memory at address 0x34400000
Python Exception <class 'gdb.MemoryError'> Cannot access memory at address 0x34400000: 
```

I am using Arch Linux if that matters, also:
```
[dc@dc:pwndbg|master]$ gdb --version
GNU gdb (GDB) 7.12
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
[dc@dc:pwndbg|master]$ git show HEAD
commit 064a2c1ea84a65c30592f7d6ef8f693097454603
Author: Zach Riggle <riggle@google.com>
Date:   Mon Mar 6 11:37:50 2017 -0600

    Add docs requirements file
```
Does adfa644 work for you, @disconnect3d
In your GDB script you didn't "set endian big"
The commit adfa644 works for me  (even without `set endian big`).
The latest commit on master doesn't work even when I set endianness.

I have made a `git bisect` and found out the "first bad commit" is 8110dacacbc69ba14e22aa2a49c7bc22b5348fe2.

Reverting this commit on master didn't fix the issue. I tried looking more, by bisecting with temporary modifications (reverting and reseting the "bad commit") and found out the problem stays there just for the next commit - 9d19daaea3815066ea0b4fa83bfb30c8bf5f260f.
I have spent some time debugging the issue and here is what is wrong with current approach:

In [elf.py the `update()`](https://github.com/pwndbg/pwndbg/blob/master/pwndbg/elf.py#L42) sets `pwndbg.elf.Ehdr` to `pwndbg.elftypes.ElfXX_Ehdr`.
The `pwndb.elftypes.ElfXX_Ehdr` inherits from `pwndbg.ctypes.Structure`.

When initializing pwndbg, the `pwndbg.arch.endian` is set to `little` and so the `pwndbg.ctypes` sets its `Structure` attribute to `LittleEndianStructure`. Because of that the `pwndbg.elftypes.ElfXX_Ehdr` already inherits from `Structure` which is `LittleEndianStructure`.

There is the [`update` method in `pwndbg.ctypes`](https://github.com/pwndbg/pwndbg/blob/master/pwndbg/ctypes.py#L25) which is meant to set proper endianness structure in particular times but it isn't really helpful at all. It changes the `pwndbg.ctypes.Structure` to proper value, but the  `pwndbg.elftypes.ElfXX_Ehdr` has already been set and inherits from the old `pwndbg.ctypes.Structure` which is `LittleEndianStructure`.

Setting endianness to `big` in .gdbinit (`set endian big`) before initializing pwndbg doesn't help because the `pwndbg.arch.endian` isn't really a gdb parameter (or at least I believe so) and [is set to `little` in pwndbg/arch.py](https://github.com/pwndbg/pwndbg/blob/master/pwndbg/arch.py#L23).

Setting `endian = 'big'` in pwndbg/arch.py seems to be a workaround for the problem.

---
The easiest/fastest(?) solution that comes to my mind is to have different `pwndbg.elftypes.ElfXX_Ehdr` classes for different endiannesses and make [this dispatching handling the endianness as well](https://github.com/pwndbg/pwndbg/blob/master/pwndbg/elf.py#L42).
Thanks for tracking this down, @disconnect3d.  I presume that some of the changes to `pwndbg.ctypes` might have caused this regression.  At some point in time we had code that would re-load that entire module at run-time, so that the structures were re-created with the correct endianness -- but it looks like that was removed in favor of a different update mechanism.  Not entirely sure why I did this.
Hmm, yeah, looks like the update was removed.

https://github.com/pwndbg/pwndbg/commit/8351b3b39d930b8c05a1be6ba512eaca47cc225b#diff-87ea9ea3163bfe764f7e855dd4e8f1feL319

Resolving patch:
diff --git a/pwndbg/arch.py b/pwndbg/arch.py
--- a/pwndbg/arch.py
+++ b/pwndbg/arch.py
@@ -5,7 +5,6 @@
 from __future__ import print_function
 from __future__ import unicode_literals
 
-import collections
 import struct
 import sys
 
@@ -25,12 +24,14 @@
 fmt     = '=I'
 native_endian = str(sys.byteorder)
 
+
 def fix_arch(arch):
     arches = ['x86-64', 'i386', 'mips', 'powerpc', 'sparc', 'arm', 'aarch64', arch]
     return next(a for a in arches if a in arch)
 
+
 @pwndbg.events.start
-@pwndbg.events.stop
+@pwndbg.events.new_objfile
 def update():
     m = sys.modules[__name__]
 
@@ -67,14 +68,18 @@ def update():
     else:
         m.qemu = m.current
 
+
 def pack(integer):
     return struct.pack(fmt, integer & ptrmask)
 
+
 def unpack(data):
     return struct.unpack(fmt, data)[0]
 
+
 def signed(integer):
     return unpack(pack(integer), signed=True)
 
+
 def unsigned(integer):
     return unpack(pack(integer))
diff --git a/pwndbg/elf.py b/pwndbg/elf.py
--- a/pwndbg/elf.py
+++ b/pwndbg/elf.py
@@ -13,13 +13,10 @@
 from __future__ import unicode_literals
 
 import ctypes
-import os
-import re
-import subprocess
 import sys
-import tempfile
 
 import gdb
+from six.moves import reload_module
 
 import pwndbg.auxv
 import pwndbg.elftypes
@@ -30,6 +27,7 @@
 import pwndbg.proc
 import pwndbg.stack
 
+
 # ELF constants
 PF_X, PF_W, PF_R = 1,2,4
 ET_EXEC, ET_DYN  = 2,3
@@ -37,9 +35,12 @@
 
 module = sys.modules[__name__]
 
+
 @pwndbg.events.start
 @pwndbg.events.new_objfile
 def update():
+    reload_module(pwndbg.elftypes)
+
     if pwndbg.arch.ptrsize == 4:
         Ehdr = pwndbg.elftypes.Elf32_Ehdr
         Phdr = pwndbg.elftypes.Elf32_Phdr


Test patch: