суббота, 4 марта 2023 г.

 

Сканер установленных перехватчиков в памяти процесса

 


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

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

Где-то полгода назад у нас появилась очередная идея, есть очень много разноплановых ошибок которые достаточно проблематично покрыть тестами, но по результатам накопленного методом проб и ошибок опыта было выяснено что большая часть из них происходит по причине вмешательства стороннего софта в тело нашего процесса. Это могут быть как антивирусы, так и всякие DLP, а то и вовсе зловреды, которые лезут к нам в процесс, перехватывают некоторые критичные для выполнения API на себя и в обработчиках перехваченных функций ломают полностью логику исполнения кода.

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

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

Правда при общей простоте идеи реализация получилось достаточно трудоемкая.
Самая большая проблема при этом была в том что утилита 32 битная, а софт, работающий у пользователя может быть как 32 бита, так и 64 (второе более вероятно), поэтому для работы с 64 битным процессом пришлось писать соответствующую обвязку.

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

Короче в итоге получилось такое, как бы это назвать... антивирусный сканер на минималках :)

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

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

вторник, 1 марта 2016 г.

Раннее исполнение кода в Delphi приложениях

Как вы думаете, сколько кода выполняется до того момента, как приложение запустится, или отладчик передаст управление в ваши руки?

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

Нет, не вирусы, зачем? Вполне легальные продукты, контролирующие все и вся.

Думаете - вру и считаете что при нажатии кнопки F7 (Trace Into) вы полностью контролируете процесс отладки?
Тогда я вас разочарую, есть множество способов выполнить код, до того момента, как вы приступили к его дебагу.

О нескольких я попробую рассказать.

среда, 29 апреля 2015 г.

Анализ задачи №18 от Александра Алексеева (ака GunSmoker)

Кажется я первый раз попал в тупик.
Не то, чтобы я сильно умный, но и задачка — не "Балтика 9".

Первая часть задачи выглядела вот так:

Что не так с этим кодом?

procedure TForm1.Button1Click(Sender: TObject);
var
  Wnd: HWND;
 
  function EnumWindowsProc(const AWnd: HWND; const AParam: LPARAM): BOOL; stdcall;
  begin
    if AWnd = Wnd then
      Caption := 'OK';
    Result := True;
  end;
 
begin
  Wnd := Handle;
  EnumWindows(@EnumWindowsProc, 0);
end;

Приступим.

пятница, 10 апреля 2015 г.

Чем меня порадовала ХЕ8

В последнее время я все чаще задумываюсь о смысле апдейта текущей версии Delphi на более новую. Есть ли в этом необходимость?
С учетом, что я разрабатываю 32 битные приложения только под Win - вся эта петрушка в виде возможности разработки под Андроид или iOS прямо на Delphi, ну... скажем так — не сильно востребована.
А вот что более востребовано — так это стабильная работа среды, отсутствие ошибок при работе с дженериками/лямдами и всем тем "синтаксическим сахаром", который внедряется уже какой год.
Вы будете смеяться, но я однажды вообще от инлайнов отказался по многим причинам, в частности одна из них была в том, что кодогенератор выдавал абсолютно невалидный асм код в определенных ситуациях, абсолютно переиначивая всю логику работы inline функции (банально не тот результат возвращала). Кажется это было на 2010 или ХЕ первой.

Впрочем посмотрим что мы имеем сейчас.

вторник, 31 марта 2015 г.

Работаем с Compound File

С составными файлами я работаю давно, больше 15 лет. За все время работы у меня накопилось достаточно информации о плюсах и минусах составных файлов.
С одной стороны они являются действительно очень удобным хранилищем информации, позволяющим менять данные на лету, с другой стороны это удобство частично нивелируется скоростью доступа к данным.
Вообще для чего обычно используют составные файлы?
Для всего, что нужно хранить в некоем контейнере.
К примеру, файлы старых версий Microsoft Office от 97 до 2003 включительно (состоящие на самом деле из нескольких десятков файлов), хранились как раз в составном файле. Сейчас тоже хранятся, только в качестве контейнера используется ZIP.

Инсталляционные пакеты MSI тоже являются составными файлами, и даже файл кэша эскизов папок Thumbs.db использует этот формат.

Правда для того же Word есть целый комплекс утилит (Recovery for Word, Word Recovery Toolbox, Munsoft Easy Word Recovery) восстанавливающих, ну или по крайней мере пытающихся восстановить, поврежденные документы. Выводы можете сделать сами.
Хотя, при должной работе с составными файлами проблему их повреждения можно решить (и я покажу как).

Ну и, конечно же, несомненным плюсом этого формата является то, что внутри хранилища эмулируется полноценная файловая система со своими файлами и папками.

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

воскресенье, 22 февраля 2015 г.

Работаем с "заданиями" (Job)

Буквально на неделе на форуме появились два интересных вопроса, ответ на которые был очевиден, но... Программист, как вы знаете, существо с очень пытливым мозгом, он любит различные эксперименты, не смотря на то, что ответ на задачу мог быть уже озвучен :)

Впрочем, давайте посмотрим на первый вопрос:
Запускаю в отдельном потоке некий процесс (не мой, переделывать его не имею возможности), который необходимо завершить вместе с завершением основной (моей) программы.Если моя программа завершается штатно - то ничего сложного нет. Но если не штатно (пользователь убил через диспетчер задач) - так как быть тут?
В голову пока приходит только CreateRemoteThread+LoadLibrary+моя dll, которая будет следить за основным процессом.Подскажите более изящные решения.
Попробуем подсказать...

пятница, 2 января 2015 г.

Ответ на предновогоднюю задачку

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

Задача действительно интересная.
На данный момент мне прислали два её решения, причем у каждого оказался свой индивидуальный подход.

Примерное время на решение, около 3-4 часов (один вечер).
Именно столько было затрачено каждым из решивших задачу программистов, включая меня.

Ну и ответ на введенное максимальное пороговое число (10 в 12 степени) будет: $259814D6C9AAF914221E (это вам для самопроверки).

Пора перейти к самой сути.