News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

push 0

Started by Nyatta, January 23, 2016, 08:38:32 AM

Previous topic - Next topic

Nyatta

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?

jj2007

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.

Nyatta

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!

dedndave

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

Nyatta

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

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:

jj2007

You should discover the joys of OllyDbg. In your code, insert an int 3 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.

dedndave

"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

Nyatta

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. In your code, insert an int 3 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.

Nyatta

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?

dedndave

no - you want to pass the file handle to the CloseHandle function

Nyatta

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.

dedndave

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

Nyatta

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.

Grincheux

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.

dedndave

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