interfaces: pack both asm and bin into a single TestCase object

This commit is contained in:
Oleksii Oleksenko 2022-03-24 11:04:41 +00:00
Родитель 0ef77dcc6a
Коммит c7d338c1a0
7 изменённых файлов: 103 добавлений и 22 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -1,6 +1,5 @@
cmake-build-*/
build/
.idea/
.vscode/
.lsync*
venv/

39
docs/architecture.md Normal file
Просмотреть файл

@ -0,0 +1,39 @@
# Architecture
![architecture](Arch.png)
**Under construction**
[comment]: <> (## Instruction Set Spec)
[comment]: <> (This XML file: https://www.uops.info/xml.html originating from Intel XED &#40;https://intelxed.github.io/&#41;)
[comment]: <> (Received from: `--instruction-set` &#40;or `-s`&#41; CLI argument.)
[comment]: <> (Passed down to: `Generator.__init__`)
[comment]: <> (## Generator Initializer)
[comment]: <> (None so far.)
[comment]: <> (In future, may include test case templates, grammar, etc.)
[comment]: <> (## Test Case)
[comment]: <> (An assembly file. Currently, in Intel syntax.)
[comment]: <> (Received from: `self.generator.create_test_case&#40;&#41;` + `self.generator.materialize&#40;filename&#41;`)
[comment]: <> (Passed down to: `model.load_test_case` and `executor.load_test_case`)
[comment]: <> (## Inputs)
[comment]: <> (Currently, each input is a single 32-bit integer, used later as a PRNG seed inside the test case to initialize memory and registers.)
[comment]: <> (Inputs are generated in batches; that is, Input Generator returns `List[int]`.)
[comment]: <> (Received from: `input_gen.generate&#40;...&#41;`)
[comment]: <> (Passed down to: `model.trace_test_case&#40;inputs&#41;` and `executor.trace_test_case&#40;inputs&#41;`.)

Просмотреть файл

@ -210,7 +210,7 @@ class PatternCoverage(Coverage):
self.current_patterns.append(PatternInstance(pair, pair_ids, type_))
def load_test_case(self, test_case: TestCase):
obj_file = test_case.to_binary()
obj_file = test_case.bin_path
output = run(f'objdump -D {obj_file} -b binary --no-show-raw-insn -m i386:x86-64',
shell=True,
check=False,

Просмотреть файл

@ -33,7 +33,7 @@ class X86Intel(Executor):
write_to_pseudo_file(CONF.attack_variant, "/sys/x86-executor/measurement_mode")
def load_test_case(self, test_case: TestCase):
write_to_pseudo_file(test_case.to_binary(), "/sys/x86-executor/code")
write_to_pseudo_file(test_case.bin_path, "/sys/x86-executor/code")
def trace_test_case(self, inputs: List[Input], num_measurements: int = 0) \
-> List[CombinedHTrace]:

Просмотреть файл

@ -8,12 +8,13 @@ from __future__ import annotations
import random
import abc
import iced_x86
from typing import List, Dict, Set
from instruction_set import OperandSpec, InstructionSpec, InstructionSet
from interfaces import Generator, TestCase, Operand, RegisterOperand, FlagsOperand, MemoryOperand, \
ImmediateOperand, AgenOperand, LabelOperand, OT, Instruction, BasicBlock, Function
from helpers import NotSupportedException
from helpers import NotSupportedException, run
from config import CONF
@ -58,12 +59,8 @@ class ConfigurableGenerator(Generator, abc.ABC):
pass
def create_test_case(self, asm_file: str) -> TestCase:
"""
Create a simple test case with a single BB
Run instrumentation passes and print the result into a file
"""
self.reset_generator()
self.test_case = TestCase(asm_file)
self.test_case = TestCase()
# create the main function
func = self.generate_function("test_case_main")
@ -81,9 +78,27 @@ class ConfigurableGenerator(Generator, abc.ABC):
p.run_on_test_case(self.test_case)
self.printer.print(self.test_case, asm_file)
self.test_case.asm_path = asm_file
bin_file = asm_file[:-4] + ".o"
self.assemble(asm_file, bin_file)
self.test_case.bin_path = bin_file
self.map_addresses(self.test_case, bin_file)
return self.test_case
@staticmethod
def assemble(asm_file: str, bin_file: str) -> None:
"""Assemble the test case into a stripped binary"""
run(f"as {asm_file} -o {bin_file}", shell=True, check=True)
run(f"strip --remove-section=.note.gnu.property {bin_file}", shell=True, check=True)
run(f"objcopy {bin_file} -O binary {bin_file}", shell=True, check=True)
@abc.abstractmethod
def map_addresses(self, test_case: TestCase, bin_file: str) -> None:
pass
@abc.abstractmethod
def generate_function(self, name: str) -> Function:
pass
@ -414,6 +429,28 @@ class X86Generator(ConfigurableGenerator, abc.ABC):
self.printer = X86Printer()
self.register_set = X86Registers()
def map_addresses(self, test_case: TestCase, bin_file: str) -> None:
with open(bin_file, "rb") as f:
bin_file_contents = f.read()
# get a list of relative instruction addresses
decoder = iced_x86.Decoder(64, bin_file_contents)
address_list: List[int] = []
for instruction in decoder:
address_list.append(instruction.ip)
# connect them with instructions in the test case
address_map: Dict[int, Instruction] = {}
counter = self.printer.prologue_size
for func in test_case.functions:
for bb in func:
for inst in bb:
address = address_list[counter]
address_map[address] = inst
counter += 1
test_case.address_map = address_map
def get_return_instruction(self) -> Instruction:
return Instruction("RET")

Просмотреть файл

@ -13,7 +13,6 @@ import numpy as np
from enum import Enum
from config import CONF
from helpers import run
# ==================================================================================================
@ -325,22 +324,17 @@ class Function:
class TestCase:
asm_path: str = ''
bin_path: str = ''
main: Function
functions: List[Function]
address_map: Dict[int, Instruction]
def __init__(self, path: str):
def __init__(self):
self.functions = []
self.asm_path = path
def to_binary(self) -> str:
"""
Assemble the test case into a stripped binary
"""
outfile = self.asm_path[:-4] + ".o"
run(f"as {self.asm_path} -o {outfile}", shell=True, check=True)
run(f"strip --remove-section=.note.gnu.property {outfile}", shell=True, check=True)
run(f"objcopy {outfile} -O binary {outfile}", shell=True, check=True)
return outfile
def __iter__(self):
for func in self.functions:
yield func
# ==================================================================================================
@ -479,6 +473,18 @@ class Generator(ABC):
@abstractmethod
def create_test_case(self, path: str) -> TestCase:
"""
Create a simple test case with a single BB
Run instrumentation passes and print the result into a file
"""
pass
@abstractmethod
def parse_existing_test_case(self, asm_file: str) -> TestCase:
"""
Read a test case from a file and create a complete TestCase object based on it.
Used instead of create_test_case when Revizor works with a user-provided test case.
"""
pass

Просмотреть файл

@ -196,7 +196,7 @@ class X86UnicornModel(Model):
def load_test_case(self, test_case: TestCase) -> None:
# create and read a binary
with open(test_case.to_binary(), 'rb') as f:
with open(test_case.bin_path, 'rb') as f:
self.code = f.read()
# initialize emulator in x86-64 mode