mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
Added Interrupt structure to State structure for x86. It's not an ideal way of configuring interrupts, but it currently seems like the only way to handle conditional interrupts, e.g. via BOUND or INTO. I might need something similar for SYSCALL vs. SYSENTER. Split the build script out into a build script and a build system. Made it so that the IDA script splits interrupts off into their own blocks. This is so that we can restart interrupts by jumping back to the blocks associated with the address of the instruction. Moving the tools stuff out into its own repo as it complicates things to have it in the source.
This commit is contained in:
@@ -36,3 +36,5 @@ generated/*
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
*.pyc
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
ALWAYS_INLINE static void name ( \
|
||||
State &state, const addr_t next_pc, ##__VA_ARGS__) noexcept
|
||||
|
||||
// Define a semantics implementing function that is also an instruction.
|
||||
#define DEF_ISEL_SEM(name, ...) \
|
||||
extern "C" ALWAYS_INLINE void name ( \
|
||||
State &state, const addr_t next_pc, ##__VA_ARGS__) noexcept
|
||||
|
||||
// An instruction where the implementation is the same for all operand sizes.
|
||||
#define DEF_ISEL_ALL(name, func) \
|
||||
DEF_ISEL(name ## _8) = func ; \
|
||||
|
||||
@@ -246,22 +246,22 @@ bool Instr::CheckArgumentTypes(const llvm::Function *F,
|
||||
|
||||
llvm::Function *Instr::GetInstructionFunction(void) {
|
||||
auto func_name = InstructionFunctionName(xedd);
|
||||
llvm::Function *F = FindFunction(M, func_name);
|
||||
llvm::Function *IF = FindFunction(M, func_name);
|
||||
llvm::GlobalVariable *FP = FindGlobaVariable(M, func_name);
|
||||
|
||||
if (!F && FP) {
|
||||
if (!IF && FP) {
|
||||
CHECK(FP->isConstant() && FP->hasInitializer())
|
||||
<< "Expected a `constexpr` variable as the function pointer.";
|
||||
llvm::Constant *FC = FP->getInitializer()->stripPointerCasts();
|
||||
F = llvm::dyn_cast<llvm::Function>(FC);
|
||||
IF = llvm::dyn_cast<llvm::Function>(FC);
|
||||
}
|
||||
|
||||
// TODO(pag): Memory leak of `args`, `prepend_instrs`, and `append_instrs`.
|
||||
if (F && !CheckArgumentTypes(F, func_name)) {
|
||||
if (!IF || !CheckArgumentTypes(IF, func_name)) {
|
||||
LOG(WARNING) << "Missing instruction semantics for " << func_name;
|
||||
}
|
||||
|
||||
return F;
|
||||
return IF;
|
||||
}
|
||||
|
||||
// Lift a generic instruction.
|
||||
@@ -282,10 +282,10 @@ void Instr::LiftGeneric(const Translator &lifter) {
|
||||
LiftOperand(lifter, i);
|
||||
}
|
||||
|
||||
if (auto F = GetInstructionFunction()) {
|
||||
if (auto IF = GetInstructionFunction()) {
|
||||
auto &IList = B->getInstList();
|
||||
IList.insert(IList.end(), prepend_instrs.rbegin(), prepend_instrs.rend());
|
||||
IList.push_back(llvm::CallInst::Create(F, args));
|
||||
IList.push_back(llvm::CallInst::Create(IF, args));
|
||||
IList.insert(IList.end(), append_instrs.begin(), append_instrs.end());
|
||||
}
|
||||
}
|
||||
@@ -607,7 +607,8 @@ bool Instr::IsSystemReturn(void) const {
|
||||
}
|
||||
|
||||
bool Instr::IsInterruptCall(void) const {
|
||||
return XED_ICLASS_INT <= iclass && XED_ICLASS_INTO >= iclass;
|
||||
return (XED_ICLASS_INT <= iclass && XED_ICLASS_INTO >= iclass) ||
|
||||
XED_ICLASS_BOUND == iclass;
|
||||
}
|
||||
|
||||
bool Instr::IsInterruptReturn(void) const {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "mcsema/Arch/X86/Semantics/CMOV.h"
|
||||
#include "mcsema/Arch/X86/Semantics/COND_BR.h"
|
||||
#include "mcsema/Arch/X86/Semantics/DATAXFER.h"
|
||||
#include "mcsema/Arch/X86/Semantics/INTERRUPT.h"
|
||||
#include "mcsema/Arch/X86/Semantics/FMA.h"
|
||||
#include "mcsema/Arch/X86/Semantics/LOGICAL.h"
|
||||
#include "mcsema/Arch/X86/Semantics/MISC.h"
|
||||
|
||||
@@ -258,6 +258,18 @@ enum : size_t {
|
||||
kNumVecRegisters = 32
|
||||
};
|
||||
|
||||
// Representing the state of an in-progress interrupt request.
|
||||
//
|
||||
// TODO(pag): This is kind of an ugly solution to there not being a particularly
|
||||
// nice way of "configuring" interrupts for the
|
||||
// `__mcsema_interrupt_call` intrinsic, especially in the case of
|
||||
// conditional interrupts.
|
||||
struct alignas(sizeof(addr_t)) Interrupt {
|
||||
addr_t vector;
|
||||
addr_t next_pc;
|
||||
bool trigger;
|
||||
};
|
||||
|
||||
struct alignas(64) State final {
|
||||
// Native `FXSAVE64` representation of the FPU, plus a semi-duplicate
|
||||
// representation of all vector regs (XMM, YMM, ZMM).
|
||||
@@ -275,6 +287,8 @@ struct alignas(64) State final {
|
||||
Segments seg; // 12 bytes, padded to 16 bytes.
|
||||
GPR gpr; // 136 bytes.
|
||||
|
||||
Interrupt interrupt; // 2 * sizeof(addr_t) bytes.
|
||||
|
||||
// Total: 2728 bytes, padded to 2752 bytes.
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -296,6 +310,7 @@ static_assert(2576 == __builtin_offsetof(State, seg),
|
||||
static_assert(2592 == __builtin_offsetof(State, gpr),
|
||||
"Invalid packing of `State::seg`.");
|
||||
|
||||
static_assert(2752 == sizeof(State), "Invalid packing of `State`.");
|
||||
static_assert(2728 == __builtin_offsetof(State, interrupt),
|
||||
"Invalid packing of `State::interrupt`.");
|
||||
|
||||
#endif // MCSEMA_ARCH_X86_RUNTIME_STATE_H_
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved. */
|
||||
|
||||
#ifndef MCSEMA_ARCH_X86_SEMANTICS_INTERRUPT_H_
|
||||
#define MCSEMA_ARCH_X86_SEMANTICS_INTERRUPT_H_
|
||||
|
||||
namespace {
|
||||
|
||||
#if 32 == ADDRESS_SIZE_BITS
|
||||
template <typename S1, typename S2>
|
||||
DEF_SEM(BOUND, S1 idx_, S2 bounds) {
|
||||
const auto idx = R(idx_);
|
||||
const auto lb = R(bounds);
|
||||
const auto ub = R(S2{A(bounds) + sizeof(idx)});
|
||||
if (idx < lb || ub < idx) {
|
||||
state.interrupt.next_pc = next_pc;
|
||||
state.interrupt.vector = 5;
|
||||
state.interrupt.trigger = true;
|
||||
} else {
|
||||
state.interrupt.next_pc = next_pc;
|
||||
state.interrupt.trigger = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL_SEM(INT_IMMb, I8 num) {
|
||||
state.interrupt.next_pc = next_pc;
|
||||
state.interrupt.vector = R(num);
|
||||
state.interrupt.trigger = true;
|
||||
}
|
||||
|
||||
DEF_ISEL_SEM(INT1) {
|
||||
INT_IMMb(state, next_pc, {1});
|
||||
state.interrupt.trigger = true;
|
||||
}
|
||||
|
||||
DEF_ISEL_SEM(INT3) {
|
||||
INT_IMMb(state, next_pc, {3});
|
||||
state.interrupt.trigger = true;
|
||||
}
|
||||
|
||||
#if 32 == ADDRESS_SIZE_BITS
|
||||
DEF_ISEL_SEM(INTO) {
|
||||
if (state.aflag.of) {
|
||||
INT_IMMb(state, next_pc, {4});
|
||||
} else {
|
||||
state.interrupt.next_pc = next_pc;
|
||||
state.interrupt.trigger = false;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_ISEL(BOUND_GPRv_MEMa16_16) = BOUND<R16, M16>;
|
||||
DEF_ISEL(BOUND_GPRv_MEMa32_32) = BOUND<R32, M32>;
|
||||
#endif // 32 == ADDRESS_SIZE_BITS
|
||||
|
||||
#endif // MCSEMA_ARCH_X86_SEMANTICS_INTERRUPT_H_
|
||||
@@ -29,8 +29,8 @@ if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
XED_VERSION=xed-install-base-2015-09-10-mac-x86-64
|
||||
LLVM_RELEASE_DIR=releases/3.7.1
|
||||
LLVM_RELEASE=3.7.1
|
||||
LLVM_RELEASE_DIR=releases/3.7.0
|
||||
LLVM_RELEASE=3.7.0
|
||||
LLVM_VERSION=clang+llvm-${LLVM_RELEASE}-x86_64-apple-darwin
|
||||
MCSEMA_OS_NAME="mac"
|
||||
LIB_EXT=dylib
|
||||
|
||||
+24
-336
@@ -1,326 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import glob
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
MCSEMA_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
CC = os.path.join(MCSEMA_DIR, "third_party", "bin", "clang")
|
||||
CXX = os.path.join(MCSEMA_DIR, "third_party", "bin", "clang++")
|
||||
|
||||
OS = {
|
||||
"darwin": "mac",
|
||||
"linux": "linux",
|
||||
"linux2": "linux",
|
||||
"win32": "win",
|
||||
}[sys.platform]
|
||||
|
||||
SHARED_LIB_EXT = {
|
||||
"linux": "so",
|
||||
"mac": "dylib",
|
||||
"win": "dll",
|
||||
}[OS]
|
||||
|
||||
SRC_DIR = os.path.join(MCSEMA_DIR, "mcsema")
|
||||
BUILD_DIR = os.path.join(MCSEMA_DIR, "build")
|
||||
TEST_DIR = os.path.join(MCSEMA_DIR, "tests")
|
||||
GEN_DIR = os.path.join(MCSEMA_DIR, "generated")
|
||||
INCLUDE_DIR = os.path.join(MCSEMA_DIR, "third_party", "include")
|
||||
BIN_DIR = os.path.join(MCSEMA_DIR, "third_party", "bin")
|
||||
LIB_DIR = os.path.join(MCSEMA_DIR, "third_party", "lib")
|
||||
TOOLS_DIR = os.path.join(MCSEMA_DIR, "tools")
|
||||
|
||||
try:
|
||||
import concurrent.futures
|
||||
POOL = concurrent.futures.ThreadPoolExecutor(max_workers=32)
|
||||
TASKS = []
|
||||
def Task(func, *args, **kargs):
|
||||
future = POOL.submit(func, *args, **kargs)
|
||||
TASKS.append(future)
|
||||
return future
|
||||
def FinishAllTasks():
|
||||
for task in TASKS:
|
||||
task.result()
|
||||
POOL.shutdown()
|
||||
|
||||
# Don't have a thread pool available.
|
||||
except:
|
||||
def Task(func, *args, **kargs):
|
||||
return func(*args, **kargs)
|
||||
def FinishAllTasks():
|
||||
pass
|
||||
|
||||
CXX_FLAGS = [
|
||||
# Enable warnings.
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-pedantic",
|
||||
|
||||
# Disable specific warnings.
|
||||
"-Wno-nested-anon-types",
|
||||
"-Wno-extended-offsetof",
|
||||
"-Wno-gnu-anonymous-struct",
|
||||
"-Wno-variadic-macros",
|
||||
"-Wno-gnu-zero-variadic-macro-arguments",
|
||||
"-Wno-error=unused-command-line-argument",
|
||||
"-Wno-override-module",
|
||||
|
||||
# Features.
|
||||
"-fno-omit-frame-pointer",
|
||||
"-fno-rtti",
|
||||
"-fno-exceptions",
|
||||
"-fvisibility-inlines-hidden",
|
||||
"-std=gnu++11",
|
||||
|
||||
# Macros.
|
||||
'-DMCSEMA_DIR="{}"'.format(MCSEMA_DIR),
|
||||
'-DMCSEMA_OS="{}"'.format(OS),
|
||||
"-D__STDC_LIMIT_MACROS",
|
||||
"-D__STDC_CONSTANT_MACROS",
|
||||
"-DGOOGLE_PROTOBUF_NO_RTTI",
|
||||
"-DNDEBUG",
|
||||
|
||||
# Includes.
|
||||
"-isystem", INCLUDE_DIR,
|
||||
"-I{}".format(MCSEMA_DIR),
|
||||
|
||||
# Output info.
|
||||
"-fPIC",
|
||||
"-fpie",
|
||||
"-g3",
|
||||
"-m64",
|
||||
]
|
||||
|
||||
# Dictionary for memoizing the compilation of source files.
|
||||
SOURCE_FILES = {}
|
||||
|
||||
|
||||
# Execute a command.
|
||||
def Command(*args):
|
||||
args = [str(a) for a in args]
|
||||
try:
|
||||
return subprocess.check_output(args)
|
||||
except:
|
||||
print "{}\n\n".format(" ".join(args))
|
||||
pass
|
||||
|
||||
|
||||
# Recursively make directors.
|
||||
def MakeDirsForFile(file_name):
|
||||
dir_name = os.path.dirname(file_name)
|
||||
while not os.path.exists(dir_name):
|
||||
try:
|
||||
os.makedirs(dir_name)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class FileName(object):
|
||||
"""File name wrapper. A file name is either a string or a
|
||||
Future returning a string."""
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def __str__(self):
|
||||
if isinstance(self.path, str) or isinstance(self.path, unicode):
|
||||
return os.path.abspath(self.path)
|
||||
elif hasattr(self.path, 'result'):
|
||||
return os.path.abspath(str(self.path.result()))
|
||||
assert False
|
||||
|
||||
|
||||
class _File(object):
|
||||
"""Generic file abstraction with a method of extracting the
|
||||
file location."""
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def Paths(self):
|
||||
return [self.path]
|
||||
|
||||
def __str__(self):
|
||||
return str(self.path)
|
||||
|
||||
|
||||
class _SourceFile(_File):
|
||||
"""Source file that will be compiled."""
|
||||
def __init__(self, source_path, target_path, extra_args):
|
||||
super(_SourceFile, self).__init__(FileName(Task(
|
||||
self._Build,
|
||||
source_path,
|
||||
target_path,
|
||||
extra_args)))
|
||||
|
||||
def _Build(self, source_path, target_path, extra_args):
|
||||
MakeDirsForFile(target_path)
|
||||
args = [CXX]
|
||||
|
||||
if "mac" == OS:
|
||||
args.append("-stdlib=libc++")
|
||||
|
||||
args.extend(CXX_FLAGS)
|
||||
args.extend(extra_args)
|
||||
args.extend([
|
||||
"-c", source_path,
|
||||
"-o", target_path])
|
||||
Command(*args)
|
||||
return target_path
|
||||
|
||||
|
||||
# Memoized source file compiler. Names compiled object files
|
||||
# in terms of the extra args and the path to the source file.
|
||||
def SourceFile(path, extra_args=[]):
|
||||
path = os.path.abspath(str(path))
|
||||
key = hashlib.md5("{}{}".format(path, "".join(extra_args))).hexdigest()
|
||||
target_path = os.path.join(BUILD_DIR, "{}.o".format(key))
|
||||
if target_path not in SOURCE_FILES:
|
||||
SOURCE_FILES[target_path] = _SourceFile(
|
||||
path, target_path, extra_args)
|
||||
return SOURCE_FILES[target_path]
|
||||
|
||||
|
||||
class StaticLibrary(_File):
|
||||
"""Pre-compiled library within the source/library dirs."""
|
||||
def __init__(self, name):
|
||||
super(StaticLibrary, self).__init__(self._FindLib(name))
|
||||
|
||||
def _FindLib(self, name):
|
||||
abs_path = os.path.abspath(name)
|
||||
if os.path.exists(abs_path):
|
||||
return abs_path
|
||||
for where in (LIB_DIR, BUILD_DIR):
|
||||
for ext in ("o", "bc", "so", "dylib", "a"):
|
||||
for prefix in ("lib", ""):
|
||||
path = os.path.join(where, "{}{}.{}".format(prefix, name, ext))
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
print "Warning: cannot find object file: {}".format(name)
|
||||
return name
|
||||
|
||||
class ConfigLibraries(object):
|
||||
"""Set of libraries returned from a configuration command."""
|
||||
def __init__(self, *args):
|
||||
self.paths = subprocess.check_output(args).strip().split(" ")
|
||||
|
||||
def Paths(self):
|
||||
return self.paths
|
||||
|
||||
|
||||
class LinkerLibrary(object):
|
||||
"""A library that the linker will figure out how to find."""
|
||||
def __init__(self, name, os=None):
|
||||
global OS
|
||||
self._name = name
|
||||
self._include = True
|
||||
if os and OS != os:
|
||||
self._include = False
|
||||
|
||||
def Paths(self):
|
||||
paths = []
|
||||
if self._include:
|
||||
paths.append("-l{}".format(self._name))
|
||||
return paths
|
||||
|
||||
|
||||
class _Target(_File):
|
||||
"""Generic target that must be compiled."""
|
||||
def __init__(self, path, source_files=[], object_files=[], libraries=[]):
|
||||
global POOL
|
||||
path = os.path.abspath(path)
|
||||
MakeDirsForFile(path)
|
||||
super(_Target, self).__init__(FileName(Task(
|
||||
self._Build,
|
||||
path,
|
||||
source_files,
|
||||
object_files,
|
||||
libraries)))
|
||||
|
||||
def _Build(self, path, source_files, object_files, libraries):
|
||||
args = [CXX]
|
||||
args.extend(CXX_FLAGS)
|
||||
|
||||
if "mac" == OS:
|
||||
args.append("-stdlib=libc++")
|
||||
|
||||
args.extend(self.extra_args)
|
||||
args.extend([
|
||||
"-o",
|
||||
path,
|
||||
"-L{}".format(LIB_DIR)])
|
||||
|
||||
if "linux" == OS:
|
||||
args.extend([
|
||||
"-Wl,-z,now",
|
||||
"-Wl,-rpath={}".format(LIB_DIR),
|
||||
"-Wl,-gc-sections",
|
||||
"-Wl,-E"])
|
||||
|
||||
elif "mac" == OS:
|
||||
args.extend([
|
||||
"-Xlinker", "-rpath", "-Xlinker", LIB_DIR,
|
||||
"-Wl,-dead_strip",])
|
||||
|
||||
for src in source_files:
|
||||
args.extend(src.Paths())
|
||||
|
||||
for obj in object_files:
|
||||
args.extend(obj.Paths())
|
||||
|
||||
for lib in libraries:
|
||||
args.extend(lib.Paths())
|
||||
|
||||
Command(*args)
|
||||
return path
|
||||
|
||||
def Wait(self):
|
||||
[str(p) for p in self.Paths()]
|
||||
|
||||
|
||||
class TargetExecutable(_Target):
|
||||
"""Represents an individual binary executable file."""
|
||||
def __init__(self, *args, **kargs):
|
||||
self.extra_args = []
|
||||
super(TargetExecutable, self).__init__(*args, **kargs)
|
||||
|
||||
def Execute(self, *args):
|
||||
Command(self.path, *args)
|
||||
|
||||
|
||||
class TargetLibrary(_Target):
|
||||
"""Shared library that must be compiled by the system."""
|
||||
def __init__(self, *args, **kargs):
|
||||
if "linux" == OS:
|
||||
self.extra_args = ["-shared"]
|
||||
elif "mac" == OS:
|
||||
self.extra_args = [
|
||||
"-Wl,-flat_namespace",
|
||||
"-Wl,-undefined,suppress",
|
||||
"-dynamiclib"]
|
||||
super(TargetLibrary, self).__init__(*args, **kargs)
|
||||
|
||||
# Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved.
|
||||
|
||||
if "__main__" != __name__:
|
||||
exit(0)
|
||||
|
||||
from buildsystem import *
|
||||
|
||||
if OS not in ("mac", "linux"):
|
||||
print "Unsupported platform: {}".format(OS)
|
||||
exit(1)
|
||||
|
||||
# Find all source code.
|
||||
source_paths = [os.path.join(SRC_DIR, "Translate.cpp")]
|
||||
source_paths = []
|
||||
source_paths.append(
|
||||
os.path.join(MCSEMA_SRC_DIR, "Translate.cpp"))
|
||||
source_paths.extend(list(
|
||||
glob.glob(os.path.join(SRC_DIR, "Arch", "*.cpp"))))
|
||||
glob.glob(os.path.join(MCSEMA_SRC_DIR, "Arch", "*.cpp"))))
|
||||
source_paths.extend(list(
|
||||
glob.glob(os.path.join(SRC_DIR, "Arch", "X86", "*.cpp"))))
|
||||
glob.glob(os.path.join(MCSEMA_SRC_DIR, "Arch", "X86", "*.cpp"))))
|
||||
source_paths.extend(list(
|
||||
glob.glob(os.path.join(SRC_DIR, "CFG", "*.cpp"))))
|
||||
glob.glob(os.path.join(MCSEMA_SRC_DIR, "CFG", "*.cpp"))))
|
||||
source_paths.extend(list(
|
||||
glob.glob(os.path.join(SRC_DIR, "BC", "*.cpp"))))
|
||||
glob.glob(os.path.join(MCSEMA_SRC_DIR, "BC", "*.cpp"))))
|
||||
source_paths.extend(list(
|
||||
glob.glob(os.path.join(SRC_DIR, "OS", "*.cpp"))))
|
||||
glob.glob(os.path.join(MCSEMA_SRC_DIR, "OS", "*.cpp"))))
|
||||
|
||||
# Find the pre-existing static libraries to link in.
|
||||
object_files = [
|
||||
@@ -339,19 +42,19 @@ system_libraries = [
|
||||
|
||||
# Find the LLVM libraries to link in.
|
||||
libraries = [ConfigLibraries(
|
||||
os.path.join(BIN_DIR, "llvm-config"), "--libs")]
|
||||
os.path.join(MCSEMA_BIN_DIR, "llvm-config"), "--libs")]
|
||||
libraries.extend(system_libraries)
|
||||
|
||||
# Create a program that lifts a CFG protobuf file into
|
||||
cfg_to_bc = TargetExecutable(
|
||||
os.path.join(BUILD_DIR, "cfg_to_bc"),
|
||||
os.path.join(MCSEMA_BUILD_DIR, "cfg_to_bc"),
|
||||
source_files=[SourceFile(f) for f in source_paths],
|
||||
object_files=object_files,
|
||||
libraries=libraries)
|
||||
|
||||
# Create an LLVM plugin for optimizing lifted bitcode files.
|
||||
libOptimize = TargetLibrary(
|
||||
os.path.join(BUILD_DIR, "libOptimize.{}".format(SHARED_LIB_EXT)),
|
||||
os.path.join(MCSEMA_BUILD_DIR, "libOptimize.{}".format(SHARED_LIB_EXT)),
|
||||
source_files=[
|
||||
SourceFile(os.path.join(MCSEMA_DIR, "mcsema", "Optimize.cpp"))])
|
||||
|
||||
@@ -372,32 +75,32 @@ def BuildTests(arch, bits, suffix, has_avx, has_avx512):
|
||||
# Create an executable that will create a CFG file describing all of
|
||||
# the testcases for this arch/config.
|
||||
gen_cfg = TargetExecutable(
|
||||
os.path.join(BUILD_DIR, "gen_cfg_{}{}".format(arch, suffix)),
|
||||
os.path.join(MCSEMA_BUILD_DIR, "gen_cfg_{}{}".format(arch, suffix)),
|
||||
source_files=[
|
||||
SourceFile(
|
||||
os.path.join(TEST_DIR, "X86", "Generate.cpp"),
|
||||
os.path.join(MCSEMA_TEST_DIR, "X86", "Generate.cpp"),
|
||||
extra_args=macro_args),
|
||||
|
||||
# The `Tests.S` file needs to be compiled with extra arguments
|
||||
# that enable Clang to assemble code with extra features.
|
||||
SourceFile(
|
||||
os.path.join(TEST_DIR, "X86", "Tests.S"),
|
||||
os.path.join(MCSEMA_TEST_DIR, "X86", "Tests.S"),
|
||||
extra_args=macro_args+target_args+["-DIN_TEST_GENERATOR"]),
|
||||
|
||||
SourceFile(os.path.join(SRC_DIR, "CFG", "CFG.cpp"))],
|
||||
SourceFile(os.path.join(MCSEMA_SRC_DIR, "CFG", "CFG.cpp"))],
|
||||
object_files=object_files,
|
||||
libraries=libraries)
|
||||
|
||||
# CFG protobuf that will describe the test cases.
|
||||
cfg_file = os.path.join(
|
||||
GEN_DIR, "tests", "cfg_{}{}".format(arch, suffix))
|
||||
MCSEMA_GEN_DIR, "tests", "cfg_{}{}".format(arch, suffix))
|
||||
|
||||
# Lifted bitcode of the test cases.
|
||||
bc_file = os.path.join(
|
||||
GEN_DIR, "tests", "bc_{}{}".format(arch, suffix))
|
||||
MCSEMA_GEN_DIR, "tests", "bc_{}{}".format(arch, suffix))
|
||||
|
||||
sem_file = os.path.join(
|
||||
GEN_DIR, "sem_{}{}.bc".format(arch, suffix))
|
||||
MCSEMA_GEN_DIR, "sem_{}{}.bc".format(arch, suffix))
|
||||
|
||||
MakeDirsForFile(cfg_file)
|
||||
|
||||
@@ -416,16 +119,16 @@ def BuildTests(arch, bits, suffix, has_avx, has_avx512):
|
||||
|
||||
# Build the test runner.
|
||||
run_tests = TargetExecutable(
|
||||
os.path.join(BUILD_DIR, "run_tests_{}{}".format(arch, suffix)),
|
||||
os.path.join(MCSEMA_BUILD_DIR, "run_tests_{}{}".format(arch, suffix)),
|
||||
source_files=[
|
||||
SourceFile(
|
||||
"{}.bc".format(bc_file),
|
||||
extra_args=["-O3", "-mno-avx", "-mno-sse"]),
|
||||
SourceFile(
|
||||
os.path.join(TEST_DIR, "X86", "Tests.S"),
|
||||
os.path.join(MCSEMA_TEST_DIR, "X86", "Tests.S"),
|
||||
extra_args=macro_args+target_args),
|
||||
SourceFile(
|
||||
os.path.join(TEST_DIR, "X86", "Run.cpp"),
|
||||
os.path.join(MCSEMA_TEST_DIR, "X86", "Run.cpp"),
|
||||
extra_args=macro_args)],
|
||||
object_files=[
|
||||
StaticLibrary("gflags"),
|
||||
@@ -442,20 +145,5 @@ for target in [("x86", 32), ("amd64", 64)]:
|
||||
suffix, has_avx, has_avx512 = avx
|
||||
Task(BuildTests, arch, bits, suffix, has_avx, has_avx512)
|
||||
|
||||
decree = TargetExecutable(
|
||||
os.path.join(BUILD_DIR, "decree"),
|
||||
source_files=[
|
||||
SourceFile(os.path.join(TOOLS_DIR, "CGC", "Process.cpp")),
|
||||
SourceFile(os.path.join(TOOLS_DIR, "CGC", "Snapshot.cpp")),
|
||||
SourceFile(os.path.join(TOOLS_DIR, "CGC", "Runtime.cpp"))],
|
||||
object_files=[
|
||||
StaticLibrary("gflags"),
|
||||
StaticLibrary("glog")],
|
||||
libraries=[
|
||||
LinkerLibrary("dl"),
|
||||
LinkerLibrary("pthread"),
|
||||
LinkerLibrary("c++", os="mac"),
|
||||
LinkerLibrary("c++abi", os="mac")])
|
||||
|
||||
# Wait for all executors to finish.
|
||||
FinishAllTasks()
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved.
|
||||
|
||||
import glob
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
MCSEMA_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
CC = os.path.join(MCSEMA_DIR, "third_party", "bin", "clang")
|
||||
CXX = os.path.join(MCSEMA_DIR, "third_party", "bin", "clang++")
|
||||
|
||||
OS = {
|
||||
"darwin": "mac",
|
||||
"linux": "linux",
|
||||
"linux2": "linux",
|
||||
"win32": "win",
|
||||
}[sys.platform]
|
||||
|
||||
SHARED_LIB_EXT = {
|
||||
"linux": "so",
|
||||
"mac": "dylib",
|
||||
"win": "dll",
|
||||
}[OS]
|
||||
|
||||
MCSEMA_SRC_DIR = os.path.join(MCSEMA_DIR, "mcsema")
|
||||
MCSEMA_BUILD_DIR = os.path.join(MCSEMA_DIR, "build")
|
||||
MCSEMA_TEST_DIR = os.path.join(MCSEMA_DIR, "tests")
|
||||
MCSEMA_GEN_DIR = os.path.join(MCSEMA_DIR, "generated")
|
||||
MCSEMA_INCLUDE_DIR = os.path.join(MCSEMA_DIR, "third_party", "include")
|
||||
MCSEMA_BIN_DIR = os.path.join(MCSEMA_DIR, "third_party", "bin")
|
||||
MCSEMA_LIB_DIR = os.path.join(MCSEMA_DIR, "third_party", "lib")
|
||||
|
||||
try:
|
||||
import concurrent.futures
|
||||
POOL = concurrent.futures.ThreadPoolExecutor(max_workers=32)
|
||||
TASKS = []
|
||||
|
||||
def Task(func, *args, **kargs):
|
||||
global POOL, TASKS
|
||||
future = POOL.submit(func, *args, **kargs)
|
||||
TASKS.append(future)
|
||||
return future
|
||||
|
||||
def FinishAllTasks():
|
||||
global TASKS
|
||||
for task in TASKS:
|
||||
task.result()
|
||||
POOL.shutdown()
|
||||
|
||||
# Don't have a thread pool available.
|
||||
except:
|
||||
def Task(func, *args, **kargs):
|
||||
return func(*args, **kargs)
|
||||
def FinishAllTasks():
|
||||
pass
|
||||
|
||||
CXX_FLAGS = [
|
||||
# Enable warnings.
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-pedantic",
|
||||
|
||||
# Disable specific warnings.
|
||||
"-Wno-nested-anon-types",
|
||||
"-Wno-extended-offsetof",
|
||||
"-Wno-gnu-anonymous-struct",
|
||||
"-Wno-variadic-macros",
|
||||
"-Wno-gnu-zero-variadic-macro-arguments",
|
||||
"-Wno-error=unused-command-line-argument",
|
||||
"-Wno-override-module",
|
||||
|
||||
# Features.
|
||||
"-fno-omit-frame-pointer",
|
||||
"-fno-rtti",
|
||||
"-fno-exceptions",
|
||||
"-fvisibility-inlines-hidden",
|
||||
"-std=gnu++11",
|
||||
|
||||
# Macros.
|
||||
'-DMCSEMA_DIR="{}"'.format(MCSEMA_DIR),
|
||||
'-DMCSEMA_OS="{}"'.format(OS),
|
||||
"-D__STDC_LIMIT_MACROS",
|
||||
"-D__STDC_CONSTANT_MACROS",
|
||||
"-DGOOGLE_PROTOBUF_NO_RTTI",
|
||||
"-DNDEBUG",
|
||||
|
||||
# Includes.
|
||||
"-isystem", MCSEMA_INCLUDE_DIR,
|
||||
"-I{}".format(MCSEMA_DIR),
|
||||
|
||||
# Output info.
|
||||
"-fPIC",
|
||||
"-fpie",
|
||||
"-g3",
|
||||
"-m64",
|
||||
]
|
||||
|
||||
|
||||
def Command(*args):
|
||||
"""Executes a command and waits for it to finish. If it fails
|
||||
then the command itself is printed out."""
|
||||
args = [str(a) for a in args]
|
||||
try:
|
||||
return subprocess.check_output(args)
|
||||
except:
|
||||
print "{}\n\n".format(" ".join(args))
|
||||
pass
|
||||
|
||||
|
||||
def MakeDirsForFile(file_name):
|
||||
"""Recursively make directories that lead to a particular
|
||||
file name."""
|
||||
dir_name = os.path.dirname(file_name)
|
||||
while not os.path.exists(dir_name):
|
||||
try:
|
||||
os.makedirs(dir_name)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class FileName(object):
|
||||
"""File name wrapper. A file name is either a string or a
|
||||
Future returning a string."""
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def __str__(self):
|
||||
if isinstance(self.path, str) or isinstance(self.path, unicode):
|
||||
return os.path.abspath(self.path)
|
||||
elif hasattr(self.path, 'result'):
|
||||
return os.path.abspath(str(self.path.result()))
|
||||
assert False
|
||||
|
||||
|
||||
class _File(object):
|
||||
"""Generic file abstraction with a method of extracting the
|
||||
file location."""
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def Paths(self):
|
||||
return [self.path]
|
||||
|
||||
def __str__(self):
|
||||
return str(self.path)
|
||||
|
||||
|
||||
class _SourceFile(_File):
|
||||
"""Source file that will be compiled."""
|
||||
# Dictionary for memoizing the compilation of source files.
|
||||
CACHE = {}
|
||||
|
||||
def __init__(self, source_path, target_path, extra_args):
|
||||
super(_SourceFile, self).__init__(FileName(Task(
|
||||
self._Build,
|
||||
source_path,
|
||||
target_path,
|
||||
extra_args)))
|
||||
|
||||
def _Build(self, source_path, target_path, extra_args):
|
||||
MakeDirsForFile(target_path)
|
||||
args = [CXX]
|
||||
|
||||
if "mac" == OS:
|
||||
args.append("-stdlib=libc++")
|
||||
|
||||
args.extend(CXX_FLAGS)
|
||||
args.extend(extra_args)
|
||||
args.extend([
|
||||
"-c", source_path,
|
||||
"-o", target_path])
|
||||
Command(*args)
|
||||
return target_path
|
||||
|
||||
|
||||
def SourceFile(path, extra_args=[]):
|
||||
"""Memoized source file compiler. Names compiled object files
|
||||
in terms of the extra args and the path to the source file."""
|
||||
|
||||
path = os.path.abspath(str(path))
|
||||
key = hashlib.md5("{}{}".format(path, "".join(extra_args))).hexdigest()
|
||||
target_path = os.path.join(os.sep, "tmp", "build", "{}.o".format(key))
|
||||
|
||||
if target_path not in _SourceFile.CACHE:
|
||||
_SourceFile.CACHE[target_path] = _SourceFile(
|
||||
path, target_path, extra_args)
|
||||
|
||||
return _SourceFile.CACHE[target_path]
|
||||
|
||||
|
||||
class StaticLibrary(_File):
|
||||
"""Pre-compiled library within the source/library dirs."""
|
||||
SEARCH_PATHS = [MCSEMA_LIB_DIR, MCSEMA_BUILD_DIR]
|
||||
|
||||
def __init__(self, name):
|
||||
super(StaticLibrary, self).__init__(self._FindLib(name))
|
||||
|
||||
def _FindLib(self, name):
|
||||
abs_path = os.path.abspath(name)
|
||||
if os.path.exists(abs_path):
|
||||
return abs_path
|
||||
for where in self.SEARCH_PATHS:
|
||||
for ext in ("o", "bc", "so", "dylib", "a"):
|
||||
for prefix in ("lib", ""):
|
||||
path = os.path.join(where, "{}{}.{}".format(prefix, name, ext))
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
print "Warning: cannot find object file: {}".format(name)
|
||||
return name
|
||||
|
||||
|
||||
class ConfigLibraries(object):
|
||||
"""Set of libraries returned from a configuration command."""
|
||||
def __init__(self, *args):
|
||||
self.paths = subprocess.check_output(args).strip().split(" ")
|
||||
|
||||
def Paths(self):
|
||||
return self.paths
|
||||
|
||||
|
||||
class LinkerLibrary(object):
|
||||
"""A library that the linker will figure out how to find."""
|
||||
def __init__(self, name, os=None):
|
||||
global OS
|
||||
self._name = name
|
||||
self._include = True
|
||||
if os and OS != os:
|
||||
self._include = False
|
||||
|
||||
def Paths(self):
|
||||
paths = []
|
||||
if self._include:
|
||||
paths.append("-l{}".format(self._name))
|
||||
return paths
|
||||
|
||||
|
||||
class _Target(_File):
|
||||
"""Generic target that must be compiled."""
|
||||
def __init__(self, path, source_files=[], object_files=[], libraries=[]):
|
||||
path = os.path.abspath(path)
|
||||
MakeDirsForFile(path)
|
||||
super(_Target, self).__init__(FileName(Task(
|
||||
self._Build,
|
||||
path,
|
||||
source_files,
|
||||
object_files,
|
||||
libraries)))
|
||||
|
||||
def _Build(self, path, source_files, object_files, libraries):
|
||||
args = [CXX]
|
||||
args.extend(CXX_FLAGS)
|
||||
|
||||
if "mac" == OS:
|
||||
args.append("-stdlib=libc++")
|
||||
|
||||
args.extend(self.extra_args)
|
||||
args.extend([
|
||||
"-o",
|
||||
path,
|
||||
"-L{}".format(MCSEMA_LIB_DIR)])
|
||||
|
||||
if "linux" == OS:
|
||||
args.extend([
|
||||
"-Wl,-z,now",
|
||||
"-Wl,-rpath={}".format(MCSEMA_LIB_DIR),
|
||||
"-Wl,-gc-sections",
|
||||
"-Wl,-E"])
|
||||
|
||||
elif "mac" == OS:
|
||||
args.extend([
|
||||
"-Xlinker", "-rpath", "-Xlinker", MCSEMA_LIB_DIR,
|
||||
"-Wl,-dead_strip",])
|
||||
|
||||
for src in source_files:
|
||||
args.extend(src.Paths())
|
||||
|
||||
for obj in object_files:
|
||||
args.extend(obj.Paths())
|
||||
|
||||
for lib in libraries:
|
||||
args.extend(lib.Paths())
|
||||
|
||||
Command(*args)
|
||||
return path
|
||||
|
||||
def Wait(self):
|
||||
[str(p) for p in self.Paths()]
|
||||
|
||||
|
||||
class TargetExecutable(_Target):
|
||||
"""Represents an individual binary executable file."""
|
||||
def __init__(self, *args, **kargs):
|
||||
self.extra_args = []
|
||||
super(TargetExecutable, self).__init__(*args, **kargs)
|
||||
|
||||
def Execute(self, *args):
|
||||
Command(self.path, *args)
|
||||
|
||||
|
||||
class TargetLibrary(_Target):
|
||||
"""Shared library that must be compiled by the system."""
|
||||
def __init__(self, *args, **kargs):
|
||||
if "linux" == OS:
|
||||
self.extra_args = ["-shared"]
|
||||
elif "mac" == OS:
|
||||
self.extra_args = [
|
||||
"-Wl,-flat_namespace",
|
||||
"-Wl,-undefined,suppress",
|
||||
"-dynamiclib"]
|
||||
super(TargetLibrary, self).__init__(*args, **kargs)
|
||||
|
||||
@@ -21,7 +21,7 @@ from generated.CFG import CFG_pb2
|
||||
CALL_ITYPES = frozenset([idaapi.NN_call, idaapi.NN_callfi, idaapi.NN_callni])
|
||||
JMP_ITYPES = frozenset([idaapi.NN_jmp, idaapi.NN_jmpfi, idaapi.NN_jmpni])
|
||||
SYSCALL_ITYPES = frozenset([idaapi.NN_int, idaapi.NN_syscall,
|
||||
idaapi.NN_sysenter])
|
||||
idaapi.NN_sysenter, idaapi.NN_bound])
|
||||
PREFIX_ITYPES = frozenset([idaapi.NN_lock, idaapi.NN_rep, idaapi.NN_repe,
|
||||
idaapi.NN_repne])
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/* Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved. */
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
#include <sstream>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "mcsema/Arch/Runtime/Intrinsics.h"
|
||||
|
||||
#include "tools/CGC/Process.h"
|
||||
#include "tools/CGC/Snapshot.h"
|
||||
|
||||
DECLARE_string(lifted_dir);
|
||||
|
||||
namespace cgc {
|
||||
namespace {
|
||||
|
||||
// Open up a shared library that represents the lifted code from a
|
||||
// CGC binary.
|
||||
static void *OpenLiftedCode(int process_id) {
|
||||
std::stringstream file_name;
|
||||
const char *begin = "";
|
||||
if ('/' != FLAGS_lifted_dir[0]) {
|
||||
begin = ".";
|
||||
}
|
||||
file_name << FLAGS_lifted_dir << begin << "/mcsema.lifted." << process_id;
|
||||
LOG_IF(FATAL, access(file_name.str().c_str(), R_OK))
|
||||
<< "Unable to locate lifted code: " << file_name.str();
|
||||
|
||||
dlerror(); // Clear out any errors.
|
||||
|
||||
// Go find the symbol.
|
||||
auto code = dlopen(file_name.str().c_str(), RTLD_NOW | RTLD_LOCAL);
|
||||
LOG_IF(FATAL, !code)
|
||||
<< "Unable to load lifted code from " << file_name.str()
|
||||
<< ": " << dlerror();
|
||||
return code;
|
||||
}
|
||||
|
||||
// Allocate a new State structure. These can't be allocated directly because
|
||||
// they have deleted constructors.
|
||||
static State *CreateMachineState(void) {
|
||||
typedef std::aligned_storage<sizeof(State), alignof(State)>::type T;
|
||||
auto state = reinterpret_cast<State *>(new T);
|
||||
memset(state, 0, sizeof(State));
|
||||
return state;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Find the address of a lifted block. Once found, cache the address in a
|
||||
// hash table.
|
||||
LiftedBlock *Process::Find(addr_t addr) {
|
||||
auto &block = blocks[addr];
|
||||
if (!block) {
|
||||
block = &__mcsema_undefined_block;
|
||||
|
||||
// We have to find the block by its function name, which will be
|
||||
// `__lifted_block_<id>_<address>`, where `id` will be `1` (because the
|
||||
// code we lifted will be the "first" binary in the bitcode file, and we
|
||||
// won't add in more).
|
||||
std::stringstream func;
|
||||
func << "__lifted_block_1_0x" << std::hex << addr;
|
||||
|
||||
if (auto sym = dlsym(code, func.str().c_str())) {
|
||||
block = reinterpret_cast<LiftedBlock *>(sym);
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
// Execute some lifted code.
|
||||
void Process::Execute(void) {
|
||||
Find(this->state->gpr.rip.dword)(*state);
|
||||
}
|
||||
|
||||
// Initialize the process data structures. This chains the processes into
|
||||
// a circularly linked list.
|
||||
Process *CreateProcesses(int num_processes) {
|
||||
Process *first_process = nullptr;
|
||||
Process **next_process = nullptr;
|
||||
for (auto i = 0; i < num_processes; ++i) {
|
||||
auto process = new Process;
|
||||
if (next_process) {
|
||||
*next_process = process;
|
||||
} else {
|
||||
next_process = &(process->next);
|
||||
first_process = process;
|
||||
}
|
||||
|
||||
process->state = CreateMachineState();
|
||||
process->code = OpenLiftedCode(i + 1);
|
||||
process->is_running = true;
|
||||
LoadMemoryFromSnapshot(process, i + 1);
|
||||
}
|
||||
*next_process = first_process;
|
||||
return first_process;
|
||||
}
|
||||
|
||||
} // namespace cgc
|
||||
@@ -1,36 +0,0 @@
|
||||
/* Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved. */
|
||||
|
||||
#ifndef TOOLS_CGC_PROCESS_H_
|
||||
#define TOOLS_CGC_PROCESS_H_
|
||||
|
||||
#define ADDRESS_WIDTH_BITS 32
|
||||
#define HAS_FEATURE_AVX 0
|
||||
#define HAS_FEATURE_AVX512 0
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "mcsema/Arch/X86/Runtime/State.h"
|
||||
|
||||
namespace cgc {
|
||||
|
||||
typedef void (LiftedBlock)(State &);
|
||||
|
||||
struct Process final {
|
||||
Process *next;
|
||||
bool is_running;
|
||||
void *code;
|
||||
uintptr_t base;
|
||||
uintptr_t limit;
|
||||
std::unordered_map<addr_t, LiftedBlock *> blocks;
|
||||
|
||||
State *state;
|
||||
|
||||
void Execute(void);
|
||||
LiftedBlock *Find(addr_t addr);
|
||||
};
|
||||
|
||||
Process *CreateProcesses(int num_processes);
|
||||
|
||||
} // namespace cgc
|
||||
|
||||
#endif // TOOLS_CGC_PROCESS_H_
|
||||
@@ -1,154 +0,0 @@
|
||||
/* Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved. */
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "tools/CGC/Process.h"
|
||||
|
||||
DEFINE_string(snapshot_dir, "", "Directory where snapshots are stored.");
|
||||
DEFINE_string(lifted_dir, "", "Directory where lifted binaries are stored.");
|
||||
DEFINE_int32(num_exe, 1, "Number of executables to run.");
|
||||
|
||||
namespace cgc {
|
||||
namespace {
|
||||
|
||||
// Currently running process.
|
||||
Process *gProcess = nullptr;
|
||||
|
||||
// Schedule the next process.
|
||||
void Schedule(void) {
|
||||
gProcess = gProcess->next;
|
||||
}
|
||||
|
||||
// Run some code from the current process. If the process was running then
|
||||
// this will return `true`, even if the process hit an error, because it
|
||||
// still made progress by going from a running to error state.
|
||||
bool Run(void) {
|
||||
auto was_running = gProcess->is_running;
|
||||
if (was_running) gProcess->Execute();
|
||||
return was_running;
|
||||
}
|
||||
|
||||
// Access memory within the current process.
|
||||
//
|
||||
// TODO(pag): Implement a try read/write operations instead of using this.
|
||||
template <typename T>
|
||||
ALWAYS_INLINE static T &AccessMemory(addr_t addr) {
|
||||
return *reinterpret_cast<T *>(gProcess->base + addr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace cgc
|
||||
|
||||
extern "C" {
|
||||
|
||||
#define MAKE_RW_MEMORY(size) \
|
||||
uint ## size ## _t __mcsema_read_memory_ ## size(addr_t addr) {\
|
||||
return cgc::AccessMemory<uint ## size ## _t>(addr); \
|
||||
} \
|
||||
void __mcsema_write_memory_ ## size ( \
|
||||
addr_t addr, const uint ## size ## _t in) { \
|
||||
cgc::AccessMemory<uint ## size ## _t>(addr) = in; \
|
||||
}
|
||||
|
||||
#define MAKE_RW_VEC_MEMORY(size) \
|
||||
void __mcsema_read_memory_v ## size(\
|
||||
addr_t addr, vec ## size ## _t &out) { \
|
||||
out = cgc::AccessMemory<vec ## size ## _t>(addr); \
|
||||
} \
|
||||
void __mcsema_write_memory_v ## size (\
|
||||
addr_t addr, const vec ## size ## _t &in) { \
|
||||
cgc::AccessMemory<vec ## size ## _t>(addr) = in; \
|
||||
}
|
||||
|
||||
MAKE_RW_MEMORY(8)
|
||||
MAKE_RW_MEMORY(16)
|
||||
MAKE_RW_MEMORY(32)
|
||||
MAKE_RW_MEMORY(64)
|
||||
|
||||
MAKE_RW_VEC_MEMORY(8)
|
||||
MAKE_RW_VEC_MEMORY(16)
|
||||
MAKE_RW_VEC_MEMORY(32)
|
||||
MAKE_RW_VEC_MEMORY(64)
|
||||
MAKE_RW_VEC_MEMORY(128)
|
||||
MAKE_RW_VEC_MEMORY(256)
|
||||
MAKE_RW_VEC_MEMORY(512)
|
||||
|
||||
// Address computation intrinsic. This is only used for non-zero
|
||||
// `address_space`d memory accesses.
|
||||
addr_t __mcsema_compute_address(const State &, addr_t addr, int) {
|
||||
LOG(FATAL) << "Computing address of FS- or GS-segmented memory.";
|
||||
return addr;
|
||||
}
|
||||
|
||||
void __mcsema_barrier_load_load(void) {}
|
||||
void __mcsema_barrier_load_store(void) {}
|
||||
void __mcsema_barrier_store_load(void) {}
|
||||
void __mcsema_barrier_store_store(void) {}
|
||||
|
||||
void __mcsema_atomic_begin(addr_t, uint32_t) {}
|
||||
void __mcsema_atomic_end(addr_t, uint32_t) {}
|
||||
|
||||
void __mcsema_defer_inlining(void) {}
|
||||
|
||||
void __mcsema_error(State &state) {
|
||||
LOG(ERROR) << "Error: " << std::hex << state.gpr.rip.dword;
|
||||
cgc::gProcess->is_running = false;
|
||||
}
|
||||
|
||||
void __mcsema_undefined_block(State &state) {
|
||||
LOG(ERROR) << "Undefined block: " << std::hex << state.gpr.rip.dword;
|
||||
cgc::gProcess->is_running = false;
|
||||
}
|
||||
|
||||
void __mcsema_function_call(State &) {}
|
||||
void __mcsema_function_return(State &) {}
|
||||
void __mcsema_jump(State &) {}
|
||||
|
||||
addr_t __mcsema_conditional_branch(bool cond, addr_t addr_true,
|
||||
addr_t addr_false) {
|
||||
return cond ? addr_true : addr_false;
|
||||
}
|
||||
|
||||
void __mcsema_system_call(State &state) {
|
||||
LOG(FATAL) << "System call: " << std::hex << state.gpr.rip.dword;
|
||||
}
|
||||
|
||||
void __mcsema_system_return(State &state) {
|
||||
LOG(FATAL) << "System return: " << std::hex << state.gpr.rip.dword;
|
||||
}
|
||||
|
||||
void __mcsema_interrupt_call(State &state) {
|
||||
LOG(FATAL) << "Interrupt call: " << std::hex << state.gpr.rip.dword;
|
||||
}
|
||||
|
||||
void __mcsema_interrupt_return(State &state) {
|
||||
LOG(FATAL) << "Interrupt return: " << std::hex << state.gpr.rip.dword;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **) {
|
||||
google::SetUsageMessage(std::string(argv[0]) + " [options]");
|
||||
google::ParseCommandLineFlags(&argc, &argv, false);
|
||||
|
||||
LOG_IF(FATAL, 0 >= FLAGS_num_exe)
|
||||
<< "One or more executables must be available.";
|
||||
|
||||
LOG_IF(FATAL, FLAGS_snapshot_dir.empty())
|
||||
<< "Must provide a unique path to a directory where the "
|
||||
<< "snapshots are located persisted.";
|
||||
|
||||
LOG_IF(FATAL, FLAGS_lifted_dir.empty())
|
||||
<< "Must provide a unique path to a directory where the "
|
||||
<< "lifted code for each process is located.";
|
||||
|
||||
cgc::gProcess = cgc::CreateProcesses(FLAGS_num_exe);
|
||||
for (bool made_progress = true; made_progress; cgc::Schedule()) {
|
||||
for (made_progress = false; cgc::Run(); cgc::Schedule()) {
|
||||
made_progress = true;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} // extern C
|
||||
@@ -1,153 +0,0 @@
|
||||
/* Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved. */
|
||||
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
#include <sstream>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "tools/CGC/Snapshot.h"
|
||||
#include "tools/CGC/Process.h"
|
||||
|
||||
DECLARE_string(snapshot_dir);
|
||||
|
||||
namespace granary {
|
||||
|
||||
enum : uintptr_t {
|
||||
kPageSize = 4096,
|
||||
k1GiB = 1ULL << 30ULL,
|
||||
kProcessSize = k1GiB * 4ULL,
|
||||
k1MiB = 1048576,
|
||||
kStackSize = 8 * k1MiB,
|
||||
kStackLimitPage = 0xbaaaa000U,
|
||||
kStackEnd = kStackLimitPage + kPageSize,
|
||||
kStackBegin = kStackEnd - kStackSize,
|
||||
kMaxAddress = 0xB8000000U,
|
||||
kReserveNumRanges = 32UL,
|
||||
kMagicPageBegin = 0x4347c000U,
|
||||
kMagicPageEnd = kMagicPageBegin + kPageSize,
|
||||
kTaskSize = 0xFFFFe000U
|
||||
};
|
||||
|
||||
// A range of mapped memory.
|
||||
struct MappedRange32 final {
|
||||
public:
|
||||
inline size_t Size(void) const {
|
||||
return end - begin;
|
||||
}
|
||||
|
||||
off_t fd_offs;
|
||||
uint32_t begin;
|
||||
uint32_t end;
|
||||
bool is_r;
|
||||
bool is_w;
|
||||
bool is_x;
|
||||
};
|
||||
|
||||
// On-disk layout of a snapshot file.
|
||||
struct alignas(kPageSize) Snapshot32File final {
|
||||
public:
|
||||
struct Meta {
|
||||
struct {
|
||||
char magic[4];
|
||||
int exe_num;
|
||||
} __attribute__((packed));
|
||||
struct user_regs_struct gregs;
|
||||
struct user_fpregs_struct fpregs;
|
||||
};
|
||||
|
||||
Meta meta;
|
||||
|
||||
enum {
|
||||
kNumPages = 4,
|
||||
kNumBytes = kNumPages * kPageSize,
|
||||
|
||||
kMaxNumMappedRanges = (kNumBytes - sizeof(Meta)) / sizeof(MappedRange32)
|
||||
};
|
||||
|
||||
MappedRange32 ranges[kMaxNumMappedRanges];
|
||||
};
|
||||
|
||||
static_assert(
|
||||
sizeof(FPU) == sizeof(struct user_fpregs_struct),
|
||||
"Invalid structure packing of `FPU` or `struct user_fpregs_struct`.");
|
||||
|
||||
} // namespace granary
|
||||
namespace cgc {
|
||||
|
||||
void LoadMemoryFromSnapshot(Process *process, int pid) {
|
||||
using namespace granary;
|
||||
|
||||
auto base = mmap64(
|
||||
nullptr,
|
||||
kProcessSize,
|
||||
PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
|
||||
-1,
|
||||
0);
|
||||
|
||||
process->base = reinterpret_cast<uintptr_t>(base);
|
||||
process->limit = process->base + kProcessSize;
|
||||
|
||||
const char *begin = "";
|
||||
if ('/' != FLAGS_snapshot_dir[0]) {
|
||||
begin = ".";
|
||||
}
|
||||
|
||||
std::stringstream file_name;
|
||||
file_name << FLAGS_snapshot_dir << begin
|
||||
<< "/grr.snapshot." << pid << ".persist";
|
||||
|
||||
// Open the snapshot file.
|
||||
auto fd = open(file_name.str().c_str(), O_RDONLY | O_LARGEFILE);
|
||||
LOG_IF(FATAL, -1 == fd)
|
||||
<< "Unable to open snapshot file: " << file_name.str();
|
||||
|
||||
auto file = reinterpret_cast<Snapshot32File *>(
|
||||
mmap64(nullptr, sizeof(Snapshot32File),
|
||||
PROT_READ, MAP_POPULATE | MAP_PRIVATE, fd, 0));
|
||||
|
||||
// Map each page range into the process memory.
|
||||
for (const auto &range : file->ranges) {
|
||||
auto range_base = reinterpret_cast<void *>(process->base + range.begin);
|
||||
mmap64(
|
||||
range_base,
|
||||
range.end - range.begin,
|
||||
(range.is_w ? PROT_READ | PROT_WRITE : PROT_READ),
|
||||
MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE,
|
||||
fd,
|
||||
range.fd_offs);
|
||||
}
|
||||
|
||||
// Initialize the XMM registers.
|
||||
memcpy(&(process->state->fpu), &(file->meta.fpregs), sizeof(FPU));
|
||||
for (auto i = 0; i < 16; ++i) {
|
||||
process->state->vec[i].xmm = process->state->fpu.xmm[i];
|
||||
}
|
||||
|
||||
// Initialize the flags.
|
||||
process->state->rflag.flat = file->meta.gregs.eflags;
|
||||
process->state->aflag.af = process->state->rflag.af;
|
||||
process->state->aflag.cf = process->state->rflag.cf;
|
||||
process->state->aflag.df = process->state->rflag.df;
|
||||
process->state->aflag.of = process->state->rflag.of;
|
||||
process->state->aflag.pf = process->state->rflag.pf;
|
||||
process->state->aflag.sf = process->state->rflag.sf;
|
||||
process->state->aflag.zf = process->state->rflag.zf;
|
||||
|
||||
// Initialize the registers.
|
||||
process->state->gpr.rax.qword = file->meta.gregs.rax;
|
||||
process->state->gpr.rbx.qword = file->meta.gregs.rbx;
|
||||
process->state->gpr.rcx.qword = file->meta.gregs.rcx;
|
||||
process->state->gpr.rdi.qword = file->meta.gregs.rdi;
|
||||
process->state->gpr.rdx.qword = file->meta.gregs.rdx;
|
||||
process->state->gpr.rip.qword = file->meta.gregs.rip;
|
||||
process->state->gpr.rsi.qword = file->meta.gregs.rsi;
|
||||
process->state->gpr.rsp.qword = file->meta.gregs.rsp;
|
||||
}
|
||||
|
||||
} // namespace cgc
|
||||
@@ -1,14 +0,0 @@
|
||||
/* Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved. */
|
||||
|
||||
#ifndef TOOLS_CGC_SNAPSHOT_H_
|
||||
#define TOOLS_CGC_SNAPSHOT_H_
|
||||
|
||||
namespace cgc {
|
||||
|
||||
struct Process;
|
||||
|
||||
void LoadMemoryFromSnapshot(Process *process, int pid);
|
||||
|
||||
} // namespace cgc
|
||||
|
||||
#endif // TOOLS_CGC_SNAPSHOT_H_
|
||||
Reference in New Issue
Block a user