.386
MODEL	FLAT, STDCALL
OPTION	CaseMap: None

includelib	import32.lib
include		windows.inc
include		procname.inc
include		image.inc


extrn	ExitProcess: Proc
extrn   GetModuleHandle: Proc
extrn	GetProcAddress: Proc
extrn	FindWindow: Proc
extrn	FindWindowEx: Proc
extrn	PostMessage: Proc
extrn	GetClientRect: Proc
extrn	GetWindowLong: Proc
extrn	SystemParametersInfo: Proc
extrn	lstrlen: Proc
extrn	lstrcpy: Proc


WM_IMPLANT	= WM_USER + 1234

OFDELTA		= offset DELTA
HANDLER_SIZE	= (offset HANDLER_END - offset HANDLER_BEGIN)
IMPLANT_SIZE	= (offset IMPLANT_END - offset IMPLANT_BEGIN)

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

.DATA
	DllKernel	db	"kernel32.dll", 0
	DllUser		db	"user32.dll", 0
	DllGDI		db	"gdi32.dll", 0
	ProcName	db	"DispatchMessageA", 0

	ProcMB			db	"MessageBoxA", 0
	ProcSetTimer		db	"SetTimer", 0
	ProcKillTimer		db	"KillTimer", 0
	ProcInvalidateRect	db	"InvalidateRect", 0
	ProcUpdateWindow	db	"UpdateWindow", 0
	ProcGlobalAlloc		db	"GlobalAlloc", 0
	ProcSetWindowLong	db	"SetWindowLongA", 0
	ProcSetBkMode		db	"SetBkMode", 0
	ProcSetTextColor	db	"SetTextColor", 0
	ProcTextOut		db	"TextOutA", 0
	ProcCreateFontIndirect	db	"CreateFontIndirectA", 0
	ProcSelectObject	db	"SelectObject", 0
	ProcBeginPaint		db	"BeginPaint", 0
	ProcEndPaint		db	"EndPaint", 0


	CNShell		db	"Shell_TrayWnd", 0
	CNTray		db	"TrayNotifyWnd", 0
	CNClock		db	"TrayClockWClass", 0

	TrayText	db	"Win32asm is cool !", 0


.DATA?
	hLibKernel	HANDLE		?
	hLibUser	HANDLE		?
	hLibGDI		HANDLE		?

	HandlerAddr	dd		?
	paVxdCall0	dd		?

	ncm		NONCLIENTMETRICS	?


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

.CODE
START:
	; Находим адрес процедуры VxDCall0
	call	GetModuleHandle, offset DllKernel
	mov	hLibKernel, eax
	mov	ebx, eax			; ebx = Kernel32 handle
	mov	eax, [ebx.e_lfanew]
	lea	edi, [eax.ebx]
	mov	esi, [edi.NTOptionalHeader.OHDataDirectory.Dir_Export.DDVirtualAddress]
	mov	esi, [esi.ebx.EDAddressOfFunctions]
	mov	ecx, [esi.ebx]
	add	ecx, ebx		; ecx = адрес процедуры VxDCall0
	mov	paVxdCall0, ecx

	; Разрешим запись в текст обрабочика (свой)
	lea	eax, HANDLER_BEGIN
	shr	eax, 12
	call	ecx, 01000Dh, eax, 01h, 00h, 020060000h


	; Найдем описание первой секции Kernel32.dll
	mov	esi, hLibKernel			; esi -> MZ
	add	esi, dword ptr [esi.e_lfanew]	; esi -> PE
	movzx	eax, word ptr [esi.NTFileHeader.FHSizeOfOptionalHeader]
	lea	esi, [esi.NTOptionalHeader]
	add	esi, eax			; esi -> section table
	; Найдем адрес остатка первой секции
	mov	eax, dword ptr [esi.SHSizeOfRawData]
	sub	eax, dword ptr [esi.SHVirtualSize]	; eax = длина остатка первой секции в байтах
	cmp	eax, HANDLER_SIZE			; хватит ли места ?
	jb	@NoSpace
	mov	edi, hLibKernel
	add	edi, dword ptr [esi.SHVirtualAddress]	; edi -> начало первой секции
	add	edi, dword ptr [esi.SHVirtualSize]	; edi -> остаток первой секции
	mov	HandlerAddr, edi

	; Разрешим запись по найденному адресу
	shr	edi, 12
	call	paVxdCall0, 01000Dh, edi, 01h, 00h, 020060000h


	; Определяем адрес перехватываемой процедуры
	call	GetModuleHandle, offset DllUser
	mov	hLibUser, eax
	call	GetModuleHandle, offset DllGDI
	mov	hLibGDI, eax
	call	GetPA, offset ProcName, offset ProcAddr

	; Разрешаем запись по этому адресу
	shr	eax, 12
	call	paVxdCall0, 01000Dh, eax, 01h, 00h, 020060000h	; _PageModifyPermissions


	; Сохраняем 5 байт
	mov	esi, ProcAddr
	mov	al, byte ptr [esi]
	mov	OneB, al
	push	dword ptr [esi+1]
	pop	FourB


	; Ищем нужное окно
	call	FindWindow, offset CNShell, NULL
	call	FindWindowExA, eax, NULL, offset CNTray, NULL
	call	FindWindowExA, eax, NULL, offset CNClock, NULL
	mov	Handle, eax
	or	eax, eax
	jz	@NoClock


	; Запоминаем адрес старой процедуры окна
	call	GetWindowLong, Handle, GWL_WNDPROC
	mov	WPAddr, eax

	call	GetClientRect, Handle, offset rect
	mov	eax, rect.rcRight
	sub	eax, rect.rcLeft
	mov	Width, eax
	mov	X, eax
	mov	MinX, -160
	mov	Y, 1

	call	lstrlen, offset TrayText
	mov	StrLen, eax
	call	lstrcpy, offset str, offset TrayText

	; Определим шрифт, которым отображаются часы
	mov	ncm.cbSize, SIZE NONCLIENTMETRICS
	call	SystemParametersInfo, SPI_GETNONCLIENTMETRICS, SIZE NONCLIENTMETRICS, offset ncm, 0
	mov	ncm.lfCaptionFont.lfWeight, 100
	lea	esi, ncm.lfCaptionFont
	lea	edi, lf
	mov	ecx, SIZE LOGFONT
	cld
	rep	movsb

	; ----- Определяем адреса функций API -----
	call	GetPA, offset ProcMB, offset paMB
	call	GetPA, offset ProcSetTimer, offset paSetTimer
	call	GetPA, offset ProcKillTimer, offset paKillTimer
	call	GetPA, offset ProcInvalidateRect, offset paInvalidateRect
	call	GetPA, offset ProcUpdateWindow, offset paUpdateWindow
	call	GetPA, offset ProcGlobalAlloc, offset paGlobalAlloc
	call	GetPA, offset ProcSetWindowLong, offset paSetWindowLong
	call	GetPA, offset ProcBeginPaint, offset paBeginPaint
	call	GetPA, offset ProcSetBkMode, offset paSetBkMode
	call	GetPA, offset ProcSetTextColor, offset paSetTextColor
	call	GetPA, offset ProcTextOut, offset paTextOut
	call	GetPA, offset ProcEndPaint, offset paEndPaint
	call	GetPA, offset ProcCreateFontIndirect, offset paCreateFontIndirect
	call	GetPA, offset ProcSelectObject, offset paSelectObject


	; Помещаем в начало процедуры переходник
	mov	edi, ProcAddr
	mov	byte ptr [edi], 0E9h		; "jmp "
	mov	eax, HandlerAddr
	sub	eax, edi
	sub	eax, 5
	mov	JumpVal, eax
	mov	dword ptr [edi+1], eax

	; Копируем код обработчика в остаток секции
	mov	esi, offset HANDLER_BEGIN
	mov	edi, HandlerAddr
	mov	ecx, HANDLER_SIZE
	cld
	rep	movsb


	; Отправляем окну свое сообщение
	call	PostMessage, Handle, WM_IMPLANT, 0, 0

@NoClock:
@NoSpace:
	call	ExitProcess, NULL


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

GetPA	PROC	pname: POINTER, paddr: POINTER
	call	GetProcAddress, hLibKernel, pname
	or	eax, eax
	jnz	@GPAE
	call	GetProcAddress, hLibUser, pname
	or	eax, eax
	jnz	@GPAE
	call	GetProcAddress, hLibGDI, pname
@GPAE:	mov	edx, paddr
	mov	dword ptr [edx], eax
	ret
GetPA	ENDP


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



; LONG DispatchMessage(
; 	CONST MSG *lpmsg	//Points to an MSG structure that contains the message
; 	);

; struct MSG {
;    HWND   hwnd;	 
;    UINT   message; 
;    WPARAM wParam; 
;    LPARAM lParam; 
;    DWORD  time; 
;    POINT  pt; 
; } MSG; 


HANDLER_BEGIN:
	push	ebp
	call	DELTA
DELTA:	pop	ebp
	pop	dword ptr [ebp+(offset ValEBP - OFDELTA)]
	pop	dword ptr [ebp+(offset RetPoint - OFDELTA)]
	mov	dword ptr [ebp+(offset ValEDI - OFDELTA)], edi

; ----------------------------------------------------------------------------
	; Проверяем хэндл окна
	mov	eax, dword ptr [esp+00]			; lpmsg
	mov	edx, dword ptr [ebp+(offset Handle - OFDELTA)]
	cmp	dword ptr [eax], edx
	jnz	@CALL

	; Проверяем номер сообщения
	mov	eax, dword ptr [esp+00]			; lpmsg
	cmp	dword ptr [eax+4], WM_IMPLANT		; MSG.message
	jnz	@CALL

	mov	byte ptr [ebp+(offset JumpRet - OFDELTA)], 0

; ----------------------------------------------------------------------------
@CALL:
	; Восстанавливаем 5 байт
	db	0BFh		; mov edi,
ProcAddr	dd	0	;	   <dword>
	db	0C6h
	db	007h		; mov byte ptr [edi],
OneB	db	0		;		      <byte>
	db	0C7h
	db	047h
	db	001h		; mov dword ptr [edi+1],
FourB	dd	0		;			 <dword>

	; Вызываем настоящую процедуру
	call	edi
	push	eax
; ----------------------------------------------------------------------------

	db	0EBh				; jmp
JumpRet	db	(offset @BACK - offset @JMP)	;	<byte>
@JMP:

	; Выделяем память
	call	pGlobalAlloc, GMEM_FIXED, IMPLANT_SIZE
	mov	dword ptr [ebp+(offset ImplAddr - OFDELTA)], eax

	lea	eax, [ebp+(offset lf - OFDELTA)]
	call	pCreateFontIndirect, eax
	mov	dword ptr [ebp+(offset hfont - OFDELTA)], eax

	; ------------------------------------------------------
	; Копируем в выделенную память процедуры окна и таймеров
	push	esi
	lea	esi, [ebp+(offset IMPLANT_BEGIN - OFDELTA)]
	mov	edi, dword ptr [ebp+(offset ImplAddr - OFDELTA)]
	mov	ecx, IMPLANT_SIZE
	cld
	rep	movsb
	pop	esi
	; ------------------------------------------------------

	; Зададим новый адрес процедуры окна
	db	068h		; push
ImplAddr	dd	0	;	<dword>
	call	pSetWindowLong, dword ptr [ebp+(offset Handle - OFDELTA)], GWL_WNDPROC; , ImplAddr


	; Устанавливаем таймер
	mov	eax, dword ptr [ebp+(offset ImplAddr - OFDELTA)]
	add	eax, (offset TimerProc - offset IMPLANT_BEGIN)
SPEED	= 40
	call	pSetTimer, 0, 0, SPEED, eax

	jmp	@RET
; ----------------------------------------------------------------------------

@BACK:
	; Возвращаем переходник
	mov	edi, dword ptr [ebp+(offset ProcAddr - OFDELTA)]
	db	0C6h
	db	007h		; mov byte ptr [edi],
Jump	db	0E9h		;			jmp
	db	0C7h
	db	047h
	db	001h		; mov dword ptr [edi+1],
JumpVal	dd	0		;			     <dword>

; ----------------------------------------------------------------------------
@RET:
	pop	eax
	; Восстанавливаем edi, ebp и возвращаем управление программе
	db	0BFh		; mov edi,
ValEDI	dd	0		;          <dword> (значение edi)
	db	0BDh		; mov ebp,
ValEBP	dd	0		;          <dword> (значение ebp)
	db	068h		; push
RetPoint	dd	0	; 	<dword> (адрес точки возврата в программу)
	ret

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

DATAREG:
		; Адреса функций API
pGlobalAlloc:	db	0B8h
paGlobalAlloc	dd	0
		jmp	eax
pSetWindowLong:	db	0B8h
paSetWindowLong	dd	0
		jmp	eax
pCreateFontIndirect:	db	0B8h
paCreateFontIndirect	dd	0
			jmp	eax
pSetTimer:	db	0B8h
paSetTimer	dd	0
		jmp	eax

		; data
lf		LOGFONT		?

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

IMPLANT_BEGIN:

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

; Процедура окна

; WindowProc (
;    HWND hwnd,		// handle of window
;    UINT uMsg,		// message identifier
;    WPARAM wParam,	// first message parameter
;    LPARAM lParam 	// second message parameter
;   );

WinProc:
	push	ebp
	push	edi
	call	BETA
BETA:	pop	ebp

	; Проверяем номер сообщения
	cmp	dword ptr [esp+16], WM_TIMER
	jz	@KillTimer
	cmp	dword ptr [esp+16], WM_PAINT		; uMsg
	jnz	@CALLWP

; ----------------------------------------------------------------------------
	; Выполняем отрисовку
	lea	eax, [ebp+(offset ps - offset BETA)]
	call	pBeginPaint, dword ptr [ebp+(offset Handle - offset BETA)], eax
	mov	edi, eax		; hdc

	db	068h
hfont	dd	0
	call	pSelectObject, edi;, hfont
	call	pSetBkMode, edi, TRANSPARENT
	call	pSetTextColor, edi, 00000000h

	db	068h
StrLen	dd	0
	lea	edx, [ebp+(offset str - offset BETA)]
	push	edx
	db	068h
Y	dd	1
	call	pTextOut, edi, dword ptr [ebp+(offset X - offset BETA)];, Y;, edx; , StrLen

	lea	eax, [ebp+(offset ps - offset BETA)]
	call	pEndPaint, dword ptr [ebp+(offset Handle - offset BETA)], eax

	jmp	@RETW

; ----------------------------------------------------------------------------
@KillTimer:
	mov	eax, dword ptr [esp+20]
	mov	edx, dword ptr [esp+12]
	call	pKillTimer, edx, eax
	jmp	@RETW

; ----------------------------------------------------------------------------

@CALLWP:
	; Вызываем прежнюю процедуру окна
VAL	= 8
	push	dword ptr [esp+16+VAL]
	push	dword ptr [esp+16+VAL]
	push	dword ptr [esp+16+VAL]
	push	dword ptr [esp+16+VAL]
	db	0B8h
WPAddr	dd	0
	call	eax

	; Finita la comedia
@RETW:	pop	edi
	pop	ebp
	ret	16

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

TimerProc:
	push	ebp
	push	edi
	call	GAMMA
GAMMA:	pop	ebp

	db	0B8h		; mov eax, <dword>
X	dd	0
	dec	eax
	db	03Dh		; cmp eax, <dword>
MinX	dd	0
	jnl	@TP1
	db	0B8h		; mov eax, <dword>
Width	dd	0
@TP1:	mov	dword ptr [ebp+(offset X - offset GAMMA)], eax

	mov	edi, dword ptr [ebp+(offset Handle - offset GAMMA)]
	lea	edx, [ebp+(offset rect - offset GAMMA)]
	call	pInvalidateRect, edi, edx, TRUE
	call	pUpdateWindow, edi

	pop	edi
	pop	ebp
	ret	16

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

Handle		dd		0
rect		RECT		?
ps		PAINTSTRUCT	?
str		db		500	dup (0)

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

pMB:		db	0B8h
paMB		dd	0
		jmp	eax
pKillTimer:	db	0B8h
paKillTimer	dd	0
		jmp	eax
pInvalidateRect: db	0B8h
paInvalidateRect dd	0
		 jmp	eax
pUpdateWindow:	db	0B8h
paUpdateWindow	dd	0
		jmp	eax
pBeginPaint:	db	0B8h
paBeginPaint	dd	0
		jmp	eax
pEndPaint:	db	0B8h
paEndPaint	dd	0
		jmp	eax
pSetBkMode:	db	0B8h
paSetBkMode	dd	0
		jmp	eax
pSetTextColor:	db	0B8h
paSetTextColor	dd	0
		jmp	eax
pTextOut:	db	0B8h
paTextOut	dd	0
		jmp	eax
pSelectObject:	db	0B8h
paSelectObject	dd	0
		jmp	eax

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

IMPLANT_END:

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

HANDLER_END:

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

END	START