Сканер виртуальной памяти «внутри» процесса — Архив WASM.RU
1. Введение
Целью статьи является рассмотрение принципов создания сканера виртуальной памяти, находящегося «внутри» исследуемого процесса.
Задача осталась прежней: Найти адрес переменной в адресном пространстве целевого процесса.
Требования: Необходимо внедрить процедуры сканирования в целевой процесс.
2. Идея:
Создаваемое приложение должно состоять из двух модулей: загрузчик; и модуль поиска, внедряемый в процесс. Естественно желание избежать передачи данных между процессами, следовательно, модуль поиска должен быть автономным: то есть предоставлять кроме функций поиска поддержку пользовательского интерфейса. Нетрудно заметить, что при внедрении модуля сканирования (представленном в виде dll) «внутрь» процесса естественно возникает проблема обеспечения поточной независимости исполнимого кода. При исполнении следующей копии приложения библиотека (уже загружена в адресное пространство целевого процесса) будет использовать те же данные что и первая загруженная копия. Можно попытаться создавать новый блок данных и, например, найти и вызвать функцию поиска с передачей в параметрах указателя на этот блок (то есть обеспечить объектно-ориентированный принцип реализации функций) но, на практике, это будет затруднительно. Рассмотренную проблему можно устранить загружая файловые копии модуля сканирования (предварительно сохранив файл dll с измененным именем): «различные» библиотеки загрузятся в различные участки памяти. Если выполняется одна основная нить в одной dll, то можно легко обеспечить одновременное использование нескольких копий приложения. Учитывая малый размер библиотеки можно предполагать достаточную эффективность приведенного подхода.
3. Алгоритмы
Приложение состоит из двух модулей: загрузчика и библиотеки. Загрузчик сначала выполняет переименование и копирование файла библиотеки, затем загружает ее в адресное пространство процесса и закрывается. При инициализации, библиотека выводит окно интерфейса пользователя и обеспечивает его функциональность (поиск внутри исследуемого процесса).
О копировании файла библиотеки можно лишь отметить: копирование происходит с перезаписью неиспользуемой библиотеки в Temp папку пользователя, к имени файла добавляется номер от 00 до 99.
Загрузка dll в адресное пространство процесса [1]:
- Открыть процесс с возможностью создания нити и обеспечения операций работы с памятью;
- Выделить внутри процесса блок памяти и записать в него имя файла библиотеки;
- Определить адрес функции LoadLibraryA внутри процесса;
- Создать нить в процессе, исполняющую 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 ;удалить из стека адрес handler4. Недостатки
Созданная программа имеет, по крайней мере, два серьезных недостатка: медленная процедура первичного сканирования; проверка зарезервированных с передачей памяти при обращении блоков виртуальной памяти целиком.
5. Достаточные источники:
© FrostFix
Сканер виртуальной памяти «внутри» процесса
Дата публикации 1 июл 2005