This is a file shredder utility that runs on Linux. It writes random bytes over the file contents, repeating this 48 times and calling sys_fsync
after each pass to make sure data goes to disk.
; Description : A file shredder, it reads a dword from urandom then replaces
; the file contents with it, this process is repeated 48 times
; using a different random dword on each pass. It calls fsync
; after each pass is complete to ensure that the changes are
; reflected in the disk.
;
; Build using these commands:
; nasm -f elf shredder.asm
; ld -o shredit shredder.o -m elf_i386 -s
section .data
UsagePrefix: db 'Usage: '
USAGEPREFIXLEN: equ $-UsagePrefix
UsageSuffix: db ' file_name',0ah
USAGESUFFIXLEN: equ $-UsageSuffix
UrandomPath: db '/dev/urandom',0
ErrorMsg: db 'There was an error, file NOT shredded',0ah
ERRORMSGLEN: equ $-ErrorMsg
PASSES: equ 48 ; how many passes
section .bss
UrandomFd: resd 1
Size: resd 1
RandomBuffer: resd 128
RANDOMBUFFERSIZE: equ $-RandomBuffer ; changing this breaks the code that
; calculates the number of passes
section .text
global _start
; in : eax
; what in : null-terminated string
; out : eax
; what out : length
; trashes : nothing
; description : very simple function to find the null terminator ('\0'), it uses
; the naive algorithm and avoids repne scasb because this is ~15%
; faster than repne scasb on my machine...
GetLength:
push ecx ; save caller's registers
push esi ;
mov esi,eax ; iterate using esi then sub later
xor ecx,ecx ; we are looking for the null byte
jmp .skip ; it might be the first byte
.loop:
inc esi ; count this iteration
.skip:
cmp byte [esi],cl ; check if this is the byte
jne .loop ; repeat until found
sub eax,esi ; calculate the length
pop esi ; restore caller's registers
pop ecx ;
ret ; done
Usage:
; write 'Usage: '
mov eax,4 ; sys_write
mov ebx,2 ; stderr
mov ecx,UsagePrefix ; string
mov edx,USAGEPREFIXLEN ; length
int 80h ; call
; write executable name
pop ecx ; program name is next on the stack
mov eax,ecx ; GetLength gets its input in eax
call GetLength
mov edx,eax ; set up length for the write
mov eax,4 ; sys_write
mov ebx,2 ; stderr
int 80h ; call
; write ' file_name\n'
mov eax,4 ; sys_write
mov ebx,2 ; stderr
mov ecx,UsageSuffix ; string
mov edx,USAGESUFFIXLEN ; length
int 80h ; call
; fall through
ExitFailure:
mov eax,1 ; sys_exit
mov ebx,-1 ; failure code
int 80h ; call
Error:
mov eax,4 ; sys_write
mov ebx,2 ; stderr
mov ecx,ErrorMsg ; string
mov edx,ERRORMSGLEN ; length
int 80h ; call
jmp ExitFailure ; let's stop execution
_start:
pop eax ; get arg count
cmp eax,2 ; there should be 2 args: ./shredit file_name
jne Usage ; show user how to run the program
; get file name
lea esp,[esp+4] ; jump over program name
pop ebx ; file name
; open the file to be shredded
mov eax,5 ; sys_open
; ebx has the name
mov ecx,1 ; O_WRONLY
int 80h ; call sys_open
cmp eax,0 ; check fd
jl Error ; < 0 means error
mov esi,eax ; save fd
; get file size
mov ebx,eax
mov eax,19 ; sys_lseek
xor ecx,ecx ; offset
mov edx,2 ; SEEK_END
int 80h ; call
cmp eax,0 ; check return value
jl Error ; negative values indicate errors
mov [Size],eax ; save file size
; now open urandom
mov eax,5 ; sys_open
mov ebx,UrandomPath ; path
xor ecx,ecx ; O_RDONLY
int 80h ; call open
cmp eax,0 ; check returned fd
jl Error ; < 0 means error
mov [UrandomFd],eax ; save fd for later
; rewind file
mov eax,19 ; sys_lseek
mov ebx,esi ; fd
xor ecx,ecx ; offset 0
xor edx,edx ; SEEK_SET
int 80h ; call
cmp eax,0 ; error?
jl Error ; exit
; We have a random source and the file to shred
mov ebp,PASSES ; how many times we are going to overwrite
.wholeLoop:
mov edi,[Size] ; get size
shr edi,9 ; how many full 512 byte runs?
.fillRandom:
mov eax,3 ; sys_read
mov ebx,[UrandomFd] ; fd
mov ecx,RandomBuffer ; destination
mov edx,4 ; size
int 80h ; call
cmp eax,4 ; did it read all?
jl Error ; we can't risk writing non-random bytes
mov eax,[RandomBuffer] ; copy the dword to fill the buffer
mov ebx,RandomBuffer + 4 ; start with the next dword
mov ecx,127 ; repeat until it's full
.expand:
mov [ebx],eax ; do the actual buffer writing
add ebx,4 ; move to the next dword
dec ecx ; count this iteration
jnz .expand ; repeat until done
; This is where we exit the 512-byte loop
cmp edi,0 ; is there any other 512 byte block to write?
je .handleRemaining ; if not, handle remaining bytes
; Overwrite with random
dec edi ; count this iteration
mov eax,4 ; sys_write
mov ebx,esi ; fd
mov ecx,RandomBuffer ; string
mov edx,RANDOMBUFFERSIZE ; size
int 80h ; call
cmp eax,RANDOMBUFFERSIZE ; did it write everything?
jl Error ; something is wrong
jmp .fillRandom ; close the loop
.handleRemaining:
mov eax,4 ; sys_write
mov ebx,esi ; fd
mov ecx,RandomBuffer ; string
mov edx,[Size] ; remaining size is the first byte only
and edx,0ffh ; ok, now we counted the 512-byte passes
int 80h ; call
cmp eax,edx ; did it write everything?
jl Error ; something is wrong
dec ebp ; count this iteration
jz Exit ; if done, exit
mov eax,19 ; sys_lseek
mov ebx,esi ; file
xor ecx,ecx ; offset is 0, we are overwriting again
xor edx,edx ; SEEK_SET
int 80h ; call
cmp eax,0 ; success?
jl Error ; < 0 means error
mov eax,118 ; sys_fsync
int 80h ; call, sys_fsync can't fail if fd is valid
jmp .wholeLoop ; overwrite again
Exit:
mov eax,6 ; sys_close
mov ebx,esi ; shredded file
int 80h
mov eax,1 ; sys_exit
xor ebx,ebx ; EXIT_SUCCESS
int 80h ; call
mov edx, edi; and edx, 0xffh
(like you do in handleRemaining) always gets you the right size to write. Andsub edi, edx
after thejl Error
keeps the correct remaining size in edi. You are done when edi hits zero. \$\endgroup\$