[Reversme 09 solution]

Discussion in 'Реверсинг' started by 0x0c0de, 7 Mar 2009.

  1. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    [Reverseme09 by 0x0c0de]

    Итак, моя девятая работа. Активности в разделе мало, да и как-то вяло реверсится. Ну да ладно.. Расскажу как это должно было быть.
    В первый раз так, что ни одного дельного поста за несколько суток. Обычно 1 дня хватает для первого решения ну или хотя бы признаков того, что кто-то решает и двигается в ВЕРНОМ направлении. Я тут не стану описывать алгоритм генерации ключа - там и разбирать-то нечего. Объясню самое интересное.

    [Приступим]

    Загрузим кодес в иду. Именно в иду, не нужно это отлаживать. Точнее, совсем не обязательно.

    В начале WinMain видим

    Code:
    .text:004017AB                 push    ebp             ; hTemplateFile
    .text:004017AC                 push    80h             ; dwFlagsAndAttributes
    .text:004017B1                 push    3               ; dwCreationDisposition
    .text:004017B3                 push    ebp             ; lpSecurityAttributes
    .text:004017B4                 push    3               ; dwShareMode
    .text:004017B6                 push    0C0000000h      ; dwDesiredAccess
    .text:004017BB                 push    offset a_Reverseme0 ; "\\\\.\\reverseme0"
    
    Ага, значит будем иметь дело с драйвером. Но это не значит, что надо хватать ядерный отладчик и ставить бряки на загрузку драйвера.
    Здесь мы пойдем другим путем. Посмотрим чуть дальше

    Code:
    .text:004017C6                 cmp     eax, 0FFFFFFFFh
    .text:004017C9                 mov     hDevice, eax
    .text:004017CE                 jnz     short loc_4017E1
    .text:004017D0                 call    sub_401289
    
    Здесь, в процедуре по адресу sub_401289 извлекается из ресурсов драйвер и его файл создается на диске. Драйвер можно вытащить прямо из ресурсов, так как они не криптованы ничем . Что сейчас и сделаем. Думаю, как пользоваться Restorator знают все, поэтому на этом не останавливаюсь.

    Итак, загружаем драйвер в дизассемблер

    Code:
    INIT:00012000                 sub     esp, 10h
    INIT:00012003                 push    esi
    INIT:00012004                 mov     esi, [esp+14h+DeviceObject]
    INIT:00012008                 push    edi
    INIT:00012009                 mov     edi, ds:RtlInitUnicodeString
    INIT:0001200F                 push    offset SourceString ; "\\Device\\reverseme0"
    INIT:00012014                 lea     eax, [esp+1Ch+DestinationString]
    INIT:00012018                 push    eax             ; DestinationString
    INIT:00012019                 mov     dword ptr [esi+34h], offset loc_11160
    INIT:00012020                 call    edi ; RtlInitUnicodeString
    INIT:00012022                 lea     ecx, [esp+18h+DeviceObject]
    INIT:00012026                 push    ecx             ; DeviceObject
    INIT:00012027                 push    0               ; Exclusive
    INIT:00012029                 push    0               ; DeviceCharacteristics
    INIT:0001202B                 push    22h             ; DeviceType
    INIT:0001202D                 lea     edx, [esp+28h+DestinationString]
    
    Все бы ничего и все стандартно, на первый взгляд. DriverEntry как DriverEntry. Но тут-то надо быть внимательней. Прокрутите чуть ниже листинг и вы увидите, что

    Code:
    INIT:00012099 loc_12099:                             
    INIT:00012099                 call    sub_110C0
    INIT:0001209E                 test    eax, eax
    INIT:000120A0                 jz      short loc_120A7
    INIT:000120A2                 mov     esi, 40010003h // хм, возвращаемое значение?
    INIT:000120A7
    INIT:000120A7 loc_120A7:                            
    INIT:000120A7                 mov     eax, esi
    INIT:000120A9
    INIT:000120A9 loc_120A9:                             
    INIT:000120A9                 pop     edi
    INIT:000120AA                 pop     esi
    INIT:000120AB                 add     esp, 10h
    INIT:000120AE                 retn    8
    

    То, что DriverEntry может вернуть 40010003h в зависимости от того, как выполниться процедура по адресу sub_110C0 - уже должно насторожить. Посмотрим, что это за процедура.


    Code:
    .text:000110C0                 push    ebp
    .text:000110C1                 mov     ebp, esp
    .text:000110C3                 sub     esp, 40h
    .text:000110C6                 push    esi
    .text:000110C7                 push    edi
    .text:000110C8                 xor     eax, eax
    .text:000110CA                 push    31h             ; size_t
    .text:000110CC                 push    eax             ; int
    .text:000110CD                 mov     [ebp+var_4], eax
    .text:000110D0                 mov     [ebp+var_40], al
    .text:000110D3                 lea     eax, [ebp+var_3F]
    .text:000110D6                 push    eax             ; void *
    .text:000110D7                 call    memset
    .text:000110DC                 add     esp, 0Ch
    .text:000110DF                 sidt    fword ptr [ebp+var_C]
    
    Опа, sidt. Инструкция, получающая содержимое регистра idtr. Посмотрим что происходит дальше. Я облегчила задачу в разы и не замусоривала код вообще. Поэтому можно заюзать Hex-Rays. жмем F5 и смотрим псевдокод.

    Немного забегая вперед я обозначила имена функций. Читайте комментарии ниже и все станет ясно.

    Code:
    int __cdecl CheckIdtHook()
    {
    
    ….
    
      __asm { sidt    fword ptr [ebp+var_C] }
      v0 = *(unsigned __int16 *)&v5[2] | (*(unsigned __int16 *)&v5[4] << 16);
      GetIdtModuleName(*(_WORD *)((*(unsigned __int16 *)&v5[2] | (*(unsigned __int16 *)&v5[4] << 16)) + 0x168) | (*(_WORD *)((*(unsigned __int16 *)&v5[2] | (*(unsigned __int16 *)&v5[4] << 16)) + 0x16E) << 16),
        &v3);
      if ( *(_DWORD *)&v3 == 'esyS' ) // первая часть имени
      {
        if ( v6 == 'ys.r' ) // вторая часть
          ++v2;
      }
      GetIdtModuleName(*(_WORD *)(v0 + 104) | (*(_WORD *)(v0 + 110) << 16), &v3);
      if ( *(_DWORD *)&v3 == 'stdr' ) // первая часть имени
      {
        if ( v6 == 'ys.c' ) // вторая часть
          ++v2;
      }
      return v2;
    }
    

    И? ) 'ys.r'+'esyS' - "Syser.sy". хм. Проверка на драйвер Syser? Да, именно она. Syser.sys перехватывает довольно много прерываний (вы можете это глянуть в каком-нибудь RKU на вкладке Code Hooks): int 1,2,3,6,B,C,D,E,2D... Я проверяю конкретно int 0x2d. KiDebugService )). Смотрим дальше. 'ys.c'+'stdr' - "rdtsc.sy" - проверка на ольгу. Проверяю вход 0xD в идт. Намек на то, что значение, возвращаемое DriverEntry используется где-то. И логично предположить, что это где-то будет в юзермодной части реверсми. Функция получает имя модуля по заданному адресу. И надо сказать вызываться она будет не только в DriverEntry..

    На время вернемся в юзермодную часть. Посмотрим внимательней на процесс загрузки драйвера

    Code:
    .text:004011FB LoadDriver:                             ; CODE XREF: sub_4010F4+9E j
    .text:004011FB                 push    offset unk_40CC68
    .text:00401200                 call    ds:NtLoadDriver
    .text:00401206                 pop     edi
    .text:00401207                 pop     esi
    .text:00401208                 mov     NtstatusSave, eax // это будет играть роль при генерации ключа
    .text:0040120D                 pop     ebx
    .text:0040120E                 leave
    .text:0040120F                 retn
    .text:0040120F sub_4010F4      endp
    
    Я сразу переименовала в иде переменную, куда сохраняется значение NTSTATUS, возращенное ZwLoadDriver. Хорошо, теперь снова вернемся в WinMain из процедуры создания файла драйвера


    Code:
    .text:004017E1 loc_4017E1:                             ; CODE XREF: wWinMain(x,x,x,x)+C7 j
    .text:004017E1                                         ; wWinMain(x,x,x,x)+D0 j
    .text:004017E1                 push    ebp             ; lpOverlapped
    .text:004017E2                 lea     eax, [esp+18h+BytesReturned]
    .text:004017E6                 push    eax             ; lpBytesReturned
    .text:004017E7                 push    8               ; nOutBufferSize
    .text:004017E9                 push    offset byte_40CCA0 ; lpOutBuffer
    .text:004017EE                 push    ebp             ; nInBufferSize
    .text:004017EF                 push    ebp             ; lpInBuffer
    .text:004017F0                 push    222014h         ; dwIoControlCode
    .text:004017F5                 push    hDevice         ; hDevice
    .text:004017FB                 call    ds:DeviceIoControl
    
    Итак, первое обращение к дрову. Хм... IOCTL запрос 222014h. Теперь, нужно найти функцию обработки IOCTL запросов в драйвере. И ее расковырять. Определять местоположение процедур обработки можно и нужно из DriverEntry

    Типичный код в DriverEntry.

    Code:
    
    DriverObject->MajorFunction[IRP_MJ_CREATE]= OpenDeviceHandler;
    	
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = CloseHandler;
    	
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]= DeviceControlRoutine;
        
    DriverObject->MajorFunction[IRP_MJ_READ]  = ReadHandler;
    	
    DriverObject->MajorFunction[IRP_MJ_WRITE] = WriteHandler;
    
    
    Аналог дизассемблированном листинге DriverEntry

    Code:
    INIT:00012055                 mov     dword ptr [esi+38h], offset loc_111E0
    INIT:0001205C                 mov     dword ptr [esi+40h], offset loc_111E0
    INIT:00012063                 mov     dword ptr [esi+70h], offset sub_115B0
    INIT:0001206A                 mov     dword ptr [esi+44h], offset loc_117E0
    INIT:00012071                 mov     dword ptr [esi+48h], offset loc_11800
    
    Все, считайте, адреса основных обработчиков у нас в кармане.
    На данный момент нужный нам - 000115B0 - это и есть обработчик IOCTL запросов. Я кое-что из стандартных фрагментов переименовала сразу. Что выдал HexRays по F5 [в общем виде]

    Code:
    int __stdcall DeviceControlRoutine(int a1, PIRP Irp)
    {
      code = *((_DWORD *)v14 + 3) - 0x222004; // то, что мы отсылаем из юзермода - 0x222004
      status = 0;
      v17 = 0;
      switch ( code )
      {
        case 20:
    
    …
        case 16:
    …
        case 4:
    ….
      }
    }
    
    Пока вот так. Нам нужен первый ioctl запрос. 222014h. Отлично, вычитаем 0x222004. Получаем 10h или 16. Нам HexRays как раз-таки и выдал 16-й кейз. Разберемся с ним. На первый взгляд выполняется куча каких-то непонятных операций

    Code:
     cmp     ecx, 8          ; jumptable 000115E7 case 16
    .text:0001161D                 jb      short loc_115F3
    .text:0001161F                 mov     eax, dword_14004
    .text:00011624                 cmp     eax, ebx
    .text:00011626                 jz      short loc_1162F
    .text:00011628                 mov     [esi], eax
    .text:0001162A                 jmp     loc_116BA
    .text:0001162F ; ---------------------------------------------------------------------------
    .text:0001162F
    .text:0001162F loc_1162F:                              ; CODE XREF: DeviceControlRoutine+76 j
    .text:0001162F                 mov     cl, [esp+20h+var_A]
    .text:00011633                 mov     al, [esp+20h+var_B]
    .text:00011637                 or      cl, 40h
    .text:0001163A                 and     cl, 0EFh
    .text:0001163D                 or      cl, 0Fh
    .text:00011640                 and     al, 9Fh
    .text:00011642                 or      cl, 80h
    .text:00011645                 or      al, 10h
    .text:00011647                 and     cl, 0DFh
    .text:0001164A                 and     al, 0FBh
    .text:0001164C                 mov     [esp+20h+var_A], cl
    .text:00011650                 lea     ecx, [esp+20h+var_10]
    .text:00011654                 or      al, 8Bh
    .text:00011656                 push    ecx
    .text:00011657                 mov     word ptr [esp+24h+var_10+2], bx
    .text:0001165C                 mov     [esp+24h+var_C], bl
    .text:00011660                 mov     [esp+24h+var_9], bl
    .text:00011664                 mov     word ptr [esp+24h+var_10], 0FFFFh
    .text:0001166B                 mov     [esp+24h+var_B], al
    .text:0001166F                 call    sub_11250
    
    Однако, заглянем, что у нас по адресу sub_11250 и тогда все станет ясно.

    Code:
    .text:00011250 sub_11250       proc near               ; CODE XREF: DeviceControlRoutine+BF p
    .text:00011250                                         ; DeviceControlRoutine+1A6 p
    .text:00011250
    .text:00011250 var_10          = dword ptr -10h
    .text:00011250 var_C           = dword ptr -0Ch
    .text:00011250 var_8           = byte ptr -8
    .text:00011250 arg_0           = dword ptr  4
    .text:00011250
    .text:00011250                 sub     esp, 10h
    .text:00011253                 mov     ecx, ds:KeNumberProcessors
    .text:00011259                 xor     eax, eax
    .text:0001125B                 cmp     byte ptr [ecx], 1
    .text:0001125E                 mov     [esp+10h+var_C], eax
    .text:00011262                 mov     [esp+10h+var_10], 1
    .text:00011269                 jl      loc_1131A
    .text:0001126F                 push    ebx
    .text:00011270                 mov     ebx, [esp+14h+arg_0]
    .text:00011274                 push    ebp
    .text:00011275                 push    esi
    .text:00011276                 push    edi
    .text:00011277                 jmp     short loc_11280
    .text:00011277 ; ---------------------------------------------------------------------------
    .text:00011279                 align 10h
    .text:00011280
    .text:00011280 loc_11280:                              ; CODE XREF: sub_11250+27 j
    .text:00011280                                         ; sub_11250+BC j
    .text:00011280                 mov     edx, [esp+20h+var_10]
    .text:00011284                 push    edx
    .text:00011285                 call    KeGetCurrentThread
    .text:0001128A                 push    eax
    .text:0001128B                 call    KeSetAffinityThread
    .text:00011290                 sgdt    fword ptr [esp+20h+var_8]
    
    А тут вы должны были насторожиться второй раз. KeNumberProcessors, вызов KeGetCurrentThread и KeSetAffinityThread, получение gdtr инструкцией sgdt. А если присмотреться к коду далее - уже явно идет поиск свободного дескриптора в gdt

    Code:
    t:000112B0                 mov     eax, dword ptr [esp+20h+var_8+2]
    .text:000112B4                 lea     esi, [eax+edi*8]
    .text:000112B7                 push    esi             ; VirtualAddress
    .text:000112B8                 call    ds:MmIsAddressValid
    .text:000112BE                 test    al, al
    .text:000112C0                 jz      short loc_112CD
    .text:000112C2                 cmp     dword ptr [esi], 0 // чекаем первый дворд
    .text:000112C5                 jnz     short loc_112CD
    .text:000112C7                 cmp     dword ptr [esi+4], 0 // чекаем второй дворд
    .text:000112CB                 jz      short loc_112D6
    .text:000112CD
    .text:000112CD loc_112CD:                              ; CODE XREF: sub_11250+70 j
    .text:000112CD                                         ; sub_11250+75 j
    .text:000112CD                 add     edi, 1
    .text:000112D0                 cmp     edi, ebp
    .text:000112D2                 jb      short loc_112B0
    .text:000112D4                 jmp     short loc_112F9
    
    Проверка 2-х частей дескриптора. Как известно дескриптор равен 8-ми байтам, вот и проверяется по двордам. Как только мы нашли свободный дескриптор

    Code:
    .text:000112D6 add_descriptor:                         ; CODE XREF: AddDescriptorIntoGdt+7B j
    .text:000112D6                 cli
    .text:000112D7                 mov     ecx, [ebx]
    .text:000112D9                 mov     [esi], ecx
    .text:000112DB                 mov     edx, [ebx+4]
    .text:000112DE                 mov     [esi+4], edx
    .text:000112E1                 sti
    
    добавляем новый. И этот дескриптор заюзается на следующем этапе, но об этом позже. То есть все те действия, которые выполнялись перед вызовом исследуемой процедуры - это было формирование структуры дескриптора. Исходники, как я говорила, я не утаиваю и кому надо могут их увидеть. Саму процедуру, после того, как поняли, что она делает обзовем AddDescriptorIntoGdt. Она принимает 1 параметр – указатель на структуру дескриптора и возвращает селектор на созданный дескриптор.
    Итак, функция AddDescriptorIntoGdt добавит в GDT свой дескриптор и вернет на него селектор (селектор – 16 бит, первые 2 бита – RPL, второй бит TI, определяющий таблицу (ldt/gdt) и 3:15 биты – индекс в таблице дескрипторов). Забегая вперед, скажу, что добавление своего дескриптора в gdt конкретно в этой части кодеса в принципе бессмысленно [с точки зрения кодера, конечно, бессмысленно, неподготовленного реверсера сбивают все эти манипуляции]. Это - атавизм первых вариантов. У меня первоначально была пара хороших идей, но, к сожалению, тут мне помешали некоторые обстоятельства осуществить свой план.

    Окей, выходим из этой подпроцедуры и двигаемся дальше

    Code:
    .text:0001166F                 call    AddDescriptorIntoGdt
    .text:00011674                 cmp     eax, ebx
    .text:00011676                 jz      short zero_selector_erro
    .text:00011678                 mov     [esp+20h+var_6], ax
    .text:0001167D                 mov     eax, offset sub_113E0
    .text:00011682                 lea     ecx, [esp+20h+var_8]
    .text:00011686                 mov     edx, offset sub_113E0
    .text:0001168B                 shr     eax, 10h
    .text:0001168E                 push    ecx
    .text:0001168F                 mov     [esp+24h+var_8], dx
    .text:00011694                 mov     [esp+24h+var_2], ax
    .text:00011699                 mov     [esp+24h+var_3], 0EEh
    .text:0001169E                 call    sub_114E0
    
    sub_114E0 - разберем ее.

    Code:
     
    .text:000114E0 sub_114E0       proc near               ; CODE XREF: DeviceControlRoutine+EE p
    .text:000114E0
    .text:000114E0 var_8           = byte ptr -8
    .text:000114E0 arg_0           = dword ptr  4
    .text:000114E0
    .text:000114E0                 mov     eax, ds:KeNumberProcessors
    .text:000114E5                 sub     esp, 8
    .text:000114E8                 push    ebx
    .text:000114E9                 push    esi
    .text:000114EA                 mov     ebx, 1
    .text:000114EF                 xor     esi, esi
    .text:000114F1                 cmp     [eax], bl
    .text:000114F3                 jl      loc_115A2
    .text:000114F9                 push    edi
    .text:000114FA                 mov     edi, [esp+14h+arg_0]
    .text:000114FE                 mov     edi, edi
    .text:00011500
    .text:00011500 loc_11500:                              ; CODE XREF: sub_114E0+BB j
    .text:00011500                 push    ebx
    .text:00011501                 call    KeGetCurrentThread
    .text:00011506                 push    eax
    .text:00011507                 call    KeSetAffinityThread
    .text:0001150C                 sidt    fword ptr [esp+14h+var_8]
    .text:00011511                 movzx   eax, word ptr [esp+14h+var_8+4]
    .text:00011516                 movzx   ecx, word ptr [esp+14h+var_8+2]
    .text:0001151B                 shl     eax, 10h
    .text:0001151E                 or      eax, ecx
    .text:00011520                 mov     ecx, 20h
    .text:00011525                 lea     edx, [eax+105h]
    .text:0001152B                 jmp     short loc_11530
    
     
    #1 0x0c0de, 7 Mar 2009
    Last edited: 7 Mar 2009
    6 people like this.
  2. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    Ага, опять похожий код на предыдущую функцию. только на этот раз sidt. Эта функция добавляет вход в idt.

    Причем, поиск свободного входа осуществляется только среди 0x20 - 0x29 прерываниями. Почему? Да потому что они обычно свободны. Смысла первые чекать нет - они стандартны и уже заняты системой. Хорошо, раз это прерывание новое, то надо бы найти адрес обработчика. Да его и искать не надо. смотрим снова

    Code:
    .text:0001166F                 call    AddDescriptorIntoGdt
    .text:00011674                 cmp     eax, ebx
    .text:00011676                 jz      short zero_selector_erro
    .text:00011678                 mov     [esp+20h+var_6], ax
    .text:0001167D                 mov     eax, offset InterruptHandler
    .text:00011682                 lea     ecx, [esp+20h+var_8]
    .text:00011686                 mov     edx, offset InterruptHandler
    .text:0001168B                 shr     eax, 10h
    .text:0001168E                 push    ecx
    .text:0001168F                 mov     [esp+24h+var_8], dx
    .text:00011694                 mov     [esp+24h+var_2], ax
    .text:00011699                 mov     [esp+24h+var_3], 0EEh
    .text:0001169E                 call    AddDescriptorIntoIdt
    
    В исходном коде это было так

    Code:
    intEntry.wSelector = uSel; 
    
    intEntry.wLowOffset = LOWORD(&InterruptHandler);
    
    intEntry.wHiOffset = HIWORD(&InterruptHandler);
    
    intEntry.DPL = 3;
    
    intEntry.P = 1;
    
    // 32 битный шлюз прерывания
    intEntry.unused_hi = 14;
    
    puInfoBuf[0] = CreateInterruptGateIntoIdt(&intEntry);
    
    В общем вот так . Хендлер прерывания мы нашли. Селектор на добавленный нами дескриптор (как вы уже могли заметить), используется при составлении дескриптора шлюза в idt. Теперь снова в юзермод, разбираться что где юзоется и где вызывается это наше прерывание.

    Code:
    .text:00401805                 lea     eax, [esp+14h+flOldProtect]
    .text:00401809                 push    eax             ; lpflOldProtect
    .text:0040180A                 push    40h             ; flNewProtect
    .text:0040180C                 push    2               ; dwSize
    .text:0040180E                 mov     ebx, offset sub_401388
    .text:00401813                 push    ebx             ; lpAddress
    .text:00401814                 call    ds:VirtualProtect
    .text:0040181A                 test    eax, eax
    .text:0040181C                 jz      short loc_401860
    .text:0040181E                 push    ebp             ; dwInitParam
    .text:0040181F                 push    offset DialogFunc ; lpDialogFunc
    .text:00401824                 push    ebp             ; hWndParent
    .text:00401825                 mov     eax, ebx
    .text:00401827                 push    67h             ; lpTemplateName
    .text:00401829                 push    hModule         ; hInstance
    .text:0040182F                 mov     byte ptr [eax], 0CDh // опкод команды int
    .text:00401832                 mov     al, byte_40CCA0 // то, что вернул драйвер [вектор добавленного прерывания] 
    .text:00401837                 mov     [ebx+1], al
    .text:0040183A                 call    ds:DialogBoxParamW
    
    Окей, кажется, мы нашли место, где будет юзотся прерывание. Обзовем эту процедуру int_call. Теперь, самое интересное. Отправляемся в DialogFunc и начинаем непосредственно изучать что происходит после того, как мы нажали кнопку Try!. Ковырять DialogFunc все умеют, думаю, поэтому я не останавливаюсь на поиске обработки конкретной кнопки.

    Code:
    .text:0040191D                 push    0               ; bEnable
    .text:0040191F                 push    dword_40CCC8    ; hWnd
    .text:00401925                 call    ds:EnableWindow
    .text:0040192B                 push    offset sub_4016A4
    .text:00401930                 push    large dword ptr fs:0
    .text:00401937                 mov     large fs:0, esp
    .text:0040193E                 mov     edi, 'bran'
    .text:00401943                 call    int_call
     
    Итак, как видите вызывается та процедура, которая патчится в WinMain. Устанавливается обработчик исключений, потом что-то ложится в edi и вызывается прерывание.

    Итак, вызываем в первый раз прерывание. Инструкция sub eax, eax будет изменена на int xx. Далее, устанавливается флаг трассировки и все бы ничего. Но это пока так кажется, что ничего и мы должны отправиться в сех с первого нопа.

    Code:
    .text:00401388 int_call        proc near               ; CODE XREF: DialogFunc+B5 p
    .text:00401388                                         ; DATA XREF: wWinMain(x,x,x,x)+107 o
    .text:00401388                 sub     eax, eax
    .text:0040138A                 pushf
    .text:0040138B                 pop     edi
    .text:0040138C                 bts     edi, 8
    .text:00401390                 push    edi
    .text:00401391                 popf
    .text:00401392                 nop // в SEH отсюда? Нет! Читаем комменты ниже 
    .text:00401393                 nop
    .text:00401394                 nop
    .text:00401395                 nop
    .text:00401396                 nop
    .text:00401397                 nop
    .text:00401398                 nop
    .text:00401399                 xor     eax, eax
    .text:0040139B
    .text:0040139B infinite_loop:                          ; CODE XREF: int_call:infinite_loop j
    .text:0040139B                 jmp     short infinite_loop
    .text:0040139B int_call        endp
    .text:0040139B
    .text:0040139D ; ---------------------------------------------------------------------------
    .text:0040139D                 retn
    
    А теперь разберем хендлер прерывания. Ага, значит все-таки значение в edi имеет смысл. Что же происходит? Если в edi - 'bran', то начинается манипуляция с msr регистром MSR_DEBUGCTLA (1D9h). Как вы можете помнить 0 –бит там LBR, а 1-й BTF. В результате копипаста со своего пробного кодеса, устанавливаю 2 бита. Реально же, нужен нам BTF.

    Code:
    .text:000113E0 InterruptHandler proc near              ; DATA XREF: DeviceControlRoutine+CD o
    .text:000113E0                                         ; DeviceControlRoutine+D6 o
    .text:000113E0
    .text:000113E0 arg_4           = dword ptr  8
    .text:000113E0
    .text:000113E0                 cmp     edi, 'bran' 
    .text:000113E6                 jz      short loc_11453
    
    ….
    
    .text:00011453 loc_11453:                              ; CODE XREF: InterruptHandler+6 j
    .text:00011453                 mov     ecx, 1D9h // MSR_DEBUGCTLA
    .text:00011458                 rdmsr
    .text:0001145A                 or      eax, 3 // LBR BTF
    .text:0001145D                 wrmsr
    .text:0001145F
    .text:0001145F locret_1145F:                           ; CODE XREF: InterruptHandler+30 j
    .text:0001145F                                         ; InterruptHandler+41 j ...
    .text:0001145F                 iret
    .text:0001145F InterruptHandler endp
    
    Как это работает? Когда мы устанавливаем в MSR регистре MSR_DEBUGCTLA бит BTF установленный в EFLAGS бит TF начинает интерпретироваться как BTF – то есть мы будем останавливаться только на переходах, а не на любой инструкции. То есть, в данном случае, после вызова прерывания и установки TF флажка мы остановимся не на первом нопе, а именно на прыжке

    Code:
    .text:0040139B infinite_loop:                          ; CODE XREF: int_call:infinite_loop j
    .text:0040139B                 jmp     short infinite_loop
    
    Дальше, конечно, возникнет исключение и мы благополучно отправимся в SEH –обработчик, нами установленный. Что же там?

    Code:
    text:004016A4 SEHFirstHandler proc near               ; DATA XREF: DialogFunc+9D o
    .text:004016A4
    .text:004016A4 arg_0           = dword ptr  8
    .text:004016A4 arg_8           = dword ptr  10h
    .text:004016A4
    .text:004016A4                 push    ebp
    .text:004016A5                 mov     ebp, esp
    .text:004016A7                 mov     eax, [ebp+arg_0]
    .text:004016AA                 cmp     dword ptr [eax+0Ch], 4013C0h // это не играет роли
    .text:004016B1                 jz      short loc_4016DC
    .text:004016B3                 str     ax // если мы на варе в eax 4000h
    .text:004016B6                 cmp     ax, 4000h
    .text:004016BA                 jnz     short loc_4016DC
    .text:004016BC
    .text:004016BC loc_4016BC:                             ; CODE XREF: SEHFirstHandler+36 j
    .text:004016BC                 push    'cann'
    .text:004016C1                 push    'ot w'
    .text:004016C6                 push    'ork '
    .text:004016CB                 push    'with'
    .text:004016D0                 push    'vmwa'
    .text:004016D5                 push    're  '
    .text:004016DA                 jmp     short loc_4016BC
    .text:004016DC ; ---------------------------------------------------------------------------
    .text:004016DC
    .text:004016DC loc_4016DC:                             ; CODE XREF: SEHFirstHandler+D j
    .text:004016DC                                         ; SEHFirstHandler+16 j
    .text:004016DC                 push    offset ThreadId ; lpThreadId
    .text:004016E1                 xor     eax, eax
    .text:004016E3                 push    eax             ; dwCreationFlags
    .text:004016E4                 push    eax             ; lpParameter
    .text:004016E5                 push    offset StartAddress ; lpStartAddress
    .text:004016EA                 push    eax             ; dwStackSize
    .text:004016EB                 push    eax             ; lpThreadAttributes
    .text:004016EC                 call    ds:CreateThread
    .text:004016F2                 mov     dword_40CC94, eax
    .text:004016F7                 mov     eax, [ebp+arg_8]
    .text:004016FA                 add     dword ptr [eax+0B8h], 2
    .text:00401701                 xor     eax, eax
    .text:00401703                 pop     ebp
    .text:00401704                 retn    10h
     
    На самом деле, тут я поленилась немного. Значение 4013C0h было когда-то адресом джампа, посредством которого, после установки BTF мы окажемся в хендлере. Понятное дело, что после перекомпиляций этот код сместился. Потом я порывалась поправить, а потом подумала, что нахер его исправлять, если следующий код все равно проверяет на варю, а на варе полюбому не пашет трассировка ветвлений. Далее я просто устраиваю переполнение стека и все слетает. Ну метод с str ax в общем-то немудреный и практически всем известен. Далее, если мы не на варе и все круто мы создаем тред, который дальше будет работать. Давайте заглянем что как в этом треде. Помимо стандартного кода чтения имени с эдита, там есть 1 подъ№б с KiGetTickCount (int 0x2a). Расчет сделан та тех, кто опрометчиво начнет останавливаться на всяких GetWindowText и тут-то время выполнения увеличиться и далее реверсми упадет после возврата из первого же исключения. Ну это я опять забежала вперед. Зайдем в первый, не связанный с различными манипуляциями с именем call


    Code:
    .text:004013AB sub_4013AB      proc near               ; CODE XREF: StartAddress+EA p
    .text:004013AB
    .text:004013AB flOldProtect    = dword ptr -8
    .text:004013AB var_4           = dword ptr -4
    .text:004013AB
    .text:004013AB                 push    ebp
    .text:004013AC                 mov     ebp, esp
    .text:004013AE                 sub     esp, 14h
    .text:004013B1                 push    ebx
    .text:004013B2                 push    esi
    .text:004013B3                 push    edi
    .text:004013B4                 lea     eax, [ebp+flOldProtect]
    .text:004013B7                 push    eax             ; lpflOldProtect
    .text:004013B8                 push    40h             ; flNewProtect
    .text:004013BA                 push    3               ; dwSize
    .text:004013BC                 mov     esi, offset int_call2
    .text:004013C1                 xor     edi, edi
    .text:004013C3                 push    esi             ; lpAddress
    .text:004013C4                 mov     [ebp+var_4], edi
    .text:004013C7                 call    ds:VirtualProtect
    .text:004013CD                 test    eax, eax
    .text:004013CF                 jz      short loc_4013E8
    .text:004013D1                 mov     eax, esi
    .text:004013D3                 mov     byte ptr [eax], 0CDh // опкод int XX
    .text:004013D6                 mov     cl, byte_40CCA0 
    .text:004013DC                 mov     [eax+1], cl
    .text:004013DF                 mov     byte ptr [eax+2], 90h // nop 
    .text:004013E3                 call    int_call2
     

    Как видим, снова VirtualProtect и снова запись каких-то инструкций прерыванием. Обзовем модифицируемый код int_call2

    Code:
    .text:0040139E int_call2       proc near               ; CODE XREF: sub_4013AB+38 p
    .text:0040139E                                         ; sub_4013AB+46 p ...
    .text:0040139E                 push    4 // это будет перезаписано прерыванием
    .text:004013A0                 pop     eax
    .text:004013A1                 add     eax, 0FFFFFFFCh // + (-4)
    .text:004013A4                 jz      short loc_4013AA
    .text:004013A6
    .text:004013A6 loc_4013A6:                             ; CODE XREF: int_call2+A j
    .text:004013A6                 push    0FFFFFFFFh // переполнение стека
    .text:004013A8                 jmp     short loc_4013A6
    .text:004013AA ; ---------------------------------------------------------------------------
    .text:004013AA
    .text:004013AA loc_4013AA:                             ; CODE XREF: int_call2+6 j
    .text:004013AA                 retn
    
    В чем тут фишка? Снова смотрим выше на хендлер прерывания. Если в edi не передано никаких значений, то проверяем флаг трассировки.

    Code:
    .text:00011412                 mov     eax, [esp+arg_4] // EFLAGS
    .text:00011416                 bt      eax, 8 // 8-й бит
    .text:0001141A                 jnb     short loc_1144C
    .text:0001141C                 mov     eax, 0FFFFFFFCh
    .text:00011421                 jmp     short locret_1145F
    .text:00011423 ; ---------------------------------------------------------------------------
    
    …
    
    .text:0001144C loc_1144C:                              ; CODE XREF: InterruptHandler+3A j
    .text:0001144C                 mov     eax, 4 // нет трассировки
    .text:00011451                 jmp     short locret_1145F
    .text:00011453 ; ---------------------------------------------------------------------------
    .text:00011453
    
    …
    
    .text:0001145F                 iret
    .text:0001145F InterruptHandler endp
    
     
    #2 0x0c0de, 7 Mar 2009
    Last edited: 7 Mar 2009
  3. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    В случае, если по прерыванию прошлись по F7 в ольге то в eax положим -4, если все ок, то 4. А значит, в юзермоде эта проверка

    Code:
    .text:004013A1                 add     eax, 0FFFFFFFCh // + (-4)
    .text:004013A4                 jz      short loc_4013AA
    .text:004013A6
    .text:004013A6 loc_4013A6:                             ; CODE XREF: int_call2+A j
    .text:004013A6                 push    0FFFFFFFFh // переполнение стека
    .text:004013A8                 jmp     short loc_4013A6
    
    приобрела много больше смысла. 4-4 = 0 и нас не трассирут. -4-4!=0 => нас трассируют. Окей. Двигаемся дальше

    Code:
    .text:004013E8 loc_4013E8:                             ; CODE XREF: sub_4013AB+24 j
    .text:004013E8                 and     eax, 0
    .text:004013EB                 mov     ax, fs
    .text:004013EE                 mov     [ebp+var_4], eax
    .text:004013F1                 call    int_call2
    .text:004013F1 sub_4013AB      endp
    .text:004013F1
    .text:004013F6 ; ---------------------------------------------------------------------------
    .text:004013F6                 push    edi
    .text:004013F7                 lea     eax, [ebp-10h]
    .text:004013FA                 push    eax
    .text:004013FB                 push    0Ch
    .text:004013FD                 push    offset dword_40CCB4
    .text:00401402                 push    4
    .text:00401404                 lea     eax, [ebp-4]
    .text:00401407                 push    eax
    .text:00401408                 push    222004h
    .text:0040140D                 push    hDevice
    .text:00401413                 call    ds:DeviceIoControl
    .text:00401419                 test    eax, eax
    .text:0040141B                 jz      loc_4014FE
    .text:00401421                 call    int_call2
    .text:00401426 ; ---------------------------------------------------------------------------
    .text:00401426                 call    int_call2 // проверка на трассировку
    .text:0040142B ; ---------------------------------------------------------------------------
    .text:0040142B                 push    offset loc_40135D
    .text:00401430                 mov     eax, dword_40CCB4
    .text:00401435                 push    dword ptr [eax]
    .text:00401437                 mov     [eax], esp
    .text:00401439                 ud2
    

    Всматриваемся в код внимательно и видим, что в драйвер передается содержание сегментного регистра fs. Окей. Ну тогда снова в драйвер. В процедуру обработки ioctl запросов. На этот раз запрос 222004h. Вычитаем как обычно 222004h. Получаем нулевой кейз. Ай-да его ковырять. Всякие проверки, имеющие отношение только к стабильности я опускаю. Перейдем сразу к делу.

    Code:
    .text:00011728                 sgdt    fword ptr gdtr__ // опять? )))))))
    .text:0001172F                 mov     eax, [esi]
    .text:00011731                 lea     edx, [esp+20h+var_8]
    .text:00011735                 push    edx
    .text:00011736                 push    eax
    .text:00011737                 push    offset gdtr__
    .text:0001173C                 call    sub_11200
     
    Зайдем в sub_11200. Судя по всему этой процедуре зачем-то нужна gdt.

    Code:
    .text:00011200 GetFsBase       proc near               ; CODE XREF: DeviceControlRoutine+18C p
    .text:00011200
    .text:00011200 arg_0           = dword ptr  4
    .text:00011200 selector        = dword ptr  8
    .text:00011200 arg_8           = dword ptr  0Ch
    .text:00011200
    .text:00011200                 mov     ecx, [esp+arg_0]
    .text:00011204                 mov     eax, [esp+selector]
    .text:00011208                 mov     edx, [ecx+2]
    .text:0001120B                 push    esi
    .text:0001120C                 shr     eax, 3
    .text:0001120F                 lea     esi, [edx+eax*8]
    .text:00011212                 push    esi             ; VirtualAddress
    .text:00011213                 call    ds:MmIsAddressValid
    .text:00011219                 test    al, al
    .text:0001121B                 jnz     short valid
    .text:0001121D                 xor     eax, eax
    .text:0001121F                 pop     esi
    .text:00011220                 retn    0Ch
    .text:00011223 ; ---------------------------------------------------------------------------
    .text:00011223
    .text:00011223 valid:                                  ; CODE XREF: GetFsBase+1B j
    .text:00011223                 mov     eax, [esp+4+arg_8]
    .text:00011227                 mov     ecx, [esi]
    .text:00011229                 mov     [eax], ecx
    .text:0001122B                 mov     edx, [esi+4]
    .text:0001122E                 xor     ecx, ecx
    .text:00011230                 mov     [eax+4], edx
    .text:00011233                 mov     ch, [eax+7]
    .text:00011236                 pop     esi
    .text:00011237                 mov     cl, dl
    .text:00011239                 movzx   edx, word ptr [eax+2]
    .text:0001123D                 shl     ecx, 10h
    .text:00011240                 or      ecx, edx
    .text:00011242                 mov     eax, ecx
    .text:00011244                 retn    0Ch
    .text:00011244 GetFsBase       endp
    

    Объясняю суть того, что здесь происходит. Как мы помним, передан был селектор на дескриптор. Я уже написала как по селектору высчитать индекс в дескрипторной таблице.
    Ну так вот. Сначала получаем указатель на сам дескриптор, а потом копируем его содержимое.

    Code:
    .text:0001173C                 call    GetFsBase
    .text:00011741                 add     [esp+20h+var_6], 0F000h
    .text:00011748                 add     [esp+20h+var_8], 3E8h
    .text:0001174F                 lea     ecx, [esp+20h+var_8]
    .text:00011753                 push    ecx
    .text:00011754                 mov     edi, eax
    .text:00011756                 call    AddDescriptorIntoGdt
    .text:0001175B                 cmp     eax, ebx
    .text:0001175D                 jnz     short all_good_
    .text:0001175F                 mov     ebp, [esp+20h+var_10]
    .text:00011763                 mov     ebx, STATUS_UNSUCCESSFUL
    .text:00011768                 jmp     short loc_11791
    .text:0001176A ; ---------------------------------------------------------------------------
    .text:0001176A
    .text:0001176A all_good_:                              ; CODE XREF: DeviceControlRoutine+1AD j
    .text:0001176A                 mov     uFsDescr, eax
    .text:0001176F                 mov     [esi+4], eax
    .text:00011772                 mov     [esi], edi
    .text:00011774                 mov     ebp, 8
    .text:00011779                 call    CheckIdtHook // снова проверка на хуки в итд, это все будет иметь смысл при генерации ключа
    .text:0001177E                 neg     eax
    .text:00011780                 sbb     eax, eax
    .text:00011782                 and     eax, DBG_TERMINATE_THREAD
    .text:00011787                 mov     [esi+8], eax
    .text:0001178A                 jmp     short loc_11791
    .text:0001178C ; -------------------------------------------------- 
    
    Значит скопировали дескриптор, на который указывает селектор в fs. Потом мы его редактируем, а именно новая база нового сегмента будет на 0x1000 меньше. Окей, потом пересчитываем лимит (причем я пересчитываю его не точно, но реально это не имеет значения, так как нужный диапазон это с лихвой покрывает). Скажу тут сразу, что селектор на новый дескриптор будет использован мной для установки seh обработчиков через gs. Так же как и база старого fs. Так что подробно уже на этом не останавливаюсь. Покажу как это в юзермоде

    Code:
    0040142B   68 5D134000      PUSH reversem.0040135D
    00401430   A1 B4CC4000      MOV EAX,DWORD PTR DS:[40CCB4]
    00401435   FF30             PUSH DWORD PTR DS:[EAX]
    00401437   8920             MOV DWORD PTR DS:[EAX],ESP
    00401439   0F0B             UD2
    0040143B   66:8EE8          MOV GS,AX                                ; Modification of segment register
    0040143E   59               POP ECX
    0040143F   59               POP ECX
    00401440   68 DD124000      PUSH reversem.004012DD
    00401445   65:FF35 00100000 PUSH DWORD PTR GS:[1000] 
    0040144C   65:8925 00100000 MOV DWORD PTR GS:[1000],ESP
    00401453   0F0B             UD2
    
    Видите? Сначала устанавливается обработчик по имеющейся базе. Вот его код

    Code:
    .text:0040135D loc_40135D:                             ; DATA XREF: .text:0040142B o
    .text:0040135D                 mov     ecx, [esp+0Ch]
    .text:00401361                 lea     eax, [ecx+0B8h]
    .text:00401367                 add     dword ptr [eax], 2 // модификация адреса возврата из seh
    .text:0040136A                 mov     edx, [eax]
    .text:0040136C                 push    esi
    .text:0040136D                 mov     esi, dwCount // а вот тут мы при возврате из seh добавляем еще наш счетчик между int 2a в начале 
    .text:00401373                 add     edx, esi
    .text:00401375                 mov     [eax], edx
    .text:00401377                 mov     eax, dword_40CCB8
    .text:0040137C                 mov     [ecx+0B0h], eax
    .text:00401382                 xor     eax, eax
    .text:00401384                 pop     esi
    .text:00401385                 retn    10h
    
    Этот обработчик не делает ничего интересного, кроме как неявно проверяет счетчки между вызовами KiGetTickCount и кладет в ax селектор на новый созданный дескриптор. Окей! Разобрались. Двигаемся дальше. Второй обработчик

    Code:
    .text:004012DD ; int __cdecl SEH2Handler(DWORD NumberOfBytesWritten, int, int)
    .text:004012DD SEH2Handler     proc near
    .text:004012DD
    .text:004012DD NumberOfBytesWritten= dword ptr  8
    .text:004012DD arg_8           = dword ptr  10h
    .text:004012DD
    .text:004012DD                 push    ebp
    .text:004012DE                 mov     ebp, esp
    .text:004012E0                 mov     eax, [ebp+NumberOfBytesWritten]
    .text:004012E3                 mov     eax, [eax]
    .text:004012E5                 cmp     eax, EXCEPTION_BREAKPOINT
    .text:004012EA                 jz      short loc_401329
    .text:004012EC                 cmp     eax, EXCEPTION_ILLEGAL_INSTRUCTION
    .text:004012F1                 jnz     short loc_401342
    .text:004012F3                 push    0               ; lpOverlapped
    .text:004012F5                 lea     eax, [ebp+NumberOfBytesWritten]
    .text:004012F8                 push    eax             ; lpNumberOfBytesWritten
    .text:004012F9                 push    nNumberOfBytesToWrite ; nNumberOfBytesToWrite
    .text:004012FF                 push    offset byte_40CC70 ; lpBuffer
    .text:00401304                 push    hDevice         ; hFile
    .text:0040130A                 call    ds:WriteFile
    .text:00401310                 push    0               ; lpOverlapped
    .text:00401312                 push    offset NumberOfBytesRead ; lpNumberOfBytesRead
    .text:00401317                 push    0               ; nNumberOfBytesToRead
    .text:00401319                 push    0               ; lpBuffer
    .text:0040131B                 push    hDevice         ; hFile
    .text:00401321                 call    ds:ReadFile
    .text:00401327                 jmp     short loc_401342
    .text:00401329 ; ---------------------------------------------------------------------------
    …
    …
    …
    
    .text:0040135C SEH2Handler     endp
    
    Итак, инструкция ud2 спровоцирует EXCEPTION_ILLEGAL_INSTRUCTION => Начинается процесс обращения к драйверу через ReadFile/WriteFile. Скажу сразу, что тут только калькуляция серийного номера. WriteFile считает серийник, ReadFile заканчивает калькуляцию вот так

    Code:
    .text:000117E0 ReadHandler:                            ; DATA XREF: DllEntryPoint+6A o
    .text:000117E0                 mov     eax, [esp+8]
    .text:000117E4                 xor     serialnumb, 33223322h
    .text:000117EE                 push    0
    .text:000117F0                 push    0
    .text:000117F2                 push    eax
    .text:000117F3                 call    CompleteIrp
    .text:000117F8                 retn    8
    
    Переменная serialnumb сохранена в драйвере и будет использоваться при заключительной проверке. WriteHandler я оставляю интересующимся.

    Отлично, мы отработали в seh – хендлере. Теперь

    Code:
    .text:00401342 return:                                 ; CODE XREF: SEH2Handler+14 j
    .text:00401342                                         ; SEH2Handler+4A j
    .text:00401342                 mov     eax, [ebp+arg_8]
    .text:00401345                 add     eax, 0B8h
    .text:0040134A                 add     dword ptr [eax], 2 // eip+2
    .text:0040134D                 mov     ecx, [eax]
    .text:0040134F                 mov     edx, dwCount // снова учет времени между int 0x2a
    .text:00401355                 add     ecx, edx
    .text:00401357                 mov     [eax], ecx
    .text:00401359                 xor     eax, eax
    .text:0040135B                 pop     ebp
    .text:0040135C                 retn
    .text:0040135C SEH2Handler     endp
    
    Возврат из хендлера.

    Code:
    .text:0040145A                 push    edi
    .text:0040145B                 lea     eax, [ebp-0Ch]
    .text:0040145E                 push    eax
    .text:0040145F                 push    3EAh
    .text:00401464                 push    dword_40CCCC
    .text:0040146A                 call    ds:GetDlgItemInt // серийник- число онли!
    .text:00401470                 cmp     [ebp-0Ch], edi
    .text:00401473                 mov     [ebp-14h], eax
    .text:00401476                 jz      short loc_4014B1
    .text:00401478                 mov     eax, NtstatusSave // а вот и наш NtStatus, о котором я говорила в самом начале
    .text:0040147D                 mov     ecx, dword_40CCBC
    .text:00401483                 add     ecx, eax
    .text:00401485                 add     [ebp-14h], ecx
    .text:00401488                 pusha
    .text:00401489                 mov     eax, [ebp-14h] // eax - серийник
    .text:0040148C                 mov     edi, 'last' // окей, снова в прерывание
    .text:00401491                 call    int_call2
    .text:00401496 ; ---------------------------------------------------------------------------
    .text:00401496                 sub     esi, 0 // серийник верный, в esi адрес перехода?
    .text:00401499                 jz      short loc_4014A5
    .text:0040149B                 push    esi
    .text:0040149C                 pop     edi
    .text:0040149D                 add     edi, dword_40CCEC
    .text:004014A3                 call    edi 
    .text:004014A5
    .text:004014A5 loc_4014A5:                             ; CODE XREF: .text:00401499 j
    
    Обратите внимание, что серийник, введенный юзером, считается с учетом наличия отладчика. И если вы решили пойти по пути отладки и были невнимательны, даже в случае нахождения верного ключа - аплодесментов не получите ). Ага, снова отправляемся в наше прерывание и смотрим что будет, если edi == ‘last’.

    Code:
    .text:000113E0 InterruptHandler proc near              ; DATA XREF: DeviceControlRoutine+CD o
    .text:000113E0                                         ; DeviceControlRoutine+D6 o
    .text:000113E0
    .text:000113E0 arg_4           = dword ptr  8
    .text:000113E0
    .text:000113E0                 cmp     edi, 'bran'
    .text:000113E6                 jz      short msrset
    .text:000113E8                 cmp     edi, 'sele'
    .text:000113EE                 jz      short secondcheck
    .text:000113F0                 cmp     edi, 'last'
    .text:000113F6                 jnz     short check_tf
    .text:000113F8                 mov     edi, serialnumb // вот серийник, сохраненный в дрове
    .text:000113FE                 xor     eax, edi // ксорим что в eax [значение введенное юзером в поле серийника + учет отладки] 
    .text:00011400                 mov     ebx, eax
    .text:00011402                 mov     edi, 'sele' // прерывание готовится вернутся снова на нструкцию int xx и вызвать прерывание снова
    .text:00011407                 mov     eax, [esp+0]
    .text:0001140A                 sub     eax, 2 // eip_ret - 2
    .text:0001140D                 mov     [esp+0], eax
    .text:00011410                 jmp     short intreturn
    .text:00011412 ; ---------------------------------------------------------------------------
    .text:00011412
    .text:00011412 check_tf:                               ; CODE XREF: InterruptHandler+16 j
    .text:00011412                 mov     eax, [esp+arg_4]
    .text:00011416                 bt      eax, 8
    .text:0001141A                 jnb     short not_tf
    .text:0001141C                 mov     eax, 0FFFFFFFCh
    .text:00011421                 jmp     short intreturn
    .text:00011423 ; ---------------------------------------------------------------------------
    .text:00011423
    .text:00011423 secondcheck:                            ; CODE XREF: InterruptHandler+E j
    .text:00011423                 cmp     ebx, 0 // результат ксора
    .text:00011426                 jnz     short not_valid_serial
    .text:00011428                 mov     esi, 4010F1h // если валид сериал.
    .text:0001142D                 mov     eax, [esp+0]
    .text:00011430                 sub     eax, 2
    .text:00011433                 mov     [esp+0], eax
    .text:00011436                 sub     edi, edi
    .text:00011438                 jmp     short intreturn
    .text:0001143A ; ---------------------------------------------------------------------------
    .text:0001143A
    .text:0001143A not_valid_serial:                       ; CODE XREF: InterruptHandler+46 j
    .text:0001143A                 mov     esi, 0
    .text:0001143F                 mov     eax, [esp+0]
    .text:00011442                 sub     eax, 2
    .text:00011445                 mov     [esp+0], eax
    .text:00011448                 sub     edi, edi
    .text:0001144A                 jmp     short intreturn
    .text:0001144C ; ---------------------------------------------------------------------------
    .text:0001144C
    .text:0001144C not_tf:                                 ; CODE XREF: InterruptHandler+3A j
    .text:0001144C                 mov     eax, 4
    .text:00011451                 jmp     short intreturn
    .text:00011453 ; ---------------------------------------------------------------------------
    .text:00011453
    .text:00011453 msrset:                                 ; CODE XREF: InterruptHandler+6 j
    .text:00011453                 mov     ecx, 1D9h
    .text:00011458                 rdmsr
    .text:0001145A                 or      eax, 3
    .text:0001145D                 wrmsr
    .text:0001145F
    .text:0001145F intreturn:                              ; CODE XREF: InterruptHandler+30 j
    .text:0001145F                                         ; InterruptHandler+41 j ...
    .text:0001145F                 iret
    .text:0001145F InterruptHandler endp
    
    А вот тут уже надо быть еще внимательней. Хендлер прерывания, получая 'last' в edi, ксорит серийник из юзермода с вычисленным значением в дрове, затем изменяет eip (то место, куда по идее надо бы вернуться после iret) и кладет в edi 'sele' . И возврат снова на инструкцию прерывания. Но в edi будет уже 'sele' и если разница между верным серийником и тем, что введено пользователем 0, то в esi кладется адрес 4010F1h.

    Usermode part

    Code:
    .text:00401496                 sub     esi, 0
    .text:00401499                 jz      short loc_4014A5
    .text:0040149B                 push    esi
    .text:0040149C                 pop     edi
    .text:0040149D                 add     edi, dword_40CCEC
    .text:004014A3                 call    edi
    
    Если серийник верный, то мы перейдем по адресу 4010F1h. Что там?

    Code:
    .text:004010F1 loc_4010F1:                             ; DATA XREF: sub_401630+8 o
    .text:004010F1                 int     3               ; Trap to Debugger
    .text:004010F2                 nop
    .text:004010F3                 retn
    
    И все =) Однако, вспомним, что у нас до сих пор установлен обработчик исключений SEH2Handler. И там была проверка на эксепшн EXCEPTION_BREAKPOINT.

    Code:
    ]
    text:004012E5                 cmp     eax, EXCEPTION_BREAKPOINT
    .text:004012EA                 jz      short loc_401329
    …
    …
    …
    
    .text:00401329 loc_401329:                             ; CODE XREF: SEH2Handler+D j
    .text:00401329                 push    40004h          ; fdwSound
    .text:0040132E                 push    0               ; lpModuleName
    .text:00401330                 call    ds:GetModuleHandleW
    .text:00401336                 push    eax             ; hmod
    .text:00401337                 push    83h             ; pszSound
    .text:0040133C                 call    ds:PlaySoundW
    .text:00401342
    .text:00401342 return:                                 ; CODE XREF: SEH2Handler+14 j
    
    И))))) Если серийник верный, хендлер прерывания возвращает в esi адрес перехода, мы переходим по нему в юзермоде, по этому адресу int 3 == EXCEPTION_BREAKPOINT и мы слышим аплодисменты. Вот таки дела.

    Я не объяснила ВСЕ. Но тем, кто пробовал, пускай даже не отписался, этого будет достаточно, чтобы понять что к чему. Надеюсь, эта моя работа будет для кого-то познавательной. Тогда я не зря потратила время.



    На этом пока прощаюсь, удачи!
     
    #3 0x0c0de, 7 Mar 2009
    Last edited: 7 Mar 2009
    2 people like this.
  4. ex3me

    ex3me Member

    Joined:
    7 Jan 2009
    Messages:
    0
    Likes Received:
    63
    Reputations:
    0
    Пусть модеры удалят этот пост, но тут слов нету. Апплодисменты!!!
    Я не супер-реверсер, и даже по рангу - ниже новичка, но изложенное выше меня поразило оО!

    Кстати, ветка Реверсинга на ачате дохлая =\ Может не мое дело, но имхо - надо как-то оживлять... Заполнять чтоли или хз =\
     
  5. RedAlert

    RedAlert Elder - Старейшина

    Joined:
    26 May 2008
    Messages:
    66
    Likes Received:
    10
    Reputations:
    0
    Огромная работа , респект от сердца . Все понятно и не к чему придраться =)
     
  6. De-visible

    De-visible [NDC] Network develope c0ders

    Joined:
    6 Jan 2008
    Messages:
    916
    Likes Received:
    550
    Reputations:
    66
    протеус походу сначало не видел твою темку, так бы наверняка поковырял...
    taha в армии, Грейт редко бывает, neprovad хз, ну а остальные наврядли бы стали браться:D
     
  7. Tigger

    Tigger Elder - Старейшина

    Joined:
    27 Aug 2007
    Messages:
    936
    Likes Received:
    527
    Reputations:
    204
    Хех) Молодец!
     
  8. winterfrost

    winterfrost Elder - Старейшина

    Joined:
    18 Aug 2008
    Messages:
    42
    Likes Received:
    18
    Reputations:
    15
    всё это конечно здорово, но, имхо, солюшен рано выложен. Очень рано.
     
  9. Unregistered

    Unregistered Member

    Joined:
    15 Jan 2009
    Messages:
    24
    Likes Received:
    10
    Reputations:
    1
    0x0c0de, с Вами можно связаться как-то еще кроме жабы и ЖЖ?
     
  10. -m0rgan-

    -m0rgan- Elder - Старейшина

    Joined:
    29 Sep 2008
    Messages:
    514
    Likes Received:
    170
    Reputations:
    17
    респект!
    Отличная работа, большой труд!
     
  11. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297

    У тех, кого ты перечислил объективно времени нет на реверсми. У всех работа. Это, кстати не полный список, правда остальные тоже здесь нечасто бывают.

    2winterfrost если бы было обсуждение нормальное, я бы не стала писать солюшн. В следующий раз активней будут ) Привыкли, что задачи нерешенными месяцами лежат. Ждала 5 суток, что кто-то отпишет про прерывание хотя бы. Тогда и разговора бы не было.

    2Unregistered да можно, icq

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


    Хотелось бы отметить еще некоторые особенности. Как вы помните в этом фрагменте заюзана трассировка ветвлений

    Code:
    00401388   $ CD 20          INT 20
    0040138A   . 9C             PUSHFD
    0040138B   . 5F             POP EDI
    0040138C   . 0FBAEF 08      BTS EDI,8
    00401390   . 57             PUSH EDI
    00401391   . 9D             POPFD
    00401392   . 90             NOP
    00401393   . 90             NOP
    00401394   . 90             NOP
    00401395   . 90             NOP
    00401396   . 90             NOP
    00401397   . 90             NOP
    00401398   . 90             NOP
    00401399   . 33C0           XOR EAX,EAX
    0040139B   >-EB FE          JMP SHORT reversem.0040139B
    
    устанавливается флаг tf, при установленном btf в msr регистре. При этом уже установлен SEH-обработчик, который должен сработать когда управление дойдет до jmp. Ну так вот.
    Если мы попытаемся пройтись по прерыванию по f7, то ольга понятное дело установит tf и установит раньше меня (раньше пары инструкций pushfd/popfd). При этом ее трассировка будет учитывать btf. Оля перебросит нас на jmp после прерывания, и после этого если мы нажмем shift+f9 SEH управления не получит, а программа зациклиться на джампе.

    Если вообще эту процедуру не трогать, то при отсутствии игнора эксепшнов мы остановимся как раз на джампе [single-step event]

    По крайней мере это так в имунити и обычной ольге.

    Поэтому, как я говрила, лучше ковырять в иде.
     
    #11 0x0c0de, 8 Mar 2009
    Last edited: 8 Mar 2009