NO

Author Topic: Avoid multiple instance of an application  (Read 5723 times)

Offline Vortex

  • Member
  • *
  • Posts: 797
    • http://www.vortex.masmcode.com
Avoid multiple instance of an application
« on: September 29, 2011, 10:26:16 AM »
The CreateMutex API can detect a second instance of your application. Here is an example :
Code: [Select]
include     OneAppInstance.inc

.data

DlgBox      db 'DLGBOX',0,0
ObjectName  db 'MutexObjectName31415',0
errmsg      db 'The application is already running',0
capt        db 'Error',0


.data?

hMutex      dd ?


.code

start:


    invoke  CreateMutex,NULL,FALSE,ADDR ObjectName
    mov     hMutex,eax

    invoke  GetLastError
    cmp     eax,ERROR_ALREADY_EXISTS
    jne     @f
    invoke  MessageBox,0,ADDR errmsg,ADDR capt,MB_ICONEXCLAMATION
    jmp     finish
@@:
    invoke  GetModuleHandle,0
    invoke  DialogBoxParam,eax,ADDR DlgBox,0,ADDR DlgProc,0

finish:

    invoke  CloseHandle,hMutex
    invoke  ExitProcess,eax


DlgProc PROC hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

    .IF uMsg==WM_CLOSE

            invoke  EndDialog,hWnd,0

    .ELSE

            xor     eax,eax
            ret

    .ENDIF

    mov     eax,1
    ret

DlgProc ENDP

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

Offline Vortex

  • Member
  • *
  • Posts: 797
    • http://www.vortex.masmcode.com
Re: Avoid multiple instance of an application
« Reply #1 on: October 02, 2011, 11:36:40 AM »
Here is another example using FindWindow  The sample application creates a new class inheriting the default dialog box class #32770

Code: [Select]
RegisterNewClass PROC USES esi hMod:HMODULE,OldClassName:DWORD,NewClassName:DWORD

LOCAL wc:WNDCLASSEX

    lea     esi,wc
    mov     wc.cbSize,SIZEOF WNDCLASSEX
    invoke  GetClassInfoEx,hMod,OldClassName,esi

    push    hMod
    pop     wc.hInstance
    mov     eax,NewClassName
    mov     WNDCLASSEX.lpszClassName[esi],eax

    invoke  RegisterClassEx,esi
    ret

RegisterNewClass ENDP
Code it... That's all...

Jokaste

  • Guest
Re: Avoid multiple instance of an application
« Reply #2 on: October 10, 2017, 05:24:30 PM »
When launching a program only the data segment is duplicated, not the code segment. So, if into the current data segment the hInstance variable is equal to zero, this is the first instance else it is not the first instance. this can be done in tue Start function before calling WinMain.

Offline Vortex

  • Member
  • *
  • Posts: 797
    • http://www.vortex.masmcode.com
Re: Avoid multiple instance of an application
« Reply #3 on: October 10, 2017, 08:24:31 PM »
Hi Jokaste,

That won't work as the return value of GetModuleHandle cannot be NULL :

Code: [Select]
.data?
hInstance   HINSTANCE ?

.code

start:

    invoke  GetModuleHandle,0
    mov     hInstance,eax

Quote
Return value

If the function succeeds, the return value is a handle to the specified module.

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

https://msdn.microsoft.com/en-us/library/windows/desktop/ms683199%28v=vs.85%29.aspx
Code it... That's all...

Jokaste

  • Guest
Re: Avoid multiple instance of an application
« Reply #4 on: October 11, 2017, 05:34:38 AM »
Sorry.
You are right... again...

There is a solution using a mutex but I don't remember because all the programs don't create any window.
Happily someone created backups...
Here is my (old) solution:
Code: [Select]
int PASCAL WinMain(HINSTANCE __hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)
{
   INITCOMMONCONTROLSEX   _Icc ;
   WNDCLASS            _Wc ;
   HWND               _hWnd ;
   MSG                  _Msg ;
   HANDLE               _hMutex ;
   char               _szTmp[1024] ;

   _hMutex = NULL ;

   _hMutex = OpenMutex(SYNCHRONIZE,FALSE,szProgramTitle) ;
   if(_hMutex)
   {
      Error(NULL,"Only one instance can run at a time!") ;

      ReleaseMutex(_hMutex) ;
      CloseHandle(_hMutex) ;
      return (FALSE) ;
   }

   _hMutex = CreateMutex(0,0,szProgramTitle) ;
   hInstance = __hInstance ;

   SetErrorMode(SEM_FAILCRITICALERRORS) ;

   GetModuleFileName(hInstance,szProgramPathAndName,MAX_PATH) ;
   lstrcpy(_szTmp,szProgramPathAndName) ;
   lstrcpy(szProgramName,PathFindFileName(_szTmp)) ;
   *(PathFindFileName(_szTmp)) = '\0' ;
   lstrcpy(szProgramFolder,_szTmp) ;

   SystemParametersInfo(SPI_GETWORKAREA,0,&RcWorkArea,0) ;

   _Icc.dwSize = sizeof(_Icc) ;
   _Icc.dwICC = ICC_WIN95_CLASSES|ICC_COOL_CLASSES|ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES ;

   InitCommonControlsEx(&_Icc) ;

   hRichEdit = LoadLibrary(szRichEditV4) ;
   if(!hRichEdit)
   {
      Error(NULL,"Please update your RichEdit library to version 4.1 at less!") ;
      return (TRUE) ;
   }

   uFindReplaceMsg = RegisterWindowMessage(FINDMSGSTRING) ;

   hDlgFind = NULL ;

   _Wc.lpszClassName = szProgramClass ;
   _Wc.lpfnWndProc = MainWndProc ;
   _Wc.style = CS_OWNDC|CS_VREDRAW|CS_HREDRAW|CS_DROPSHADOW ;
   _Wc.hInstance = hInstance ;
   _Wc.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDR_ICO_MAIN)) ;
   _Wc.hCursor = LoadCursor(NULL,IDC_ARROW) ;
   _Wc.hbrBackground =(HBRUSH)(COLOR_WINDOW + 1) ;
   _Wc.lpszMenuName = MAKEINTRESOURCE(IDR_MNU_MAIN) ;
   _Wc.cbClsExtra = 0 ;
   _Wc.cbWndExtra = 256 ;

   if(!RegisterClass(&_Wc))
      return (TRUE) ;

   _Wc.lpszClassName = szNewFileClass ;
   _Wc.lpfnWndProc = E00_WndProc ;
   _Wc.hIcon = NULL ;
   _Wc.lpszMenuName = NULL ;

   if(!RegisterClass(&_Wc))
      return (TRUE) ;

   _Wc.lpszClassName = szSavedFileClass ;
   _Wc.lpfnWndProc = E01_WndProc ;
   _Wc.hIcon = NULL ;
   _Wc.lpszMenuName = NULL ;

   if(!RegisterClass(&_Wc))
      return (TRUE) ;

   _hWnd = CreateWindowEx(WS_EX_ACCEPTFILES|WS_EX_CLIENTEDGE|WS_EX_LEFT|WS_EX_CLIENTEDGE|WS_EX_WINDOWEDGE,
      szProgramClass,szProgramTitle,
      WS_BORDER|WS_CAPTION|WS_MAXIMIZEBOX|WS_MINIMIZEBOX|WS_SYSMENU|WS_SIZEBOX|WS_SYSMENU|WS_VISIBLE,
      RcWorkArea.left,RcWorkArea.top,RcWorkArea.right - RcWorkArea.left,RcWorkArea.bottom - RcWorkArea.top,
      NULL,NULL,hInstance,NULL) ;

   if(!_hWnd)
      return (TRUE) ;

   ShowWindow(_hWnd,nCmdShow) ;
   UpdateWindow(_hWnd) ;

   while(GetMessage(&_Msg,NULL,0,0))
   {
      if(!IsDialogMessage(hDlgFind,&_Msg))
      {
         TranslateMessage(&_Msg) ;
         DispatchMessage(&_Msg) ;
      }
   }

   FreeLibrary(hRichEdit) ;

   ReleaseMutex(_hMutex) ;
   CloseHandle(_hMutex) ;

   return (_Msg.wParam) ;
}
« Last Edit: October 11, 2017, 05:38:49 AM by Jokaste »

Jokaste

  • Guest
Re: Avoid multiple instance of an application
« Reply #5 on: October 11, 2017, 06:13:09 AM »
The same in assembler:
Code: [Select]
;   __________________________________________________________________________________
;   _______________________ S t a r t ________________________________________________
;   __________________________________________________________________________________

                     ALIGN   16

Start                  PROC   PARMAREA=13*QWORD
                     LOCAL   _Icc:INITCOMMONCONTROLSEX
                     LOCAL   _szTmp[MAX_PATH + 4]:BYTE
                     LOCAL   _Wc:WNDCLASSEX
                     LOCAL   _Msg:MSG
                     LOCAL   _hMutex:HANDLE

                     mov      rcx,SYNCHRONIZE
                     xor      rdx,rdx
                     mov      r8,OFFSET szAppName
                     call   OpenMutexA

                     test   rax,rax
                     jz      @ThisIsTheFirstInstance

                     mov      _hMutex,rax

                     mov      rcx,OFFSET szOnlyOneInstance
                     call   Error

                     mov      rcx,_hMutex
                     call   ReleaseMutex

                     mov      rcx,_hMutex
                     call   CloseHandle

                     mov      rcx,0CAFEFADEh
                     call   ExitProcess

                     ret

;   ================================================================================
;   ================================================================================

                     ALIGN   16

@ThisIsTheFirstInstance :
.
.
.

;   ==================================================================================
;   ==================================================================================

                     ALIGN   16

@MsgLoop :

                     lea      rcx,_Msg
                     xor      rdx,rdx
                     xor      r8,r8
                     xor      r9,r9
                     call   GetMessageA

                     test   rax,rax
                     jz      @EndOfJob

                        mov      rax,OFFSET hDlgFiles
                        lea      rdx,_Msg
                        mov      rcx,[rax]
                        call   IsDialogMessage

                        test   rax,rax
                        jnz      @MsgLoop

                           mov      rax,OFFSET hWndMain
                           mov      rdx,OFFSET hAccel
                           mov      rcx,[rax]
                           mov      rdx,[rdx]
                           lea      r8,_Msg
                           call   TranslateAccelerator

                           test   rax,rax
                           jnz      @MsgLoop

                              lea      rcx,_Msg
                              call   TranslateMessage

                              mov      rax,OFFSET @MsgLoop
                              lea      rcx,_Msg
                              push   rax
                              jmp      DispatchMessageA

;   ==================================================================================
;   ==================================================================================

                     ALIGN   16

@EndOfJob :

                     mov      rcx,OFFSET hSQLite
                     call   sqlite3_close

                     mov      rax,OFFSET @Exit
                     mov      rdx,OFFSET gflLibraryExit
                     push   rax
                     push   rdx
                     jmp      sqlite3_shutdown

;   ==================================================================================
;   ==================================================================================

                     ALIGN   16

@Exit :

                     mov      rcx,_hMutex
                     call   ReleaseMutex

                     mov      rcx,_hMutex
                     call   CloseHandle

                     mov      rdx,OFFSET __imp_ExitProcess
                     mov      rcx,_Msg.wParam
                     mov      rax,[rdx]
                     call   rax

                     Start   ENDP

;   __________________________________________________________________________________
;   _______________________ F I N I S H E D __________________________________________
;   __________________________________________________________________________________

                     END      Start
« Last Edit: October 11, 2017, 06:17:10 AM by Jokaste »

Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2091
Re: Avoid multiple instance of an application
« Reply #6 on: October 11, 2017, 12:38:39 PM »
CreateMutex is kernel32 function that create or open mutex.
GetLastError() gives mutex existence.
Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
TCHAR *szApp = TEXT("TestMutex");
TCHAR *szMsg = TEXT("New Mutex");
void __cdecl WinMainCRTStartup(void)
{
TCHAR *pMsg = szMsg;
HANDLE hMutex = CreateMutex(NULL, FALSE, szApp); // create or open the mutex
if (hMutex) {
if (GetLastError() == ERROR_ALREADY_EXISTS) // test a mutex existence
pMsg = szMsg + 3;
ReleaseMutex(hMutex); // release it
}
MessageBox(0, pMsg, szApp, MB_OK); // keep it available
// if (hMutex)
// CloseHandle(hMutex); // finally free it even no need for that, as ExitProcess closes it anyway
ExitProcess(0);
}
« Last Edit: October 11, 2017, 12:43:23 PM by TimoVJL »
May the source be with you

Offline Robert

  • Member
  • *
  • Posts: 245
Re: Avoid multiple instance of an application
« Reply #7 on: October 11, 2017, 09:15:26 PM »
CreateMutex is kernel32 function that create or open mutex.
GetLastError() gives mutex existence.
Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
TCHAR *szApp = TEXT("TestMutex");
TCHAR *szMsg = TEXT("New Mutex");
void __cdecl WinMainCRTStartup(void)
{
TCHAR *pMsg = szMsg;
HANDLE hMutex = CreateMutex(NULL, FALSE, szApp); // create or open the mutex
if (hMutex) {
if (GetLastError() == ERROR_ALREADY_EXISTS) // test a mutex existence
pMsg = szMsg + 3;
ReleaseMutex(hMutex); // release it
}
MessageBox(0, pMsg, szApp, MB_OK); // keep it available
// if (hMutex)
// CloseHandle(hMutex); // finally free it even no need for that, as ExitProcess closes it anyway
ExitProcess(0);
}

GetLastError() is very interesting.

The following phrase from the CreateMutex function documentation at

https://msdn.microsoft.com/en-us/library/windows/desktop/ms682411(v=vs.85).aspx

"If lpName matches the name of an existing named mutex object, this function requests the MUTEX_ALL_ACCESS access right. In this case, the bInitialOwner parameter is ignored because it has already been set by the creating process. If the lpMutexAttributes parameter is not NULL, it determines whether the handle can be inherited, but its security-descriptor member is ignored."

led the authors of

"The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities"
Publisher: Addison-Wesley Professional; 1 edition (Nov. 20 2006)
Language: English
ISBN-10: 9780321444424
ISBN-13: 978-0321444424
ASIN: 0321444426

to comment

"Interesting. So if the ERROR_ALREADY_EXISTS value isn’t checked for using GetLastError(), it’s possible for an attacker to create a mutex with the same name before the real application does. This can undermine the security attributes that would otherwise be placed on the object because they are ignored when the application calls the CreateMutex() function. Furthermore, consider any code that calls CreateMutex() with the bInitialOwner parameter passed as TRUE. The caller might manipulate a shared object under the assumption that it holds the mutex lock, when in fact it doesn’t, thus resulting in a race condition."

Jokaste

  • Guest
Re: Avoid multiple instance of an application
« Reply #8 on: October 12, 2017, 01:15:16 PM »
Is it right Doctor?

If the mutex already exist, I check GetLastError for the 183 (ERROR_ALREADY_EXISTS) result code. If it already exist, I display an error message "Only one instance" else I exit.

Code: [Select]
Start                  PROC   PARMAREA=13*QWORD
                     LOCAL   _Icc:INITCOMMONCONTROLSEX
                     LOCAL   _szTmp[MAX_PATH + 4]:BYTE
                     LOCAL   _Wc:WNDCLASSEX
                     LOCAL   _Msg:MSG
                     LOCAL   _hMutex:HANDLE

                     mov      rcx,SYNCHRONIZE
                     xor      rdx,rdx
                     mov      r8,OFFSET szAppName
                     call   OpenMutexA

                     test   rax,rax
                     jz      @ThisIsTheFirstInstance

                     mov      _hMutex,rax

                     call   GetLastError

                     cmp      eax,ERROR_ALREADY_EXISTS
                     jne      @Exit

                     mov      rcx,_hMutex
                     call   ReleaseMutex

                     mov      rcx,_hMutex
                     call   CloseHandle

                     mov      rcx,OFFSET szOnlyOneInstance
                     call   Error

                     mov      rcx,0CAFEFADEh
                     call   ExitProcess

                     ret