I am trying to figure this out but am stumped a bit. What I am trying to do is to use the ReadConsole/WriteConsole functions from the win32 lib and got it to work a degree but just not there. I am having trouble getting the second write console working correctly. I don't think I am sending the buffer to the variable right. I must be missing something here and I dont know what it is.
Here is what I have so far
TITLE MASM Template (main.asm)
; Description:
;
; Revision date:
INCLUDE Irvine32.inc
BufSize = 80
.data
endl EQU <0dh,0ah> ; end of line sequence
;variable holders
firstN db ?
fNSize db ($-firstN)
;messages for getting input
fName LABEL BYTE
BYTE "Enter your first name", endl
fNameSize DWORD ($-fName)
;output handlers
consoleHandle HANDLE 0 ; handle to standard output device
bytesWritten DWORD ? ; number of bytes written
;input handlers
buffer BYTE BufSize DUP(?)
stdInHandle HANDLE ?
bytesRead DWORD ?
.code
main PROC
; Get the console output handle:
INVOKE GetStdHandle, STD_OUTPUT_HANDLE
mov consoleHandle,eax
; Write a string to the console:
INVOKE WriteConsole,
consoleHandle, ; console output handle
ADDR fName, ; string pointer
fNameSize, ; string length
ADDR bytesWritten, ; returns num bytes written
0 ; not used
; Get handle to standard input
INVOKE GetStdHandle, STD_INPUT_HANDLE
mov stdInHandle,eax
; Wait for user input
INVOKE ReadConsole, stdInHandle, ADDR buffer,
BufSize, ADDR bytesRead, 0
;cheack to see if read consol worked
mov esi, OFFSET buffer
mov ecx, bytesRead
mov ebx, TYPE buffer
call DumpMem
;move the input into the variable
mov firstN, TYPE buffer
; Write a string to the console:
INVOKE WriteConsole,
consoleHandle, ; console output handle
ADDR firstN, ; string pointer
fNSize, ; string length
ADDR bytesWritten, ; returns num bytes written
0 ; not used
INVOKE ExitProcess,0
main ENDP
END main
Thanks for any help :)
firstN and fNSize are each a single byte and that's not what you want. Simply do this for the second WriteConsole call:
INVOKE WriteConsole,
consoleHandle, ; console output handle
ADDR buffer, ; string pointer
bytesRead, ; string length
ADDR bytesWritten, ; returns num bytes written
0 ; not used
Related
I am trying to learn how to use the windows api (instead of just using C calls, irvine32 or masm32) And are running into issues with ReadConsoleInputA (WriteConsoleA works fine).
Also, I don't get why in the PROC prototype for the function, most examples append either an A or a W at the end of ReadConsoleInput/WriteConsole, can you explain why?
.data
consoleOutHandle dd ?
consoleInHandle dd ?
bufferlen dd ?
buffer db ?
bufferSize DWORD ?
message db "Enter a number:", 0
lmessage equ $-message
.code
main PROC
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
invoke ReadConsoleInputA, consoleOutHandle, offset buffer, 128, bufferSize
main endp
end main
It throws: Access violation writing location 0x00000004.
Following the advice from Michael Petch, I have this code now:
.data
consoleOutHandle dd ?
consoleInHandle dd ?
byteswritten dd ?
bufferlen dd ?
buffer db 128 DUP(?)
bufferSize dd ?
message db "Enter a number:", 0
lmessage equ $-message
.code
main PROC
invoke GetStdHandle, STD_INPUT_HANDLE
mov consoleInHandle, eax
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
mov eax, lmessage
invoke WriteConsoleA, consoleOutHandle, offset message, eax, bytesWritten, 0
invoke ReadConsoleInputA, consoleInHandle, offset buffer, 128, offset bufferSize
main endp
end main
And now it throws "triggered a breakpoint".
Disassembly:
invoke ReadConsoleInputA, consoleInHandle, offset buffer, 128, offset bufferSize
00E71066 push offset bufferSize (0E74090h)
00E7106B push 80h
00E71070 push offset buffer (0E74010h)
00E71075 push dword ptr [consoleInHandle (0E74004h)]
00E7107B call _ReadConsoleInputA#16 (0E7100Ah)
--- No source file -------------------------------------------------------------
00E71080 int 3 **---> Breakpoint here**
00E71081 int 3
You asked what the A and W suffix on the end of the WinAPI functions are for. Functions ending with A denote Ansi, and functions ending with W are Wide. Microsoft documents them this way:
Unicode and ANSI Functions
When Microsoft introduced Unicode support to Windows, it eased the transition by providing two parallel sets of APIs, one for ANSI strings and the other for Unicode strings. For example, there are two functions to set the text of a window's title bar:
SetWindowTextA takes an ANSI string.
SetWindowTextW takes a Unicode string.
In the first version of the code
You don't allocate space necessary for buffer. You had:
buffer db ?
That allocated a single byte to the buffer. It should have been:
buffer db 128 DUP(?)
You used STD_OUTPUT_HANDLE instead of STD_INPUT_HANDLE
The last parameter to ReadConsoleInputA is a pointer to a DWORD that will return the number of events read. Changing the variable name bufferSize might make the code more readable. From the ReadConsoleInputA documentation:
BOOL WINAPI ReadConsoleInput(
_In_ HANDLE hConsoleInput,
_Out_ PINPUT_RECORD lpBuffer,
_In_ DWORD nLength,
_Out_ LPDWORD lpNumberOfEventsRead
);
If you are reading just the keyboard you should be using ReadConsoleA as ReadConsoleInputA will process keyboard and mouse events and may prematurely return before your string is read. ReadConsoleA takes one extra parameter and you can set it to NULL:
BOOL WINAPI ReadConsole(
_In_ HANDLE hConsoleInput,
_Out_ LPVOID lpBuffer,
_In_ DWORD nNumberOfCharsToRead,
_Out_ LPDWORD lpNumberOfCharsRead,
_In_opt_ LPVOID pInputControl
);
To exit the program you need to invoke ExitProcess .
In the second version of the code
Your code does:
invoke WriteConsoleA, consoleOutHandle, offset message, eax, bytesWritten, 0
bytesWritten needs to be a pointer because that is an output parameter. From WriteConsoleA documentation:
BOOL WINAPI WriteConsole(
_In_ HANDLE hConsoleOutput,
_In_ const VOID *lpBuffer,
_In_ DWORD nNumberOfCharsToWrite,
_Out_ LPDWORD lpNumberOfCharsWritten,
_Reserved_ LPVOID lpReserved
);
A version of the code that uses ReadConsoleA instead of ReadConsoleInputA based on your second code example could look like:
.data
consoleOutHandle dd ?
consoleInHandle dd ?
bytesWritten dd ?
bufferlen dd ?
buffer db 128 DUP(?)
numEvents dd ?
message db "Enter a number:", 0
lmessage equ $-message
.code
main PROC
invoke GetStdHandle, STD_INPUT_HANDLE
mov consoleInHandle, eax
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
mov eax, lmessage
invoke WriteConsoleA, consoleOutHandle, offset message, eax, offset bytesWritten, 0
invoke ReadConsoleA, consoleInHandle, offset buffer, 128, offset numEvents, 0
invoke ExitProcess, 0
main endp
end main
This code can be cleaned up a bit by using MASM's sizeof operator. The code could be written as:
.data
consoleOutHandle dd ?
consoleInHandle dd ?
buffer db 128 DUP(?)
bytesWritten dd ?
numEvents dd ?
message db "Enter a number:", 0
.code
main PROC
invoke GetStdHandle, STD_INPUT_HANDLE
mov consoleInHandle, eax
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
invoke WriteConsoleA, consoleOutHandle, offset message, sizeof message, offset bytesWritten, 0
invoke ReadConsoleA, consoleInHandle, offset buffer, sizeof buffer, offset numEvents, 0
invoke ExitProcess, 0
main endp
end main
I do a MapViewOfFile to get a pointer on the begin of my file, after that I want to print the value of it in hexa.
When I print it as string I get "MZ" which is the good value (the magic number) but I want it in hexa (5A4D).
I tried to format with %x in wsprintf but it doesn't work, I got 230000 as value..
EDIT the tried for %x:
.data
header_format db "The header is: %x",0
buffer db 256 dup(?) ; File data
.data?
pMemory DWORD ? ; Pointer to the data in the source file
getData:
;pMemory is the ptr which is correctly printed with %s
invoke wsprintf, ADDR buffer, ADDR header_format, [pMemory] ;
invoke MessageBox, NULL, Addr buffer, Addr header_test, MB_OK
Have you any suggestions ?
Thanks.
It finally works by using this solution :
push STD_OUTPUT_HANDLE
call GetStdHandle
mov eax, pMemory
push eax
print right$(uhex$(eax),2),13,10
pop eax
mov eax, pMemory
push eax
print right$(uhex$([eax]),2),13,10
pop eax
Supposing we have got a text file sample.txt:
one
two
...
Now we want to remove the first line:
two
...
A quick way to do that is to use input redirection, set /P and findstr1 (I know there are other ways using more or for /F, but let us forget about them for now):
#echo off
< "sample.txt" (
set /P =""
findstr "^"
)
The output is going to be as expected.
However, why is the output empty when I replace the input redirection < by type and a pipe | :
#echo off
type "sample.txt" | (
set /P =""
findstr "^"
)
When I replace set /P ="" by pause > nul, the output is what I expect -- the input file is output but with the first character of the first line missing (as it is consumed by pause). But why does set /P seem to consume everything instead of only the first line like it does with the redirection < approach? Is that a bug?
To me it looks like set /P fails to adequately initialise the reading pointer to the piped data.
I watched that strange behaviour on Windows 7 and on Windows 10.
It becomes even more weird: when calling the script containing the pipe multiple times, for instance by a loop like for /L %I in (1,1,1000) do #pipe.bat, and the input file contains about fifteen lines or more, sometimes (a few times out of thousand) a fragment of the input file is returned; that fragment is exactly the same each time; it seems that there are always 80 bytes missing at the beginning.
1) findstr hangs in case the last line is not terminated by a line-break, so let us assume such is there.
When retrieving data, the set /p tries to fill a 1023 character buffer (if they are available) with data from stdin. Once this read operation has ended, the first end of line is searched and once it has been found (or the end of the buffer has been reached), the SetFilePointer API is called to reposition the input stream pointer after the end of the read line. This way the next read operation will start to retreive data after the read line.
This works flawlessly when a disk file is associated with the input stream, but as Microsoft states in the SetFilePointer documentation
The hFile parameter must refer to a file stored on a seeking device;
for example, a disk volume. Calling the SetFilePointer function with a
handle to a non-seeking device such as a pipe or a communications
device is not supported, even though the SetFilePointer function may
not return an error. The behavior of the SetFilePointer function in
this case is undefined.
What is happening is that, while not generating any error, the call to reposition the read pointer fails when stdin is associated with a pipe, the pointer is not moved back and the 1023 bytes (or the number of available read bytes) keep read.
edited in response to Aacini request
The set command is processed by the eSet function, who calls SetWork to determine which type of set command will be executed.
As it is a set /p the SetPromptUser function is called and from this function the ReadBufFromInput function is called
add esp, 0Ch
lea eax, [ebp+var_80C]
push eax ; int
push 3FFh ; int
lea eax, [ebp+Value]
push eax ; int
xor esi, esi
push 0FFFFFFF6h ; nStdHandle
mov word ptr [ebp+Value], si
call edi ; GetStdHandle(x) ; GetStdHandle(x)
push eax ; hFile
call _ReadBufFromInput#16 ; ReadBufFromInput(x,x,x,x)
it requests 3FFh (1023) characters from standard input handle (0FFFFFFF6h = -10 = STD_INPUT_HANDLE)
ReadBufFromInput uses the GetFileType API to determine if it should read from the console or from a file
; Attributes: bp-based frame
; int __stdcall ReadBufFromInput(HANDLE hFile, int, int, int)
_ReadBufFromInput#16 proc near
hFile= dword ptr 8
; FUNCTION CHUNK AT .text:4AD10D3D SIZE 00000006 BYTES
mov edi, edi
push ebp
mov ebp, esp
push [ebp+hFile] ; hFile
call ds:__imp__GetFileType#4 ; GetFileType(x)
and eax, 0FFFF7FFFh
cmp eax, 2
jz loc_4AD10D3D
and, as in this case it is a pipe (GetFileType returns 3) the code jumps to the ReadBufFromFile function
; Attributes: bp-based frame
; int __stdcall ReadBufFromFile(HANDLE hFile, LPWSTR lpWideCharStr, DWORD cchWideChar, LPDWORD lpNumberOfBytesRead)
_ReadBufFromFile#16 proc near
var_C= dword ptr -0Ch
cchMultiByte= dword ptr -8
NumberOfBytesRead= dword ptr -4
hFile= dword ptr 8
lpWideCharStr= dword ptr 0Ch
cchWideChar= dword ptr 10h
lpNumberOfBytesRead= dword ptr 14h
This function will call the ReadFile API function to retrive the indicated number of characters
push ebx ; lpOverlapped
push [ebp+lpNumberOfBytesRead] ; lpNumberOfBytesRead
mov [ebp+var_C], eax
push [ebp+cchWideChar] ; nNumberOfBytesToRead
push edi ; lpBuffer
push [ebp+hFile] ; hFile
call ds:__imp__ReadFile#20 ; ReadFile(x,x,x,x,x)
The returned buffer is iterated in search of an end of line, and once it is found, the pointer in the input stream is moved after the found poisition
.text:4AD06A15 loc_4AD06A15:
.text:4AD06A15 cmp [ebp+NumberOfBytesRead], 3
.text:4AD06A19 jl short loc_4AD06A2D
.text:4AD06A1B mov al, [esi]
.text:4AD06A1D cmp al, 0Ah
.text:4AD06A1F jz loc_4AD06BCF
.text:4AD06A25
.text:4AD06A25 loc_4AD06A25:
.text:4AD06A25 cmp al, 0Dh
.text:4AD06A27 jz loc_4AD06D14
.text:4AD06A2D
.text:4AD06A2D loc_4AD06A2D:
.text:4AD06A2D movzx eax, byte ptr [esi]
.text:4AD06A30 cmp byte ptr _DbcsLeadCharTable[eax], bl
.text:4AD06A36 jnz loc_4AD12018
.text:4AD06A3C dec [ebp+NumberOfBytesRead]
.text:4AD06A3F inc esi
.text:4AD06A40
.text:4AD06A40 loc_4AD06A40:
.text:4AD06A40 cmp [ebp+NumberOfBytesRead], ebx
.text:4AD06A43 jg short loc_4AD06A15
.text:4AD06BCF loc_4AD06BCF:
.text:4AD06BCF cmp byte ptr [esi+1], 0Dh
.text:4AD06BD3 jnz loc_4AD06A25
.text:4AD06BD9 jmp loc_4AD06D1E
.text:4AD06D14 loc_4AD06D14:
.text:4AD06D14 cmp byte ptr [esi+1], 0Ah
.text:4AD06D18 jnz loc_4AD06A2D
.text:4AD06D1E
.text:4AD06D1E loc_4AD06D1E:
.text:4AD06D1E mov eax, [ebp+var_C]
.text:4AD06D21 mov [esi+2], bl
.text:4AD06D24 sub esi, edi
.text:4AD06D26 inc esi
.text:4AD06D27 inc esi
.text:4AD06D28 push ebx ; dwMoveMethod
.text:4AD06D29 push ebx ; lpDistanceToMoveHigh
.text:4AD06D2A mov [ebp+cchMultiByte], esi
.text:4AD06D2D add esi, eax
.text:4AD06D2F push esi ; lDistanceToMove
.text:4AD06D30 push [ebp+hFile] ; hFile
.text:4AD06D33 call ds:__imp__SetFilePointer#16 ; SetFilePointer(x,x,x,x)
The short summarize of a long discussion at dostips (mentioned already by Aacini
set /p problems with pipes).
Reading with set /p from a redirect, reads always to the end of the line and removes the \r\n characters.
Reading with set /p from a pipe, reads up to 1023 bytes from the pipe buffer.
It doesn't stop at any \r or \n characters but it drops all content after a \n.
After closing the pipe on the left side, a set /p on the right side will read empty lines.
hey I have been using masm for 2 weeks now and I am trying to read from a text file line by line that has paths of files in them
example of text file
C:\a.rar
C:\a.txt
C:\a.png
then I want to read in the whole contents of the file path and get the md5 check sum of the file path
the below code works perfectly for the first time(the first message box is the file path,the second is the contents of the file and the third is the md5 check sum)
but then after the first loop it reads the second files path but can not read the contents of the second file and then crashes because it has nothing to md5 check sum.
it must be a easy mistake of not resetting something or not closing something but I have spent like 20 hours on this and can not get it to work
for example the below code is in a button and when you press the button this is what it is supposed to do
message box C:\a.rar
message box "contents of file"
message box 44644af7515bc4870d44fa688684798
message box C:\a.txt
message box "contents of file"
message box 6057f13c496ecf7fd777ceb9e79ae285
message box C:\a.png
message box "contents of file"
message box 01654ab48d84f484z446ba48453cb48
but this is what happens
message box C:\a.rar
message box "contents of file"
message box 44644af7515bc4870d44fa688684798
message box C:\a.txt
blank contents cant read the contents of the file on loop (this is the problem)
message box blank because it cant md5 no contents
crash
can someone please help
LOCAL Buffer3 :DWORD
invoke CreateFile, ADDR filestoscan, GENERIC_READ, 0, 0,OPEN_EXISTING, 0, 0
mov hFile, eax
invoke GetFileSize,hFile,NULL
mov Byteforstreamreader,eax
streamreader2:
.if Byteforstreamreader != 0
invoke ReadFile, hFile, ADDR Buffer2,1, ADDR BytesWritten, 0
.if Buffer2 == 13
invoke CreateFile, ADDR StringBuffer, GENERIC_READ, 0, 0,OPEN_EXISTING, 0, 0
mov hFile2, eax
invoke GetFileSize,hFile2,NULL
mov Bytes,eax
invoke ReadFile, hFile2, ADDR FileBuffer,Bytes, ADDR BytesWritten, 0
invoke CloseHandle,hFile2
invoke MessageBoxA, NULL, addr StringBuffer, offset BoxCaption, NULL
invoke MessageBoxA, NULL, addr FileBuffer, offset BoxCaption, NULL
invoke MD5_Startup
invoke MD5_Init, offset ctxt
invoke MD5_Read, offset ctxt, offset FileBuffer, Bytes
invoke MD5_Digest, offset ctxt, offset filehash
invoke MD52String, offset filehash, offset strn, 1
invoke MessageBoxA, NULL, addr strn, offset BoxCaption, NULL
mov FileBuffer,0
mov StringBuffer,0
dec Byteforstreamreader
jmp streamreader2
.endif
mov eax,offset Buffer2
mov Buffer3,eax
invoke lstrcat,ADDR StringBuffer,addr Buffer2
dec Byteforstreamreader
jmp streamreader2
.endif
.if Byteforstreamreader == 0
invoke CloseHandle,hFile
.endif
.data
filestoscan db "myfiles.txt",0
FileBuffer DB 50000 DUP(?)
Bytes dd ?
Bytes2 dd ?
BytesWritten dd ?
BytesWritten3 dd ?
hFile dd ?
hFile2 dd ?
.data ?
hFile dd ?
Byteforstreamreader dd ?
BytesWritten2 dd ?
StringBuffer DB 100 DUP(?)
Buffer2 db 500 dup (?)
ctxt db 1000 dup (?)
filehash db 1000 dup (?)
strn db 33 dup(?) ; use dw for unicode
also as a side question if someone could please answer it does not seem right to me to have to reserve 50000 bytes on filebuffer so I can open a 50000 bytes or smaller file. how can I open any file size without reserving all that memory because some of these files may be 100 mb or more
thank you
Here's a somewhat annotated version of your streamreader2 loop:
streamreader2:
.if Byteforstreamreader != 0
invoke ReadFile, hFile, ADDR Buffer2,1, ADDR BytesWritten, 0
; check for end-of-line (CR)
.if Buffer2 == 13
; open the file named in Buffer2 and
; do a bunch of stuff to it to get the MD5
;
; ...
mov FileBuffer,0
mov StringBuffer,0
dec Byteforstreamreader
jmp streamreader2
.endif
; I don't know what the purpose of these two lines are
mov eax,offset Buffer2
mov Buffer3,eax
; append the character just read from hFile to StringBuffer
invoke lstrcat,ADDR StringBuffer,addr Buffer2
dec Byteforstreamreader
jmp streamreader2
.endif
So no characters read from hFile are handled specially except for 13 (CR). However, I think there's a good chance that the line endings in that file are CRLF so when the next file is opened the name will have a LF control character at the start of it. So the CreateFile or fopen call will fail to open the file.
I am currently trying to append a null terminator to an(a?) user inputted string:
.386
.model flat, stdcall
WriteFile PROTO STDCALL:DWORD, :PTR, :DWORD, :PTR DWORD, :PTR OVERLAPPED
ReadFile PROTO STDCALL:DWORD, :PTR, :DWORD, :PTR DWORD, :PTR OVERLAPPED
GetStdHandle PROTO STDCALL:DWORD
.data
buff DB 100h DUP(?)
stdInHandle DWORD 0
bytesRead DWORD ?
.code
start:
;read string from stdin
INVOKE GetStdHandle, -10
MOV stdInHandle, eax
INVOKE ReadFile, stdInHandle, BYTE PTR[buff], 100, ADDR bytesRead, 0
;append null terminator on CR,LF
MOV eax, bytesRead
MOV edx, BYTE PTR[buff]
SUB eax, 2
AND BYTE PTR [eax+edx], 0
RET
END start
It refuses to assemble at MOV edx, BYTE PTR[buff] and gives me an error:
error: Invalid combination of opcode and operands (or wrong CPU setting).
So I'm assuming I cannot MOV the value of BYTE PTR[buff] into register edx. So I can't even begin to test if this method of trying to apply a NULL terminator to a string will even work.
My question is, what is wrong with the above code (should I use a different register instead of edx?)
What is the best way to apply a NULL terminator to the string?
You can't move a byte value into a dword sized register. You either need to use a byte sized register such as dl, or zero-extend it with movzx. As you are working with bytes, I suggest you go with the first option.
When I had to create methods for strings without using anything from good ole Irvine, I got the length of the string, incremented what the length returned as (you need to include an extra +1 for the null-terminator) by 1, and then added 0h to the end of the string where the pointer was where the counter is.
MOV EAX, SIZEOF lpSourceString + 1 ; Get the string length of string, add 1 to include null-terminator
INVOKE allocMem, EAX ; Allocate memory for a target to copy to
LEA ESI, [lpSourceString] ; put source address in ESI
MOV EDI, EAX ; copy the dest address to another register we can increment
MOV ECX, SIZEOF lpSourceString ; Set up loop counter
We have the size of the string. Now we can add the null-terminate to it. To do that, we need to make sure that we have a pointer looking at the end of the string. So if we have a method that returns a string in EAX, EAX needs to point to the start of the string (so we leave the allocMem unmodified, instead incrementing a copy in EDI). Let's say that we are putting characters in a string:
nextByte: ; Jump label, get the next byte in the string until ECX is 0
MOV DL, [ESI] ; Get the next character in the string
MOV [EDI], DL ; Store the byte at the position of ESI
INC ESI ; Move to next char in source
INC EDI ; INCrement EDI by 1
loop nextByte ; Re-loop to get next byte
MOV byte ptr[EDI], 0h ; Add null-terminator to end of string
; EAX holds a pointer to the start of the dynamically-allocated
; 0-terminated copy of lpSourceString
MOV requires the byte ptr size specifier because neither the [EDI] memory operand nor the 0 immediate operand would imply a size for the operation. The assembler wouldn't know if you meant a byte, word, or dword store.
I have this in my MASM, but I use a String_length stdcall method I had written due to a class requirement.
This is so common that the MASM32 runtime supplies this functionality as part of its runtime. All you need to do is include the relevant code:
include \masm32\include\masm32rt.inc
Then use the StripLF function as so:
invoke StripLF, addr buff
To fix your current problem (if you want to do it manually) , you need to move the address of buff to edx instead.
mov edx, offset buff