News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Securely deleting files

Started by white scorpion, July 02, 2006, 11:37:22 AM

Previous topic - Next topic

white scorpion

Lol..
What i did in the past was freezing the disk, and then hitting it on the table one time with a lot of power.
The platter totally shatters in pieces.
This definitely makes retrieving data much more difficult...

But to be honest, i don't care about hardware solutions to destroy data because there are some minor issues with that:
- unable to implement that in my program
- often it's unreversable. Once a harddisk is damaged, you can't fix it anymore.
- the user wouldn't be too happy if my program would destroy part of his computer.
- you can't specify the data you want to destroy. It's all or nothing.

So i need a software solution which also protects against hardware recovery.

Tedd

repeat 10 (or more :wink) times:
- val = random number (byte)
- repeat 10: fill file-data with val
- val = neg(val)
- repeat 10: fill file-data with val

The point of negating the value is to attempt to corrupt the 'echos' which are left on the disk surface when you write data, which can be used for advanced recovery.
I can't say this will make the data completely unrecoverable, but the only way you can assure that is to burn/destroy the disk platters.

Also, consider the file-system structures which will indicate where the file-data was, not just the file-data itself. While a cluster-chain will only tell you where the file was, this at least limits the sectors you have to attempt recovery on.
No snowflake in an avalanche feels responsible.

skywalker

Quote from: Mark Jones on July 03, 2006, 10:54:32 PM
...and have you tried this?

How strong is "strong?"

AFAICR, the magentic field strength at the track-level is very high because of the way the head channels the magnetic lines of force into the platter. I doubt even rubbing a neodymium straight on the disk surface itself would do much good, although I could be mistaken.

I used a magnet about 1 x 3 inch. It works quite well. Try it out.

white scorpion

QuoteAlso, consider the file-system structures which will indicate where the file-data was, not just the file-data itself. While a cluster-chain will only tell you where the file was, this at least limits the sectors you have to attempt recovery on.
I agree, but as far as i know there is no way of doing this without the use of a driver. This means that the application has to be installed or at least run with administrator privileges.
I want to keep the app available for everyone, it should even work on a guest account.
That's why i decided to implement it like this. If you know of a way to do this without elevated privileges then i really want to hear about it.

to neg the value. as far as i know i can do either:

not eax

or

xor eax,0FFFFFFFFh

the not instruction uses 2 bytes, xor uses 3. so that means that not eax would be best?

Mark Jones

Generate a seeded random-number stream for each file or for linear sectors... write it unmodified, then write each byte logical-NOT, unmodified, not, unmodified, not...
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

arafel

Quote from: skywalker on July 04, 2006, 01:58:39 PM
I used a magnet about 1 x 3 inch. It works quite well. Try it out.

Good thing you didn't said it was one of those flexible magnets as well  :green

All recent hdrives come with appropriate shielding. For completely destroying the data you'll need at least a 10inch neodymium-iron-boron magnet in order for it to be able to penetrate the shielding. For a "no name" ancient hard drive a 5" magnet will be sufficient probably.

I can tell you for sure that a circular 2" in diameter/0.2" thick, bipolar, nib magnet placed on a ten years old Seagate drive for two weeks hadn't made any damage.

skywalker

I only used the magnet on a floppy disk.


white scorpion

#22
QuoteGenerate a seeded random-number stream for each file or for linear sectors... write it unmodified, then write each byte logical-NOT, unmodified, not, unmodified, not...
So that would be something like:

mov byte ptr [eax],bl
not ebx
mov byte ptr [eax],bl
not ebx

etc
etc

but in the mean time flushing it to disk as well.
That's pretty easy to implement ;)
Will post source shortly.

[update]
here's the resulting code:

.686                     
.model flat, stdcall     
option casemap :none   

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

RemoveFile              PROTO :DWORD, :DWORD
GenerateRandomByte      PROTO

.data
FileName    db 'test.txt',0
base        dw 256
.data?
myseed      dd ?

.code
start:

invoke RemoveFile,addr FileName,10
invoke ExitProcess,0

RemoveFile proc file_to_delete:DWORD,number_of_times:DWORD
    LOCAL hFile:DWORD
    LOCAL inputfilesize:DWORD
    LOCAL hMap:DWORD
    LOCAL myoffset:DWORD
    LOCAL backup:DWORD

    cmp number_of_times,0
    jnz @F 
    xor eax,eax
    dec eax
    ret
@@:
    invoke CreateFile,file_to_delete,GENERIC_WRITE + GENERIC_READ, 0, NULL, OPEN_EXISTING,\
    FILE_ATTRIBUTE_NORMAL, NULL
    mov hFile,eax
    invoke GetFileSize,eax,NULL
    mov inputfilesize,eax
    invoke CreateFileMapping,hFile,NULL,PAGE_READWRITE,0,0,NULL
    .if eax==NULL
@@:
        invoke CloseHandle,hFile
        xor eax,eax
        dec eax
        ret
    .endif   
    mov hMap,eax
    invoke MapViewOfFile,hMap,FILE_MAP_WRITE,0,0,0
    .if eax==NULL
        invoke CloseHandle,hMap
        jmp @B
    .endif
    mov myoffset,eax
;----Rounds Start -----
        invoke GetTickCount
        mov backup,eax
        mov edx,number_of_times
times_loop:
        mov eax,backup
        mov myseed,eax
        mov ecx,inputfilesize
        push myoffset
        pop esi
        push edx
@@:
        invoke GenerateRandomByte
        mov byte ptr [esi],al
        inc esi
        dec ecx
        jnz @B
        invoke FlushViewOfFile,myoffset,inputfilesize
        mov eax,backup
        mov myseed,eax
        mov ecx,inputfilesize
        push myoffset
        pop esi
@@:
        invoke GenerateRandomByte
        not eax
        mov byte ptr [esi],al
        inc esi
        dec ecx
        jnz @B
        invoke FlushViewOfFile,myoffset,inputfilesize
        pop edx
        dec edx
        jnz times_loop     
;----Rounds End ------- 
    invoke UnmapViewOfFile,myoffset
    invoke CloseHandle,hMap
    invoke CloseHandle,hFile
    invoke DeleteFile,file_to_delete
    xor eax,eax
    ret
RemoveFile endp


GenerateRandomByte proc
        mov eax,myseed
        mov ebx,127923
        mul ebx
        xor eax,479573
        xor eax,edx
        mov myseed,eax
        xor edx,edx
        div base
        mov eax,edx
        ret
GenerateRandomByte endp

end start


IMO i can implement it safely. Even if it wouldn't be secure (which i doubt), it will always be more secure then just deleting the file ;)

white scorpion

#23
for those interested.
I've been given some tips and suggestions by f0dder which resulted in the following code:

RemoveFile proc file_to_delete:DWORD,number_of_times:DWORD
    LOCAL hFile:DWORD
    LOCAL inputfilesize:DWORD
    LOCAL backup:DWORD
    LOCAL BytesWr:DWORD
    LOCAL shredbuf[1024*10]:BYTE
    LOCAL buffer_times:DWORD
    LOCAL my_remainder:DWORD

    cmp number_of_times,0
    jnz @F 
    xor eax,eax
    dec eax
    ret
@@:
    invoke CreateFile,file_to_delete,GENERIC_WRITE, 0, NULL, OPEN_EXISTING,\
    FILE_ATTRIBUTE_NORMAL or FILE_FLAG_NO_BUFFERING, NULL
    .if eax==INVALID_HANDLE_VALUE
        xor eax,eax
        dec eax
    ret
    .endif   
    mov hFile,eax
    invoke GetFileSize,eax,NULL
    mov ebx,512
    xor edx,edx
    div ebx
    .if edx==0
        mul ebx
    .else
        mul ebx
        add eax,512
    .endif       
    mov inputfilesize,eax
   
;----Rounds Start -----
    invoke GetTickCount
    mov backup,eax
    mov myseed,eax
   .if inputfilesize<=sizeof shredbuf
        mov edx,number_of_times
times_loop_small:
        push edx       
        mov ecx,inputfilesize 
        lea esi,shredbuf       
@@:
        invoke GenerateRandomByte
        mov byte ptr [esi],al
        inc esi
        dec ecx
        jnz @B
        mov eax,backup
        mov myseed,eax
        invoke SetFilePointer,hFile,0,0,FILE_BEGIN
        invoke WriteFile,hFile,addr shredbuf,inputfilesize,addr BytesWr,NULL
        mov ecx,inputfilesize
        lea esi,shredbuf
@@:
        invoke GenerateRandomByte
        not eax
        mov byte ptr [esi],al
        inc esi
        dec ecx
        jnz @B
        invoke SetFilePointer,hFile,0,0,FILE_BEGIN
        invoke WriteFile,hFile,addr shredbuf,inputfilesize,addr BytesWr,NULL
        pop edx
        dec edx
        jnz times_loop_small   
;----Rounds End ------- 
    .else
        mov eax,inputfilesize
        mov ebx,sizeof shredbuf
        xor edx,edx
        div ebx
        mov buffer_times,eax
        mov my_remainder,edx
        mov edx,number_of_times
times_loop_big:
        push edx
        invoke SetFilePointer,hFile,0,0,FILE_BEGIN
        mov eax,buffer_times
bufloop1: 
        push eax     
        mov ecx,sizeof shredbuf
        lea esi,shredbuf       
@@:       
        invoke GenerateRandomByte
        mov byte ptr [esi],al
        inc esi
        dec ecx
        jnz @B
        invoke WriteFile,hFile,addr shredbuf,sizeof shredbuf,addr BytesWr,NULL
        pop eax
        dec eax
        jnz bufloop1
        mov ecx,my_remainder
        .if ecx==0
            jmp second_round
        .endif
        lea esi,shredbuf
@@:
        invoke GenerateRandomByte
        mov byte ptr [esi],al
        inc esi
        dec ecx
        jnz @B
        invoke WriteFile,hFile,addr shredbuf,my_remainder,addr BytesWr,NULL   
second_round:             
        invoke SetFilePointer,hFile,0,0,FILE_BEGIN
        mov eax,buffer_times
bufloop2:   
        push eax   
        mov ecx,sizeof shredbuf
        lea esi,shredbuf
@@:       
        invoke GenerateRandomByte
        not eax
        mov byte ptr [esi],al
        inc esi
        dec ecx
        jnz @B
        invoke WriteFile,hFile,addr shredbuf,sizeof shredbuf,addr BytesWr,NULL
        pop eax
        dec eax
        jnz bufloop2
        mov ecx,my_remainder
        .if ecx==0
            jmp finish_round
        .endif
        lea esi,shredbuf
@@:
        invoke GenerateRandomByte
        not eax
        mov byte ptr [esi],al
        inc esi
        dec ecx
        jnz @B
        invoke WriteFile,hFile,addr shredbuf,my_remainder,addr BytesWr,NULL       
finish_round:
        pop edx
        dec edx
        jnz times_loop_big
    .endif       
    invoke CloseHandle,hFile
    invoke DeleteFile,file_to_delete
    xor eax,eax
    ret
RemoveFile endp

No mapping to memory anymore, directly writing to disk. This function goes with a rate of 2,4MB's per second for deletion (if you give argument 5) on a 1600 mhz computer with a 4200rpm disk.
while doing so, it runs at 50% cpu, so it could be even faster on a faster machine.
The total write rate is 24mb's per second, so it closely matches my harddisk speed.

i've implemented it in TableCrypt and in Shredder.


asmfan

Scorp, some suggestions- CreateFile returns (-1) if unable to obtain a handle so compare to it, and you shouldn't close -1 handle:). The second when writing to file file pointer automatically moving. So set it up only on the beginnig of new round. The rest will be later...
Russia is a weird place

asmfan

Also i would suggest you to compact code by making a loop (to roll it) because i see the same fragments that fit one code. And the logic of program is so simple that you can avoid some strange mul.
All you need is to know how many times your buffer is in file: fileSize = k * buffSize + Q, k =0, 1, ... Q < buffSize. Then k times you write the full buffer and 1 (or 0) time you write Q bytes of buffer. Cant be easier.
Russia is a weird place

white scorpion

1. you are right.it seems i've done too much copy and paste work from the previous code with MapViewOfFile...it should be INVALID_HANDLE_VALUE. willl change immediately.
2. This is happening. Take a good look.
3. any suggestions on making a loop?
4. Not exactly. Since im using flag FILE_FLAG_NO_BUFFERING the number of bytes written has to be in steps of sector size (mostly 512bytes) that's the reason i put in that code.

[EDIT]
updated above code.

asmfan

Ok, a suggestion on simplification. Make a buffer divisible by 512 (you have it). Then divide fileSize by buffSize. Proceed the quotient in eax in a loop if needed (eax not 0) then proceed the remainder in edx, using the same buffer. By the way FILE_FLAG_NO_BUFFERING cannot proceed files than less 512 bytes (AFAIC...?) or the last bytes that less 512 (the remainder)
Russia is a weird place

white scorpion

Simply put, that's what above code does.

Devide filesize by 512
if remainder !=0
then add 512 to the quotient*512.
since we multiple it again we have the original filesize rounded up till the next step of 512 bytes.

I'm still not sure how to simplify this to make it work with every possible filesize (bigger or smaller then 512,devidable by 512 or not).

Tedd

Maybe I misread somewhere, but you don't appear to be resetting the seed before you call GenerateRandomByte again, which means you will get a different number each time.
This is bad for two reasons. The whole point of overwriting with an inverse pattern is to flatten out the magnetic echos, if you overwrite with the inverse of another random number that's just the same as overwriting with a random number. Secondly, while it may seem like a good idea to write different values, this creates interference between boundaries, but you're supposed to be avoiding interference and flattening out the magnetic signals. So it's still a better idea to fill the whole file with the same number, and then its inverse.

Generate a random value, save it, use that. Flush the file. NOT the previous value, save that, and use it. This is also obviously more effiecent than calling GenerateRandomByte each time.
No snowflake in an avalanche feels responsible.