News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Invoke internals?

Started by mineiro, May 17, 2011, 09:03:03 AM

Previous topic - Next topic

mineiro

Hello Sr's, I have read the manual before post this, and do a search in this board too before create this topic.
How exactly works invoke? Last day, I have spent 3 hours to find a bug in one program that I have coded. And yes, that was my fault. Well, I'm putting here an ambiguous code. Take a look, I have added a prototype to MessageBox. And yes, the program have compiled without errors.

.586
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

MessageBox PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data
notify_me db "report this",00h

.code
start:
invoke MessageBox,0, notify_me,addr notify_me,MB_OK
invoke ExitProcess,0
end start

In the Messagebox above, I have forgotten to put an offset or addr. The disassembled code is:

00401000 <>/$  6A 00           PUSH    0
00401002   |.  68 00304000     PUSH    invok.00403000                             ; /Style = MB_OK|3000|400000
00401007   |.  6A 00           PUSH    0                                           ; |Title = NULL
00401009   |.  A0 00304000     MOV     AL, BYTE PTR [403000]                       ; |
0040100E   |.  66:0FB6C0       MOVZX   AX, AL                                      ; |
00401012   |.  66:50           PUSH    AX                                          ; |Text
00401014   |.  6A 00           PUSH    0                                           ; |hOwner = NULL
00401016   |.  E8 07000000     CALL    <JMP.&user32.MessageBoxA>                   ; \MessageBoxA
0040101B   |.  6A 00           PUSH    0                                           ; /ExitCode = 0
0040101D   \.  E8 06000000     CALL    <JMP.&kernel32.ExitProcess>                 ; \ExitProcess
00401022    $- FF25 08204000   JMP     DWORD PTR [<&user32.MessageBoxA>]           ;  user32.MessageBoxA
00401028    .- FF25 00204000   JMP     DWORD PTR [<&kernel32.ExitProcess>]         ;  kernel32.ExitProcess

This show that MB have pushed more values than allowable.
Thanks in advance.

hutch--

I get this. I think it is getting confused with the address which is specified as DB and incorrectly zero extends it to AX. Do the word size pushes backwards ALA STDCALL

push AX
pushW 0

Its obviously wrong but thye logic is 2 WORD size data pushes.


00401000                    start:
00401000 6A00                   push    0
00401002 6800304000             push    403000h
00401007 666A00                 pushw   0
0040100A A000304000             mov     al,[403000h]
0040100F 660FB6C0               movzx   ax,al
00401013 6650                   push    ax
00401015 6A00                   push    0
00401017 E808000000             call    jmp_MessageBoxA
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

jj2007

Here is a variant that deliberately starts TheTitle with a zero byte - it will generate address zero, which is fine for the title of a MsgBox (it will use the word "error" in your local language).

include \masm32\include\masm32rt.inc

.data
TheText db "The text", 0
TheTitle db 0, "The title", 0

.code
start:
invoke MessageBox, 0, offset TheText, TheTitle, MB_OK
invoke ExitProcess, 0
end start


The interesting point is that Masm 6.14, 6.15 and 8.0 generate this:
00401005                ³? A0 09204000      mov al, [402009]                     ; ³
0040100A                ³? 66:0FB6C0        movzx ax, al                         ; ³
0040100E                ³. 66:50            push ax                              ; ³Text
00401010                ³? 68 00204000      push offset 00402000                 ; ³
00401015                ³? 6A 00            push 0
00401017                ³? E8 08000000      call <jmp.&user32.MessageBoxA>       ; ³Jump to user32.MessageBoxA

... a partial push that usually displays a crippled MsgBox.

Masm 9.0 generates this code:
00401008                ³? 66:6A 00         push word 0                          ; ³
0040100B                ³? A0 09204000      mov al, [402009]                     ; ³
00401010                ³? 66:0FB6C0        movzx ax, al                         ; ³
00401014                ³? 66:50            push ax
00401016                ³. 68 00204000      push offset 00402000                 ; ÚExitCode = 4202496.
0040101B                ³? 6A 00            push 0
0040101D                ³. E8 0C000000      call <jmp.&user32.MessageBoxA>       ; Jump to user32.MessageBoxA

... and finally JWasm with the most elegant solution:
00401008                ³? 0FB605 09204000  movzx eax, byte ptr [402009]         ; ³
0040100F                ³. 50               push eax                             ; ³hOwner => NULL
00401010                ³? 68 00204000      push offset 00402000                 ; ³
00401015                ³? 6A 00            push 0
00401017                ³? E8 0C000000      call <jmp.&user32.MessageBoxA>       ; ³Jump to user32.MessageBoxA

Both Masm 9.0 and Jwasm display a correct MsgBox titled "Error".

mineiro

#3
Thank Sr Hutch and Sr jj2007 for the answers and tests.
In masm reference says:
Quote
Syntax:   INVOKE expression [,arguments]
...   arguments   =   Sequential list of parameters passed. Can be register::register (for a register pair), expression, or ADDR label. ADDR label passes the address of <label>
(segment and offset if DWORD, segment only if WORD) to the procedure.


In first moment, I have thinked that the variable used in that code is suposed to be a Label, because it push words. But now I don't know if it is really a Label.
If I do many pushes and after do a call to MB, masm reports an error to me. But with invoke no.
And If I make an avalanche in that error something like:
invoke MessageBox,0, notify_me, notify_me,MB_OK
The stack stays with more arguments than allowable.

.code
my_label:
notify_me db "report this",00h
start:
invoke MessageBox,0, notify_me, notify_me,MB_OK  ;masm don't report error and unbalance the stack
push MB_OK
push 0
push notify_me ;masm report error here
push 0
call MessageBox
invoke ExitProcess,0
end start

Hey jj2007, you have found a new MB_style :lol
Sr Hutch, I have tryed compile this using others calling conventions without lucky, received some "unresolved external...".

jj2007

TheText  db "The text", 0
...
invoke whatever, TheText


In this construction, invoke passes a byte-sized parameter on the stack (remember there is no addr or offset before TheText).
Pushing bytes is not possible, but passing words is indeed. However, the PROTO tells invoke that a DWORD is needed; so in order to resolve this tricky problem, invoke first converts the byte variable to a word, then pushes two words to comply with the PROTO. That ends up in more or less big disasters, depending on which assembler you use. My advice: add the offset ;-)

mineiro

I fully agree with your words Sr jj2007, I have figured this after 3 hours of debugging.
Well, using a simple call masm reported this error, and using invoke, no. I have learned this lesson.
And, after think a bit, while I'm receiving a msg "access violation", in your example you don't receive some SEH message. This error is hard to find in one program that have many lines of code. The best aproach to avoid this error is doing some "getlasterror"? Man, If i do this in all the program(after each call) I get insane, but I start doing this style of code. SEH is another option too, but in your example you dont have received exception messages and the return from MB is ok, exactly what happened with my original code.

jj2007

> .data
> notify_me db "report this",00h
> ...
> invoke MessageBox,0, notify_me,addr notify_me,MB_OK

Hello Mineiro,

That is indeed a tricky bug, not easy to find because after a while you are simply "blind" for the missing addr or offset.
GetLastError won't help in this case, as it triggers an access violation at 77D2204F, i.e. somewhere in "no man's land", depending on where the first 4 bytes of notify_me misdirect the MessageBox API.
SEH can make things worse, as it may hide the error even further - no access violation...
Unfortunately none of the assemblers tests for the discrepancy between the expected (DWORD) and the received (BYTE) parameter.
It would be possible to write a parser that checks for such errors. After all, user32.inc has the necessary info:
MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD
MessageBox equ <MessageBoxA>

Probably, one of the advanced IDEs already has that feature implemented. If not, take it easy: bugchasing is a sport :bg

mineiro

Quote from: jj2007 on May 18, 2011, 02:34:17 AM
If not, take it easy: bugchasing is a sport :bg
include \masm32\include\masm32rt.inc
.data
error_A2108: ;a label inside data section generates error A2108
.code
start:
invoke offset error_A2006  ;invoke can't deal with fowards reference, error A2006
error_A2006:
ret
end start
  :bg :U

hutch--

One thing I know about MASM over its long lifetime is that its not designed like consumer software, it expects that you use it correctly and can dump you with garbage if you don't. Its a terse bad mannered old pig which is part of its fatal charm, get it right or it explodes in your face. It actually helps you to develop more reliable software in that its the right way or no way with its results, there is little grey area between.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

mineiro

Yes Sr Hutch, I agree with yours words.
I think I have found this detail in proguide.
Quote
If the procedure uses VARARG, INVOKE can pass a number of arguments different from the number in the parameter list without generating an error or warning.
...Detecting Errors
If the assembler needs to widen an argument, it first copies the value to AL or AX. It widens an unsigned value by placing a zero in the higher register area, and widens a signed value with a CBW, CWD, or CWDE instruction as required.

About a better way to detect this in your final program without need of debugging many hours, the easy solution that I have found is search for that opcode in your program.(660FB6C06650) and check with simbolic debug, like Sr jj2007 have reported using different masm versions.

So, I apreciatet much the answers Sr's, have a nice day, and thank you.