Developer Tales or everything about everything

28Май/123

Запись памяти процесса

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

Поиск процесса

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

Существует несколько способов сделать это. Мы рассмотрим два из них: поиск окна приложения и поиск в списке процессов.

Поиск окна приложения

Один из способов получения указателя на процесс системы - поиск окна приложения. В большинстве случаев при необходимости внедрения в процесс это требуется именно для оконных приложений, имеющих GUI. В таком случае процесс можно найти по названию заголовка его главного окна. Делается это очень просто:

Здесь выполняется функция FindWindow, которая, собственно, и ищет окно с заданным заголовком, а затем, найдя хэндл (англ. handler) окна, мы можем получить идентификатор процесса с помощью функции GetWindowThreadProcessId. Т.к. идентификаторы процессов в системе уникальны (не существует двух и более процессов с одинаковым идентификатором), то мы сможем использовать этот ID для того, чтобы открыть процесс с помощью функции OpenProcess.

Поиск процесса в списке процессов

Поиск процесса по заголовку окна не самое удобное и не всегда удачное решение. Например, программа может быть консольной, или название заголовка программы очень часто меняется. В таком случае мы можем найти процесс по его имени. Все, что нам нужно в данном случае - 4 API функции ОС Windows:

  1. CreateToolhelp32Snapshot, чтобы получить список всех процессов в системе;
  2. Process32First, чтобы получить указатель на первый процесс списка;
  3. Process32Next, чтобы перебрать все процессы в полученном списке;
  4. OpenProcess, чтобы открыть найденный процесс.

Собственно, не многим более сложный код, чем в предыдущем примере:

Здесь функция получает список процессов, затем получает ссылку на первый процесс списка, проходит по всем процессам до тех пор, пока не найдет нужный процесс по его имени. Отдельного разговора заслуживает структура PROCESSENTRY32. Это API-структура Windows, которая является описателем процесса. Используя ее можно получить много полезной информации:

  • cntThreads (count threads) - количество потоков (англ. thread) процесса;
  • th32ProcessID - ID процесса;
  • dwSize -  размер структуры;
  • pcPriClassBase - базовый приоритет процесса. Этот приоритет будут иметь все потоки, порождаемые процессом. Приоритет определяет, насколько "любвеобильно" ОС относится к процессу. Чем он выше, тем больше процессорного времени отдается процессу;
  • szExeFile - наименование исполняемого файла процесса. Если есть желаение вычислить полный адрес исполняемого файла, то можно воспользоваться функцией Module32First, которая выяснит всю информацию о модуле;
  • th32DefaultHeapID -  не используется (deprecated);
  • dwFlags -  не используется (deprecated);
  • th32ParentProcessID -  не используется (deprecated);
  • th32ModuleID -  не используется (deprecated);
  • cntUsage - не используется (deprecated).

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

Запись данных в процесс

Остается коснуться темы о записи данных в процесс. На самом деле, это нелегкая задача, если необходимо автоматически патчить (англ. patch) память процесса. Проблема заключается не в самом алгоритме записи в процесс, а в вычислении адреса, куда необходимо осуществлять запись. Проще дело обстоит с DLL - в большинстве случаев загружаемые DLL всегда имеют один и тот же адрес и смещение, что позволяет задать адреса константами. Однако, запись данных в процесс не всегда так необходима - порой гораздо проще перехватывать функции приложения.

Для того, чтобы записать что-либо в процесс, нужно его открыть, установить права доступа и записать данные.

Чтобы открыть процесс, нужно использовать функцию OpenProcess. Делается это так:

PROCESS_ALL_ACCESS означает, что приложение открывает процесс с полным доступом (на чтение, запись и пр.). Можно открыть процесс только на чтение, указав, например, PROCESS_VM_READ. Но в этом случае, память процесса нельзя будет записать.

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

Чтобы рассказать о своих честных намерениях, необходима функция VirtualProtect. Эта функция устанаваливает права на определенную область памяти в процессе. После вызова этой функции можно использовать WriteProcessMemory для записи памяти процесса. Выглядит это вот так:

VirtualProtect принимает 4 параметра:

  • lpAddress - адрес, для которого необходимо изменить доступ;
  • dwSize - размер данных, которые будут записаны в память. Собственно, это размер области памяти, для которой необходимо изменить права доступа;
  • flNewProtect - данные, которые необходимо поместить в заданный участок памяти;
  • lpflOldProtect - указатель на структуру, которая содержит информацию о предыдущем состоянии прав доступа к области памяти.

С первыми двумя параметрами все предельно ясно - указали адрес для записи и для скольких байт в памяти нужно изменить права. Третий параметр - флаг уровня доступа, который нужно установить для области памяти. В примере указан флаг PAGE_EXECUTE_READ_WRITE, что означает, что заданный участок памяти можно "читать" и "писать".

Четвертый параметр возвращает старый флаг прав доступа. Зачем он нужен? Далеко не самая хорошая идея открыть возможность записи в участок памяти и так это бросить - это может в последствии породить сбои в приложении, если абстрактное нечто попытается случайно что-то туда записать. Также, возможен случай, когда приложение закроет доступ на запись и забудет вернуть эти права, а другое приложения заранее зная, что у участка памяти есть права на запись, попытается туда что-либо записать. Поэтому, после записи в память, необходимо вернуть старые права для этого участка памяти, что и делается в примере: после записи в память процесса функция VirtualProtect вызывается повторно и ей передаются старые параметры доступа.

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

  • hProcess - хэндлер процесса;
  • lpBaseAddress - адресное смещение;
  • lpBuffer - Указатель на значение, которое нужно записать (точнее, указатель на начало структуры данных для записи);
  • nSize - Размер данных для записи;
  • lpNumberOfBytesWritten - необязательная переменная, в которую будет записано количество записанных байт. Этот параметр можно использовать для информативности и анализа ошибок.

Собственно, это все, что нужно знать, чтобы изменить память процесса. Порой сложное лишь кажется сложным. Сейчас, использовав порядка 10 API-функций операционной системы, я записал память процесса - это ведь не сложно, нужно только уметь гуглить 

В дальнейшем поговорим о перехвате выполняемых приложением функций и о технологии DLL Injections и расширении функциональности приложений.

Исходный код вы можете найти в разделе Ссылки.

Ссылки

Просмотров: 6142
Комментарии (3) Пинги (0)
  1. что делает эта строка?
    как её можно заменить?
    if (name.compare(processEntry.szExeFile) == 0)

    • // szExeFile - название исполняемого файла процесса, по нему-то мы и будем искать нужные процесс.
      name - имя процесса, которое передается функции поиска.
      szExeFile - элемент структуры PROCESSENTRY32, содержащий название исполняемого файла процесса.

      Таким образом, обходится дерево текущих процессов в системе, каждый процесс представляется структурой PROCESSENTRY32, а имя исполняемого файла содержится в PROCESSENTRY32.szExeFile.

      Ну, а функция std::string.compare(char*) является частью библиотеки STL и выполняет посимвольное стравнение строк. Возвращает 0, если строки полностью совпадают.

      Не совсем понимаю, какую часть этого кода Вы хотите заменить. Можно обойтись без C++/STL и использовать функцию C strcmp для сравнения массивов символов. Но это понадобится, разве что, в том случае, если нужна адаптация кода для C.

  2. Замечательная статья! Спасибо автору. Очень доходчиво всё объясняет.
    Правда так пока и не ясно, как найти интересующее нас значение.


Leave a comment


+ четыре = 10

http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_bye.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_good.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_negative.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_scratch.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_wacko.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_yahoo.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_cool.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_heart.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_rose.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_smile.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_whistle3.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_yes.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_cry.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_mail.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_sad.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_unsure.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_wink.gif 
 

Trackbacks are disabled.