Calling a function from DLL at runtime

Started by Vortex, October 01, 2023, 01:30:33 PM

Previous topic - Next topic

Vortex

Here is an example of calling the undocumented function MessageBoxTimeout at runtime.

include     Test.inc

CallFunc64  PROTO :QWORD,:QWORD,:QWORD

.data

user32  db 'user32.dll',0
MyFunc  db 'MessageBoxTimeoutA',0

message db 'This message box will be destroyed after 4 seconds.',0
caption db 'Hello',0

;   int MessageBoxTimeoutA(HWND hWnd, LPCSTR lpText,
;       LPCSTR lpCaption, UINT uType,
;       WORD wLanguageId, DWORD dwMilliseconds);

array   dq 0,OFFSET message,OFFSET caption
        dq MB_ICONWARNING,LANG_NEUTRAL,TIMEOUT

.code


main PROC PARMAREA=4*QWORD

LOCAL hMod:QWORD

    invoke  LoadLibrary,ADDR user32
    mov     hMod,rax

    invoke  GetProcAddress,rax,ADDR MyFunc

    invoke  CallFunc64,rax,6,ADDR array

    invoke  FreeLibrary,hMod

    invoke  ExitProcess,0

main ENDP


END main
Code it... That's all...

John Z

Thanks Vortex!

Very neat, always wanted something like this, never knew it was there.
Hope I can get it to work in C.

John Z

MrBcx

#2
Quote from: John Z on October 02, 2023, 03:46:02 PM

Hope I can get it to work in C.

John Z

Hi JJ,

This will get you there.  This is the A version. 
If you want the W versions, it's only minor edits.



#include <windows.h>
#include <stdio.h>

long __stdcall MessageBoxTimeoutA (HWND, char*, char*, int, int, int);

long __stdcall MessageBoxTimeoutA (HWND hwnd,char* Txt,char* Title,int MBType,int Langid,int Milliseconds)
{
  typedef int (__stdcall *MBTYPEDEF)(HWND, char *, char *, long, int, int);
  static MBTYPEDEF MsgBoxTimeout;
  static int Result;
  HMODULE  HMOD_USER32 = LoadLibrary("user32.dll");
  MsgBoxTimeout = (MBTYPEDEF)GetProcAddress(HMOD_USER32, "MessageBoxTimeoutA");
  Result=MsgBoxTimeout(hwnd,Txt,Title,MBType,Langid,Milliseconds);
  return Result;
}

#define MBTYPE MB_YESNOCANCEL|MB_SETFOREGROUND|MB_SYSTEMMODAL|MB_ICONINFORMATION


// Demo

int main(int argc, char *argv[])
{
   printf("% d",(int)MessageBoxTimeoutA(NULL, "This works with all compilers...", "Title Goes Here", MBTYPE, 0, 4000));
   return EXIT_SUCCESS;
}


Bcx Basic to C/C++ Translator
https://www.bcxbasiccoders.com

Vortex

Hi Kevin,

Thanks for your code. Checking the object module with Agner Fog's objconv tool, I saw that the stack was balanced after calling MessageBoxTimeoutA. This should not happen as MessageBoxTimeoutA is a STDCALL function. The code is built as 32-bit in this case.

_main   PROC NEAR
        push    4000
        push    0
        push    69699
        push    offset @1044
        push    offset @1043
        push    0           
        call    _MessageBoxTimeoutA
        add     esp, 24           
        push    eax               
        push    offset @1042       
        call    _printf           
        add     esp, 8             
        xor     eax, eax
        ret                       
_main   ENDP


The solution is inserting the __stdcall statment just before MessageBoxTimeoutA :

/*long __stdcall MessageBoxTimeoutA (HWND, char*, char*, int, int, int);*/

long __stdcall MessageBoxTimeoutA (HWND hwnd,char* Txt,char* Title,int MBType,int Langid,int Milliseconds)
{
  typedef int (__stdcall *MBTYPEDEF)(HWND, char *, char *, long, int, int);
  static MBTYPEDEF MsgBoxTimeout;
  static int Result;
  HMODULE  HMOD_USER32 = LoadLibrary("user32.dll");
  MsgBoxTimeout = (MBTYPEDEF)GetProcAddress(HMOD_USER32, "MessageBoxTimeoutA");
  Result=MsgBoxTimeout(hwnd,Txt,Title,MBType,Langid,Milliseconds);
  return Result;
}


Objconv reports now the correct output :

_main   PROC NEAR
        push    4000
        push    0   
        push    69699
        push    offset @104
        push    offset @104
        push    0   
        call    _MessageBoxTimeoutA@24  ;  no stack balance after the call to MessageBoxTimeoutA
        push    eax                   
        push    offset @1042         
        call    _printf               
        add     esp, 8               
        xor     eax, eax             
        ret                           
_main   ENDP
Code it... That's all...

Vortex

Hello,

Pelle's Podump tool can be used to disassemble the object module :

podump.exe /DISASM MBoxTimeOut.obj

Dump of MBoxTimeOut.obj

File type: OBJ

_MessageBoxTimeoutA@24:
  [00000000] 55                     push              ebp
  [00000001] 89E5                   mov               ebp,esp
  [00000003] 53                     push              ebx
  [00000004] 56                     push              esi
  [00000005] 57                     push              edi
  [00000006] 8B5D08                 mov               ebx,dword ptr [ebp+8]
  [00000009] 8B750C                 mov               esi,dword ptr [ebp+C]
  [0000000C] 8B7D10                 mov               edi,dword ptr [ebp+10]
  [0000000F] 6800000000             push              @1025
  [00000014] FF1500000000           call              dword ptr [__imp__LoadLibraryA@4]
  [0000001A] 6800000000             push              @1027
  [0000001F] 50                     push              eax
  [00000020] FF1500000000           call              dword ptr [__imp__GetProcAddress@8]
  [00000026] A300000000             mov               dword ptr [@1023],eax
  [0000002B] FF751C                 push              dword ptr [ebp+1C]
  [0000002E] FF7518                 push              dword ptr [ebp+18]
  [00000031] FF7514                 push              dword ptr [ebp+14]
  [00000034] 57                     push              edi
  [00000035] 56                     push              esi
  [00000036] 53                     push              ebx
  [00000037] FF1500000000           call              dword ptr [@1023]
  [0000003D] A300000000             mov               dword ptr [@1024],eax
  [00000042] 5F                     pop               edi
  [00000043] 5E                     pop               esi
  [00000044] 5B                     pop               ebx
  [00000045] 89EC                   mov               esp,ebp
  [00000047] 5D                     pop               ebp
  [00000048] C21800                 ret               18
  [0000004B] 0F1F440000             nop               [eax+eax+0]

_main:
  [00000050] 68A00F0000             push              FA0
  [00000055] 6A00                   push              0
  [00000057] 6843100100             push              11043
  [0000005C] 6800000000             push              @1044
  [00000061] 6800000000             push              @1043
  [00000066] 6A00                   push              0
  [00000068] E893FFFFFF             call              00000000
  [0000006D] 50                     push              eax
  [0000006E] 6800000000             push              @1042
  [00000073] E800000000             call              _printf
  [00000078] 83C408                 add               esp,8
  [0000007B] 31C0                   xor               eax,eax
  [0000007D] C3                     ret

SUMMARY
       8 .bss
      10 .debug$F
      28 .drectve
      53 .rdata
      7E .text
Code it... That's all...

Robert

Quote from: Vortex on October 01, 2023, 01:30:33 PM
Here is an example of calling the undocumented function MessageBoxTimeout at runtime.

include     Test.inc

CallFunc64  PROTO :QWORD,:QWORD,:QWORD

.data

user32  db 'user32.dll',0
MyFunc  db 'MessageBoxTimeoutA',0

message db 'This message box will be destroyed after 4 seconds.',0
caption db 'Hello',0

;   int MessageBoxTimeoutA(HWND hWnd, LPCSTR lpText,
;       LPCSTR lpCaption, UINT uType,
;       WORD wLanguageId, DWORD dwMilliseconds);

array   dq 0,OFFSET message,OFFSET caption
        dq MB_ICONWARNING,LANG_NEUTRAL,TIMEOUT

.code


main PROC PARMAREA=4*QWORD

LOCAL hMod:QWORD

    invoke  LoadLibrary,ADDR user32
    mov     hMod,rax

    invoke  GetProcAddress,rax,ADDR MyFunc

    invoke  CallFunc64,rax,6,ADDR array

    invoke  FreeLibrary,hMod

    invoke  ExitProcess,0

main ENDP


END main


Hi Vortex:

There is no "test.inc" file in the zip hence error

QuoteCallFunc64\Test.asm(1): fatal error: Can't open file 'Test.inc'.

Vortex

Hi Robert,

My apologies, it's now fixed. New upload at the top.
Code it... That's all...

MrBcx

Erol .. thanks for the insight and correction.

I've updated the code in my post.

Bcx Basic to C/C++ Translator
https://www.bcxbasiccoders.com

John Z

Vortex, MrBCX,

Thanks very much!  It will be very handy to now have this proc.
No longer need to thread out a dialog box and start a timer to kill it.

Very neat!'

John Z

Vortex

Hi John,

Your method based on dialog boxes is safer. Not easy to predict if MS is deciding one day to remove a particular undocumented function.
Code it... That's all...

Pelle

... I can't really see the point/need for a message box that disappears after a while:
- If you have something (reasonably) important to say, don't you want confirmation it was seen?
- If it wasn't that important, why bother with a message box in the first place?

... anyway, not that important since it was an example for this example...  ::)
/Pelle

Vortex

Hi Pelle,

No worries, this is just a quick example. The LoadLibrary\GetProcAddress\Call Function method is useful to exit gracefully when a particular DLL is not everytime available.
Code it... That's all...

John Z

No worries - I would not knowingly use any undocumented API feature in software released into the wild.
BUT
I see uses for it during development as part of a debug process.  Plus the sample C code is informative.


John Z

TimoVJL

What we learned, missing WINAPI leads problems.

;   int WINAPI MessageBoxTimeoutA(HWND hWnd, LPCSTR lpText,
;       LPCSTR lpCaption, UINT uType,
;       WORD wLanguageId, DWORD dwMilliseconds);
x64 don't need it, only x86
May the source be with you

Vortex

Thanks to Pelle, the user32.lib import libraries are already providing the function MessageBoxTimeout :

D:\PellesC\Bin>podump.exe /EXPORTS ..\Lib\Win\user32.lib | findstr "MessageBoxTimeout"
User32.dll: MessageBoxTimeoutA (_MessageBoxTimeoutA@24)
User32.dll: MessageBoxTimeoutW (_MessageBoxTimeoutW@24)


D:\PellesC\Bin>podump.exe /EXPORTS ..\Lib\Win64\user32.lib | findstr "MessageBoxTimeout"
User32.dll: MessageBoxTimeoutA (MessageBoxTimeoutA)
User32.dll: MessageBoxTimeoutW (MessageBoxTimeoutW)
Code it... That's all...