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.
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
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".
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...".
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 ;-)
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.
> .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
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
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.
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.