The CreateMutex API can detect a second instance of your application. Here is an example :
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
Here is another example using FindWindow The sample application creates a new class inheriting the default dialog box class #32770
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
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.
Hi Jokaste,
That won't work as the return value of GetModuleHandle cannot be NULL :
.data?
hInstance HINSTANCE ?
.code
start:
invoke GetModuleHandle,0
mov hInstance,eax
QuoteReturn 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
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:
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) ;
}
The same in assembler:
; __________________________________________________________________________________
; _______________________ 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
CreateMutex is kernel32 function that create or open mutex.
GetLastError() gives mutex existence.
#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);
}
Quote from: TimoVJL on October 11, 2017, 12:38:39 PM
CreateMutex is kernel32 function that create or open mutex.
GetLastError() gives mutex existence.
#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."
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.
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