понедельник, 20 августа 2012 г.

Эмулируем GetLocalTime

Обычным приемом при анализе стороннего приложения, является установка BP на API функциях и исследование вызывающего данные функции кода. Как контрприем, применяется эмуляция  API  функций внутри тела приложения.
Например, при анализе триального приложения с привязкой ко времени, BP будет установлен на функции GetLocalTime(). Если проэмулировать ее вызов, то можно немного усложнить анализ кода приложения и/или уйти от различных утилит подменяющих дату, принцип которых построен на перехвате API . Ну как пример вот таких.

Вот так выглядит код эмуляции данной функции:
type
  KSYSTEM_TIME = packed record
    LowPart: DWORD;
    High1Time: Integer;
    High2Time: Integer;
  end;

  PKUSER_SHARED_DATA = ^KUSER_SHARED_DATA;
  KUSER_SHARED_DATA  = packed record
    TickCountLow: ULONG;
    TickCountMultiplier: Integer;
    InterruptTime: KSYSTEM_TIME;
    SystemTime: KSYSTEM_TIME;
    TimeZoneBias: KSYSTEM_TIME;
  end;

  TIME_FIELDS = packed record
     Year,
     Month,
     Day,
     Hour,
     Minute,
     Second,
     Milliseconds,
     Weekday: Short;
  end;

procedure GetLocalTime_Emul(var lpSystemTime: TSystemTime);
const
  MM_SHARED_USER_DATA_VA = $7FFE0000;
  USER_SHARED_DATA: PKUSER_SHARED_DATA = PKUSER_SHARED_DATA(MM_SHARED_USER_DATA_VA);

  Magic10000: LARGE_INTEGER = (LowPart: $E219652C; HighPart: Integer($D1B71758));
  SHIFT10000 = 13;

  Magic86400000: LARGE_INTEGER = (LowPart: $FA67B90E; HighPart: Integer($C6D750EB));
  SHIFT86400000 = 26;

  WEEKDAY_OF_1601 = 1;


  LeapYearDayToMonth: array [0..365] of Byte = (
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // January
     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,        // February
     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,  // March
     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,     // April
     4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,  // May
     5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,     // June
     6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,  // July
     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,  // August
     8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // September
     9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,  // October
    10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,     // November
    11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11); // December

  NormalYearDayToMonth: array [0..364] of Byte = (
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // January
     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,           // February
     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,  // March
     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,     // April
     4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,  // May
     5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,     // June
     6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,  // July
     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,  // August
     8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // September
     9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,  // October
    10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,     // November
    11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11); // December

  LeapYearDaysPrecedingMonth: array[0..12] of Word = (
    0,                                 // January
    31,                                // February
    31+29,                             // March
    31+29+31,                          // April
    31+29+31+30,                       // May
    31+29+31+30+31,                    // June
    31+29+31+30+31+30,                 // July
    31+29+31+30+31+30+31,              // August
    31+29+31+30+31+30+31+31,           // September
    31+29+31+30+31+30+31+31+30,        // October
    31+29+31+30+31+30+31+31+30+31,     // November
    31+29+31+30+31+30+31+31+30+31+30,  // December
    31+29+31+30+31+30+31+31+30+31+30+31);

  NormalYearDaysPrecedingMonth: array[0..12] of Word = (
    0,                                 // January
    31,                                // February
    31+28,                             // March
    31+28+31,                          // April
    31+28+31+30,                       // May
    31+28+31+30+31,                    // June
    31+28+31+30+31+30,                 // July
    31+28+31+30+31+30+31,              // August
    31+28+31+30+31+30+31+31,           // September
    31+28+31+30+31+30+31+31+30,        // October
    31+28+31+30+31+30+31+31+30+31,     // November
    31+28+31+30+31+30+31+31+30+31+30,  // December
    31+28+31+30+31+30+31+31+30+31+30+31);

var
  SystemTime, LocalTime, Bias, Temp: LARGE_INTEGER;
  TotalMilliseconds: LARGE_INTEGER;
  Years, Month, Days, Hours, Minutes, Seconds, Milliseconds: ULONG;
  NumberOf400s, NumberOf100s, NumberOf4s, ElapsedDays: ULONG;
  NumberOfLeapYears: ULONG;
  _IsLeapYear: Boolean;
begin
    // эмуляция работы GetLocalTime

    SystemTime.HighPart := USER_SHARED_DATA^.SystemTime.High1Time;
    SystemTime.LowPart := USER_SHARED_DATA^.SystemTime.LowPart;

    Bias.HighPart := USER_SHARED_DATA^.TimeZoneBias.High1Time;
    Bias.LowPart := USER_SHARED_DATA^.TimeZoneBias.LowPart;

    LocalTime.QuadPart := SystemTime.QuadPart - Bias.QuadPart;

    // эмуляция вызова RtlTimeToTimeFields(&LocalTime,&TimeFields);

    // RtlTimeToTimeFields->TimeToDaysAndFraction
    // TotalMilliseconds = Convert100nsToMilliseconds( *(PLARGE_INTEGER)Time );
    {
      #define Convert100nsToMilliseconds(LARGE_INTEGER) (
        RtlExtendedMagicDivide( (LARGE_INTEGER), Magic10000, SHIFT10000 ))
    }

    asm
      push SHIFT10000
      push Magic10000.HighPart
      push Magic10000.LowPart
      push LocalTime.HighPart
      push LocalTime.LowPart
      call @RtlExtendedMagicDivide
      jmp @end

    @RtlExtendedMagicDivide:
      push ebp
      mov ebp,esp
      sub esp,$0c
      push esi
      mov esi,[ebp+$0c]
      test esi,$80000000
      jz @remd10
      neg dword ptr [ebp+$0c]
      neg dword ptr [ebp+$08]
      sbb dword ptr [ebp+$0c],$00
    @remd10:
      mov eax,[ebp+$10]
      mul dword ptr [ebp+$08]
      mov [ebp-$04],edx
      mov eax,[ebp+$10]
      mul dword ptr [ebp+$0c]
      mov [ebp-$08],eax
      mov [ebp-$0c],edx
      mov eax,[ebp+$14]
      mul dword ptr [ebp+$08]
      xor ecx,ecx
      add eax,[ebp-$04]
      adc ecx,$00
      add eax,[ebp-$08]
      adc ecx,$00
      mov [ebp-$04],edx
      mov eax,[ebp+$14]
      mul dword ptr [ebp+$0c]
      add eax,[ebp-$04]
      adc edx,$00
      add eax,[ebp-$0c]
      adc edx,$00
      add eax,ecx
      adc edx,$00
      mov cl,[ebp+$18]
    @remd20:
      cmp cl,$1f
      jbe @remd30
      sub cl,$1f
      shrd eax,edx,$1f
      shr edx,$1f
      jmp @remd20
    @remd30:
      shrd eax,edx,cl
      shr edx,cl
      test esi,$80000000
      jz @remd40
      neg edx
      neg eax
      sbb edx,$00
    @remd40:
      pop esi
      mov esp,ebp
      pop ebp
      ret $0014

    @end:
      mov TotalMilliseconds.LowPart, eax
      mov TotalMilliseconds.HighPart, edx

    // RtlTimeToTimeFields->TimeToDaysAndFraction
    // Temp = ConvertMillisecondsToDays( TotalMilliseconds );
    {
      #define ConvertMillisecondsToDays(LARGE_INTEGER) (
        RtlExtendedMagicDivide( (LARGE_INTEGER), Magic86400000, SHIFT86400000 ))
    }

      push SHIFT86400000
      push Magic86400000.HighPart
      push Magic86400000.LowPart
      push TotalMilliseconds.HighPart
      push TotalMilliseconds.LowPart
      call @RtlExtendedMagicDivide
      mov Temp.LowPart, eax
      mov Temp.HighPart, edx
    end;

    // RtlTimeToTimeFields->TimeToDaysAndFraction
    // *ElapsedDays = Temp.LowPart;

    Days := Temp.LowPart;

    // RtlTimeToTimeFields->TimeToDaysAndFraction
    // Temp.QuadPart = ConvertDaysToMilliseconds( *ElapsedDays );
    {
      #define ConvertDaysToMilliseconds(DAYS) (
        Int32x32To64( (DAYS), 86400000 ))
    }

    Temp.QuadPart := Days;
    Temp.QuadPart := Temp.QuadPart * 86400000;

    // RtlTimeToTimeFields->TimeToDaysAndFraction
    // Temp.QuadPart = TotalMilliseconds.QuadPart - Temp.QuadPart;

    Temp.QuadPart := TotalMilliseconds.QuadPart - Temp.QuadPart;

    // RtlTimeToTimeFields->TimeToDaysAndFraction
    // *Milliseconds = Temp.LowPart;

    Milliseconds := Temp.LowPart;

    // реализация TimeToDaysAndFraction закочена

    // TimeFields->Weekday = (CSHORT)((Days + WEEKDAY_OF_1601) % 7);

    lpSystemTime.wDayOfWeek := (Days + WEEKDAY_OF_1601) mod 7;

    // Years = ElapsedDaysToYears( Days );

    // ElapsedDaysToYears - обычная математика
    ElapsedDays   := Days;
    NumberOf400s  := Days div 146097;
    Dec(ElapsedDays, NumberOf400s * 146097);
    NumberOf100s  := (ElapsedDays * 100 + 75) div 3652425;
    Dec(ElapsedDays, NumberOf100s * 36524);
    NumberOf4s    := ElapsedDays div 1461;
    Dec(ElapsedDays, NumberOf4s * 1461);
    Years := (NumberOf400s * 400) +
             (NumberOf100s * 100) +
             (NumberOf4s * 4) +
             (ElapsedDays * 100 + 75) div 36525;


    // Days = Days - ElapsedYearsToDays( Years );
    {
      #define ElapsedYearsToDays(YEARS) (
        ((YEARS) * 365) + NumberOfLeapYears(YEARS))

      #define NumberOfLeapYears(YEARS) (
        ((YEARS) / 4) - ((YEARS) / 100) + ((YEARS) / 400))
    }

    NumberOfLeapYears := (Years div 4) - (Years div 100) + (Years div 400);
    Dec(Days, Years * 365 + NumberOfLeapYears);

    // if (IsLeapYear( Years + 1 )) {
    {
      #define IsLeapYear(YEARS) (                        \
          (((YEARS) % 400 == 0) ||                       \
           ((YEARS) % 100 != 0) && ((YEARS) % 4 == 0)) ? \
              TRUE                                       \
          :                                              \
              FALSE                                      \
          )
    }

    Inc(Years);
    _IsLeapYear := (Years mod 400 = 0) and
      (Years mod 100 <> 0) or (Years mod 4 = 0);
    Dec(Years);

    // и еще немного математики

    if _IsLeapYear then
    begin
      Month := LeapYearDayToMonth[Days];
      Dec(Days, LeapYearDaysPrecedingMonth[Month]);
    end
    else
    begin
      Month := NormalYearDayToMonth[Days];
      Dec(Days, NormalYearDaysPrecedingMonth[Month]);
    end;
    Seconds := Milliseconds div 1000;
    Milliseconds := Milliseconds mod 1000;
    Minutes := Seconds div 60;
    Seconds := Seconds mod 60;
    Hours := Minutes div 60;
    Minutes := Minutes mod 60;

    lpSystemTime.wYear         := (Years + 1601);
    lpSystemTime.wMonth        := (Month + 1);
    lpSystemTime.wDay          := (Days + 1);
    lpSystemTime.wHour         := Hours;
    lpSystemTime.wMinute       := Minutes;
    lpSystemTime.wSecond       := Seconds;
    lpSystemTime.wMilliseconds := Milliseconds;
end;


Код совмести со всеми 32-битными версиями Delphi. Работает под всеми операционными системами начиная с W2K, до Windows 8 RTM включительно, как 32, так и 64 битные редакции.

Забрать полный код с коментариями и пример использования можно здесь.

Подсветка кода выполнена при помощи: http://tohtml.com/pascal/

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

  1. запустил Вашу функцию эмуляции времени. Запустил свою программу, которая использует GetLocalTime. Время в моей программе не изменилось и я не понял- каким образом это использовать в защите... Извиняюсь я только начинающий, хотя и старый человек.

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

      Удалить
  2. Что за хуйня!? Александр ты что там микросхемы программируешь!? Код какой-то, я думал Pascal хорошо знаю, теперь я разочарован собой

    ОтветитьУдалить
    Ответы
    1. Мы много чего программируем, как пики так и поноценную прошивку под ARM делаем. Но попрошу впредь без мата, т.к. в данном блоге посты нельзя редактировать - только удалять.

      Удалить