• Keine Ergebnisse gefunden

OS Abstraction and Driver Harness

Im Dokument Static Analysis of x86 Executables (Seite 157-161)

5.5 Related Work

6.1.3 OS Abstraction and Driver Harness

Executables in general and drivers in particular frequently interact with the op-erating system using calls to the system and driver API. As in source based analyses, these calls can be abstracted using stubs, which model the relevant side effects such as memory allocation or the invocation of callback routines.

Following the approach of the source code software model checker SDV [12], Jakstab loads the driver along with a separate harnessmodule, which includes

system call abstractions relevant to drivers and contains amainfunction that non-deterministically exercises the driver’s initialization and dispatch routines. The harness is written in C and compiled into an dynamic library (DLL) for loading;

it is based on SDV’s osmodel.c and follows SDV’s invocation scheme for PnP drivers defined therein (using macros for invoking driver functions):

DriverEntry ; sdv RunAddDevice;

sdv RunStartDevice;

( DoNothing ||

sdv RunStartIo ||

sdv RunDPC ||

sdv RunISR ||

sdv RunCancelRoutine ||

sdv RunDispatchFunction );

sdv RunRemoveDevice;

sdv RunUnload;

That is, it first runs theDriverEntryroutine (the main function of the driver exe-cutable) that initializes internal data structures and registers the other functions provided by the driver. From these the harness calls the functions for adding and starting a new physical device to allow the driver to set up all data for a new device. After these steps are complete, the harness nondeterministically executes either one of the registered dispatch functions for the various types of IRPs, or it invokes deferred procedure calls (DPCs), cancel routines, or interrupt service requests (ISR) set up by the driver. Finally, the harness signals to the driver that the device is removed and calls the driver’s unload function.

For the experiments, the specifications were manually encoded into the har-ness by inserting state variables and assertions at the locations where SDV places hooks into its specification files. The IL statementsalloc,free,havoc, andassertare exclusively generated by the harness, since they do not correspond to any real x86 instructions. These statements are encoded into the compiled harness object

6.1 Analyzing Untrusted Driver Binaries

IL Statement ASM code Notes

alloc v,e lock rep inc eax Implemented as function with allo-cated pointer as return value (eax).

free p lock rep not p not can take registers or memory operands.

assert x>u y mov edx, x

lock rep add edx, y

The second instruction is translated to the actual assertion.

assert x≥u y mov edx, x

lock rep adc edx, y

assert x=y mov edx, x

lock rep cmp edx, y

Currently only assertion expressions

>u,u, and=are implemented.

havoc v<u x lock rep sub eax, x mov v, eax

The first instruction is translated to the actual havoc statement, the sec-ond stores the result in the desig-nated variable.

Table 6.1: (Pseudo-) instructions that can be inlined for using abstract IL state-ments in the C-language harness.

file using combinations of instructions and prefixes that are illegal according to official Intel documentation [74], but which are supported by the inline assem-bler nonetheless. During on-demand disassembly, Jakstab detects these instruc-tions and directly maps them to the corresponding IL statements. The available instruction patterns to encode IL statements are listed in Table 6.1. Only the 32 bit variants of the instructions are shown; for each statement there are also 16 and 8 bit variants that use the 16 and 8 bit subregisters, respectively (e.g.,axand alforeax).

Several parts of the SDV harness and rules had to be modified to make it suit-able for binary analysis. For example, the preprocessor macroIoMarkIrpPending, which sets a bit in the control word of IRPs, is intercepted by SDV to change the state for thePendedCompletedRequestrule. Since macro invocations are no longer

explicit in the binary, the rule’s assertion had to be modified to check the bit directly instead of a separate state variable. Furthermore, SDV’s statement for non-determinism had to be replaced by eitherhavocornondet, depending on the context.

Another issue arises from the fact that the SDV harness was not constructed with memory safety in mind. Many function stubs coarsely abstract return val-ues where they actually should allocate memory, which leads to potential deref-erences of uninitialized pointers. In several places it was therefore necessary to refine SDV’s harness with explicit memory allocation. For the same reason it was also required to add rudimentary support for threads, which are used for initialization or packet handling by some drivers. The original SDV harness sim-ply ignores calls to thread creating Windows API functions such as PsCreateSys-temThread, and thus many data structures stay uninitialized. Jakstab does not model concurrency, but the harness can emulate the behavior of initialization threads that only run for a short amount of time by simply calling the thread’s entry routine. In case of a long running thread (e.g., for IRP processing), this causes problems, however, when the thread loops forever until a shared flag is set from the main thread. In such cases the analysis will reach a fixpoint over the infinite loop and never exit the loop, leaving large parts of the real driver code unexplored. Without real support for reasoning about concurrency, the exper-iments used two different harness configurations depending on the nature of threads used in the drivers: Either calls to functions for thread creation are sim-ply translated to calls of the thread’s entry function, or they are skipped, which is the default behavior of the SDV harness. Which harness configuration was chosen for the experiments is shown in the column labeledMTof Table 6.3.

There are occasions where the execution model of the harness causes memory safety violations in the driver which are impossible at runtime. SDV’s execution model for PnP drivers processes a deferred procedure call (DPC) if it has been registered by the driver. In actual executions, however, synchronization opera-tions force the DPC to finish at some specific point in time. After that, the context of the procedure call (e.g., the IRP currently being processed) can become

in-6.1 Analyzing Untrusted Driver Binaries

valid. In this case, the harness invokes the DPC on an invalid context, which can cause false alarms on using uninitialized pointers. To alleviate this problem, a second configuration option for the Jakstab harness defines whether the objects representing IRPs are reinitialized between the non-deterministic method invo-cations. If IRPs are not reset but kept, the context stays valid for DPCs. A full solution to this issue would have to take synchronization and timer events into account, which are yet another concurrency mechanism besides threads.

Im Dokument Static Analysis of x86 Executables (Seite 157-161)