Repository: Gallopsled/pwntools

Issue:
aarch64.linux.pushstr_array is broken
There's also a missing colon after the `if` statement, and it seems this was never never finished since there's no `%endif`.

https://github.com/Gallopsled/pwntools/blob/813749493288fd7330b7b44b81a7f2f7a8a02dac/pwnlib/shellcraft/templates/aarch64/pushstr_array.asm#L76-L79


```
>>> shellcraft.pushstr_array(['hello', 'world'])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/lookup.py in get_template(self, uri)
    248             if self.filesystem_checks:
--> 249                 return self._check(uri, self._collection[uri])
    250             else:

KeyError: 'aarch64/pushstr_array.asm'

During handling of the above exception, another exception occurred:

CompileException                          Traceback (most recent call last)
<ipython-input-2-b064486e8883> in <module>
----> 1 shellcraft.pushstr_array(['hello', 'world'])

~/pwntools/pwnlib/shellcraft/__init__.py in __getattr__(self, key)
     84         for m in self._context_modules():
     85             try:
---> 86                 return getattr(m, key)
     87             except AttributeError:
     88                 pass

~/pwntools/pwnlib/shellcraft/__init__.py in __getattr__(self, key)
     78         # This function lazy-loads the shellcodes
     79         if key in self._shellcodes:
---> 80             real = internal.make_function(key, self._shellcodes[key], self._dir)
     81             setattr(self, key, real)
     82             return real

~/pwntools/pwnlib/shellcraft/internal.py in make_function(funcname, filename, directory)
    112     import inspect
    113     path       = os.path.join(directory, filename)
--> 114     template   = lookup_template(path)
    115
    116     args, varargs, keywords, defaults = inspect.getargspec(template.module.render_body)

~/pwntools/pwnlib/shellcraft/internal.py in lookup_template(filename)
     80
     81     if filename not in loaded:
---> 82         loaded[filename] = lookup.get_template(filename)
     83
     84     return loaded[filename]

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/lookup.py in get_template(self, uri)
    258                 srcfile = posixpath.normpath(posixpath.join(dir_, u))
    259                 if os.path.isfile(srcfile):
--> 260                     return self._load(srcfile, uri)
    261             else:
    262                 raise exceptions.TopLevelLookupException(

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/lookup.py in _load(self, filename, uri)
    320                 else:
    321                     module_filename = None
--> 322                 self._collection[uri] = template = Template(
    323                     uri=uri,
    324                     filename=posixpath.normpath(filename),

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/template.py in __init__(self, text, filename, uri, format_exceptions, error_handler, lookup, output_encoding, encoding_errors, module_directory, cache_args, cache_impl, cache_enabled, cache_type, cache_dir, cache_url, module_filename, input_encoding, disable_unicode, module_writer, bytestring_passthrough, default_filters, buffer_filters, strict_undefined, imports, future_imports, enable_loop, preprocessor, lexer_cls, include_error_handler)
    346             else:
    347                 path = None
--> 348             module = self._compile_from_file(path, filename)
    349         else:
    350             raise exceptions.RuntimeException(

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/template.py in _compile_from_file(self, path, filename)
    428             # in memory
    429             data = util.read_file(filename)
--> 430             code, module = _compile_text(self, data, filename)
    431             self._source = None
    432             self._code = code

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/template.py in _compile_text(template, text, filename)
    731 def _compile_text(template, text, filename):
    732     identifier = template.module_id
--> 733     source, lexer = _compile(
    734         template,
    735         text,

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/template.py in _compile(template, text, filename, generate_magic_comment)
    710         preprocessor=template.preprocessor,
    711     )
--> 712     node = lexer.parse()
    713     source = codegen.compile(
    714         node,

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/lexer.py in parse(self)
    262             if self.match_expression():
    263                 continue
--> 264             if self.match_control_line():
    265                 continue
    266             if self.match_comment():

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/lexer.py in match_control_line(self)
    474                             **self.exception_kwargs
    475                         )
--> 476                 self.append_node(parsetree.ControlLine, keyword, isend, text)
    477             else:
    478                 self.append_node(parsetree.Comment, text)

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/lexer.py in append_node(self, nodecls, *args, **kwargs)
    150         kwargs.setdefault("pos", self.matched_charpos)
    151         kwargs["filename"] = self.filename
--> 152         node = nodecls(*args, **kwargs)
    153         if len(self.tag):
    154             self.tag[-1].nodes.append(node)

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/parsetree.py in __init__(self, keyword, isend, text, **kwargs)
     91             self._undeclared_identifiers = []
     92         else:
---> 93             code = ast.PythonFragment(text, **self.exception_kwargs)
     94             self._declared_identifiers = code.declared_identifiers
     95             self._undeclared_identifiers = code.undeclared_identifiers

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/mako/ast.py in __init__(self, code, **exception_kwargs)
     87         m = re.match(r"^(\w+)(?:\s+(.*?))?:\s*(#|$)", code.strip(), re.S)
     88         if not m:
---> 89             raise exceptions.CompileException(
     90                 "Fragment '%s' is not a partial control statement" % code,
     91                 **exception_kwargs

CompileException: Fragment 'if len(array[-1] != 'sp')' is not a partial control statement in file '/Users/heapcrash/pwntools/pwnlib/shellcraft/templates/aarch64/pushstr_array.asm' at line: 76 char: 1
```


Discussion:
It looks like `pushstr` itself is also broken
It looks like each 16-byte block is correct, but they are pushed in the wrong order.  e.g.:

```
00:0000│ sp  0x40007ffaa0 ◂— 0x6161616661616165 ('eaaafaaa')
01:0008│     0x40007ffaa8 ◂— 0x6161616861616167 ('gaaahaaa')
02:0010│     0x40007ffab0 ◂— 0x6161616261616161 ('aaaabaaa')
03:0018│     0x40007ffab8 ◂— 0x6161616461616163 ('caaadaaa')
```

Resolving patch:
diff --git a/pwnlib/version.py b/pwnlib/version.py
--- a/pwnlib/version.py
+++ b/pwnlib/version.py
@@ -1 +1 @@
-__version__ = '4.1.5'
+__version__ = '4.1.6'
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -83,7 +83,7 @@
     name                 = 'pwntools',
     python_requires      = '>=2.7',
     packages             = find_packages(),
-    version              = '4.1.5',
+    version              = '4.1.6',
     data_files           = [('',
                              glob.glob('*.md') + glob.glob('*.txt')),
                             ],


Test patch: