xObf
Simple x86/x86_64 instruction level obfuscator based on a basic SBI engine
How it works
This is straightforward:
- Create new section to hold the de-obfuscation blocks
- Recursively disassemble all of the possible branches
- Replace long instructions with a
call
to the appropriate block
- Each block will decode/write/execute/clear the instruction at runtime
Impact
This really gonna mess up with the static code analysis making it almost impossible, consider using this code snippet:
#include <stdio.h>
int main(int argc, char** argv){
if (argc < 2){
puts("Wrong number of arguments");
} else {
printf("Hello %s\n", argv[1]);
};
};
After compiling and using the obfuscator upon it, this is the difference between the main subroutine instructions before and after obfuscation
This will mislead any disassembler/decompiler to preview wrong output
Thoughts
- Invalid out PE
- The tool is basically a static binary instrumentation engine
- The PE should be disassembled right if not, any changes made by the engine may break the binary
- Any type of self modifying binaries will make the tool generate executables with undefined behavior
- It may generate binaries with unexpected behavior but for now all of the tests shows it's very stable
- Not fully disassembled
- The engine uses recursive traversal disassembling starting from the entry point so a lot of branches won't be disassembled, like the branches accessed as statically undefined destination
- To get over this problem, the tool will manipulate any of tls table, COFF symbol table, debug table (pdb file), exception table and export table if exists; to collect all of the possible subroutine entries
- To have all of the branches obfuscated, make sure the binary is built with debug symbols not stripped
- By default the tool will strip any of the debug symbols (COFF and debug table)
- Out binary properties
- When fully obfuscated its size may reach 500% of the original size, the performance may reach 10% of the original performance and the obfuscated instructions will be 50-60% at x64 binaries and 20-30% at x86 binaries of the total number of the disassembled instructions
- Obfuscation is hugely increased if the original binary is statically linked
- Because the obfuscated instructions are built on the fly, trying to use x-referencing (an import or a string) will be useless even at runtime because the instruction will be removed once executed
- Multi Threading
- The problem with multi threaded executable is that two or more threads may have race condition over write/clear of the same instruction
- The tool can efficiently handle this by using
xchg
instruction that has lock
prefix by default to make sure this won't happen
- De-obfuscation
- Of course the generated PE can be de-obfuscated, but it requires a lot of work regarding decoding the instructions because every instruction decoding depends on the executing of the previous block so it can be decoded at runtime only
- Dumping out the binary from memory will be useless because of the fact that there is no time at which the whole binary will be de-obfuscated at memory
- If the original binary is built with debug symbols not stripped, the tool will use it to obfuscate all of the unreachable subroutines and it will strip the symbols, so there is no way a de-obfuscator can reach those subroutines again.
- Long story short, if the binary has the debug symbols (COFF or debug table), it's almost impossible to de-obfuscate it !!
- Development
- The SBI engine uses the trampoline approach, so not all of the instructions are obfuscated (only the long instructions)
- To obfuscate 100% of the instructions, it should use int3 approach, so I'm going to work on this implementation
- Other binaries types
- For now it handles only PEs, but the same core and techniques can be applied for any other type
Additional
- Download source with
git clone --recursive https://github.com/d35ha/xObf
- Download the binaries from the release section