This blog post introduces the release 0.8.0 of QBDI.

Tl;dr: QBDI v0.8.0 is out. This new version adds support for SIMD memory accesses and some performance improvements. You can find the prebuilt package on the QBDI website as well as the changelog detailing all the changes.

Introduction

We are glad to announce the release of QBDI 0.8.0. This new version adds support for SIMD memory accesses and a new type of callback.

For those who are not familiar with QBDI, you may have a look at the presentation at 34C3 [1].

Support for SIMD memory accesses

QBDI now supports most SIMD memory accesses [2]. As SIMD instructions may load and store a large memory range, the value of the access is not captured when the access size is too big.

Moreover, support for SIMD instructions comes with a refactoring of the existing mechanism and with support for the REP prefix for the MOVS/STOS/CMPS/LODS/SCAS instructions.

Instrumentation Rule callback

A new type of callback was added to QBDI for advanced users: InstrRuleCallback. This new callback should be used when the other APIs for instruction callbacks do not allow to precisely target the instruction to instrument.

Once registered, this callback will be called during the instrumentation process for all instructions. Given the instruction details, it enables a user to customise the callback to be used on a given instruction.

Here is an example for registering callbacks to instructions that set or use the flags register.

VMAction setFlagsCBK(VMInstanceRef vm, GPRState *gprState, FPRState *fprState, void *data) {
    // ..
    return CONTINUE;
}

VMAction useFlagsCBK(VMInstanceRef vm, GPRState *gprState, FPRState *fprState, void *data) {
    // ..
    return CONTINUE;
}

std::vector<InstrRuleDataCBK> FlagsInstrumentCB(VMInstanceRef vm, const InstAnalysis *inst, void *data) {
    if (inst->flagsAccess & REGISTER_WRITE) {
        return {InstrRuleDataCBK {POSTINST, setFlagsCBK, data}};
    }
    if (inst->flagsAccess & REGISTER_READ) {
        return {InstrRuleDataCBK {PREINST, useFlagsCBK, data}};
    }
    return {};
}

vm.addInstrRule(FlagsInstrumentCB, ANALYSIS_OPERANDS, &CBData);

Performance improvement

This release includes a new mechanism to improve the performance when floating-point registers are not used by the instruction to instrument. When QBDI detects that these registers are not used the instrumented code will run without its FPRState.

Modification of the instruction analysis

The instruction analysis structure (InstAnalysis) was updated to include SIMD, flags and segment registers.

As the new QBDI version uses LLVM 10, some mnemonics have changed. All conditional jumps have been merged into the new JCC_* mnemonics. The condition of the jump is available in the field InstAnalysis.condition. The following output shows some of these conditions:

JCC_4     CONDITION_GREAT         jg  276
JCC_1     CONDITION_BELOW_EQUALS  jbe -76
JCC_4     CONDITION_BELOW         jb  -236
JCC_4     CONDITION_ABOVE_EQUALS  jae 251
JCC_4     CONDITION_EQUALS        je  -136
JCC_4     CONDITION_NOT_EQUALS    jne 212
CMOV64rr  CONDITION_EQUALS        cmove rdi, rax
SETCCr    CONDITION_BELOW_EQUALS  setbe al

The operands of the analysis have also been reworked. Now optional operands of all mnemonics are kept in the same position, and the type field of the missing ones is set to INVALID. This way the operand order better matches the one from the Intel syntax. The registers that are implicitly used by the instruction have now a dedicated flag. Here are some examples:

MOV64rm       mov rax, qword ptr [rsp + 88]
    [0] type=OPERAND_GPR      regName=RAX regCtxIdx=0 regOff=0 size=8 regAccess=-w flags=OPERANDFLAG_NONE
    [1] type=OPERAND_GPR      regName=RSP regCtxIdx=15 regOff=0 size=8 regAccess=r- flags=OPERANDFLAG_ADDR
    [2] type=OPERAND_IMM      value=1 size=8 flags=OPERANDFLAG_ADDR
    [3] type=OPERAND_INVALID  flags=OPERANDFLAG_ADDR
    [4] type=OPERAND_IMM      value=58 size=8 flags=OPERANDFLAG_ADDR
    [5] type=OPERAND_INVALID  flags=OPERANDFLAG_ADDR
MOV64rm       mov rdx, qword ptr [rax + 8*rdx]
    [0] type=OPERAND_GPR      regName=RDX regCtxIdx=3 regOff=0 size=8 regAccess=-w flags=OPERANDFLAG_NONE
    [1] type=OPERAND_GPR      regName=RAX regCtxIdx=0 regOff=0 size=8 regAccess=r- flags=OPERANDFLAG_ADDR
    [2] type=OPERAND_IMM      value=8 size=8 flags=OPERANDFLAG_ADDR
    [3] type=OPERAND_GPR      regName=RDX regCtxIdx=3 regOff=0 size=8 regAccess=r- flags=OPERANDFLAG_ADDR
    [4] type=OPERAND_IMM      value=0 size=8 flags=OPERANDFLAG_ADDR
    [5] type=OPERAND_INVALID  flags=OPERANDFLAG_ADDR
XOR64rm       xor rax, qword ptr fs:[40]
    [0] type=OPERAND_GPR      regName=RAX regCtxIdx=0 regOff=0 size=8 regAccess=rw flags=OPERANDFLAG_NONE
    [1] type=OPERAND_INVALID  flags=OPERANDFLAG_ADDR
    [2] type=OPERAND_IMM      value=1 size=8 flags=OPERANDFLAG_ADDR
    [3] type=OPERAND_INVALID  flags=OPERANDFLAG_ADDR
    [4] type=OPERAND_IMM      value=28 size=8 flags=OPERANDFLAG_ADDR
    [5] type=OPERAND_SEG      regName=FS size=2 regAccess=r- flags=OPERANDFLAG_ADDR
RETQ          ret
    [0] type=OPERAND_GPR      regName=RSP regCtxIdx=15 regOff=0 size=8 regAccess=rw flags=OPERANDFLAG_IMPLICIT

Future changes

With this version, the documentation [3] has been reworked in order to separate the API reference from the handover documentation. In the next months we will add tutorials with use cases for each API.


If you would like to learn more about our security audits and explore how we can help you, get in touch with us!