BrokenFlow: invoke an encrypted shellcode
BrokenFlow
A simple PoC to invoke an encrypted shellcode by using a hidden call.
Introduction
This code uses a simple trick to hide the instruction that effectively will jump to our shellcode. This should make the static analysis or emulation more challenging.
As always, if this concept was already explained in some other papers just send me a message and I will happily add it to the references.
Details
The memory layout to use this technique is the standard one and is described in Figure 1.
The decryption loop will decrypt the shellcode and jump to it. All the “magic” is inside the decryption loop since after finishing decrypting the shellcode, the decryption loop will start to decrypt its own code. The decryption of the first instruction will result in a jump to our shellcode that is executed at the next loop iteration 🙂
Below is the relevant part:
As you can see, the decryption loop does not contain any instruction that jumps to the decrypted shellcode. The assembly code is assembled in the following binary format:
in this case, the decryption key must be 06799h, since the XOR operation between 8166h (the first two bytes of the first instruction of the decryption loop) and 6799h (the XOR key) is e6ffh which is assembled to jmp esi. In other words:
By setting the ESI register to the start of our shellcode, we can achieve execution 🙂
Below is an example of debugging. Initially, the encrypted shellcode is copied, followed by the code used to decrypt and call the shellcode. It is possible to notice that after the third execution of the decryption loop, the instruction xor word ptr ds:[eax],6799 changes to jmp esi.
Usage
The steps to use this technique are:
- Encrypt your shellcode with the XOR key 0x6799. The encryption loop iteration must have a WORD size step (2-bytes);
- Create the memory layout as reported in Figure 1. The encrypted shellcode size must be a multiple of two;
- Set the register ECX to the size of the allocated memory;
- Set register ESI to the start of the allocated memory (this address contains the shellcode to execute);
- Call the shellcode decryption code
Possible Improvements
In order to make the decryption code less identifiable, it is possible to use alternative methods to call the shellcode. In order to have more freedom we can consider to increase the chunk size that is encrypted during each iteration. In my PoC I used 2-bytes because jmp esi needs two bytes, but we can use 4 or 8-bytes chunk size, allowing the operator to have more alternatives that fit in 4 or 8-bytes chunks. According to the chosen way to call the shellcode, the encryption constant will change as well.