Well, i don't know wich is the most appropriate name for this topic, but i have here two almost identical programs importing external API in a different manner:
1) PROTO version:
.386
.MODEL flat, stdcall
INCLUDELIB \lib\kernel32.lib
INCLUDELIB \lib\user32.lib
ExitProcess PROTO :DWORD
MessageBoxA PROTO :DWORD, :DWORD, :DWORD, :DWORD
.CODE
start:
PUSH 0
PUSH offset title
PUSH offset msg
PUSH 0
CALL MessageBoxA
PUSH 0
CALL ExitProcess
.DATA
title db 'Title', 0
msg db 'Message', 0
END start
This is the most common/popular approach, i think mostly because this allows parameter checking with the "invoke" macro.
The external API is resolved indirectly through a "JMP table":
Quote
...
0040100E E8 0D000000 CALL <JMP.&user32.MessageBoxA> ; Jump to user32.MessageBoxA
00401013 6A 00 PUSH 0
00401015 E8 00000000 CALL <JMP.&kernel32.ExitProcess> ; Jump to kernel32.ExitProcess
0040101A - FF25 00204000 JMP NEAR DWORD PTR DS:[<&kernel32.ExitProcess>]
00401020 - FF25 08204000 JMP NEAR DWORD PTR DS:[<&user32.MessageBoxA>]
...
2) EXTERNDEF version:
.386
.MODEL flat, stdcall
INCLUDELIB \lib\kernel32.lib
INCLUDELIB \lib\user32.lib
EXTERNDEF _imp__ExitProcess@4:DWORD
EXTERNDEF _imp__MessageBoxA@16:DWORD
.CODE
start:
PUSH 0
PUSH offset title
PUSH offset msg
PUSH 0
CALL [_imp__MessageBoxA@16]
PUSH 0
CALL [_imp__ExitProcess@4]
.DATA
title db 'Title', 0
msg db 'Message', 0
END start
Here the external API is resolved directly:
Quote
...
0040100E FF15 08204000 CALL NEAR DWORD PTR DS:[<&user32.MessageBoxA>]
00401014 6A 00 PUSH 0
00401016 FF15 00204000 CALL NEAR DWORD PTR DS:[<&kernel32.ExitProcess>]
...
Ok, can somebody explain me something about this?, for e. wich is the recommended version?. There is any advantage using a "JMP table" calling indirectly? isnt slower?. Or Maybe i can mix both ways?.
Here there are two posts i found about "direct" and "indirect" call:
"Calling an imported function, the naive way":
http://blogs.msdn.com/b/oldnewthing/archive/2006/07/21/673830.aspx
"How a less naive compiler calls an imported function":
http://blogs.msdn.com/b/oldnewthing/archive/2006/07/24/676669.aspx
Everything you ever wanted to know about jump tables (http://www.masm32.com/board/index.php?topic=11541.0)
@jj WOW, really a very informative topic. Thanks
If they had some kind of official recognition for ALL-TIME GREAT MASM THREADS,...surely, the above mentioned thread,...would be a contender.
Thanks, Jochen.
for the sake of readability, the PROTO version is best
as far as speed is concerned, it makes little difference for 98% of the API functions, really
this is especially true for the functions you are using in your example code
a handful of API functions are quite fast :P
but, for speed to be an issue in those cases, the call is likely inside a loop or otherwise called thousands of times
something like TextOut, ExtTextOut, or other draw functions are the only real examples i can think of
these functions are pretty fast, but it's always nice to speed up a draw loop :U
it is worth mentioning that the import/externdef method does not yield a direct (relative) call, either
it merely once dereferences a twice referenced call :bg
near the end of the thread Jochen mentioned...
http://www.masm32.com/board/index.php?topic=11541.msg87615#msg87615
i wrote some code that completely dereferences the call
here is an updated version of that code
DeRef PROTO :LPVOID,:LPVOID,:LPVOID
;**********************************************************************************************
OPTION PROLOGUE:None
OPTION EPILOGUE:None
DeRef PROC lpStart:LPVOID,lpStop:LPVOID,lpExclusions:LPVOID
;Dereference IAT Branches
;DednDave - 7, 2011
;
; Dereferences all branches into the IAT, starting at lpStart, and ending at lpStop.
;If lpStop is NULL, the beginning of the IAT is used as the stop address.
;An exclusion list may be used to exclude specific addresses from conversion.
;Addresses in the list represent the address of the first instruction byte.
;If present, the list must be terminated with a NULL dword.
;No exclusion list is used if the lpExclusions parameter is NULL.
;
; This version allows for the conversion of JMP's, CALL's and conditional branches.
;It will convert declspec JMP's and CALL's, including those into the MSVCRT, etc.
;
; The converted instructions always result in a relative branch directly to the imported code.
;This will prevent hooking into the IAT, which may be overridden by hooking prior to DeRef
;execution or by use of the exclusion list.
;
;----------------------------------------------------
;
;[EBP+28] = lpExclusions
;[EBP+24] = lpStop
;[EBP+20] = lpStart
;[EBP-4] = address of IAT start
;[EBP-8] = address of IAT end
;[EBP-12] = adjusted lpStop (lpStop-4)
;[EBP-16] = original access protection value
;[EBP-20] = dereference count
;
;----------------------------------------------------
push ebx
push esi
push edi
push ebp
push 6
xor edx,edx
pop ebx
mov esi,VirtualProtect
mov ebp,esp
mov eax,25FFh
push esi
jmp short DeRef1
DeRef0: mov edx,esi
sub esi,ebx
DeRef1: cmp ax,[esi]
jz DeRef0
pop esi
or edx,edx
push edx ;[EBP-4] = address of IAT start
jz DeRefL
DeRef2: mov ecx,esi
add esi,ebx
cmp ax,[esi]
jz DeRef2
mov edi,[ebp+24] ;lpStop
add ecx,2
or edi,edi
push ecx ;[EBP-8] = address of IAT end
jnz DeRef3
mov edi,edx
DeRef3: mov edx,edi
mov esi,[ebp+20] ;lpStart
sub edx,4
sub edi,esi
push edx ;[EBP-12] = adjusted lpStop (lpStop-4)
mov [ebp+24],edi ;[EBP+24] is now code stream length
push eax ;[EBP-16] = previous access protection value
INVOKE VirtualProtect,esi,edi,PAGE_EXECUTE_READWRITE,esp
or eax,eax
push 0 ;[EBP-20] = dereference count
jnz DeRefH
jmp DeRefL
DeRef4: xor ecx,ecx
cmp al,0E8h ;CALL relative (to JMP indirect)
mov edx,ecx
jz DeRef5
cmp al,0E9h ;JMP relative (to JMP indirect)
jz DeRef6
inc ecx
cmp ax,15FFh ;CALL indirect (declspec)
mov edx,ecx
jz DeRef6
cmp ax,25FFh ;JMP indirect (declspec)
jz DeRef6
dec edx
cmp al,0Fh ;relative conditional branch candidate
jnz DeRefI
cmp ah,80h ;relative conditional branch candidate
jb DeRefI
cmp ah,8Fh ;relative conditional branch (to JMP indirect)
jbe DeRef6
jmp DeRefI
DeRef5: cmp esi,offset DeRefL-4
jz DeRefI
DeRef6: mov edi,esi
add edi,ecx
cmp edi,[ebp-12] ;adjusted lpStop (lpStop-4)
ja DeRefK
or edx,edx
mov ebx,[edi]
jz DeRef9
push esi
mov esi,[ebp-4] ;address of IAT start
add esi,2
DeRef7: cmp ebx,[esi]
jnz DeRef8
pop esi
jmp short DeRefA
DeRef8: add esi,6
cmp esi,[ebp-8] ;address of IAT end
jbe DeRef7
pop esi
jmp DeRefI
DeRef9: add ebx,edi
add ebx,4
cmp ebx,[ebp-4] ;address of IAT start
jb DeRefI
cmp ebx,[ebp-8] ;address of IAT end
ja DeRefI
push ebx
push eax
push edx
sub ebx,[ebp-4] ;address of IAT start
mov eax,0AAAAAAABh
push ebx
mul ebx
shr edx,2
mov eax,6
mul edx
pop ebx
cmp eax,ebx
pop edx
pop eax
pop ebx
jnz DeRefI
DeRefA: mov ecx,[ebp+28] ;lpExclusions
or ecx,ecx
jz DeRefD
push eax
dec esi
jmp short DeRefC
DeRefB: add ecx,4
cmp eax,esi
jnz DeRefC
inc esi
pop eax
jmp short DeRefI
DeRefC: mov eax,[ecx]
or eax,eax
jnz DeRefB
inc esi
pop eax
DeRefD: or edx,edx
jz DeRefF
cmp ah,15h
mov ebx,[ebx]
mov ax,0E890h
jz DeRefE
inc ah
DeRefE: mov [esi-1],ax
jmp short DeRefG
DeRefF: mov ecx,[ebx+2]
mov ebx,[ecx]
DeRefG: sub ebx,edi
sub ebx,4
mov [edi],ebx
inc dword ptr [ebp-20] ;dereference count
add edi,4
mov esi,edi
DeRefH: mov ax,[esi]
inc esi
jmp short DeRefJ
DeRefI: inc esi
mov al,ah
mov ah,[esi]
DeRefJ: cmp esi,[ebp-12] ;adjusted lpStop (lpStop-4)
jbe DeRef4
DeRefK: lea edx,[ebp-12]
INVOKE VirtualProtect,[ebp+20],[ebp+24],[ebp-16],edx
DeRefL: pop eax ;return dereference count
leave
pop edi
pop esi
pop ebx
ret 12
DeRef ENDP
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
;**********************************************************************************************
@dedndave Sorry, i dont understand what you mean with "Dereference IAT Branches", what is the proposit ?? :eek
well, when you use INVOKE, it generates a "relative" CALL to a JMP
call IatJumpTable+SomeOffset
at the location, there is an "indirect" JMP that branches to an address that is in yet another table
jmp dword ptr [SomePointer]
at the location "SomePointer" is the actual address of the code to be executed
(which is filled in by the operating system at load-time)
the routine in my previous post locates that address
it then uses the address of the original CALL (from your INVOKE) and calculates the "relative" CALL
call CodeAtSomePointer
when you use the EXTERNDEF (or "declspec") method, one of these references is removed
using the names in the above scenario, it generates code that looks something like this...
call dword ptr [SomePointer]
thus, eliminating the IAT table entry step
notice that it is still an indirect CALL
Oh, i get it now, really cool :U . Thanks.