Repository: pwndbg/pwndbg

Issue:
bug of function get_load_segment_info in readelf.py
when I got readelf result like below
```
readelf --program-headers /bin/ls | grep "LOAD" -A 10
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x000000000001da64 0x000000000001da64  R E    200000
```
the function crashed at line 65
```
65   fsize, msize, read, write, execute, align = re_secnd.match(line).groups()
```
the reason is in the regex format
```python
re_secnd = re.compile(r"\s+(0x[0-9A-Fa-f]+) (0x[0-9A-Fa-f]+)  (.)(.)(.)\s+(**0x**[0-9A-Fa-f]+)"
```
I mean, "0x" should not a absolute prefix of align number


Discussion:
Hey, thanks for reporting this bug!

For me it happens only on machine with GDB with Py2 (but its probably unrelated to Py version, see below):

Launched on `/bin/ls` binary:
```
pwndbg> version
e requires the readline library
pwndbg: loaded 165 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from /bin/ls...(no debugging symbols found)...done.

pwndbg> version
Gdb:      GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
Python:   2.7.9 (default, Jun 29 2016, 13:11:10)  [GCC 4.9.2]
Pwndbg:   1.0.0 build: 1a5e134
Capstone: 4.0.1024
Unicorn:  1.0.0

pwndbg> xinfo
Extended information for virtual address 0x4048c5:

  Containing mapping:
          0x400000           0x41c000 r-xp    1c000 0      /bin/ls

  Offset information:
         Mapped Area 0x4048c5 = 0x400000 + 0x48c5
       File (Memory) 0x4048c5 = 0x400000 + 0x48c5
Traceback (most recent call last):
  File "/home/dc/pwndbg/pwndbg/commands/__init__.py", line 109, in __call__
    return self.function(*args, **kwargs)
  File "/home/dc/pwndbg/pwndbg/commands/__init__.py", line 200, in _OnlyWhenRunning
    return function(*a, **kw)
  File "/home/dc/pwndbg/pwndbg/commands/xinfo.py", line 105, in xinfo
    xinfo_mmap_file(page, addr)
  File "/home/dc/pwndbg/pwndbg/commands/xinfo.py", line 70, in xinfo_mmap_file
    for segment in pwndbg.wrappers.readelf.get_load_segment_info():
  File "/home/dc/pwndbg/pwndbg/commands/__init__.py", line 189, in _OnlyWithFile
    return function(*a, **kw)
  File "/home/dc/pwndbg/pwndbg/wrappers/__init__.py", line 28, in _OnlyWithCommand
    return function(*a, **kw)
  File "/home/dc/pwndbg/pwndbg/wrappers/readelf.py", line 65, in get_load_segment_info
    fsize, msize, read, write, execute, align = re_secnd.match(line).groups()
AttributeError: 'NoneType' object has no attribute 'groups'
```
To clarify the bug report even further, the bug comes from the fact that the `Align` value presented in `readelf ...` output doesn't always have a prefix of `0x`.

Example output for two different readelf versions can be seen below (`#` is a comment):

```
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align

# GNU readelf (GNU Binutils for Debian) 2.25 (2014):
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x000000000001bad4 0x000000000001bad4  R E    200000

# GNU readelf (GNU Binutils) 2.29.1 (2017):
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x000000000001e050 0x000000000001e050  R E    0x200000
```

The regex in our code matches the second line of the presented outputs.

Resolving patch:
diff --git a/pwndbg/wrappers/readelf.py b/pwndbg/wrappers/readelf.py
--- a/pwndbg/wrappers/readelf.py
+++ b/pwndbg/wrappers/readelf.py
@@ -52,9 +52,15 @@ def get_load_segment_info():
     #                FileSiz            MemSiz             Flags  Align
     # LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
     #                0x0000000000000830 0x0000000000000830  R E    0x200000
+    #
+    ############################################################################
+    #
+    # NOTE: On some readelf versions the Align column might not be prefixed with 0x
+    # See https://github.com/pwndbg/pwndbg/issues/427
+    #
     # Account for this using two regular expressions
     re_first = re.compile(r"\s+LOAD\s+(0x[0-9A-Fa-f]+) (0x[0-9A-Fa-f]+) (0x[0-9A-Fa-f]+)")
-    re_secnd = re.compile(r"\s+(0x[0-9A-Fa-f]+) (0x[0-9A-Fa-f]+)  (.)(.)(.)\s+(0x[0-9A-Fa-f]+)")
+    re_secnd = re.compile(r"\s+(0x[0-9A-Fa-f]+) (0x[0-9A-Fa-f]+)  (.)(.)(.)\s+(0x)?([0-9A-Fa-f]+)")
     hex2int = lambda x: int(x, 16)
 
     for line in readelf_out.splitlines():
@@ -62,8 +68,8 @@ def get_load_segment_info():
             load_found = True
             offset, vaddr, paddr = map(hex2int, re_first.match(line).groups())
         elif load_found:
-            fsize, msize, read, write, execute, align = re_secnd.match(line).groups()
-            fsize, msize, align = map(hex2int, (fsize, msize, align))
+            fsize, msize, read, write, execute, _optional_prefix, align = re_secnd.match(line).groups()
+            fsize, msize, align = map(hex2int, (fsize, msize, '0x' + align))
             read = read == "R"
             write = write == "W"
             execute = execute == "E"


Test patch: