четверг, августа 10, 2006

Что можно сделать от скуки

Что-то последнее время напало на меня какое-то расслабленное и добродушное настроение -- сижу на форуме и мне даже не лень отвечать более чем 3 словами, интересно, к чему бы это? =) Вероятно, просто скучно...
Но не суть, в общем недавно наткнулся на не вполне тривиальный вопрос -- как перехватить момент закрытия программы для того, чтобы иметь возможность сохранить какие-либо данные. Честно-говоря, вариантов сделать процесс "неубиваемым" (читай отловить момент закрытия), не прибегая к Native'ным функциям и их перехвату, я не знал. Сразу мне вспомнился Антивирус Касперского, авторы которого заявляли, что их детище полностью защищено от посягательств на свою "жизнь" любых вредоносных программ. Однако же, методы нашлись, при желании можно спокойно получить хендл процесса, а дальше -- делай с бедным "касперычем" что хочешь. Т.е. вариант с перехватами отпал, во-1, в силу описанных причин, во-2, потому что, насколько я понял, человеку, которому понадобилось реализовать подобное, нафиг не упали никакие стелс-элементы. В общем, решил я пойти по вполне мирному пути.
Идеи с постоянным сохранением данных в файл и поиском окна приложения для его дальнейшего запуска, я посчитал несколько несолидными и, кроме того, нифига не практичными. Поэтому решил я копать в направлении, подсказанном мне MSDN'ом. А прочитал я об очередности событий, проходящих в момент остановки процесса. Но нифига, как оказалось, мне несветило, хоть потоки и завершаются в первую очередь, но отловить этот момент в основном потоке не удалось =(
Вторая идея была навеяна безысходностью -- попытаться отловить момент, когда ExitCode процесса перестанет быть STILL_ACTIVE, ясное дело, что нифига не вышло =)
В общем, помучавшись некоторое время, я все же вышел на верный курс. Как всегда оказалось, что все самые простые идеи приходит в голову самыми последними. Поскольку, как уже было сказано, никаких нестандартных технологий я решил не использовать, то пришлось пожертвовать еще одним приложением, которое, в принципе, можно объявить как сервис. Либо же, если боротся за максимальную надежность, то все же применить технологии кернел-хака. В голову приходят сразу две идеи:
  1. инжектировать какой-нибудь критический системный процесс, однако это чревато тем, что анти-руткиты могут попытаться завершить его и комп просто уйдет в ребут.
  2. инжектировать winlogon -- хрен знает почему, но его никто не трогает =)
В общем, это уже детали реализации, а сам процесс отлавливания закрытия приложения прост до банального -- нам нужно стать для него отладчиком =) В простейшем случае это будет выглядеть следующим образом



Code C++
PROCESS_INFORMATION pi;
STARTUPINFO si;
::ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
::CreateProcess("c:\\windows\\notepad.exe", 0, 0, 0, 0, DEBUG_PROCESS DEBUG_ONLY_THIS_PROCESS, 0, 0, &si, &pi);
DEBUG_EVENT de;
while(WaitForDebugEvent(&de, INFINITE))
{
   if(de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
   {
      ::MessageBox(0, "Closing", "Ahtung!", 0);
      break;
   }
   ::ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
}
::DebugActiveProcessStop(pi.dwProcessId);

После этого мы со спокойной душой можем делать дамп памяти процесса и сохранять все нужные данные =)
Естественно, что для того, чтобы применять этот код в "бовых" условиях, нужно позаботиться о том, чтобы в случае исключения внутри отлаживаемого процесса, наш "дебаггер" возвращал DBG_NOT_HANDLED. Кроме того, чтобы получить возможность отлаживать процесс, не созданный в режиме DEBUG_PROCESS, нужно получить в системе права отладчика, для этого можно использовать следующий подход:




Code C++
void IAmaDebugger(bool debug)
{
   HANDLE hCp;
   if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hCp))
   {
      TOKEN_PRIVILEGES tp;
      tp.PrivilegeCount = 1;
      ::LookupPrivilegeValue(0, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
      tp.Privileges[0].Attributes = debug ? SE_PRIVILEGE_ENABLED : 0;
      ::AdjustTokenPrivileges(hCp, 0, &tp, sizeof(tp), 0, 0);
      ::CloseHandle(hCp);
   }
}

В общем, я остался морально и интеллектуально удовлетворенным, почаще бы такие вопросы возникали =)
ЗЫЖ Бр-р-р-р, как же все-таки непросто мне осознать основы HTML'я -- кто бы мне сказал, как привести код в человеческий вид =(

3 комментария:

Dan Abramov комментирует...

Хм... XHTML рулит :))

Dan Abramov комментирует...

Кстати, прикольный способ решения проблемы, надо бы попробовать.

sllh комментирует...

Угу.. правда, к сожалению, отлаживать самого себя у меня не получилось, хотя я попробовал извращаться по-всякому =)