Repository: Gallopsled/pwntools

Issue:
shellcraft: incorrect symlink/link templates due to 'from' argument name
Some of the templates name one of their argument as `from` and so does not work in the end.

This is because we try to create a function like `def ANON(from=0, to=0): pass` and the `from` is Python's keyword and cannot be used as argument name.

Examples:
* https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/shellcraft/templates/common/linux/syscalls/symlink.asm#L20
* https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/shellcraft/templates/common/linux/syscalls/link.asm#L20

Stacktrace:
```
In [3]: shellcraft.amd64.link('a', 'b')
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/usr/local/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: 'amd64/linux/syscalls/link.asm'

During handling of the above exception, another exception occurred:

SyntaxError                               Traceback (most recent call last)
/usr/local/lib/python3.8/site-packages/mako/pyparser.py in parse(code, mode, **exception_kwargs)
     44     try:
---> 45         return _ast_util.parse(code, "<unknown>", mode)
     46     except Exception:

/usr/local/lib/python3.8/site-packages/mako/_ast_util.py in parse(expr, filename, mode)
     91     """Parse an expression into an AST node."""
---> 92     return compile(expr, filename, mode, PyCF_ONLY_AST)
     93

SyntaxError: invalid syntax (<unknown>, line 1)

During handling of the above exception, another exception occurred:

SyntaxException                           Traceback (most recent call last)
<ipython-input-3-09803c979eeb> in <module>
----> 1 shellcraft.amd64.link('a', 'b')

/usr/local/lib/python3.8/site-packages/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

/usr/local/lib/python3.8/site-packages/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

/usr/local/lib/python3.8/site-packages/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

/usr/local/lib/python3.8/site-packages/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)

/usr/local/lib/python3.8/site-packages/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]

/usr/local/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(

/usr/local/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),

/usr/local/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(

/usr/local/lib/python3.8/site-packages/mako/template.py in _compile_from_file(self, path, filename)
    411             ):
    412                 data = util.read_file(filename)
--> 413                 _compile_module_file(
    414                     self, data, filename, path, self.module_writer
    415                 )

/usr/local/lib/python3.8/site-packages/mako/template.py in _compile_module_file(template, text, filename, outputpath, module_writer)
    750
    751 def _compile_module_file(template, text, filename, outputpath, module_writer):
--> 752     source, lexer = _compile(
    753         template, text, filename, generate_magic_comment=True
    754     )

/usr/local/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,

/usr/local/lib/python3.8/site-packages/mako/lexer.py in parse(self)
    266             if self.match_comment():
    267                 continue
--> 268             if self.match_tag_start():
    269                 continue
    270             if self.match_tag_end():

/usr/local/lib/python3.8/site-packages/mako/lexer.py in match_tag_start(self)
    325                     text = text.replace("\r\n", "\n")
    326                     attributes[key] = text
--> 327             self.append_node(parsetree.Tag, keyword, attributes)
    328             if isend:
    329                 self.tag.pop()

/usr/local/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)

/usr/local/lib/python3.8/site-packages/mako/parsetree.py in __call__(cls, keyword, attributes, **kwargs)
    252                 filename=kwargs["filename"],
    253             )
--> 254         return type.__call__(cls, keyword, attributes, **kwargs)
    255
    256

/usr/local/lib/python3.8/site-packages/mako/parsetree.py in __init__(self, keyword, attributes, **kwargs)
    655             keyword, attributes, expressions, (), (), **kwargs
    656         )
--> 657         self.body_decl = ast.FunctionArgs(
    658             attributes.get("args", ""), **self.exception_kwargs
    659         )

/usr/local/lib/python3.8/site-packages/mako/ast.py in __init__(self, code, **kwargs)
    201
    202     def __init__(self, code, **kwargs):
--> 203         super(FunctionArgs, self).__init__(
    204             "def ANON(%s):pass" % code, **kwargs
    205         )

/usr/local/lib/python3.8/site-packages/mako/ast.py in __init__(self, code, allow_kwargs, **exception_kwargs)
    118     def __init__(self, code, allow_kwargs=True, **exception_kwargs):
    119         self.code = code
--> 120         expr = pyparser.parse(code, "exec", **exception_kwargs)
    121
    122         f = pyparser.ParseFunc(self, **exception_kwargs)

/usr/local/lib/python3.8/site-packages/mako/pyparser.py in parse(code, mode, **exception_kwargs)
     45         return _ast_util.parse(code, "<unknown>", mode)
     46     except Exception:
---> 47         raise exceptions.SyntaxException(
     48             "(%s) %s (%r)"
     49             % (

SyntaxException: (SyntaxError) invalid syntax (<unknown>, line 1) ('def ANON(from=0, to=0):pass') in file '/usr/local/lib/python3.8/site-packages/pwnlib/shellcraft/templates/amd64/linux/syscalls/link.asm' at line: 20 char: 1
```

As a workaround, I changed the `from` to `src` in the template file and it worked :shrug:.


Discussion:

Resolving patch:
diff --git a/pwnlib/data/syscalls/generate.py b/pwnlib/data/syscalls/generate.py
--- a/pwnlib/data/syscalls/generate.py
+++ b/pwnlib/data/syscalls/generate.py
@@ -75,8 +75,8 @@
 
         # The argument is not a register.  It is a string value, and we
         # are expecting a string value
-        elif name in can_pushstr and isinstance(arg, (bytes, six.text_type)):
-            if not isinstance(arg, bytes):
+        elif name in can_pushstr and isinstance(arg, (six.binary_type, six.text_type)):
+            if isinstance(arg, six.text_type):
                 arg = arg.encode('utf-8')
             string_arguments[name] = arg
 
@@ -150,6 +150,8 @@ def fix_bad_arg_names(func, arg):
         return 'length'
     if arg.name == 'repr':
         return 'repr_'
+    if arg.name == 'from':
+        return 'from_'
 
     if func.name == 'open' and arg.name == 'vararg':
         return 'mode'


Test patch: