Время вспять…

в 19:57, , рубрики: windbg, windows, x64, метки: ,

Время вспять…Вот и отгремело первое апреля. Кто-то в этот день нюхал свои девайсы в новом сервисе Google Нос, кто-то играл в «Поле чудес», а кто-то, позабыв о роковой дате, просто угрюмо отряхивал спину от мела…

Я же, воодушевлённый статьёй про скрытые возможности кастомизации процесса explorer.exe, тоже решил сделать что-нибудь забавное.
Пусть сегодня моя секундная стрелка часов в Windows идёт в обратную сторону! Не самый, конечно, полезный в хозяйстве мод, но в академических и рекреационных целях вполне сгодится :)

Часовую и минутную стрелки я оставил в правильном направлении. Иногда всё же приходится полгядывать на часы в трее — пусть они показывают время с точностью хотя бы до минуты...

Подготовка

Последовательность действий представляется примерно следующей:

  1. Поисследовать explorer.exe и понять каким образом он считает время
  2. Как-то что-то поменять в логике, чтобы часы шли обратно

Немного поменять логику какого-нибудь 32-битного usermode-процесса не представляет сложностей: давно есть полюбившийся многим отладчик OllyDbg, туториалов по которому предостаточно даже на русском языке. Поэтому, для придания изюминки было решено реализовывать задачу на моей 64-битной Windows 7 — давно хотел узнать каково́ там живётся процессам с удвоенной разрядностью…

В 64-битном мире на уровне дизассемблера пока, к сожалению, живется не очень сладко: ни тебе комфортного Olly для отладки, ни тебе удобнейшего Detours, за который просят выложить ни много ни мало $9,999.95 (ну, спасибо, что не $10k — маркетологи знают как сделать цену привлекательнее). Даже старые 32-битные инжекторы нельзя использовать для внедрения 64-битных dll. Видимо, моему поисковику по умолчанию придётся неслабо поработать…

Да простят меня любители статического анализа, с IDA Pro у меня отношения не сложились, так что выбора у меня почти не осталось — старичок WinDbg дождался своего часа.

В вопросах работы с WinDbg ценнейшим источником информации является ресурс windbg.info

Исследуем explorer.exe

Подключаемся к процессу и ищем что-нибудь связанное со словом "clock":

Время вспять…

Присмотревшись получше к выданному списку функций, можно заметить неприметную функцию explorer!CClockCtl::_RecalcCurTime. Неужели первый выстрел сразу в цель? Посмотрим что у неё внутри:

Время вспять…

Всё верно, сразу за прологом функции видим вызов GetLocalTime, которая, как известно, возвращает местное время и дату. Если мы сможем влиять на возвращаемый результат этой функции, то сможем также и изменять направление секундной стрелки — осталось только поставить хук на эту функцию.

Чтобы реализовать задуманное, нам нужно как-то попасть в адресное пространство процесса explorer.exe. И в этом как нельзя лучше нам поможет утилита командной строки CLI DLL-Injector. Она мало того что поддерживает 32 и 64 бита, так еще и умеет внедрять dll двумя способами: через инжект LoadLibrary, и с помощью прямой записи кода через WriteProcessMemory с последующим переносом reloc'ов.

Непосредственно для установки хуков внутри процесса explorer.exe воспользуемся простой и надёжной библиотекой MinHook.

Стоит обратить внимание, что за кажущейся простотой библиотеки, внутри скрывается очень продуманная логика, которая работает даже в достаточно сложных случаях. Так, библиотека Powerful x86/x64 Mini Hook-Engine, которую я пытался использовать поначалу, приводила к Access Violention всвязи с тем, что первая же инструкция функции GetLocalTime — это относительный переход JMP. В этом случае задача усложняется необходимостью перерасчёта смещения.

Реализация

С инструментами определились. Теперь осталось написать dll, которая при DLL_PROCESS_ATTACH будет ставить хук на функцию GetLocalTime:

#include <windows.h>
#include "MinHook.h"

// Статически линкуемся с libMinHook.dll
#pragma comment(lib, "libMinHook.x64.lib")

// Указатель на оригинальный GetLocalTime
static void (WINAPI *GetLocalTime_)(LPSYSTEMTIME lpSystemTime);

void WINAPI MyGetLocalTime(LPSYSTEMTIME lpSystemTime)
{
  // Вызываем оригинальный GetLocalTime
  GetLocalTime_(lpSystemTime);

  // Инвертируем количество секунд: 59 -> 0, 0 -> 59
  lpSystemTime->wSecond = 59 - lpSystemTime->wSecond;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
  switch (dwReason) {
  case DLL_PROCESS_ATTACH:
    // Не повторяйте этого в продакшене!
    // Во избежание неприятных дедлоков, внутри DllMain() нужно оставлять только самый необходимый функционал

    // Инициализируем библиотеку libMinHook
    if (MH_Initialize() != MH_OK) {
      return FALSE;
    }

    // Создаём хук (пока что он будет в выключенном состоянии)
    if (MH_CreateHook(&GetLocalTime, &MyGetLocalTime, reinterpret_cast<void**>(&GetLocalTime_)) != MH_OK) {
      return FALSE;
    }

    // Активизируем хук
    if (MH_EnableHook(&GetLocalTime) != MH_OK) {
      return FALSE;
    }
  }

  return TRUE;
}

Используем полученную dll

Теперь можно внедрять dll. Запускаем в консоли инжектор следующей командой:

Время вспять…

Всё, можно наслаждаться результатами! (осторожно, гипнотизирует)

Автор: Glowfall

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js