Recently I decided to master fasm for windows x32 (I wrote it under MSDOS before). Trying to figure out the WinAPI ReadConsoleA function. Input asks, but writes nothing to the buffer.

format PE console entry start include 'win32ax.inc' section '.text' code readable executable start: invoke AllocConsole invoke GetStdHandle, STD_OUTPUT_HANDLE mov [stdout], eax invoke GetStdHandle, STD_INPUT_HANDLE mov [stdin], eax invoke ReadConsole, [stdin], input, 8, 0, 0 invoke WriteConsole, [stdout], input, in_len, 0, 0 invoke SleepProcess, 2000 invoke ExitProcess, 0 section '.data' data readable writable stdout dd ? stdin dd ? input db 8 dup (?), 0 in_len dd ? section '.idata' import data readable writable library kernel32, 'kernel32.dll' import kernel32, \ AllocConsole, 'AllocConsole', \ GetStdHandle, 'GetStdHandle', \ SleepProcess, 'Sleep', \ ExitProcess, 'ExitProcess', \ WriteConsole, 'WriteConsoleA', \ ReadConsole, 'ReadConsoleA' 

    2 answers 2

    You pass incorrect arguments to the function:

     invoke ReadConsole, [stdin], input, 8, 0, 0 ^ 

    The point is that according to MSDN, the penultimate parameter ( lpNumberOfCharsRead ) is marked as [out] . Accordingly, the function according to the contract expects a non-zero pointer in it (otherwise it would be marked as [out, optional] - this is the MSDN agreement).

    To correct the error, you need to allocate an area on the stack and pass a pointer to it, even if you don’t need a value in it. Why on the stack? Only in order not to produce unnecessary elements in the relocation table (relocations) ) *

    Example:

     ; Вместо отмотки вершины можно использовать уже ненужную локальную переменную add esp, -4 ; TODO: удостовериться, что параметры кладутся макросом через `mov [esp + N], foo`, а не ; через `push foo`, иначе переданный нами указатель на вершину будет разрушен, что ; потребует его предварительного сохранения в отдельный регистр invoke ReadConsole, [stdin], input, 8, esp, 0 add esp, 4 

    By the way, followed by a call to WriteConsole() is the same story.

    • one
      If you try to compile the source from a question using fasm, then the resulting executable file will not have a relocation table, despite the presence of global variables. The exe-file may or may not have it. A dynamic library with global variables is guaranteed to have it. Well, in general, if you bother about global variables, it is for architecture reasons, and not the presence / absence of a relocation table. - insolor
    • I tried to put both the stack and the variable. No errors, but the entered string is still not displayed. - RedalErm
    • @RedalErm 1) What does ReadConsole() return? 2) What does GetLastError() return if called immediately after ReadConsole() ? - ߊߚߤߘ

    The correct option is:

     invoke ReadConsole, [stdin], input, 8, in_len, 0 invoke WriteConsole, [stdout], input, [in_len], 0, 0 
    1. In the ReadConsole function ReadConsole 4th parameter is to pass the address of the variable (pointer to the variable) where the number of actually read characters will be written. If to transfer 0, then the program will fall at the appeal on the zero pointer (it is visible when starting the program under the debugger). This is what Arhad has already suggested.
    2. In WriteConsole 3rd parameter is to pass this number as a number (not as the address of the variable where that number is), therefore, you need square brackets around in_len . When the number of characters displayed is not a small number, but an address (something like 402011h , which is 4202513 in the decimal system), the function understands that something is wrong and does not display anything in the end. The 4th parameter of this function (the number of bytes output) is in fact not necessary, although it is not indicated in the documentation as optional.

    In general, in such cases, the use of the debugger helps a lot - for example, after correcting the first item in the debugger, you can see that the string was considered to be in memory, although nothing was removed.

    Screenshot

    It is also helpful to read the documentation for the functions used. For example , the following is written here :

    Return value

    If the function succeeds, the return value is nonzero.

    If the function fails, the return value is zero. To get extended error information, call GetLastError.

    In the screenshot above, you can see that after a function call in EAX is zero (the function puts the return value in eax according to the stdcall calling agreement ), then something is wrong.