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.
References
[1] | https://media.ccc.de/v/34c3-9006-implementing_an_llvm_based_dynamic_binary_instrumentation_framework |
[2] | Except for VGATHER*, VPGATHER*, XOP and AVX512 instructions. For more information, refer to https://qbdi.readthedocs.io/en/stable/architecture_support.html |
[3] | https://qbdi.readthedocs.io/en/stable/ |