In order to do self modification too, I wrote a small code in nasm (which can be used as a stub), which opens itself and at the middle of the code (right after the mmap), we have a pointer which points to the bytes of the executable that we can modify.
The code looks like this:
BITS 64
section .text
global _start
_start:
call _main__
mov rax, 60
mov rdi, 0x0
syscall ; exit(0);
_main__:
push rbp
mov rbp, rsp
sub rsp, 144 ; stat_file
mov rdi, [rbp+0x18]
lea rsi, [rsp]
call _open_self ; open self
push r12 ; len file
push rax ; addr
mov r14, rsi
mov rdi, [rbp+0x18] ; pathname
pop rsi ; addr
pop rdx ; len
push rdx
push rsi
call __create
mov r13, rax ; second fd
mov rdi, r14 ; fd
pop rsi ; addr -> mmap
pop rdx ; len_file
call __close_unmap
mov rax, 87
mov rdi, [rbp+0x18]
syscall
mov rax, 0x3 ; close(scnd_fd);
mov rdi, r13
syscall
mov rax, 86
push 'nasm'
lea rdi, [rsp]
mov rsi, [rbp+0x18]
syscall ; link tmp name to original name
mov rax, 87
lea rdi, [rsp]
syscall ; delete old tmp file
leave
ret
; ===============================
; Open himself
_open_self:
push rbp
mov rbp, rsp
mov r15, rsi ; &stat_file
mov r12, rdi ; *pathname
mov rax, 0x2
mov rsi, 0x0 ; 0_RD
mov rdx, 509
syscall
push rax ; fd
mov rdi, rax ; fd
mov rsi, r15 ; struct stat
mov rax, 5 ; fstat
syscall
xor rdi, rdi
mov rsi, qword [r15+48]
mov rdx, 0x4
mov r10, 0x2
pop r8
push r8
mov r9, 0x0
mov rax, 9
syscall ; mmap
; rax -> byte of the executable that we gonna dump
mov r12, qword [r15+48]
pop rsi ; fd
leave
ret
; ===============================
; int __create(const char *pathname, void *addr, ssize_t len_bytes_mapped);
__create:
push rbp
mov rbp, rsp
push rsi ; addr
push rcx ; len
push 'nasm'
lea rdi, [rsp]
mov rax, 0x2
mov rsi, 0x42 ; 0_CREAT | O_RDWR
mov rdx, 509
syscall ; sys_open
add rsp, 0x8 ; 'nasm'
mov r9, rax ; fd
mov rdi, rax ; fd
mov rax, 0x1
pop rdx
pop rsi
syscall ; sys_write
mov rax, r9 ; fd final
leave
ret
; int __close_unmap(int fd, unsigned lon addr, ssize_t len_file);
__close_unmap:
push rbp
mov rbp, rsp
push rdi
mov rdi, rsi
mov rsi, rdx
mov rax, 11
syscall ; munmap(addr, len_file)
pop rdi
mov rax, 3
syscall ; close(fd);
leave
ret
It is a bit long but it makes just :
-Open it self in read mode (O_RD == 0x0)
-Do a stat(*pathname, &buffer_struct_stat);
-And then a mmap(0, buffer_struct_stat.st_size, 0x4, MAP_PRIVATE, fd_read_only, 0);
-Here you can edit your executable by editing the bytes at the address returned by mmap
-Create a tmp file named "nasm"
-Do a write(fd_tmp, address_of_mmap, buffer_struct_stat.st_size)
-Close the two file descriptors and munmap the mmap
-Now it's cool : unlink(pathname) and link("nasm", "pathname")