I feel very silly asking this, but what purpose does "push 0" serve?; I can't get over feeling that I am pushing 4 bytes of garbage onto the stack, which certainly is incorrect. Studying the code below, originally posted by jj2007, everything works swimmingly despite me perceiving it as there being 4 byte gap between the data it is popping off the stack... not to say my perception is altering the outcome, as this isn't some sort of quantum computing. :P
Quote from: jj2007 on April 12, 2014, 10:08:17 AMinclude \masm32\include\masm32rt.inc
.code
start:
; open a file - the handle returned in eax gets pushed on the stack:
push fopen("\Masm32\examples\exampl05\hlldemo\fileio\fileio.exe")
push 0 ; create an empty 4-byte buffer on the stack
mov ecx, esp ; and store its address to ecx
push eax ; create another 4-byte buffer on the stack
mov edx, esp ; and store its address to edx
invoke ReadFile, eax, ecx, 1, edx, 0 ; invoke ReadFile, handle, pBuffer, #bytes, pBytesRead, 0
.if !eax
print LastError$(), 13, 10 ; good to know if an error occurred
.endif
pop ecx ; get value from the second stack buffer
print str$(ecx), " bytes read", 13, 10
pop ebx ; get value from the first stack buffer
call CloseHandle ; the handle is still on the stack, so this is invoke CloseHandle, eax
print str$(ebx), " is the first byte"
exit
end start
I'd rather not run under any false assumptions so in the context of that code what is push 0 accomplishing and is it generally used to serve the same purpose in most code?
Sometimes a function expects a pointer to a variable. The push 0 creates a variable:
AAAAA stack before
0000AA stack after push 0
The function uses these 4 bytes to store a result, e.g. 1234:
1234AA
Now you use e.g.
pop ecx
... and ecx contains 1234.
Btw the example you posted above has even two stack variables.
To be sure I'm clear on this...
push fopen(offset fileLocation) ; Nothing pushed into the stack yet...
push 0 ; Faux-pointer defined, now it's in the stack!
first, "fopen" returns a file handle in EAX - it is puhed on the stack (for later use)
next, these instructions kind of work together
a temporary (local) variable is created and initialized to 0
the pointer to that variable is in ECX
this variable is used by the ReadFile function to write the number of bytes read
push 0 ; create an empty 4-byte buffer on the stack
mov ecx, esp ; and store its address to ecx
after the read, that variable is removed from the stack
if the read is successful, it is no longer 0 - it holds the number of bytes actually read
pop ecx
Quote from: jj2007 on January 23, 2016, 08:45:58 AMBtw the example you posted above has even two stack variables.
Quote from: dedndave on January 23, 2016, 10:41:45 AM
first, "fopen" returns a file handle in EAX - it is puhed on the stack (for later use)
I'm still very confused on what I'm looking at. The second buffer pushes EAX onto the stack where ReadFile then modifies it? I was under the impression the second parameter of ReadFile would point me to the data I loaded from the file into memory?
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx)
Maybe I'm misunderstanding this due to how all of the bytes are being handled and when they are being altered. EAX and EDX (ESP and EBP also) only change between every function, unless otherwise specified in the parameters, correct?
Perhaps I should temporarily fall back on something simpler to practice proper register usage.
Edit:
I did accomplish this through studying other codes, I was aiming to tweak it to load a file in place of nData.
.686
.model flat, stdcall
option casemap :none
include \masm32\include\masm32rt.inc
.data
lb db 13, 10, 0
nData dw 01408h, 08C08h, 0DF08h, 0F408h, 00D09h, 0FE08h, 0E108h, 0BA08h,\
08A08h, 05A08h, 02708h, 0E207h, 0BF07h, 09407h, 08807h, 0B607h,\
0DF07h, 02E08h, 06E08h, 0C808h, 02A09h, 0AF09h, 0470Ah, 0EC0Ah,\
07E0Bh, 0FF0Bh, 0510Ch, 05E0Ch, 0400Ch, 0FA0Bh, 0710Bh, 0D50Ah
.code
start:
inkey "Data: 32 double-words.", 13, 10, "Press any key to print hexadecimal values ...", 13, 10
mov ecx, 16
mov esi, offset nData
printNumb:
push esi
push ecx
mov eax, [esi]
and eax, 0000FFFFh
mov ebx, [esi]
shr ebx, 16
ror ax, 8
ror bx, 8
print uhex$(eax), 13, 10
print uhex$(ebx), 13, 10
pop ecx
pop esi
add esi, 4
loop printNumb
print offset lb
inkey "End of numberical values.",10,13,"Press any key to close ...", 13, 10
invoke ExitProcess,0
end start
Editing in loading from a file results in this mess...
.686
.model flat, stdcall
option casemap :none
include \masm32\include\masm32rt.inc
.code
start:
inkey "Press any key to begin ...", 13, 10, 10
push fopen("My_File.raw")
push 0
mov ecx, esp
push eax
mov edx, esp
invoke ReadFile, eax, ecx, 64, edx, 0
.if !eax
print LastError$(), 13, 10
.endif
pop ecx
push ecx
print str$(ecx), " bytes read", 13, 10
pop ecx
pop ebx
shr ecx, 2
pLoop:
push ecx
push ebx
and ebx, 0000FFFFh
print str$(ebx), 13, 10
pop ebx
push ebx
shr ebx, 16
print str$(ebx), 13, 10
pop ebx
pop ecx
add ebx, 4
loop pLoop
call CloseHandle
inkey "Press any key to exit ...", 13, 10
exit
end start
The code outputs The correct first 2 initial values, however adding 4 to ebx adds an additional 4 to the first WORD which each rep, and as the second WORD is outside of bh and bl it remains equal with each rep. I'm attempting to treat ebx as a pointer to the address, but it's treating it as a value. I've been trying to correct this for awhile now... :icon_confused:
You should discover the joys of OllyDbg (http://www.ollydbg.de/version2.html). In your code, insert an int 3 (http://int%203) e.g. before the ReadFile call, then open the exe in Olly and hit F9. You will be just in front of the ReadFile. Hit F8 slowly and watch what happens, especially in the stack window in the lower right corner.
"fopen" is a macro - part of the masm32 package
you can view it in the file \masm32\macros\macros.asm
; -------------------------------------------------------------------------
; open an existing file with read / write access and return the file handle
; -------------------------------------------------------------------------
fopen MACRO filename
invoke CreateFileA,reparg(filename),GENERIC_READ or GENERIC_WRITE,
NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
EXITM <eax> ;; return file handle
ENDM
it opens the file, and returns the handle in EAX
Quote from: dedndave on January 24, 2016, 08:11:10 AM"fopen" is a macro - part of the masm32 package
Thank you, I've noticed this said many times, sorry you have to repeat it yet again, it is however very helpful.
fopen MACRO filename
invoke CreateFileA,reparg(filename),GENERIC_READ or GENERIC_WRITE,
NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
EXITM <eax> ;; return file handle
ENDM
The macro runs something I was attempting to do earlier with no success: running the CreateFile function directly with the same parameters (not CreateFileA).
Quote from: jj2007 on January 24, 2016, 08:04:44 AMYou should discover the joys of OllyDbg (http://www.ollydbg.de/version2.html). In your code, insert an int 3 (http://int%203) e.g. before the ReadFile call, then open the exe in Olly and hit F9. You will be just in front of the ReadFile. Hit F8 slowly and watch what happens, especially in the stack window in the lower right corner.
That's amazing to watch, it'll be very helpful when I understand what I'm looking at a bit better.
My interpretation of what I am seeing is that I am changing the value of EBX which is acting to store the first 4 bytes of data opposed to storing the address and incrementing it as I had intended, not new news, but now I know what it looks like. From within the first loop:
ADD EBX, 4 ;Imm=4, EBX=088C0814
EBX is equal to 2188 and 2068, explaining why 2068 repeatedly increments by my addition value and not 2188.
This should work fine once I find some information on accessing data from addresses on registers as well as storing those addresses to the registers, I'll be back with a follow up post by tonight, hopefully sooner with proper code. :biggrin:
Edit:
Quote from: jj2007 on January 24, 2016, 08:04:44 AMHit F8 slowly and watch what happens, especially in the stack window in the lower right corner.
I feel so dense right now; I was expecting the data to be returned with an address, not pushed into the stack. Thank you so much.
Done much sooner than expected, this runs without missing a beat, hooray! :biggrin:
.686
.model flat, stdcall
option casemap :none
include \masm32\include\masm32rt.inc
.data
lb db 13, 10, 0
nFile db "My_File.raw", 0
.code
start:
inkey "Press any key to begin ...", 13, 10, 10
push fopen(offset nFile)
push 0
mov ecx, esp
push eax
mov edx, esp
invoke ReadFile, eax, ecx, 64, edx, 0
.if !eax
print LastError$(), 13, 10
.endif
push 0
call CloseHandle
pop ecx
push ecx
print str$(ecx), " bytes read.", 13, 10
pop ecx
push ecx
shr ecx, 1
print str$(ecx), " words to print.", 13, 10, 10
pop ecx
shr ecx, 2
pLoop:
pop ebx
push ecx
push ebx
and ebx, 0000FFFFh
print str$(ebx), 13, 10
pop ebx
shr ebx, 16
print str$(ebx), 13, 10
pop ecx
loop pLoop
print offset lb
inkey "Press any key to exit ...", 13, 10
exit
end start
Is the push 0 before the CloseHandle an appropriate practice?
no - you want to pass the file handle to the CloseHandle function
Quote from: dedndave on January 24, 2016, 12:13:32 PMno - you want to pass the file handle to the CloseHandle function
Okay, I understand now, I misinterpreted what I was doing. I was oblivious to the fact invoke pushes each following value to the stack, as call directly calls the function without pushing values onto the stack. Additionally, I better understand what I'm looking at when I search for a function or macro, and I think I have bettered my understanding of proper more-so fluid register use. Take a look:
.686
.model flat, stdcall
option casemap :none
include \masm32\include\masm32rt.inc
.data
lb db 13, 10, 0
nFile db "My_File.raw", 0
.code
start:
inkey "Press any key to begin ...", 13, 10, 10
invoke CreateFileA,reparg(offset nFile),GENERIC_READ or GENERIC_WRITE,
NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
push esp
push eax
invoke GetFileSize, eax, 0
pop esi
pop ecx
mov edx, ecx
add ecx, 4
invoke ReadFile, esi, ecx, eax, edx, 0
.if !eax
print LastError$(), 13, 10
.endif
invoke CloseHandle, esi
pop ebx
print str$(ebx), " bytes read.", 13, 10
shr ebx, 1
print str$(ebx), " words to print.", 13, 10, 10
mov ecx, ebx
shr ecx, 1
pLoop:
pop ebx
push ecx
mov esi, ebx
and ebx, 0000FFFFh
print uhex$(ebx), 13, 10
shr esi, 16
print uhex$(esi), 13, 10
pop ecx
loop pLoop
print offset lb
inkey "Press any key to exit ...", 13, 10
exit
end start
I was nearly about to make a reply asking for advice on it not printing the first 4 bytes of data, then I realized I had my ReadFile function set pointing both outputs to the same exact location, whoops! I hope and want to believe everything is in perfect working order now.
when you write
INVOKE SomeFunction, Arg1, Arg2, Arg3, Arg4
the assembler actually generates this code
push Arg4
push Arg3
push Arg2
push Arg1
call SomeFunction
each argument can be a memory location (global variable), local variable, register, or constant (which may be an address)
in the case of CloseHandle, there is only one argument, the handle to be closed
for StdCall functions (most window API functions), the arguments are removed from the stack when the function returns
Alright, I feel I got a grasp on all this information; I utilized PUSH 0 and CloseHandle in the code within this post.
I worked on this a bit as a way of practice and it's pretty purposeless at the moment... I plan to write it to save to a file next, having it convert the entire file into a list of signed integers represented with ASCII split into labeled columns that narrate the audio channels.
;For use with Signed 16 or 24-BIT PCM .RAW audio files.
;Define BIT length of .RAW file with data variable "fType".
.686
.MODEL flat, stdcall
OPTION casemap :none
INCLUDE masm32rt.inc
.DATA
fName DB "Audio_Seg.raw", 0
fType DD 16
exitMsg DB 13, 10, "Press any key to exit ...", 13, 10, 0
.DATA?
fBuffer DB 1024 DUP(?)
.CODE
start:
INVOKE CreateFileA,reparg( OFFSET fName ),GENERIC_READ OR GENERIC_WRITE,
NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
PUSH EAX ;Store the handle to the stack.
PUSH 0 ;Create a buffer in the stack to retrieve count of BYTEs read.
MOV ECX, ESP ;Move the buffer / stack pointer to ECX.
MOV ESI, offset fBuffer ;Set data buffer pointer to ESI.
INVOKE ReadFile, EAX, ESI, 1024, ECX, 0
.IF !EAX ;Check for error,
POP EBX ;if true move BYTEs read out of the way
POP EBX ;retrieve handle,
INVOKE CloseHandle, EBX ;close handle,
inkey LastError$(), 13, 10 ;display the error
exit ;and close the program.
.ENDIF
POP EBX ;Move BYTEs read from stack to EBX.
print str$( EBX ), " BYTEs read.", 13, 10
POP ECX ;Move the handle from the stack to ECX
INVOKE CloseHandle, ECX ;and close the program's access to the file.
MOV EAX, fType ;Move the BIT size to EAX,
CMP EAX, 24 ;if it's 24-BIT
JE print24 ;jump to print24, else continue.
SHR EBX, 1 ;Divide BYTEs read by 2 to represent WORDs read.
print str$( EBX ), " WORDs to print.", 13, 10, 10
SHR EBX, 1 ;Divide WORDs read by 2 to represent DWORDs read.
inkey "Press any key to begin ...", 13, 10
MOV ECX, EBX ;Copy DWORDs to read to count register.
printData16:
PUSH ECX ;Push count to the stack.
MOV EAX, [ ESI ] ;Copy the DWORD at address within ESI to EAX.
MOV EBX, [ ESI ] ;Copy the DWORD at address within ESI to EBX.
AND EAX, 0000FFFFh ;Remove unneeded data from first WORD.
SHR EBX, 16 ;Rotate second WORD right into AX with zeros carried left.
MOVSX EAX, AX
MOVSX EBX, BX
print str$( EAX ), 13, 10 ;Print value 1.
print str$( EBX ), 13, 10 ;Print value 2.
POP ECX ;Retrieve the count from the stack.
ADD ESI, 4 ;Increment ESI to next DWORD.
LOOP printData16
inkey OFFSET exitMsg
exit
print24:
MOV EAX, EBX ;Move BYTEs read to EAX.
MOV EBX, 3 ;Move divisor, 3, to EBX.
DIV EBX ;Divide BYTEs read by 3 to represent TRIBYTEs read.
MOV EBX, EAX ;Move TRIBYTEs to read to EBX.
print str$( EBX ), " TRIBYTEs to print.", 13, 10, 10
inkey "Press any key to begin ...", 13, 10
MOV ECX, EBX ;Copy TRIWORDs to read to count register.
printData24:
PUSH ECX ;Push count to the stack.
MOV EAX, [ ESI ] ;Copy the DWORD at address within ESI to EAX
AND EAX, 00FFFFFFh ;Remove garbage data from current TRIBYTE.
.IF EAX > 8388607 ;If value is larger than number
ADD EAX, 0FF000000h ;convert to signed DWORD to print correctly.
.ENDIF
print str$( EAX ), 13, 10 ;and then print the resulting value.
POP ECX ;Retrieve the count from the stack.
ADD ESI, 3 ;Increment ESI to next TRIBYTE.
LOOP printData24
inkey OFFSET exitMsg
exit
END start
If you mind taking the time to skim this, is there anything obvious that I am doing incorrectly or inefficiently?
PSA: Anything above 16-BIT audio is overkill.
You should test the number of bytes read
cmp DWord Ptr [ecx],0
jne EOF
cmp DWord Ptr [ecx],1024
jne Error
The code is not very clear, comment it and rewrite it.
Mine is not always very easy to understand but here you make worst than I.
this is not very sound practice
write the code so that it will work, pass or fail - and exit at the same point
notice that EBX, ESI, EDI, and EBP are preserved across API calls
you can use those resisters to hold values without PUSH/POP
.IF !EAX ;Check for error,
POP EBX ;if true move BYTEs read out of the way
POP EBX ;retrieve handle,
INVOKE CloseHandle, EBX ;close handle,
inkey LastError$(), 13, 10 ;display the error
exit ;and close the program.
.ENDIF
Quote from: Grincheux on January 27, 2016, 03:44:21 PMYou should test the number of bytes read
cmp DWord Ptr [ecx],0
jne EOF
cmp DWord Ptr [ecx],1024
jne Error
The code is not very clear, comment it and rewrite it.
Mine is not always very easy to understand but here you make worst than I.
It is intended to load any file size, but print a max of 1 KB of data, I don't understand the need to test the filesize for any means other than informing the user the file read was empty, as that would crash the program.
I added comments more as step-by-step means to keep track of what data's where, as this was started on my 3rd day using MASM or any assembler. :icon_confused:
POP EBX ;Move BYTEs read from stack to EBX.
print str$( EBX ), " BYTEs read.", 13, 10
CMP EBX, 0 ;If zero BYTEs are read
JE closeProg ;jump to closing sequence.
Quote from: dedndave on January 27, 2016, 05:31:13 PMthis is not very sound practice
write the code so that it will work, pass or fail - and exit at the same point
notice that EBX, ESI, EDI, and EBP are preserved across API calls
you can use those resisters to hold values without PUSH/POP
.IF !EAX ;Check for error,
POP EBX ;if true move BYTEs read out of the way
POP EBX ;retrieve handle,
INVOKE CloseHandle, EBX ;close handle,
inkey LastError$(), 13, 10 ;display the error
exit ;and close the program.
.ENDIF
What portion of this as practice isn't sound? Printing more data than the console holds within it's history?
My logic for split exit points was to avoid the additional JUMP where I felt it was unneeded (saving 3 clocks); I was under the impression that having a single exit point boiled down to personal preference, though it can result in lost readability. I'm trying my best to lean towards speed over readability where rational, in part to hardwire speed-based systems into my learning, but also to expand my comprehension. (I'd use a loop instead of repeating the same macro an irrational number of times.) I'm not trying to make it seem like anyone here is the bad-guy, I'm just explaining what I, most likely incorrectly, see here.
Thank you for pointing out the preservation of those registers, I will edit my code to incorporate those.
conditional pushing and popping is asking for trouble (although, i have done it myself - lol)
and - having a common exit point makes more sense
try writing the code so there is only one close handle call
then, branch as required - perhaps to the exit point
also - handles and counts, etc - don't need to always be kept in registers with push/pop
it sometimes makes for cleaner code to store the value in a memory variable
that way, push and pop mismatches are less likely to cause problems later on
you may see some of us do the push/pop thing a lot
we are very comfortable with use of the stack :biggrin:
it's not necessarily the best code, though
Quote from: dedndave on January 28, 2016, 01:47:29 AMconditional pushing and popping is asking for trouble (although, i have done it myself - lol)
The pop occurs and the program exits, but I understand you mean that in the broad scope of programming. Those pops were removed when I changed the "PUSH EAX" before "PUSH 0" to "MOV EDI, EAX", it cut down on few lines all around. :t
.IF !EAX ;Check for error,
INVOKE CloseHandle, EDI ;close handle,
inkey LastError$(), 13, 10 ;display the error
exit ;and close the program.
.ENDIF
Quote from: dedndave on January 28, 2016, 01:47:29 AMand - having a common exit point makes more sense
try writing the code so there is only one close handle call
then, branch as required - perhaps to the exit point
My code makes it impossible for CloseHandle to be called twice, and as I said, I feel I am increasing the clock count by 3 by branching when a condition that triggers closing procedures are met. It just sounds more like a decision between file-size and clocks... and I was practicing optimisation tactics here.
I'm uncertain on why I'm being told to branch beyond that, that's what I'm asking for information on.
Quote from: dedndave on January 28, 2016, 01:50:22 AMalso - handles and counts, etc - don't need to always be kept in registers with push/pop
it sometimes makes for cleaner code to store the value in a memory variable
I used a PUSH 0 and directed lpNumberOfBytesRead from the ReadFile function to this address because it requests a pointer to a variable, and not a register. If it were faster for the computer to read from the memory I would do that, until then I aim to stay away until I hit a dead-end and need to store some data.
After exit you don't POP EDI!
Quote from: Grincheux on January 28, 2016, 04:32:10 AM
After exit you don't POP EDI!
Doesn't the exit macro trigger clean-up of the process space before turning it over to the OS?
exit MACRO optional_return_value
IFNDEF optional_return_value
invoke ExitProcess, 0
ELSE
invoke ExitProcess,optional_return_value
ENDIF
ENDM
The MSDN page on ExitProcess (http://"https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx") states:
"6. All of the object handles opened by the process are closed."
Does the stack have a handle that once cleared leaves the remanence within the RAM until it's either over-written or degrades?
Quote from: Nyatta on January 28, 2016, 07:11:05 AM
Quote from: Grincheux on January 28, 2016, 04:32:10 AM
After exit you don't POP EDI!
Doesn't the exit macro trigger clean-up of the process space before turning it over to the OS?
Yes. Also, ExitProcess does never return, thus following code would be dead code.
BTW, you could make your live much easier -- without any performance lost -- by using local variables rather than this error-prone, unmaintainable and unreadably push/pop technique.
Quote from: qWord on January 28, 2016, 07:48:40 AMYes. Also, ExitProcess does never return, thus following code would be dead code.
Exactly my intended outcome if the condition is met, but from the looks of what I'm being told I used a fairly unconventional method.
Quote from: qWord on January 28, 2016, 07:48:40 AMBTW, you could make your live much easier -- without any performance lost -- by using local variables rather than this error-prone, unmaintainable and unreadably push/pop technique.
No performance loss in mind I will certainly take a look as it sounds it will make the whole process far easier. Ollydbg is simplifying all of this by far have I make a mistake in the stack, it's getting far easier to track registers and the stack.
Isn't my buffer variable, "fBuffer", a local variable as it's data is written to as the program runs?
Local variables are local to a function or procedure and are setup on the stack when entering a function and destroyed when returning from it. MASM does that for you automatically when using PROCs with the keyword LOCAL (use OllyDbg to see how it is done exactly):
include \masm32\include\masm32rt.inc
.code
main proc
LOCAL hFile:HANDLE, buffer[123]:CHAR, numberOfBytesRead:DWORD
mov hFile, fopen("...")
; ...
invoke ReadFile, hFile, ADDR buffer, SIZEOF buffer, ADDR numberOfBytesRead, 0
; ...
invoke CloseHandle, hFile
exit
main endp
end main
Remarks that the total size of local variables in a PROC should not exceed 4096 Bytes (= size of 1 page), because there is a guard-page (access to that page cause an exception) below the stack that must be touched to increase the stack size (the system does that in the corresponding exception handler). There is also the possibility to allocate more than 4KB, but this requires to "probe the stack" ... but that is another story.
Quote from: qWord on January 28, 2016, 09:04:46 AMthe total size of local variables in a PROC should not exceed 4096 Bytes (= size of 1 page), because there is a guard-page (access to that page cause an exception) below the stack that must be touched to increase the stack size (the system does that in the corresponding exception handler).
You can see the effect in the debugger when playing with the size of a local buffer:
include \masm32\include\masm32rt.inc
.code
MyTest proc arg
LOCAL LocBuffer[3*4096+3960]:BYTE ; try +3970
lea eax, LocBuffer
int 3 ; for Olly
dec dword ptr [eax] ; this modifies the start of the buffer
ret
MyTest endp
start: invoke MyTest, 123
print "ok"
exit
end start
if i were to write such a program, it might look something like this...
INCLUDE \masm32\include\masm32rt.inc
.686
;#################################################################################
FileSize PROTO :LPSTR
OpnFile PROTO :DWORD,:LPSTR
;#################################################################################
RawFileType EQU 16
FileBufferSize EQU 1024
;*********************************************************************************
OPF_FILECREATE EQU GENERIC_WRITE or CREATE_ALWAYS ;(40000002h)
OPF_FILEWRITE EQU GENERIC_WRITE or OPEN_EXISTING ;(40000003h)
OPF_FILEREAD EQU GENERIC_READ or OPEN_EXISTING ;(80000003h)
OPF_FILERANDOM EQU GENERIC_READ or GENERIC_WRITE or OPEN_EXISTING ;(C0000003h)
;#################################################################################
.DATA
ALIGN 4
szFileName db "Audio_Seg.raw",0
szFileErrMsg db "Error Reading Input File"
szExitMsg db 13,10,"Press any key to exit ...",13,10,0
;*********************************************************************************
.DATA?
ALIGN 4
hFileHandle HANDLE ?
dwBytesRead dd ?
FileBuffer db FileBufferSize dup(?)
;#################################################################################
.CODE
;*********************************************************************************
main PROC
INVOKE FileSize,offset szFileName
.if ecx==INVALID_FILE_ATTRIBUTES
mov edx,offset szFileErrMsg
.else
mov ebx,FileBufferSize
.if !(edx) && (eax<ebx)
xchg eax,ebx ;EBX = lesser of file size or buffer size
.endif
INVOKE OpnFile,OPF_FILEREAD,offset szFileName
.if eax==INVALID_HANDLE_VALUE
mov edx,offset szFileErrMsg
.else
mov hFileHandle,eax
INVOKE ReadFile,eax,offset FileBuffer,ebx,offset dwBytesRead,NULL
push eax
INVOKE CloseHandle,hFileHandle
pop eax
.if eax
;add your code here
;to process data from the buffer
mov edx,offset szExitMsg
.else
mov edx,offset szFileErrMsg
.endif
.endif
.endif
inkey edx
exit
main ENDP
;*********************************************************************************
FileSize PROC lpszFileName:LPSTR
;File Size Function
;Call With: lpszFileName = address of zero-terminated filename string
;
; Returns: EDX:EAX = file size
; ECX = file attributes, INVALID_FILE_ATTRIBUTES if file not found (-1)
;
;Also Uses: all other registers are preserved
;-----------------------------------------
LOCAL _w32fds :WIN32_FILE_ATTRIBUTE_DATA
;WIN32_FILE_ATTRIBUTE_DATA STRUCT
; dwFileAttributes DWORD ?
; ftCreationTime FILETIME <>
; ftLastAccessTime FILETIME <>
; ftLastWriteTime FILETIME <>
; nFileSizeHigh DWORD ?
; nFileSizeLow DWORD ?
;-----------------------------------------
xor ecx,ecx
mov _w32fds.dwFileAttributes,INVALID_FILE_ATTRIBUTES
mov _w32fds.nFileSizeHigh,ecx
mov _w32fds.nFileSizeLow,ecx
INVOKE GetFileAttributesEx,lpszFileName,ecx,addr _w32fds
mov edx,_w32fds.nFileSizeHigh
mov eax,_w32fds.nFileSizeLow
mov ecx,_w32fds.dwFileAttributes
ret
FileSize ENDP
;*********************************************************************************
OPTION PROLOGUE:None
OPTION EPILOGUE:None
OpnFile PROC dwOpenFlags:DWORD,lpszFileName:LPSTR
;File Open Function
;Call With: lpszFileName = address of zero-terminated filename string
; dwOpenFlags = combined creation and access flags:
; bits 0 and 1 = dwCreationDisposition flag(s)
; typically, CREATE_ALWAYS to create a new file,
; or OPEN_EXISTING to open an existing file
; bits 30 and 31 = dwDesiredAccess flag(s)
; typically, GENERIC_WRITE to write to a file,
; or GENERIC_READ to read a file,
; or both flags for read/write ("random") access
;
; Returns: EAX = file handle, INVALID_HANDLE_VALUE if unable to open file (-1)
;
;Also Uses: ECX and EDX are destroyed, all other registers are preserved
;-----------------------------------------
mov eax,[esp+4]
xor ecx,ecx
movzx edx,al
and eax,0C0000000h
and dl,3
INVOKE CreateFile,[esp+32],eax,FILE_SHARE_READ,ecx,edx,FILE_ATTRIBUTE_NORMAL,ecx
ret 8
OpnFile ENDP
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
;#################################################################################
END main
Easy to read.
Just verify the number of bytes read.
easy enough
just change this line
INVOKE CloseHandle,hFileHandle
pop eax
.if eax
to this
INVOKE CloseHandle,hFileHandle
pop eax
.if (eax) && (ebx==dwBytesRead)
That was a joke
test eax,eax
jz Eof
sub eax,FileBufferSize
jnz Error_1
and
mov edx,_w32fds.nFileSizeHigh
It is ignored and the buffer will not be big enougth : 4 294 967 296 bytes if edx = 1 and eax = 0h
if you read the code a little, you'll see that i get the file size
then, i determine the lesser of the file size or buffer size, and that read count value is in EBX
so, it's no problem to add the check :P