;=====================================================================
; [spamMonitor] - (c)2008 Jérôme Bruandet <spammon(at)spamcleaner.org>
;
; Sources + docs : http://spamcleaner.org/spammonitor
;
;---------------------------------------------------------------------
; Détecte toute connexion SMTP sortante et alerte l'utilisateur
;
; OS           : Windows (XP + Vista + Windows Server 2003/2008)
; Langage      : Assembleur
; Compilateur  : Borland Tasm32
;
;---------------------------------------------------------------------
;
; Licence      : GPL (cf LICENCE.TXT)
;
;---------------------------------------------------------------------
; Révisions    :
;
; - v0.10      : 1ère publication.
; - v0.20      : compatibilité avec les premières versions de XP
;                (SP1) et Windows Server 2003 en utilisant l'API
;                AllocateAndGetTcpExTableFromStack.
;
;=====================================================================

.586p
locals
jumps
.model flat, STDCALL

;=====================================================================
; fichiers des constantes et APIs win32

include spammon.w32API
include spammon.w32const

;=====================================================================
.data

AppName        db "spammon",0
hInst          dd 0
RunningMsg     db "spamMonitor est déjà lancé !", 0
SMtitleMsg     db "spamMonitor", 0
DLLname        db "iphlpapi.dll", 0
DLLnotfoundMsg db "Erreur : impossible de trouver [iphlpapi.dll] !"
               db 13,10,"Programme terminé.",0
; XP / Windows Server 2003 :
winOldAPIname  db "AllocateAndGetTcpExTableFromStack", 0
; XP SP2 / Vista / Windows Server 2003 SP1 / Windows Server 2008 :
winNewApiName  db "GetExtendedTcpTable",0
APInotfoundMsg db "Erreur : votre système d'exploitation n'est pas "
               db "compatible avec spamMonitor ! Merci de consulter"
               db " la documentation.",13,10
               db "Programme terminé.", 0
APIsizeBuffer  dd 10240
APIaddress     dd 0
hProcess       dd 0
APIptr         dd 0
memAlloc       dd 0
APImemAlloc    dd 0
oldWinVer      db 0
hSnapshot      dd ?
uProcess       PROCESSENTRY32 <>
cxState        dd 0
remoteIP       dd 0
cx_buffer      dd 0
total_found    dd 0
dwStateBuffer  db 60 dup (0)
wc             WNDCLASS <?,offset WndProc,?,?,?,?,?,?,?,offset AppName>
CursorPos      POINT <?,?>
NIData         NOTIFYICONDATA <size NOTIFYICONDATA,0,'MAPS',\
               NIF_ICON+NIF_TIP+NIF_MESSAGE,WM_NOTIFYICON,0,\
               'Clique gauche/droit'>
hMenu          dd ?
TrayIcon       dd ?
Message        MSG <?>
AboutDlgName   db "about",0
MainDlgName    db "main",0
stopThread     db 0
null           dd 0
dlgHandle      dd 0
dlgOpen        dd 0
helpURL        db "http://spamcleaner.org/spammonitor",0
scorgURL       db "http://spamcleaner.org",0
openStr        db "open",0
confDelLogMsg  db "Supprimer [c:\spammon.log] ?",0
cantDelLogMsg  db "Erreur : impossible de supprimer [c:\spammon.log] !", 0
noLBselectMsg  db "Sélectionnez le programme que vous souhaitez arrêter "
               db "dans la liste des connexions actives !", 0
cannotKillMsg  db "Ce processus ne peut pas être arrêté !",0
killMsg        db "Le processus a bien été arrêté !",0
CrashFormatMsg db "%lX",13,10,"- adresse : 0x%.8lX",0
CrashMsg1      db "Argh, spamMonitor a planté !"
               db " Le programme va se terminer.",13,10,13,10
               db "- type d'erreur : 0x"
CrashMsg2      db 32 dup (0)
Rect           RECT <>
screenX        dd ?
screenY        dd ?
notepad        db "notepad.exe",0
logFile        db "c:\spammon.log", 0
logFileHandle  dd 0
logFileHeader  db "Heure",9,9,"Programme",9,"Adresse IP"
               db 9,"Connexion",9,"PID",13,10
               db 68 dup (3dh),0
winPos         dd 0
GPLmsg         db "Ce programme est libre, vous pouvez "
               db "le redistribuer et/ou le modifier selon les "
               db "termes de la Licence Publique Générale GNU "
               db "publiée par la Free Software Foundation (version"
               db " 3 ou bien toute autre version ultérieure choisie"
               db " par vous).",13,10,13,10,"Ce programme est "
               db "distribué car potentiellement utile, mais SANS "
               db "AUCUNE GARANTIE, ni explicite ni implicite,"
               db " y compris les garanties de commercialisation ou "
               db "d'adaptation dans un but spécifique. Reportez-vous"
               db " à la Licence Publique Générale GNU pour plus "
               db "de détails.",13,10,13,10
               db "Vous devez avoir reçu une copie de la Licence "
               db "Publique Générale GNU en même temps que ce "
               db "programme ; si ce n'est pas le cas, consultez "
               db "<http://www.gnu.org/licenses/>.",13,10,13,10
               db "Si le code source n'était pas inclus avec ce "
               db "programme, il est téléchargeable à l'adresse : "
               db "<http://spamcleaner.org/spammonitor/>.",0
soundAlert     db "ALERT",0
ee             dd 0
ee_buffer      db 100 dup (0)
bWritten       dd 0
processID      dd 0
PIDbuffer      dd 0
processName    dd 0
noProcessName  db "??????????",0
tab_act        dd 20*4
               dd 35*4
               dd 250
tab_inact      dd 11*4
               dd 35*4
               dd 250
               dd 350
timeFormat     db 13,10,"hh:mm:ss",9,0
stringFormat   db '%s',9,'%i.%i.%i.%i',9,'[%hS]',9,'%i',0
timeBuffer     db 11 dup (0)
outBuffer      db 100 dup (0)

;=====================================================================
.code
Main  proc

   ; SEH au cas ou... :
   push     offset BetterSafeThanSorry
   call     SetUnhandledExceptionFilter

   ; on n'autorise qu'une seule instance du programme :
   call     CreateMutexA, 0, 0, offset AppName
   mov      hInst, eax
   call     GetLastError
   cmp      eax, 183                         ; ERROR_ALREADY_EXISTS
   jne      not_running
   call     CloseHandle, hInst
   push     10h
   push     offset SMtitleMsg
   push     offset RunningMsg
   push     0
   call     MessageBoxA
   jmp      quit
not_running:
   ; vérifie si on trouve iphlpapi.dll :
   call     LoadLibraryA, offset DLLname
   test     eax, eax
   jnz      DLL_found
   push     10h
   push     offset SMtitleMsg
   push     offset DLLnotfoundMsg
   push     0
   call     MessageBoxA
   jmp      quit
DLL_found:
   mov      null, eax
   ; tente de récupérer l'adresse AllocateAndGetTcpExTableFromStack
   ; (XP / Windows Server 2003) :
   call     GetProcAddress, eax, offset winOldAPIname
   test     eax, eax
   jz       isNewer
   mov      oldWinVer, 1
   mov      APIaddress, eax
   ; nécessaire pour l'appel à l'API :
   call     GetProcessHeap
   mov      hProcess, eax
   jmp      API_found

isNewer:
   ; si erreur, on test avec GetExtendedTcpTable (XP SP2 / Vista /
   ; Windows Server 2003 SP1 / Windows Server 2008) :
   call     GetProcAddress, null, offset winNewApiName
   test     eax, eax
   jnz      isNewer2
   push     10h
   push     offset SMtitleMsg
   push     offset APInotfoundMsg
   push     0
   call     MessageBoxA
   jmp      quit

isNewer2:
   mov      APIaddress, eax
   ; alloue un large (10Ko) buffer pour TcpTable :
   call     GlobalAlloc,GHND, 10240
   mov      APImemAlloc, eax
   call     GlobalLock,eax
   mov      APIptr, eax

API_found:
   ; alloue 1Ko pour y stocker les connexions SMTP en cours
   ; (cx_buffer) et les PIDs (PIDbuffer) :
   call     GlobalAlloc,GHND, 1024
   mov      memAlloc, eax
   call     GlobalLock,eax
   mov      cx_buffer, eax
   add      eax, 512
   mov      PIDbuffer, eax

   call     GetModuleHandleA, 0
   mov      hInst, eax

   mov      wc.w_hInstance, eax
   call     LoadMenuA, hInst, 103            ; menu
   mov      hMenu, eax
   call     LoadIconA, hInst, 101            ; icone
   mov      TrayIcon, eax
   mov      NIData.n_hIcon, eax
   mov      wc.w_hIcon, eax
   call     RegisterClassA, offset wc
   push     0
   push     hInst
   push     0
   push     0
   push     0
   push     0
   push     0
   push     0
   push     0
   push     0
   push     offset AppName
   push     0
   call     CreateWindowExA
   mov      NIData.n_hWnd, eax

   ; thread de surveillance des connexions SMTP :
   push     offset null
   push     0
   push     0
   push     offset monitorThread
   push     0
   push     0
   call     CreateThread

   ; ouverture de la boite de dialogue :
   push     0
   push     IDM_CREATE
   push     111h
   push     dword ptr [NIData.n_hWnd]
   call     PostMessageA

   push     0
   push     80h
   push     2
   push     0
   push     1
   push     40000000h
   push     offset logFile
   call     CreateFileA
   mov      logFileHandle, eax
   call     lstrlenA, offset logFileHeader
   push     0
   push     offset bWritten
   push     eax
   push     offset logFileHeader
   push     logFileHandle
   call     WriteFile

messageLoop:
   call     GetMessageA, offset Message, 0, 0, 0
   or       eax, eax
   jz       endLoop
   call     TranslateMessage, offset Message
   call     DispatchMessageA, offset Message
   jmp      messageLoop

endLoop:
   call     Shell_NotifyIconA, NIM_DELETE, offset NIData

   cmp      oldWinVer, 1
   jz       noneed2free
   call     GlobalUnlock, [APImemAlloc]
   call     GlobalFree, [APImemAlloc]
noneed2free:
   call     GlobalUnlock, [memAlloc]
   call     GlobalFree, [memAlloc]
   call     CloseHandle, logFileHandle

quit:
   call     ExitProcess, 0

Main endp

;=====================================================================
; BetterSafeThanSorry : récupère l'adresse et le code de l'exception
;       (SEH) dans la structure EXCEPTION_RECORD (esp+4) en cas de
;        plantage et affiche leurs valeurs dans une messagebox.

BetterSafeThanSorry proc

   mov      esi, dword ptr [esp+4]
   mov      esi, dword ptr [esi]
   mov      eax, dword ptr [esi+12]
   push     eax
   mov      eax, dword ptr [esi]
   push     eax
   push     offset CrashFormatMsg
   push     offset CrashMsg2
   call     _wsprintfA
   add      esp, 4*4
   push     40h
   push     offset SMtitleMsg
   push     offset CrashMsg1
   push     0
   call     MessageBoxA
   mov      stopThread, 1
   ; force l'adresse de retour
   push     offset endLoop
   ret

BetterSafeThanSorry endp

;=====================================================================
; monitorThread : récupère les connexions SMTP actives

monitorThread proc

next_round:

   cmp      oldWinVer, 1
   jnz      @newwin_01
   ; appel de AllocateAndGetTcpExTableFromStack :
   push     2                                ; AF_INET
   push     0                                ; dwFlags
   push     hProcess                         ; hHeap
   push     0                                ; unsorted
   push     offset APIptr                    ; ppTcpTable
   call     dword ptr [APIaddress]
   test     eax, eax                         ; ERROR_SUCCESS (0) ?
   je       APIok
   jmp      goAhead

@newwin_01:
   ; appel de GetExtendedTcpTable :
   push     0                                ; Reserved
   push     5                                ; TCP_TABLE_OWNER_PID_ALL
   push     2                                ; AF_INET
   push     0                                ; unsorted
   push     offset APIsizeBuffer             ; pdwSize
   push     [APIptr]                         ; pTcpTable
   call     dword ptr [APIaddress]
   cmp      eax, 122                         ; ERROR_INSUFFICIENT_BUFFER
   je       goAhead
APIok:
   mov      esi, [APIptr]                    ; pointe sur la structure
   test     esi, esi
   je       goAhead
   ; nombre de connexions actives :
   mov      ecx, [esi]
   test     ecx, ecx                         ; aucune ??
   je       goAhead

next_cx:
   push     ecx                              ; compteur
   push     esi                              ; pointeur

   ; verifie si le port nous interesse (25) :
   cmp      dword ptr [esi+14h], 1900h       ; dwRemotePort
   jne      unwanted

   ; recupere l'etat de la connexion :
   mov      eax, [esi+4]                     ; dwState
   mov      cxState, eax
   ; conversion en chaine ASCII :
   add      eax, 12ch
   call     LoadStringA, hInst, eax, offset dwStateBuffer, 60

   ; recupere l'IP de la connexion :
   mov      eax, dword ptr [esi+10h]         ; dwRemoteAddr
   mov      remoteIP, eax

   ; recupere le PID :
   mov      eax, [esi+18h]                   ; dwProcessId
;   ; on ne garde pas les PIDs égal à 0 ([System Process]) :
;   test     eax, eax
;   jz       unwanted
   mov      processID, eax
   ; récupère le nom du processus:
   call     getProcessName
   ; verifie qu'on a bien un nom :
   cmp      byte ptr [processName], 0
   jne      nameOK
   mov      ebx, offset noProcessName
   mov      processName, ebx

nameOK:
   ; prepare le texte a afficher :
   push     processID
   push     offset dwStateBuffer
   mov      ebx, remoteIP
   movzx    ebx, byte ptr [remoteIP+3]
   push     ebx
   movzx    ebx, byte ptr [remoteIP+2]
   push     ebx
   movzx    ebx, byte ptr [remoteIP+1]
   push     ebx
   movzx    ebx, byte ptr [remoteIP]
   push     ebx
   push     processName
   push     offset stringFormat
   push     offset outBuffer
   call     _wsprintfA
   add      esp, 4*9                         ; réajuste la pile

   ; recherche si déjà present dans la listebox :
   push     offset outBuffer
   push     0
   push     LB_FINDSTRING
   push     IDC_ACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA
   cmp      eax, -1                          ; LB_ERR
   ; si inconnu, ajoute dans listbox
   je       add2LB

   ; active le flag de positionnement :
   mov      ebx, [cx_buffer]
   mov      dword ptr [ebx+eax*4], 1
   ; ajoute le PID dans PIDbuffer (cx_buffer+512) :
   mov      ecx, processID
   mov      [ebx+512+eax*4], ecx
   inc      total_found
   jmp      unwanted

add2LB:
   ; on affiche dans la listbox :
   push     offset outBuffer
   push     0
   push     180h                             ; LB_ADDSTRING
   push     IDC_ACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA
   ; active le flag de positionnement :
   mov      ebx, [cx_buffer]
   mov      dword ptr [ebx+eax*4], 1
   mov      ecx, processID
   mov      [ebx+512+eax*4], ecx
   inc      total_found

   ; rajoute les infos au fichier log (+ heure) :
   push     11
   push     offset timeBuffer
   push     offset timeFormat
   push     0
   push     8
   push     0
   call     GetTimeFormatA
   push     0
   push     offset bWritten
   push     11
   push     offset timeBuffer
   push     logFileHandle
   call     WriteFile
   call     lstrlenA, offset outBuffer
   push     0
   push     offset bWritten
   push     eax
   push     offset outBuffer
   push     logFileHandle
   call     WriteFile

   ; insertion dans la listbox des anciennes connexions :
   push     offset timeBuffer+2              ; passe outre le CR/LF
   push     0
   push     181h                             ; LB_INSERTSTRING
   push     IDC_INACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA

   ; ouvre la dialogbox si elle est minimisée :
   cmp      dlgOpen, 0
   jnz      dlgAlreadyOpen
   call     PostMessageA, dword ptr [NIData.n_hWnd], 111h, IDM_MAIN, 0
dlgAlreadyOpen:
   ; active le boutton "Kill" :
   call     GetDlgItem, dlgHandle, IDC_KILL
   call     EnableWindow, eax, 1
   ; flash + beep :
   call     FlashWindow,dlgHandle ,1
   ; alerte sonore ?
   call     IsDlgButtonChecked, dlgHandle, IDC_ALERTE
   test     eax, eax
   jz       noWave
   call     PlaySound, offset soundAlert, hInst, 42004h
   jmp      unwanted
noWave:
   call     MessageBeep, 40h

unwanted:
   pop      esi
   pop      ecx
   dec      ecx
   jcxz     noMoreCX
   add      esi, 18h
   jmp      next_cx

noMoreCX:
   cmp      oldWinVer, 1
   jnz      @newwin_02
   ; libère la mémoire après chaque appel
   ; à AllocateAndGetTcpExTableFromStack :
   call     HeapFree, hProcess, 0, [APIptr]

@newwin_02:
   ; récupère le nombre d'éléments dans listbox :
   push     0
   push     0
   push     18bh                 ; LB_GETCOUNT
   push     IDC_ACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA
   push     eax
   test     eax, eax
   jnz      cleanupLB
   ; desative le bouton "Kill" si aucune connexion :
   call     GetDlgItem, dlgHandle, IDC_KILL
   call     EnableWindow, eax, 0
   pop      eax
   jmp      goAhead
cleanupLB:
   pop      eax
   cmp      eax, [total_found]
   jb       goAhead

   ; supprime les connexions du buffer
   ; et réactualise la listbox :
   mov      ebx, [cx_buffer]

@cleanup_loop:
   dec      eax                              ; zero-based
   cmp      eax, -1
   je       goAhead
   cmp      dword ptr [ebx+eax*4], 1
   jz       resetFlag
   push     eax
   push     0
   push     eax
   push     182h                             ; LB_DELETESTRING
   push     IDC_ACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA
   pop      eax
resetFlag:
   mov      dword ptr [ebx+eax*4], 0
   jmp      @cleanup_loop

goAhead:
   mov      dword ptr [total_found], 0
   call     Sleep, 150
   ; on continue ?
   cmp      stopThread, 1
   jnz      next_round
   ret

monitorThread endp

;=====================================================================
; getProcessName : récupère le nom du processus à partir de son PID
; Param.         : processID == PID

getProcessName proc

   pushad
   mov      processName, 0
   call     CreateToolhelp32Snapshot, 2, 0   ; snapshot
   mov      [hSnapshot], eax
   mov      [uProcess.dwSize], size PROCESSENTRY32
   push     offset uProcess
   push     eax
   call     Process32First                   ; 1er processus
   test     eax, eax
   jnz      getnext
   popad
   ret

getnext:
   mov      eax, uProcess.th32ProcessID
   ; est-ce le notre?
   cmp      eax, dword ptr [processID]
   jnz      unknown
   mov      eax, offset uProcess.szExeFile
   mov      processName, eax
   jmp      closeSnapshot
unknown:
   push     offset uProcess
   push     [hSnapshot]
   call     Process32Next                    ; processus suivants
   test     eax, eax
   jnz      getnext

closeSnapshot:
   push     [hSnapshot]
   call     CloseHandle

   popad
   ret

getProcessName endp

;=====================================================================
; WndProc
; Param.  : hWnd, uMsg, wParam et lParam

WndProc     proc

   enter    0, 0
   push     ebx
   push     esi
   push     edi

wm_destroy:
   cmp      dword ptr [ebp+0ch], 2           ; uMsg == WM_DESTROY ?
   jnz      wm_command
   call     PostQuitMessage, 0
   jmp      clear_eax

wm_command:
   cmp      dword ptr [ebp+0ch], 111h        ; uMsg == WM_COMMAND ?
   jnz      wm_notify

create_dlg:
   cmp      word ptr [ebp+10h], IDM_CREATE   ; wParam == creation ?
   jnz      menu_open
   ; supprime l'icone de la systray
   call     Shell_NotifyIconA, NIM_DELETE, offset NIData
   mov      dlgOpen, 1
   ; création de la boite de dialogue
   push     0
   push     offset MainDlg
   push     dword ptr [ebp+8]
   push     offset MainDlgName
   push     hInst
   call     DialogBoxParamA
   jmp      clear_eax

menu_open:
   cmp      word ptr [ebp+10h], IDM_MAIN     ; wParam == "Ouvrir" ?
   jnz      menu_about
   call     Shell_NotifyIconA, NIM_DELETE, offset NIData
   mov      dlgOpen, 1

   ; affiche et positionne la boite de dialogue
   ; en bas a droite :
   mov      winPos, 1
   push     dlgHandle
   call     MoveDlgBox
   add      esp, 4
   call     ShowWindow, dlgHandle, 9         ; SW_RESTORE
   jmp      clear_eax

menu_about:
   cmp      word ptr [ebp+10h], IDM_ABOUT    ; wParam == "A propos" ?
   jnz      menu_exit
   push     0
   push     offset AboutDlg
   push     dword ptr [ebp+8]
   push     offset AboutDlgName
   push     hInst
   call     DialogBoxParamA
   jmp      clear_eax

menu_exit:
   cmp      word ptr [ebp+10h], IDM_EXIT     ; wParam == "Quitter" ?
   jnz      def_win
   mov      stopThread, 1                    ; arrete notre thread
   push     dword ptr [ebp+8]
   call     DestroyWindow
   jmp      clear_eax

wm_notify:
   cmp      dword ptr [ebp+0ch], 400h        ; uMsg == WM_USER ?
   jnz      def_win

   cmp      dword ptr [ebp+14h], 205h        ; lParam == WM_RBUTTONUP ?
   jz       show_menu
   cmp      dword ptr [ebp+14h], 202h        ; lParam == WM_LBUTTONUP ?
   jnz      def_win

show_menu:
   call     GetCursorPos , offset CursorPos
   xor      ecx, ecx
   setnz    cl
   call     GetSubMenu, hMenu, ecx
   push     eax
   push     dword ptr[ebp+8]
   call     SetForegroundWindow
   pop      eax
   push     0
   push     dword ptr [ebp+8]
   push     0
   push     CursorPos.y
   push     CursorPos.x
   push     TPM_RIGHTBUTTON
   push     eax
   call     TrackPopupMenu
   call     PostMessageA, dword ptr [ebp+08h], 0, 0, 0
   jmp      clear_eax

def_win:
   push     dword ptr [ebp+14h]              ; lParam
   push     dword ptr [ebp+10h]              ; wParam
   push     dword ptr [ebp+0ch]              ; uMsg
   push     dword ptr [ebp+8]                ; hWnd
   call     DefWindowProcA
   jmp      Return
clear_eax:
   xor      eax, eax
Return:
   pop      edi
   pop      esi
   pop      ebx
   leave
   ; réajuste la pile
   retn     10h

WndProc     endp

;=====================================================================
; MainDlg : gestion de la boite de dialogue principale

MainDlg proc

   push     ebp
   mov      ebp, esp
   cmp      dword ptr [ebp+0ch], 110h        ; uMsg == WM_INITDIALOG ?
   jnz      @wm_command

   ; positionne la boite de dialogue en bas a droite :
   push     dword ptr [ebp+8]
   mov      winPos, 1
   call     MoveDlgBox
   pop      dlgHandle                        ; recupere son handle
                                             ; et réajuste la pile
   ; initialise les tabulations :
   push     offset tab_act
   push     3
   push     192h                             ; LB_SETTABSTOPS
   push     IDC_ACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA

   ; initialise les tabulations :
   push     offset tab_inact
   push     4
   push     192h                             ; LB_SETTABSTOPS
   push     IDC_INACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA

   ; checkbox alerte sonore :
   call CheckDlgButton, dlgHandle, IDC_ALERTE, 1

   mov      eax, 1
   leave
   retn     10h

@wm_command:
   cmp      dword ptr [ebp+0ch], 111h        ; uMsg == WM_COMMAND
   jne      @clear_eax

@idc_ok:
   mov      eax, dword ptr [ebp+10h]
   and      eax, 0000FFFFh
   cmp      eax, IDC_OK                      ; wParam == "OK" ?
   jne      @idc_kill

   call     ShowWindow, dlgHandle, 0         ; SW_HIDE
   call     Shell_NotifyIconA, NIM_ADD, offset NIData
   mov      dlgOpen, 0
   jmp      @clear_eax

@idc_kill:
   cmp      eax, 1005                        ; wParam == "Kill !" ?
   jne      @idc_viewlog
   push     0
   push     0
   push     188h                             ; LB_GETCURSEL
   push     IDC_ACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA
   cmp      eax, -1                          ; LB_ERR
   jne      fetchLBtext
   push     30h
   push     offset SMtitleMsg
   push     offset noLBselectMsg
   push     dlgHandle
   call     MessageBoxA
   jmp      @clear_eax
fetchLBtext:
   mov      ebx, [PIDbuffer]
   mov      ebx, [ebx+eax*4]
   test     ebx, ebx
   jnz      @kill_it
@cannotKill:
   push     10h
   push     offset SMtitleMsg
   push     offset cannotKillMsg
   push     dlgHandle
   call     MessageBoxA
   jmp      @clear_eax
@kill_it:
   push     ebx
   push     1
   push     1                                ; PROCESS_TERMINATE
   call     OpenProcess
   test     eax, eax
   jz       @cannotKill
   push     0
   push     eax
   call     TerminateProcess
   test     eax, eax
   jz       @cannotKill
   push     40h
   push     offset SMtitleMsg
   push     offset killMsg
   push     dlgHandle
   call     MessageBoxA
   jmp      @clear_eax

@idc_viewlog:
   cmp      eax, IDC_VIEWLOG                 ; wParam == "Voir Log" ?
   jne      @idc_dellog
   push     1
   push     0
   push     0
   push     offset logFile
   push     offset openStr
   push     offset notepad
   call     ShellExecuteA
   jmp      @clear_eax

@idc_dellog:
   cmp      eax, IDC_DELLOG                  ; wParam == "Effacer Log" ?
   jne      @idc_about

   ; efface le fichier log :
   push     31h
   push     offset SMtitleMsg
   push     offset confDelLogMsg
   push     dlgHandle
   call     MessageBoxA
   cmp      eax, 1
   jnz      @clear_eax
   call     CloseHandle, logFileHandle
   push     0
   push     80h
   push     2
   push     0
   push     1
   push     40000000h
   push     offset logFile
   call     CreateFileA
   mov      logFileHandle, eax
   call     lstrlenA, offset logFileHeader
   push     0
   push     offset bWritten
   push     eax
   push     offset logFileHeader
   push     logFileHandle
   call     WriteFile
   cmp      bWritten, 0
   jnz      @clear_eax
   push     10h
   push     offset SMtitleMsg
   push     offset cantDelLogMsg
   push     dlgHandle
   call     MessageBoxA
   jmp      @clear_eax

@idc_about:
   cmp      eax, IDC_ABOUT                   ; wParam == "A Propos.." ?
   jne      @idc_help

   push     0
   push     offset AboutDlg
   push     dword ptr [ebp+8]
   push     offset AboutDlgName
   push     hInst
   call     DialogBoxParamA
   jmp      @clear_eax

@idc_help:
   cmp      eax, IDC_HELP                    ; wParam == "Aide" ?
   jne      @clear_eax

   ; lance le navigateur :
   push     1
   push     0
   push     0
   push     offset helpURL
   push     offset openStr
   push     0
   call     ShellExecuteA

@clear_eax:
   xor      eax, eax
   leave
   retn     10h

MainDlg endp

;=====================================================================
; AboutDlg  : gestion de la boite de dialoque 'A propos'
AboutDlg Proc

   push  ebp
   mov   ebp, esp

@@wm_initdialog:
   cmp      dword ptr [ebp+0ch], 110h        ; WM_INITDIALOG
   jnz      @@wm_command

   push     dword ptr [ebp+8]
   mov      winPos, 0
   call     MoveDlgBox
   add      esp, 4

   push     offset GPLmsg
   push     IDC_GLP
   push     dword ptr [ebp+08h]
   call     SetDlgItemTextA
   mov      eax, 1
   leave
   retn     10h
@@wm_command:
   cmp      dword ptr [ebp+0ch], 111h                 ; WM_COMMAND
   jne      @@wm_notify
   mov      eax, dword ptr [ebp+10h]
   and      eax, 0000FFFFh
   cmp      eax, IDC_OK
   jne      @@idc_scorg
@@end_dialog:
   call     Shell_NotifyIconA, NIM_ADD, offset NIData
   call     EndDialog, dword ptr [ebp+08h], 0
   mov      eax, 1
   leave
   retn     10h
@@idc_scorg:
   cmp      eax, IDC_SCORG
   jne      @@is_icon
   push     1
   push     0
   push     0
   push     offset scorgURL
   push     offset openStr
   push     0
   call     ShellExecuteA
   jmp      @@end_dialog

@@is_icon:
   cmp      eax, 2121
   jne      @@clear_eax
   cmp      word ptr [ebp+12h], 1
   jne      @@clear_eax
   cmp      dword ptr [ee], 4
   jb       @@clear_eax
   mov      stopThread, 1
   call     PostMessageA, dword ptr [NIData.n_hWnd], 111h, IDM_EXIT, 0
   jmp      @@end_dialog
@@wm_notify:
   cmp      dword ptr [ebp+0ch], 206h
   jne      @@clear_eax
   cmp      word ptr [ebp+10h], 0ah
   jne      @@clear_eax
@@easter_egg_inside:
   inc      dword ptr [ee]
   cmp      dword ptr [ee], 1
   jb       @@clear_eax
   cmp      dword ptr [ee], 4
   je       ee_thread
   cmp      dword ptr [ee], 3
   ja       @@clear_eax
   push     40h
   push     offset SMtitleMsg
   mov      eax, [ee]
   add      eax, 989
   call     LoadStringA, hInst, eax, offset ee_buffer, 100
   push     offset ee_buffer
   push     dword ptr [ebp+8]
   call     MessageBoxA
   jmp      @@clear_eax
ee_thread:
   mov      stopThread, 1
   call     Sleep, 250
   call     EnableMenuItem, hMenu, IDM_EXIT, 1
   mov      ecx, 6
@@ee_nextbtn:
   mov      eax, ecx
   add      eax, 949
   mov      ebx, ecx
   add      ebx, 999
   push     ecx
   call     LoadStringA, hInst, eax, offset ee_buffer, 100
   call     SetDlgItemTextA, dlgHandle, ebx, offset ee_buffer
   pop      ecx
   loop     @@ee_nextbtn
   mov      stopThread, 0
   call     CreateThread, 0, 0, offset eeThread, 0, 0, offset null
   jmp      @@end_dialog

@@clear_eax:
   xor      eax, eax
   leave
   retn     10h

AboutDlg    endp

eeThread    proc
@@next_ee:
   cmp      dlgOpen, 1
   je       @@ee_open
   call     PostMessageA, dword ptr [NIData.n_hWnd], 111h, IDM_MAIN, 0
   mov      dlgOpen, 1
@@ee_open:
   call     SendDlgItemMessageA, dlgHandle, 1007, 184h, 0, 0
   mov      ecx, 0fh
   mov      ebx, ecx
   add      ebx, 900
@@ee_nextitm:
   push     ecx
   call     LoadStringA, hInst, ebx, offset ee_buffer, 100
   call     GetTickCount
   and      eax, 0fh
   push     offset ee_buffer
   push     0
   cmp      al, 8
   jb       @@ins
   push     180h
   jmp      @@add
@@ins:
   push     181h
@@add:
   push     IDC_ACTIVECX
   push     dlgHandle
   call     SendDlgItemMessageA
   pop      ecx
   dec      ebx
   loop     @@ee_nextitm
   call     PlaySound, offset soundAlert, hInst, 42004h
   call     Sleep, 100
   call     FlashWindow, dlgHandle, 1
   cmp      stopThread, 1
   jnz      @@next_ee
   ret

eeThread    endp
;=====================================================================
; MoveDlgBox : positionne une boite de dialogue au centre
;              ou en bas à droite de l'écran.
; Param.     : - winPos  : 0 (centre) ou 1 (bas à droite)
;              - [esp+4] : handle dialogbox

MoveDlgBox  proc

   push     offset Rect
   push     dword ptr [esp+8]
   call     GetWindowRect
   call     GetSystemMetrics, 16             ; SM_CXFULLSCREEN
   mov      esi, eax
   cmp      winPos, 0
   jnz      @1
   shr      esi, 1
   jmp      @2
@1:
   sub      esi, 10
@2:
   mov      eax, Rect.right
   sub      eax, Rect.left
   cmp      winPos, 0
   jnz      @3
   shr      eax, 1
@3:
   sub      esi, eax
   call     GetSystemMetrics, 17             ; SM_CYFULLSCREEN
   cmp      winPos, 0
   jnz      @4
   shr      eax, 1
   jmp      @5
@4:
   add      eax, 10
@5:
   mov      ecx, Rect.bottom
   sub      ecx, Rect.top
   cmp      winPos, 0
   jnz      @6
   shr      ecx, 1
@6:
   sub      eax, ecx
   push     0
   mov      ebx, Rect.bottom
   sub      ebx, Rect.top
   push     ebx
   mov      ebx, Rect.right
   sub      ebx, Rect.left
   push     ebx
   push     eax
   push     esi
   push     dword ptr [esp+18h]
   call     MoveWindow
   ret

MoveDlgBox     endp
;=====================================================================

End      Main

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