Add alternative emulation through unicorn engine#57
Add alternative emulation through unicorn engine#57ks0777 wants to merge 28 commits intoFraunhofer-AISEC:masterfrom
Conversation
64ab813 to
8d197d3
Compare
|
Waiting for unicorn-engine/unicorn#1812 to be merged. We need the additional Rust bindings in order to clear the tb cache after modifying instructions during a fault |
9e429da to
12a47c2
Compare
|
This is a nice addition! If I may, here is a suggestion to increase the flexibility of the solution toward supporting additional emulation engine support (even beyond qemu and unicorn):
The idea here is that if an additional emulation engine is added in the future, then the framework is already setup for that. The controller() function would just be modified to look for a new "otheremulatorname" member in the config file and take actions as needed there. It also means users don't have to match command line parameters to controller.py with the contents of the emulation JSON file-all the settings are in the JSON file. |
|
Thanks for your suggestion! Including, the choice of emulation engine into the configuration files sounds a like a good idea. Note that when using Unicorn, the pre-goldenrun is still performed using QEMU in order to ensure compatibility with more firmware since Unicorn can not handle any hardware related functions. Unlike QEMU, the unicorn engine is invoked through a Python module which wraps a Rust library. The required data for the initialization of Unicorn is retrieved from the pre-goldenrun. Hence, there is no need to supply additional arguments or a path to the emulator binary for Unicorn. This may change in the future if we ever decide to add addtional emulation engines, but for now these arguments only apply to QEMU. |
539dd18 to
9808021
Compare
| make | ||
| cd emulation_worker | ||
| cargo build --release | ||
| cp target/release/libemulation_worker.so ../emulation_worker.so |
There was a problem hiding this comment.
What's the advantage of this, can we just leave it in the build dir?
There was a problem hiding this comment.
I can't directly import the .so if its directory is not in the PATH. An alternative would be to add the build directory to the path before importing the .so in faultclass.py. This is easily done with 4 lines of code but overall not really clean either imo. With this solution, however, there would be no more copies of the .so file which might be less confusing.
There was a problem hiding this comment.
I agree, both solutions are not perfect. Maybe the best would be to adopt the same approach we are currently using for the faultplugin. That would mean not copying the library and instead adding a new config entry to qemuconf.json for specifying its location.
goldenrun.py
Outdated
| return [config_qemu["max_instruction_count"], experiment["data"], faultconfig] | ||
| return [ | ||
| config_qemu["max_instruction_count"], | ||
| experiments[0]["data"], |
There was a problem hiding this comment.
This breaks if we do not have a pregolden run (if no start address is specified in the config). Not sure what the best way to handle this is.
There was a problem hiding this comment.
(pre-)goldenrun experiment results are now stored in distinct variables instead of a list with varying size. If no pre-goldenrun was performed None is returned.
There was a problem hiding this comment.
Code looks good. Is it a problem for the unicorn system if we do not have a memory dump from the pregoldenrun in configs without start address?
There was a problem hiding this comment.
That's a problem, yes. We would either have to implement a pure unicorn mode that does not require the bootstrapping through QEMU or print an error and abort when unicorn emulation is requested without a start address. Is there a reason why you would not want to specify a start address? In the hybrid QEMU+Unicorn mode specifying a start address will always improve performance since the state is restored from the start address instead of fully emulating each run from the beginning.
There was a problem hiding this comment.
I guess the only use case would be faulting the very first executed instruction. I think at least for now it makes sense to only print an error if no start address is specified in Unicorn mode.
…n; allow multiple PT_LOAD segments
| g_string_append_printf(out, "Current Version of QEMU Plugin is %i, Min Version is %i\n", info->version.cur, info->version.min); | ||
| architecture = malloc(strlen(info->target_name)+1); | ||
| if (!architecture) return -1; | ||
| strcpy(architecture, info->target_name); |
Check failure
Code scanning / Flawfinder
Does not check for buffer overflows when copying to destination [MS-banned] (CWE-120). Error
| g_autoptr(GString) out = g_string_new(""); | ||
| g_string_printf(out, "QEMU Injection Plugin\n Current Target is %s\n", info->target_name); | ||
| g_string_append_printf(out, "Current Version of QEMU Plugin is %i, Min Version is %i\n", info->version.cur, info->version.min); | ||
| architecture = malloc(strlen(info->target_name)+1); |
Check notice
Code scanning / Flawfinder
Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126). Note
This PR adds the unicorn emulation engine to archie. It can be enabled by passing the
--unicornflag to the controller. With this option enabled, archie will emulate the experiments with the unicorn engine instead of QEMU. The (pre-)goldenrun is still emulated with QEMU which is necessary in order to obtain a state from which we can start our experiments from.The alternative emulation mode was implemented in a seperate Rust library and integrated into archie with minimal changes to the faultclass and controller scripts. This allows for easy reuse of the filtering and processing functions that have previously been implemented for the experiment data returned by QEMU.