Практика
Для примера сделаем программулину, которая автоматом убирает «Ярлык для» при создании ярлыка (да я знаю, что надоел со своими ярлыками :)). Чтобы попасть в адресное пространство explorer’a используем мой извращенный 🙂 метод инжектирования в процесс без использования библиотек. Его описание есть в ссылках. Тем не менее, код внедрения с того момента стал более компактным, логичным и грамотным. Теперь я сам делаю релокэйшены и к data’e можно обращаться напрямую. Также я правлю попорченную таблицу импорта => invoke работает, господа 😉 Код всего этого безобразия приводить здесь не буду (много места), но он будет в примерах к статье. Все остальное
прокомментировано и не должно вызывать проблем, тем не менее, если что-то непонятно – всегда обращайтесь ко мне на мыло.
; ## ANTI LNKS ###
; Убирает "Ярлык для" при создании ярлыка explorer'ом. Пример к статье "Перехват вызовов СОМ методов"
; Компилировать: вот фаил
MakeIt.bat:
;--------------------------------------- Start of MakeIt.bat ----------------------------------
comment ~
@echo off
echo Compiling resources
echo ###################
brcc32.exe res\1.rc
if exist 1.obj del 1.obj
if exist 1.exe del 1.exe
echo Compiling obj file
echo ###################
c:\masm32\bin\ml /c /coff /nologo 1.asm
echo Compliling exe file
echo ###################
c:\masm32\bin\Link /SUBSYSTEM:WINDOWS /MERGE:.rdata=.text /SECTION:.text,ERW /FIXED:NO 1.obj res\1.res
dir 1.*
pause
~
;---------------------------------------- End of MakeIt.bat -----------------------------------
; ### Directives ###
.386
.model flat, stdcall
option casemap :none
; ### Includes and Libs ###
include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\ole32.inc
include CRT_API.asm ; Внедрение в чужой процесс
include comhooks.asm ; Перехват сом методов
include shlobj.inc ; Интерфейсы IShellLink, IPersistFile
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\ole32.lib
; ### PROTOS ###
ThreadProc proto lpParameter: DWORD
; ### CONSTS ###
IDD_DIALOG1 equ 101
MM_CLOSE equ WM_QUIT
MM_SET equ WM_USER+2
MM_LOAD equ WM_USER+3
; ### DATA ###
.data
szDisabled db "All lnks must be without 'Ярлык для' string ;)",0
szEnabled db "All lnks must be with 'Ярлык для' string",0
szCapt db "Antishit :)",0
szProgman db "Progman",0
hProgman dd 0
ThreadId dd 0
msg MSG <>
oldFunc dd 0
szBuf db 255 dup(0)
szBuf1 db 255 dup(0)
wcFin dw 255 dup(0)
szTemp db "Ярлык для ",0
size_szTemp equ $-szTemp
CLSID_ShellLink GUID <0021401H, 0000H, 0000H, \
<0C0H, 00H, 00H, 00H, 00H, 00H, 00H, 046H>>
IID_IShellLink GUID <00214EEH, 0000H, 0000H, \
<0C0H, 00H, 00H, 00H, 00H, 00H, 00H, 046H>>
IID_IPersistFile GUID <000010BH, 0000H, 0000H, \
<0C0H, 00H, 00H, 00H, 00H, 00H, 00H, 046H>>
ppf dd 0
psl dd 0
; ### CODE ###
.code
; Функция, которая будет выполнятся вместо IShellLink.IPersistFile.Save
NIPersistFileSave proc a: DWORD, b: DWORD, cd: DWORD
; Переводим параметр в ANSII
invoke WideCharToMultiByte, CP_ACP, 0, b, -1, offset szBuf, 255, 0,0
; Ищем есть ли слова "Ярлык для" в создаваемом
файле (если нет - в еах 0)
invoke InString, 1, offset szBuf, offset szTemp
; Убираем Ярлык для, ...
lea ebx, szBuf
add ebx, eax
add ebx, size_szTemp-2
; копируя все, что до этих слов в буфер и...
invoke lstrcpyn, offset szBuf1, offset szBuf, eax
; соединяя с тем, что после них.
invoke lstrcat, offset szBuf1, ebx
; Переводим результат в UNICODE
invoke MultiByteToWideChar, CP_ACP, 0, offset szBuf1, -1, offset wcFin, 255
; Снимаем на время хук
invoke TableCOMHook, ppf, IPersistFile.Save, oldFunc, 0
; и вызываем IShellLink.IPersistFile.Save с нужными нам параметрами 😉
coinvoke a, IPersistFile, Save, offset wcFin, cd
push eax
; Сново ставим хук.
invoke TableCOMHook, ppf, IPersistFile.Save, offset NIPersistFileSave, offset oldFunc
pop eax
ret
NIPersistFileSave endp
DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_CLOSE
; Уходя не забудем удалиться и из explorer'a
invoke PostThreadMessage, ThreadId, MM_CLOSE,0,0
invoke ExitProcess, 0
.elseif uMsg==WM_COMMAND
.if lParam!=0
mov edx,wParam
mov ax, dx
shr edx,16
.if (dx==BN_CLICKED)&&(ax==IDOK)
; Если юзер сказал не надо "Ярлык для" - так оно и будет
invoke PostThreadMessage, ThreadId, MM_SET,0,0
.elseif (dx==BN_CLICKED)&&(ax==IDCANCEL)
; Если че - отменяем все 😉
invoke PostThreadMessage, ThreadId, MM_LOAD,0,0
.endif
.endif
.else
mov eax, FALSE
ret
.endif
mov eax, TRUE
ret
DlgProc endp
; Эта процедура будет выполняться в адресном пространстве explorer'a
ThreadProc proc lpParameter: DWORD
; Инициализируем СОМ
invoke CoInitialize, NULL
; Создаем объект от интерфейса IShellLink (или получаем на него указатель, если он уже создан)
invoke CoCreateInstance, offset CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, offset IID_IShellLink, offset psl
; Запрашиваем объект по образу и подобию IPersistFile
coinvoke psl, IShellLink, QueryInterface, offset IID_IPersistFile, offset ppf
; Цикл обработки сообщений, посланных потоку
___loooop:
invoke GetMessage, offset msg,0,0,0
; Если пришло MM_CLOSE=WM_QUIT, уходим
test eax, eax
jz bye
invoke TranslateMessage, offset msg
invoke DispatchMessage, offset msg
.if (msg.message==MM_SET)
; Если основная прога сказала нам установить хук - так и будет 🙂
invoke TableCOMHook, ppf, IPersistFile.Save, offset NIPersistFileSave, offset oldFunc
invoke MessageBox,0,offset szDisabled,offset szCapt,MB_OK
.elseif (msg.message==MM_LOAD)
; Если же сказала восстановить все, чтобы как было - мы восстановим
invoke TableCOMHook, ppf, IPersistFile.Save, oldFunc, 0
invoke MessageBox,0,offset szEnabled,offset szCapt,MB_OK
.endif
jmp ___loooop
bye:
; Уходя всегда восстанавливаем 😉
invoke TableCOMHook, ppf, IPersistFile.Save, oldFunc, 0
; Закрываем за собой дверь
coinvoke ppf, IPersistFile, Release
coinvoke psl, IShellLink, Release
invoke CoUninitialize
; Завершаем поток, выгружая так же себя из адресного пространства
call GetCurrModBase
invoke FreeLibraryAndExitThread, eax, 0
ThreadProc endp
start:
; Находим окно Progman, которое принадлежит explorer'у
invoke FindWindow, offset szProgman,0
mov hProgman, eax
; Внедряемся в процесс
invoke ECreateRemoteThreadNt, eax, 0,offset ThreadProc, 0,0
.if eax>4
; Узнаем ThreadID вновь
созданного потока в explorer'e
assume eax: PTR CRT_RES
mov eax, [eax].ThreadID
mov ThreadId, eax
; Показываем пользователю диалог
invoke GetModuleHandle, 0
invoke DialogBoxParam, eax, IDD_DIALOG1,NULL, offset DlgProc, 0
.endif
; Уходим
invoke ExitProcess, 0
end start
Сслыки:
1) Сайт Ernest’а Murphy http://ourworld.compuserve.com/homepages/ernies_world/a.htm
2) Эрик Хармон «Разработка COM-приложений в среде Delphi»
3) Нетривиальные возможности хуков (http://www.xakep.ru/post/20126/default.htm
и http://www.xakep.ru/post/20141/default.htm).