Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion patcherex/backends/reassembler_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
l = logging.getLogger('patcherex.backends.reassembler_backend')

try:
import compilerex
from patcherex import compilerex
except ImportError:
l.warning('Cannot import compilerex. Reassembler backend will not be able to recompile assembly files.')

Expand Down
14 changes: 14 additions & 0 deletions patcherex/compilerex/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from .compilerex import compile, clang_assemble, gcc_assemble, get_preferred_syntax, auto_assemble, \
compile_from_string, get_clang_args, c_to_asm, assemble

__all__ = [
"compile",
"clang_assemble",
"gcc_assemble",
"get_preferred_syntax",
"auto_assemble",
"compile_from_string",
"get_clang_args",
"c_to_asm",
"assemble",
]
Binary file added patcherex/compilerex/bin/ar
Binary file not shown.
Binary file added patcherex/compilerex/bin/clang
Binary file not shown.
Binary file added patcherex/compilerex/bin/clang++
Binary file not shown.
Binary file added patcherex/compilerex/bin/ld
Binary file not shown.
Binary file added patcherex/compilerex/bin/objcopy
Binary file not shown.
Binary file added patcherex/compilerex/bin/objdump
Binary file not shown.
149 changes: 149 additions & 0 deletions patcherex/compilerex/compilerex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import subprocess
import tempfile
import os
import magic
import re

class CompileError(Exception):
pass

def compile(args):
if not isinstance(args, list):
args = [args]
path = os.path.join(os.path.dirname(__file__), "scripts", "compile.sh")
p = subprocess.Popen(["bash", path] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res = p.communicate()
returncode = p.wait()
return returncode, res

def clang_assemble(args):
if not isinstance(args, list):
args = [args]
path = os.path.join(os.path.dirname(__file__), "scripts", "assemble.sh")
p = subprocess.Popen(["bash", path] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res = p.communicate()
returncode = p.wait()
return returncode, res


# compatibility
assemble = clang_assemble

def gcc_assemble(args):
if not isinstance(args, list):
args = [args]
p = subprocess.Popen(["gcc", "-fcf-protection=none", "-fno-stack-protector", "-no-pie",] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res = p.communicate()
returncode = p.wait()

if b"unrecognized command line option '-fcf-protection=none'" in res[1]:
# this is an older version of GCC that does not support fcf-protection=none
p = subprocess.Popen(["gcc", "-fno-stack-protector", "-no-pie",] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res = p.communicate()
returncode = p.wait()

return returncode, res

#obsolete
def get_clang_args(filename):
magic_string = magic.from_file(filename)
with open(filename, "rb") as f:
start_bytes = f.read(0x100)
if magic_string.startswith("ELF"):
if "x86-64" in magic_string:
return ["-target", "x86", "-nostdlib", "-static", "-Wl,-m64"]
elif "Intel 80386" in magic_string:
return ["-nostdlib", "-static", "-Wl,-m32"]
elif start_bytes.startswith(b"\x7fCGC"):
return ["-nostdlib", "-static", "-Wl,-mcgc_i386"]
raise Exception("Unsupported file type (magic: '%s')" % (magic_string))

def auto_assemble(input_filename, asm_filename, output_filename, extra_opts=None):
extra_opts = [] if extra_opts is None else extra_opts
magic_string = magic.from_file(input_filename)
with open(input_filename, "rb") as f:
start_bytes = f.read(0x100)
if magic_string.startswith("ELF"):
if "x86-64" in magic_string:
return gcc_assemble(["-m64", asm_filename, "-o", output_filename] + extra_opts)
elif "Intel 80386" in magic_string:
return gcc_assemble(["-m32", asm_filename, "-o", output_filename] + extra_opts)
elif start_bytes.startswith(b"\x7fCGC"):
return clang_assemble(["-nostdlib", "-static", "-Wl,-mcgc_i386", asm_filename, "-o", output_filename]
+ extra_opts)
raise Exception("Unsupported file type (magic: '%s')" % (magic_string))

def c_to_asm(c_str, compiler_flags=None, syntax="intel"):
"""
Given a string containing C code, returns a string
that is the corresponding assembly code generated by gcc.
"""
compiler_flags = (compiler_flags if compiler_flags else [])
if syntax != "intel" and syntax != "att":
raise Exception("Unsupported syntax: %s" % (syntax))

c_file = tempfile.mkstemp(prefix="c_patch_", suffix=".c")[1]
with open(c_file, "w") as f:
f.write(c_str)

asm_file = tempfile.mkstemp(prefix="c_patch_output_", suffix=".s")[1]
retcode, res = gcc_assemble([c_file, "-S", "-masm=" + syntax, "-o", asm_file] + compiler_flags)

if retcode != 0:
raise Exception("Error compiling c code: %s" % ("\n" + res[1].decode("utf-8")))

with open(asm_file, "r") as f:
lines = f.read().split("\n")

label_directive_re = re.compile(r"\.[a-zA-Z0-9_\?$#@~]+")
label_re = re.compile(r"\.[a-zA-Z0-9_\?$#@~]+:")
data_def_re = re.compile(r"\.(string|asciz|ascii|[1248]?byte|short|word|long|quad|value|zero)")
def is_not_directive(line):
stripped = line.strip()
if label_directive_re.match(stripped):
if not label_re.match(stripped) and not data_def_re.match(stripped):
return False
return True

filtered_lines = filter(is_not_directive, lines)
return "\n".join(filtered_lines) + "\n"

def get_preferred_syntax(filename):
magic_string = magic.from_file(filename)
with open(filename, "rb") as f:
start_bytes = f.read(0x100)
if start_bytes.startswith(b"\x7fCGC"):
return "at&t"
else:
return "intel"

def compile_from_string(c_str, filename=None):
'''
Compile a CGC binary from a C string.
Removes output file and original C file after compilation.

:param str c_str: The source code to compile
:returns bytes: The compiled binary
'''
c_file = tempfile.mktemp(dir='/tmp/', prefix="c_file_", suffix=".c")

with open(c_file, "w") as f:
f.write(c_str)

outfile = filename
if filename is None:
outfile = tempfile.mktemp(dir='/tmp/', prefix="compiled_")
retcode, res = compile([c_file, "-o", outfile])

if retcode != 0:
raise CompileError(res[1])

with open(outfile, "rb") as f:
result = f.read()


os.remove(c_file)
if filename is None:
os.remove(outfile)

return result
8 changes: 8 additions & 0 deletions patcherex/compilerex/include/assert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef _ASSERT_H_DECLS
#define _ASSERT_H_DECLS

#define __ASSERT_VOID_CAST (void)

#define assert(expr) (__ASSERT_VOID_CAST (0))

#endif
Loading
Loading