Сканер виртуальной памяти «внутри» процесса — Архив WASM.RU

Все статьи

Сканер виртуальной памяти «внутри» процесса — Архив WASM.RU

Исходный код

  1. Введение
  2. Идея
  3. Алгоритмы
  4. Недостатки
  5. Источники

1. Введение

Целью статьи является рассмотрение принципов создания сканера виртуальной памяти, находящегося «внутри» исследуемого процесса.

Задача осталась прежней: Найти адрес переменной в адресном пространстве целевого процесса.

Требования: Необходимо внедрить процедуры сканирования в целевой процесс.

2. Идея:

Создаваемое приложение должно состоять из двух модулей: загрузчик; и модуль поиска, внедряемый в процесс. Естественно желание избежать передачи данных между процессами, следовательно, модуль поиска должен быть автономным: то есть предоставлять кроме функций поиска поддержку пользовательского интерфейса. Нетрудно заметить, что при внедрении модуля сканирования (представленном в виде dll) «внутрь» процесса естественно возникает проблема обеспечения поточной независимости исполнимого кода. При исполнении следующей копии приложения библиотека (уже загружена в адресное пространство целевого процесса) будет использовать те же данные что и первая загруженная копия. Можно попытаться создавать новый блок данных и, например, найти и вызвать функцию поиска с передачей в параметрах указателя на этот блок (то есть обеспечить объектно-ориентированный принцип реализации функций) но, на практике, это будет затруднительно. Рассмотренную проблему можно устранить загружая файловые копии модуля сканирования (предварительно сохранив файл dll с измененным именем): «различные» библиотеки загрузятся в различные участки памяти. Если выполняется одна основная нить в одной dll, то можно легко обеспечить одновременное использование нескольких копий приложения. Учитывая малый размер библиотеки можно предполагать достаточную эффективность приведенного подхода.

3. Алгоритмы

Приложение состоит из двух модулей: загрузчика и библиотеки. Загрузчик сначала выполняет переименование и копирование файла библиотеки, затем загружает ее в адресное пространство процесса и закрывается. При инициализации, библиотека выводит окно интерфейса пользователя и обеспечивает его функциональность (поиск внутри исследуемого процесса).

О копировании файла библиотеки можно лишь отметить: копирование происходит с перезаписью неиспользуемой библиотеки в Temp папку пользователя, к имени файла добавляется номер от 00 до 99.

Загрузка dll в адресное пространство процесса [1]:

  1. Открыть процесс с возможностью создания нити и обеспечения операций работы с памятью;
  2. Выделить внутри процесса блок памяти и записать в него имя файла библиотеки;
  3. Определить адрес функции LoadLibraryA внутри процесса;
  4. Создать нить в процессе, исполняющую LoadLibraryA с параметром равным адресу блока из п.2.

;ebx указывает на стековый буфер

;получение содержания заголовка окна целевого процесса, выбранного пользователем
invoke  SendMessage,[combobox_process],WM_GETTEXT,STRSIZE,ebx

;получение hWnd окна целевого процесса
invoke  FindWindow,0,ebx

;получение идентификатора целевого процесса
push    eax
invoke  GetWindowThreadProcessId,eax,esp
pop     eax

;открыть процесс для работы с его виртуальной памятью и создания нити
invoke  OpenProcess,PROCESS_CREATE_THREAD+PROCESS_VM_OPERATION+PROCESS_VM_WRITE,0,eax

;получить нумерованное название скопированной в Temp библиотеки (в буфер ebx)
call    CopyDll

;создать блок в адресном пространстве целевого процесса
invoke  VirtualAllocEx,[hProcess],0,[fulldllnamesz],MEM_COMMIT,PAGE_READWRITE

;записать в блок полное имя файла библиотеки (eax = адресу блока внутри процесса)
invoke  WriteProcessMemory,[hProcess],eax,ebx,[fulldllnamesz],0

;получить адрес LoadLibraryA в исследуемом процессе (по [1], он
; такой же как и в исполняемом процессе)
STR     eax,'Kernel32.dll'
invoke  GetModuleHandle,eax

STR     ecx,'LoadLibraryA'
invoke  GetProcAddress,eax,ecx

;запустить нить в «удаленном» процессе, которая вызовет LoadLibraryA с адресом 
; имени файла библиотеки в параметре
invoke  CreateRemoteThread,[hProcess],0,0,eax,[dllnameaddr],0,0

Примечание: здесь и далее, для краткости, отсутствуют необходимые проверки результатов исполнения функций API.

Запуск основной нити приложения при инициализации dll и создание диалогового окна:

;процедура инициализации dll (точка входа)
proc DllEntryPoint hinstDLL,fdwReason,lpvReserved
	;только единожды создать Thread приложения
	cmp     [fdwReason],DLL_PROCESS_ATTACH
	jne      notattach

	push    eax
	invoke  CreateThread,0,0,CheatProc,[hinstDLL],0,esp
	pop     eax

notattach:
	mov     eax,1
	ret
endp

;основная процедура приложения
proc CheatProc hinstDLL

	;резервировать и передать при обращении память для хранения адресов
	invoke  VirtualAlloc,0,BSIZE,MEM_RESERVE+MEM_COMMIT,PAGE_READWRITE
	mov     [addrbuffer],eax

	;инициализировать библиотеку CommonControls
	push    8
	push    ICC_PROGRESS_CLASS
	invoke  InitCommonControlsEx,esp
	add     esp,8

	;создать и отобразить диалоговое окно приложения (блокирующая функция)
	invoke  DialogBoxParam,[hinstDLL],ID_WINDOW,0,DialogFunc,0

	;освободить блок адресов
	invoke  VirtualFree,[addrbuffer],0,MEM_RELEASE

	;выгрузить dll и выйти из Thread, подробности в [2]
	invoke  FreeLibraryAndExitThread,[hinstDLL],0
endp

Примечание: автор [1] предпочел более изощренный способ выгрузки библиотеки, чем FreeLibraryAndExitThread [2].

Организация сканирования страниц виртуальной памяти с доступом на чтение и запись.
Алгоритм прост: Для каждого блока виртуальной памяти у которого есть доступ на чтение и запись запустить процедуру сканирования.

"Сердце" процедуры сканирования выглядит так:

;вход:
;	ecx = количеству dword в блоке
;	esi содержит адрес блока
;	edi - таблица адресов
;	MM1 - 0xVVVV, где V - младший байт искомого числа

;Поиск адресов
lfind:
	movd    MM0,[esi] ;получить dword из сканируемого блока
	pcmpeqb MM0,MM1   ;сравнить и заполнить в MM0 равные байты 0xFF
	movd    ebx,MM0   ;получить результат в ebx

	test    ebx,ebx   ;если там есть единицы, то сохранить
	jnz     found

continue:
	add     esi,4     ;esi = адресу следующего dword
	dec     ecx       ;повторить для всех dword в блоке
	jnz     lfind
	jmp     getblock  ;перейти к следующему блоку

;Сохранение адресов найденных байт
found:
	mov     eax,esi   ;eax равен адресу младшего байта
	mov     edx,4     ;edx количество байт = счетчик цикла
	jmp     check     ;переход на проверку байта

nextb:
	shr     ebx,8     ;получить в bl следующий байт
	inc     eax       ;получить в eax его адрес

check:
	test    bl,bl     ;если байт = 0
	jz      notb      ;то не сохранять адрес

	stosd             ;сохранить адрес
	cmp     edi,ebp   ;проверка выхода за пределы блока адресов
	jae     threadend

notb:
	dec     edx       ;повторить 3 раза
	jnz     nextb
	jmp     continue  ;продолжить поиск

Определение действительных адресов переменных (продолжение поиска).
Получить адрес из списка, прочитать память процесса, сравнить прочитанное значение с заданым, если != то удалить адрес.

;вход:
;	esi=edi содержат адрес блока адресов
;	ebp - количество адресов + 1

	;Установить SEH - состояние памяти могло измениться
	push    handler       ;адрес процедуры обработки исключения
	push    dword [fs:0]  ;указатель на следующую структуру SEH
	mov     [fs:0],esp    ;установить SEH
	mov     [espsv],esp   ;сохранить esp для восстановления

getaddr:
	dec     ebp           ;цикл по всем адресам
	jz      secondfindend

	lodsd                 ;получить в eax адрес

	;сохранить значения esi, edi, ebp на случай исключения
	push    esi
	push    edi
	push    ebp

	mov     dl,[eax]      ;генератор исключений
	add     esp,0Ch       ;удалить из стека сохраненные esi, edi, ebp

	mov     ecx,[value]   ;есх = искомое значение
	cmp     dl,cl         ;dl!=cl то перейти к следующей переменной
	jne     getaddr

	stosd                 ;сохранить адрес
	jmp     getaddr       ;перейти к следующей переменной

handler:
	mov     esp,[espsv]   ;восстановить esp
	mov     [fs:0],esp    ;восстановить SEH
	mov     esi,[esp-04h] ;восстановить esi
	mov     edi,[esp-08h] ;восстановить edi
	mov     ebp,[esp-0Ch] ;восстановить ebp
	jmp     getaddr       ;проверить следующий адрес

secondfindend:
	pop     dword [fs:0]  ;установить предыдущий обработчик исключений
	add     esp,4         ;удалить из стека адрес handler

4. Недостатки

Созданная программа имеет, по крайней мере, два серьезных недостатка: медленная процедура первичного сканирования; проверка зарезервированных с передачей памяти при обращении блоков виртуальной памяти целиком.

5. Достаточные источники:

  1. Рихтер Дж. Windows для профессионалов: создание эффективных Win32-приложений с учетом специфики 64-разрядной версии Windows/Пер. с англ. - 4-е изд. - СПб: Питер; М.: Издадельско-торговый дом "Русская Редакция", 2001.
  2. April 2003 Release of the MSDN Library.

2002-2013 (c) wasm.ru