flat assembler
Message board for the users of flat assembler.

Index > Windows > Read the whole file and nothing else.

Author
Thread Post new topic Reply to topic
special-U



Joined: 26 Jul 2018
Posts: 19
special-U 11 Apr 2021, 18:22
First of all I want to ask a question that depending on the answer may have nothing to do with the topic.

When a HDD has damaged sectors there are times when I can read parts of a file, for example a 10 minutes video, very often happens that one can play the first 5 minutes of the video but in a certain second Windows freezes because it turns out that the video has parts stored in damaged sectors of the disk, at this moment it is impossible to close the player or kill the process, Windows will freeze and will ignore anything I tell it and the only way to unfreeze it is disconnecting the disk.

In high level languages I used instructions of the "timeOut" type where if after a certain time the "read()" function did not finish its task the program should close or how much better "read()" should stop reading the file and move on to the next one. Unfortunately Windows was still frozen and so was my program.

With that in mind here comes my question, if I read a corrupted file with FASM, is there any way to unfreeze Windows?

It's not important to me that the system doesn't freeze but obviously it's ideal.

Ok, now for the real question.

I want to give the path of a folder to FASM and have it recursively read the files in that folder, it just has to read all the bytes of each file, nothing else, if at any given time Windows freezes it is because that file is corrupted, then I will proceed to disconnect the disk to unfreeze Windows, then reconnect the HDD and move the corrupted file to a quarantine folder.

It may sound counterproductive as there could be thousands of damaged files and that would lead to disconnecting the HDD too many times, but in my case it's not overly serious and I'm just looking for the fastest way to read a file.

I do not know if this is a trivial question, if it is, I would greatly appreciate your help with the code, if it is not and you think my request is too broad, let me know and do not worry about putting code.

Update: I am not trying to retrieve any information from a HDD, I have only given a context as to why I simply want to read the files without doing anything.


Last edited by special-U on 11 Apr 2021, 18:53; edited 1 time in total
Post 11 Apr 2021, 18:22
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20404
Location: In your JS exploiting you and your system
revolution 11 Apr 2021, 18:45
For Windows there is not an easy solution AFAIAA. It doesn't give access to the low level driver. So no assembly code can help you. There is no API for that.

I suggest you boot up another OS (Linux maybe), or a dedicated program like Spinrite, and then read/recover/fix/whatever the HDD using the tools available.

I am assuming you don't backups. If you do have backups then simply throw away the failing disk, buy a new one, and restore.
Post 11 Apr 2021, 18:45
View user's profile Send private message Visit poster's website Reply with quote
special-U



Joined: 26 Jul 2018
Posts: 19
special-U 11 Apr 2021, 18:51
revolution wrote:
I suggest you boot up another OS (Linux maybe), or a dedicated program like Spinrite, and then read/recover/fix/whatever the HDD using the tools available.


Don't worry about the HDD, I'm not even trying to retrieve information, my question is just as it says above, just read and nothing else. I didn't say anything about recovering data.

I will update the question for clarity.
Post 11 Apr 2021, 18:51
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20404
Location: In your JS exploiting you and your system
revolution 11 Apr 2021, 20:13
You can read, but Windows will still hang because the driver will keep trying to read the dud sectors. Like I mentioned in the first paragraph, you get no API to tell Windows to give up sooner when reading bad sectors.
Post 11 Apr 2021, 20:13
View user's profile Send private message Visit poster's website Reply with quote
Overclick



Joined: 11 Jul 2020
Posts: 669
Location: Ukraine
Overclick 11 Apr 2021, 20:22
CHKDSK /f /r or something like that usually blocks dead sectors to avoid future usage.
Post 11 Apr 2021, 20:22
View user's profile Send private message Visit poster's website Reply with quote
special-U



Joined: 26 Jul 2018
Posts: 19
special-U 11 Apr 2021, 20:35
Overclick wrote:
CHKDSK /f /r or something like that usually blocks dead sectors to avoid future usage.


Thanks, I will make a note of it for the future.

revolution wrote:
You can read, but Windows will still hang because the driver will keep trying to read the dud sectors. Like I mentioned in the first paragraph, you get no API to tell Windows to give up sooner when reading bad sectors.


Ok, no problem, let's let Windows freeze then, now can you help me read all the files in a folder? And no, it doesn't matter if Windows freezes or the information is lost, I just want to read with FASM.
Post 11 Apr 2021, 20:35
View user's profile Send private message Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 219
FlierMate 11 Apr 2021, 20:38
I have an old code using INT 13 to read disk sector.

Just for your reference, to look at the old way of doing it. Smile
Code:
function ReadSector(Drive,Head,Track,Sector,TotalSector:byte;DiskBuffer:pointer):byte; assembler;
asm
  mov  cx, RetryTime
@retry:
  push cx
  mov  ah, 02h
  mov  al, TotalSector
  mov  ch, Track
  mov  cl, Sector
  mov  dh, Head
  mov  dl, Drive
  les  bx, DiskBuffer
  int  13h
  pop  cx
  jnc  @finish
  loop @retry
@finish:
  mov  al, ah
end;    
Post 11 Apr 2021, 20:38
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20404
Location: In your JS exploiting you and your system
revolution 11 Apr 2021, 20:43
I'm not sure what you are asking.

You can use the read API, just the same as the HLL will do. You won't get any extra magic from assembly here, Windows always has control.

Windows eventually gives up, but it takes a while, and returns a failure condition to the caller. So upon each failure you can set some internal counter and do the read again from a new offset that skips the dud sector(s). If you do lots of smaller reads then you can build a map of which blocks read fine, and which fail.
Post 11 Apr 2021, 20:43
View user's profile Send private message Visit poster's website Reply with quote
special-U



Joined: 26 Jul 2018
Posts: 19
special-U 11 Apr 2021, 20:56
revolution wrote:
I'm not sure what you are asking.

Sounds strange I know, but yes, I just want to read files recursively in a folder.

revolution wrote:
You can use the read API, just the same as the HLL will do. You won't get any extra magic from assembly here, Windows always has control.


Do you mean this API?
https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile

Can you give me a code example of how to use that API? Windows APIs always seemed cryptic to me.
Post 11 Apr 2021, 20:56
View user's profile Send private message Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 219
FlierMate 11 Apr 2021, 22:28
I am also learning modern Assembly like you.

This is my attempt to open a file to read the first 256 bytes.
You may change the _filename to an existing file in your PC.

Code:
format PE console
entry start

include 'win32a.inc'

section '.data' readable writable

_message1 db 'INVALID_HANDLE_VALUE',13,10,0
_msglen1  = $ - _message1
_message2 db 'ReadFile FALSE',13,10,0
_msglen2  = $ - _message2
_dummy    dw ?
_filename db 'C:\FASM\MSGBOX.ASM',0

section '.code' code readable writable executable

_buffer  rb 256
_len     dw ?

start:
        invoke  CreateFile, _filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
        cmp     eax, INVALID_HANDLE_VALUE
        je      .error1
        invoke  ReadFile, eax, _buffer, 256, _len, 0
        test    eax, eax
        jz      .error2
        mov     ecx, dword [_len]
        ;mov     byte [_buffer+ecx], 0
        jmp     .show

.error1:
        lea     edx, [_message1]      ; CreateFile ERROR
        mov     ecx, _msglen1
        jmp     .error

.error2:
        lea     edx, [_message2]      ; ReadFile ERROR
        mov     ecx, _msglen2

.error:
        mov     esi, edx
        lea     edx, [_buffer]
        mov     edi, edx
        mov     dword [_len], ecx
        rep     movsb

.show:
        invoke  GetStdHandle, -11
        invoke  WriteConsole, eax, _buffer, dword [_len], _dummy, 0
        invoke  ExitProcess,0

section '.idata' import readable writable

 library kernel32,'KERNEL32.DLL'

 import kernel32,\
        GetStdHandle, 'GetStdHandle', \
        ReadConsole, 'ReadConsoleA', \
        WriteConsole, 'WriteConsoleA', \
        CreateFile, 'CreateFileA', \
        ReadFile, 'ReadFile', \
        ExitProcess,'ExitProcess'            
Post 11 Apr 2021, 22:28
View user's profile Send private message Reply with quote
sleepsleep



Joined: 05 Oct 2006
Posts: 12930
Location: ˛                             ⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣Posts: 0010456
sleepsleep 11 Apr 2021, 23:55
maybe source of ddrescue,
https://www.gnu.org/software/ddrescue/
Post 11 Apr 2021, 23:55
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4060
Location: vpcmpistri
bitRAKE 12 Apr 2021, 01:10
special-U wrote:
help me read all the files in a folder? And no, it doesn't matter if Windows freezes or the information is lost, I just want to read with FASM.
FindFirstFile to get the files in a directory, then use ReadFile to get the data.

Has most the documentation for Windows that you need:
https://docs.microsoft.com/en-us/windows/win32/fileio/file-management-functions

You'd want to also read the file asynchronously (so your program can continue without waiting on a hung read command. Windows calls this "overlapped I/O". There are probably snippets on this board covering all those topics, but I'm not aware of a single source. Start with finding and reading a single file.

The controller within the drive can hang, so it's not a Windows specific problem. Fine-grained control of the drive controller would be difficult under windows. There are commands to reset the drive, etc. that specialized software would use.

_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 12 Apr 2021, 01:10
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20404
Location: In your JS exploiting you and your system
revolution 12 Apr 2021, 06:25
bitRAKE wrote:
The controller within the drive can hang, so it's not a Windows specific problem.
It definitely is the Windows HDD driver behaviour. It tries too hard, IMO, to read damaged sectors which holds up the system until the retry counter finally reaches zero. The drive controller just keeps repeatedly returning read failures to the driver.

That is why I suggested above to use other tools, not Windows. Then you can get the job much more easily and quicker without all the boring waiting around to read sectors that can't be read.
Post 12 Apr 2021, 06:25
View user's profile Send private message Visit poster's website Reply with quote
special-U



Joined: 26 Jul 2018
Posts: 19
special-U 12 Apr 2021, 07:10
FlierMate wrote:
This is my attempt to open a file to read the first 256 bytes.


Can I put C code here? or is it not allowed? while waiting for your answer, I will leave a code that reads the whole file but it is even slower than my Ruby version, what am I doing wrong?

Also the buffer is very small but C won't let me make it bigger.

Code:
#include <Windows.h>

int main(void){

        HANDLE fh = CreateFile("D:\\A_File_Of_5GB.zip", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

        DWORD bytesRead;
        bytesRead = 4294967295;

        char readBuffer[1048576];
        
        while (ReadFile(fh, readBuffer, sizeof(readBuffer), &bytesRead, NULL)){
                if (bytesRead == 0)
                        break;
}
    
Post 12 Apr 2021, 07:10
View user's profile Send private message Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 219
FlierMate 12 Apr 2021, 11:16
special-U wrote:
FlierMate wrote:
This is my attempt to open a file to read the first 256 bytes.


Can I put C code here? or is it not allowed? while waiting for your answer, I will leave a code that reads the whole file but it is even slower than my Ruby version, what am I doing wrong?

Also the buffer is very small but C won't let me make it bigger.

Code:
#include <Windows.h>

int main(void){

        HANDLE fh = CreateFile("D:\\A_File_Of_5GB.zip", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

        DWORD bytesRead;
        bytesRead = 4294967295;

        char readBuffer[1048576];
        
        while (ReadFile(fh, readBuffer, sizeof(readBuffer), &bytesRead, NULL)){
                if (bytesRead == 0)
                        break;
}
    


Done!

Code:
format PE console
entry start

include 'win32a.inc'

section '.data' readable writable

_message1 db 'INVALID_HANDLE_VALUE',13,10,0
_msglen1  = $ - _message1
_message2 db 'ReadFile FALSE',13,10,0
_msglen2  = $ - _message2
_dummy    dw ?
_filename db 'C:\FASM\LARGE.txt',0
_format   db '%d',0

section '.code' code readable writable executable

_buffer   rb 1024
_len      dd ?
_ptr      dd ?
_handle   dd ?

start:
        invoke  CreateFile, _filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
        mov     dword [_handle], eax
        cmp     eax, INVALID_HANDLE_VALUE
        je      .error1

.repeat:
        invoke  ReadFile, dword [_handle], _buffer, 1024, _len, 0
        test    eax, eax
        jz      .error2
        mov     ecx, dword [_len]
        add     dword [_ptr], ecx
        test    ecx, ecx
        jz      .done
        invoke  GetStdHandle, -11
        invoke  WriteFile, eax, _buffer, dword [_len], _dummy, 0
        invoke  SetFilePointer, dword [_handle], dword [_ptr], 0, FILE_BEGIN
        cmp     eax, -1         ;INVALID_SET_FILE_POINTER
        je      .error3
        jmp     .repeat

.error1:
        lea     edx, [_message1]
        mov     ecx, _msglen1
        jmp     .error

.error2:
        lea     edx, [_message2]
        mov     ecx, _msglen2
        jmp     .error

.error3:
        invoke  GetLastError
        ;cmp     eax, 997        ; ERROR_IO_PENDING
        ;je      .repeat
        push    eax
        push    _format
        push    _buffer
        call    [wsprintf]
        mov     dword [_len], 5
        jmp     .show

.error:
        mov     esi, edx
        lea     edx, [_buffer]
        mov     edi, edx
        mov     dword [_len], ecx
        rep     movsb

.show:
        invoke  GetStdHandle, -11
        invoke  WriteConsole, eax, _buffer, dword [_len], _dummy, 0

.done:
        invoke  ExitProcess,0

section '.idata' import readable writable

 library kernel32, 'KERNEL32.DLL', \
         user32, 'USER32.DLL'

 import kernel32,\
        GetStdHandle, 'GetStdHandle', \
        ReadConsole, 'ReadConsoleA', \
        WriteConsole, 'WriteConsoleA', \
        CreateFile, 'CreateFileA', \
        ReadFile, 'ReadFile', \
        WriteFile, 'WriteFile', \
        GetLastError, 'GetLastError', \
        SetFilePointer, 'SetFilePointer', \
        SetFilePointerEx, 'SetFilePointerEx', \
        ExitProcess,'ExitProcess'

 import user32,\
        wsprintf, 'wsprintfA'    


You can redirect the output of this program, and then compare the size of output file with original file.

E.g. FASMW --> Save as "READFILE.ASM" -- > Compile
Type "READFILE > DUMP.TXT" --> "FC /B LARGE.TXT DUMP.TXT" (only work for text files)

But my example only work for 32-bit addressing of input file size only (max 4GB?), you can refer to SetFilePointer and SetFilePointerEx for 64-bit addressing and beyond.

Hope this helps.
Post 12 Apr 2021, 11:16
View user's profile Send private message Reply with quote
Overclick



Joined: 11 Jul 2020
Posts: 669
Location: Ukraine
Overclick 12 Apr 2021, 11:36
Post 12 Apr 2021, 11:36
View user's profile Send private message Visit poster's website Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 219
FlierMate 17 Apr 2021, 19:19
Below is a better version which accepts input file name from command line.

Usage: DUMPFILE large.txt
DUMPFILE large.txt > dump.txt

Code:
; Open File and Read File (Extended Version with Command Line File Name Support)
;
; DUMPFILE.ASM
; Copyright (C) 2021 Boo Khan Ming
;
; MIT license apply
;
format PE console
entry start

include 'win32a.inc'

section '.data' readable writable

_message1 db 'INVALID_HANDLE_VALUE',13,10,0
_msglen1  = $ - _message1
_message2 db 'ReadFile FALSE',13,10,0
_msglen2  = $ - _message2
_dummy    dw ?
;_filename db 'LARGE.BIN',0
_format   db '%d',0

section '.code' code readable writable executable

_filename rb MAX_PATH
_fnlen    dd ?
_buffer   rb 1024
_len      dd ?
_ptr      dd ?
_handle   dd ?

start:
        invoke  GetCommandLine
        push    eax
        mov     edi, eax
        or      ecx, -1
        xor     eax, eax
        repnz   scasb           ; Calculate total length of command line arguments
        not     ecx
        pop     eax
        mov     dword [_fnlen], ecx
        push    eax
        mov     edi, eax
        or      ecx, -1
        mov     eax, 32
        repnz   scasb           ; Calculate length of first command line argument (APPNAME)
        not     ecx
        pop     eax
        inc     ecx
        sub     dword [_fnlen], ecx     ; Compute the length of second command line argument (_FILENAME)
        add     eax, ecx
        mov     ecx, dword [_fnlen]
        mov     esi, eax
        lea     edx, [_filename]
        mov     edi, edx
        rep     movsb
        ;invoke  GetStdHandle, -11
        ;invoke  WriteConsole, eax, _filename, dword [_fnlen], _dummy, 0
        invoke  CreateFile, _filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
        mov     dword [_handle], eax
        cmp     eax, INVALID_HANDLE_VALUE
        je      .error1

.repeat:
        invoke  ReadFile, dword [_handle], _buffer, 1024, _len, 0
        test    eax, eax
        jz      .error2
        mov     ecx, dword [_len]
        add     dword [_ptr], ecx
        test    ecx, ecx
        jz      .done
        invoke  GetStdHandle, -11
        invoke  WriteFile, eax, _buffer, dword [_len], _dummy, 0
        invoke  SetFilePointer, dword [_handle], dword [_ptr], 0, FILE_BEGIN
        cmp     eax, -1         ;INVALID_SET_FILE_POINTER
        je      .error3
        jmp     .repeat

.error1:
        lea     edx, [_message1]
        mov     ecx, _msglen1
        jmp     .error

.error2:
        lea     edx, [_message2]
        mov     ecx, _msglen2
        jmp     .error

.error3:
        invoke  GetLastError
        ;cmp     eax, 997        ; ERROR_IO_PENDING
        ;je      .repeat
        push    eax
        push    _format
        push    _buffer
        call    [wsprintf]
        mov     dword [_len], 5
        jmp     .show

.error:
        mov     esi, edx
        lea     edx, [_buffer]
        mov     edi, edx
        mov     dword [_len], ecx
        rep     movsb

.show:
        invoke  GetStdHandle, -11
        invoke  WriteConsole, eax, _buffer, dword [_len], _dummy, 0

.done:
        invoke  ExitProcess,0

section '.idata' import readable writable

 library kernel32, 'KERNEL32.DLL', \
         user32, 'USER32.DLL'

 import kernel32,\
        GetStdHandle, 'GetStdHandle', \
        ReadConsole, 'ReadConsoleA', \
        WriteConsole, 'WriteConsoleA', \
        CreateFile, 'CreateFileA', \
        ReadFile, 'ReadFile', \
        WriteFile, 'WriteFile', \
        GetLastError, 'GetLastError', \
        SetFilePointer, 'SetFilePointer', \
        SetFilePointerEx, 'SetFilePointerEx', \
        GetCommandLine, 'GetCommandLineA', \
        ExitProcess,'ExitProcess'

 import user32,\
        wsprintf, 'wsprintfA'    


This program is slower than CMD copy command.

Code:
                4MB             2GB
DUMPFILE.ASM    281 ms          1 min 42 sec 985 ms
COPY            187 ms                33 sec 687 ms    


BTW, the upper limit of file size supported by Assembly code above is 2GB (2^32 / 2)
Post 17 Apr 2021, 19:19
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.