Авторские статьи Распаковка Ntkrnl packer

Discussion in 'Статьи' started by 0x0c0de, 18 Oct 2007.

  1. 0x0c0de

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

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    Распаковка Ntkrnlpacker.

    [Intro]

    Итак, в связи с болезнью появилось свободное время, которое я трачу на реверсинг и математику. Что мне удалось написать под температурой 38 читаем ниже. Пинаемся, кусаемся и критикуем как обычно в комментариях=).

    [Начнем]

    Вот, что пишут про данный прот

    И все в таком духе. В общем как обычно. Раньше об этом агрегате не слышала, стало интересно посмотреть, что за зверь. А зверь оказался интересный. Начнем с entry point запакованной программы

    Code:
    00401061 C>  68 5D544100                  PUSH anyprog.0041545D
    00401066     E8 01000000                  CALL anyprog.0040106C
    0040106B     C3                           RETN
    0040106C     C3                           RETN
    
    И, черт возьми, в этом было нечто знакомое ;-) Проблема возникла с отладкой. Оля до определенного момента дебажила, а потом прога вылетала. Причем что только я не перепробовала. Конечно, возникла идея приаттачится к процессу, но об этом чуть позже. Сначала расскажу о тех анти-дебаг приемах, которые используются (встретившиеся мне до того, как Оля уперлась рогами и отказалась работать).
    Ниже привожу два куска кода, которые с завидным постоянством и незначительной модификацией кочуют из сорца в сорец)
    Спаливание Оли через RDTSC

    Code:
    00415F3D     0F31                         RDTSC
    00415F3F     33C9                         XOR ECX,ECX
    00415F41     03C8                         ADD ECX,EAX
    00415F43     0F31                         RDTSC
    00415F45     2BC1                         SUB EAX,ECX
    00415F47     3D FF0F0000                  CMP EAX,0FFF
    00415F4C     72 04                        JB SHORT anyprog.00415F52
    00415F4E     0F31                         RDTSC
    00415F50     50                           PUSH EAX
    
    И обнуление др-регистров (через seh)

    Code:
    00415FE6     55                           PUSH EBP
    00415FE7     8BEC                         MOV EBP,ESP
    00415FE9     BB 00010000                  MOV EBX,100
    00415FEE     F7D3                         NOT EBX
    00415FF0     8B45 10                      MOV EAX,DWORD PTR SS:[EBP+10]
    00415FF3     2198 C0000000                AND DWORD PTR DS:[EAX+C0],EBX
    00415FF9     8BB8 C4000000                MOV EDI,DWORD PTR DS:[EAX+C4]
    00415FFF     C740 04 00000000             MOV DWORD PTR DS:[EAX+4],0 ; обнуляем др0 регистр
    00416006     3E:FF37                      PUSH DWORD PTR DS:[EDI]
    00416009     64:8F05 00000000             POP DWORD PTR FS:[0]
    00416010     C740 08 00000000             MOV DWORD PTR DS:[EAX+8],0 ; обнуляем др1 регистр
    00416017     8380 C4000000 08             ADD DWORD PTR DS:[EAX+C4],8
    0041601E     8BB8 A4000000                MOV EDI,DWORD PTR DS:[EAX+A4]
    00416024     C740 0C 00000000             MOV DWORD PTR DS:[EAX+C],0 ; обнуляем др2 регистр
    0041602B     89B8 B8000000                MOV DWORD PTR DS:[EAX+B8],EDI
    00416031     8B5D 10                      MOV EBX,DWORD PTR SS:[EBP+10] 
    00416034     33C0                         XOR EAX,EAX
    00416036     8943 10                      MOV DWORD PTR DS:[EBX+10],EAX  ; обнуляем др3 регистр
    00416039     8943 14                      MOV DWORD PTR DS:[EBX+14],EAX  ; обнуляем др6 
    0041603C     C743 18 55010000             MOV DWORD PTR DS:[EBX+18],155 ; пишем в др7
    00416043     8B83 B0000000                MOV EAX,DWORD PTR DS:[EBX+B0]
     
    Потом проверка на бряки. Причем проверяет только первый байт функции. В общем не страшно.

    Code:
    00415FA7     55                           PUSH EBP
    00415FA8     8BEC                         MOV EBP,ESP
    00415FAA     8B45 08                      MOV EAX,DWORD PTR SS:[EBP+8]
    00415FAD     0340 3C                      ADD EAX,DWORD PTR DS:[EAX+3C]
    00415FB0     05 80000000                  ADD EAX,80
    00415FB5     8B08                         MOV ECX,DWORD PTR DS:[EAX]
    00415FB7     034D 08                      ADD ECX,DWORD PTR SS:[EBP+8]
    00415FBA     83C1 10                      ADD ECX,10
    00415FBD     8B01                         MOV EAX,DWORD PTR DS:[ECX]
    00415FBF     0345 08                      ADD EAX,DWORD PTR SS:[EBP+8]
    00415FC2     8B18                         MOV EBX,DWORD PTR DS:[EAX]
    00415FC4     803B CC                      CMP BYTE PTR DS:[EBX],0CC ; проверочка
    00415FC7     74 18                        JE SHORT anyprog.00415FE1
    00415FC9     8B55 0C                      MOV EDX,DWORD PTR SS:[EBP+C]
    00415FCC     891A                         MOV DWORD PTR DS:[EDX],EBX
    00415FCE     83C0 04                      ADD EAX,4
    00415FD1     8B18                         MOV EBX,DWORD PTR DS:[EAX]
    00415FD3     803B CC                      CMP BYTE PTR DS:[EBX],0CC ; проверочка
    00415FD6     74 09                        JE SHORT anyprog.00415FE1
    00415FD8     8B55 10                      MOV EDX,DWORD PTR SS:[EBP+10]
    00415FDB     891A                         MOV DWORD PTR DS:[EDX],EBX
    00415FDD     5D                           POP EBP
    
    Себя защита распаковывает долго и извратно. Там очень большой цикл (повторяющийся много раз), который здесь не приводится. А теперь вернемся к нашему процессу. Аттачимся. Оля ругнется на невалидный файл, так как присутствует антидамп, но о нем чуть позже. Теперь вопрос, как в такой ситуации найти oep? Ответ – посмотреть в стек. Кстати, как в самом протекторе, так и в нескольких тестовых запакованных программах с oep байт не крадется, что для нас большой плюс. Значит скролим окно стека вниз к самому началу и ищем первый адрес возврата, принадлежащий коду программы. Этот адрес будет находиться в непосредственной близости от oep. Будете ворчать, что способ нехороший. Знаю я, знаю) Но тем не менее в сработал успешно (несколько раз, а это уже статистика). Опять же. Дамп снимать еще пока нельзя. Так как у нас уже искажены переменные и с немалой долей вероятности программа будет работать неправильно, а некоторые (и много таких) программы вообще проверяют значение переменных. Так как в таком случае нам получить правильный дамп? Заинжектить длл, которая будет перехватывать какую-либо функцию, вызывающуюся непосредственно в oep-процедуре. Если,например, это делфи-программа, то идельно подходит GetModuleHandleA (в зависмости от компилятора это может быть __set_app_type или GetVersion). Причем нужно проверять адрес возврата, который мы знаем точно. И после этого (если все совпало), зацикливаем программу и дампим. В мое тестовом примере была прога на MingWin32 GCC 3.x. Оставим пока эту идею с dll и поговорим об импорте. Часть функций есть, а часть адресов нет. Смотрим (привожу кусок).

    Code:
    00407120  00F00000
    00407124  00F00084
    00407128  00F00108
    0040712C  00F0018C
    00407130  00F00210
    00407134  00F00294
    00407138  00F00318
    0040713C  00F0039C
    00407140  00F00420
    00407144  00000000
    00407148  00000000
    0040714C  77C0EEEB  msvcrt.__getmainargs
    00407150  77C0F1C5  msvcrt.__p__environ
    00407154  77C0F1DB  msvcrt.__p__fmode
    00407158  77C2537C  msvcrt.__set_app_type
    
    Что у нас по адресу 00F00000?

    Code:
    00F00000     60                           PUSHAD
    00F00001     E8 00000000                  CALL 00F00006
    00F00006     5D                           POP EBP
    00F00007     81ED 0F469400                SUB EBP,94460F
    00F0000D     FFB5 81469400                PUSH DWORD PTR SS:[EBP+944681]
    00F00013     FFB5 85469400                PUSH DWORD PTR SS:[EBP+944685]
    00F00019     FFB5 89469400                PUSH DWORD PTR SS:[EBP+944689]
    00F0001F     FFB5 79469400                PUSH DWORD PTR SS:[EBP+944679]
    00F00025     FFB5 7D469400                PUSH DWORD PTR SS:[EBP+94467D]
    00F0002B     E8 08000000                  CALL 00F00038
    00F00030     894424 1C                    MOV DWORD PTR SS:[ESP+1C],EAX
    00F00034     61                           POPAD
    00F00035     FFE0                         JMP EAX
    00F00037     C3                           RETN 
    
    Джамп еах передает управление оригинальной функции. Все переходники одинаковые. То же самое по адресу 00F0018C и всем остальным.
    Из этого всего делаем вывод. Запросто теперь напишем скрипт, восстанавливающий импорт. Он попросит ввести начало таблицы и ее конец. Принцип такой. Проходимся по всей таблице, берем невалидный адрес, устанавливаем eip на него ищем опкод jmp eax, ставим на него точку останова, выполняем… Кладем значение eax на место невалидного адреса.

    Code:
    var iat_begin
    var iat_end
    var element
    var iat_end_
    var jaddr
    
    ask "Iat begin"
    mov iat_begin,$RESULT
    cmp iat_begin,0
    je fin
    
    ask "Iat end"
    mov iat_end,$RESULT
    add iat_end,4
    mov iat_end_,$RESULT
    add iat_end_,4
    cmp iat_end,0
    je fin
    mov element,iat_begin
    mov eip,[element]
    
    start:
    findop eip,#FFE0#  ; jmp eax
    mov jaddr,$RESULT 
    bphws jaddr,"x" ; хардвар на джамп еах
    run
    mov [element],eax 
    add element,4
    cmp element,iat_end_
    je fin 
    cmp [element],0 ; ноль? пропускаем
    je add_
    cmp [element],50000000 ; адрес уже есть? пропускаем
    ja add_
    mov eip,[element]
    bphwc jaddr
    jmp start
    
    fin:
    msg "Done" 
    ret
    
    add_:
    add element,4
    cmp element,iat_end_
    je fin 
    je fin 
    cmp [element],0
    je add_
    cmp [element],50000000 ; адрес уже есть? пропускаем
    ja add_
    mov eip,[element]
    bphwc jaddr 
    jmp start
    
    Получаем нормальную таблицу. А теперь снова о dll. В моем случае oep

    Code:
    00401240     55                           PUSH EBP
    00401241     89E5                         MOV EBP,ESP
    00401243     83EC 08                      SUB ESP,8
    00401246     C70424 02000000              MOV DWORD PTR SS:[ESP],2
    0040124D     FF15 58714000                CALL DWORD PTR DS:[407158]                            ; msvcrt.__set_app_type
    00401253     E8 A8FEFFFF                  CALL anyprog.00401100
    
    Значит интересует нас функция __set_app_type из msvcrt.dll. Ее и будем перехватывать. Далее код искомой dll. Ассемблер …. Я здесь ничего не оптимизировала и писала на скорую руку. Важно было быстро получить результат

    Code:
    .386
    .model flat,stdcall
    option casemap:none
    include windows.inc
    include user32.inc
    include kernel32.inc
    includelib kernel32.lib
    includelib user32.lib
    .data
    lib db "msvcrt.dll",0
    proc_ db "__set_app_type",0
    buffersbytes db 6 dup (?)
    func_ dword ?
    .code
    unhook proc
    mov esi,offset buffersbytes
    mov edi,func_
    mov ecx,6
    rep movsb
    ret
    unhook  endp
    
    hook_ proc
    mov edi,func_
    mov byte ptr[edi],068h
    mov dword ptr [edi+1],offset hooked_
    mov byte ptr[edi+5],0c3h
    ret
    hook_ endp
    
    loop_ proc
    local dwOldProtect:dword
    invoke VirtualProtectEx,-1,00401253h, 3, PAGE_EXECUTE_READWRITE,addr dwOldProtect
    mov edi,00401253h
    mov word ptr[edi],0feebh ; бесконечный цикл
    ret
    loop_ endp
    
    hooked_ proc par1:dword
    cmp dword ptr[esp+4],00401253h ; адрес возврата
    jnz ret_ 
    call loop_
    ret_:
    call unhook
    push par1
    call func_
    pop par1
    call hook_
    ret
    hooked_ endp
    
    DllEntry proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD
    local dwOldProtect:dword
    	cmp  reason,DLL_PROCESS_ATTACH
    	jnz ret__
    	pushad
    	invoke GetModuleHandleA,offset lib
    	test eax,eax
    	jnz gaddress_
    	invoke LoadLibrary,offset lib
    	test eax,eax
    	je ext
    	gaddress_:
    	invoke GetProcAddress,eax,offset proc_
    	test eax,eax
    	je ext
    	mov func_,eax
    	invoke VirtualProtectEx,-1,func_, 6, PAGE_EXECUTE_READWRITE,addr dwOldProtect
    	test eax,eax
        jz ext
        mov edi,func_
        mov esi,offset buffersbytes
        mov dl,byte ptr[edi]
        mov byte ptr[esi],dl
        mov edx,dword ptr[edi+1]
        mov dword ptr[esi+1],edx
        mov dl,byte ptr[edi+5]
        mov byte ptr[esi+5],dl
        mov byte ptr[edi],068h
        mov dword ptr [edi+1],offset hooked_
        mov byte ptr[edi+5],0c3h
    	ext:
    	popad
    	jmp ret_
    	ret__:
    	call unhook
    	ret_:
    	ret
    DllEntry Endp
    End DllEntry
    
    Вот. Ее и заинжектим.
    Дамп. Выбираем опцию LordPE: Active dump engine->IntelliDump->Select! И дампим. Импорт восстанавливаем с помощью Imprec и нашего скрипта… На этом этапе последнее- это исправление двух байт, использующихся для зацикливания по адресу 00401253h (адрес возврата из функции). EBFE на E8A8.
    Все.

    -----------------------Добавлено-------------------------------
    Мешало отладке то, что прот устанавливал на память аттрибут PAGE_EXECUTE_READ|PAGE_GUARD. В Phantom plugin надо включить опцию Custom handler exceptions, тогда все будет норм отлаживаться. Антиотладка

    Code:
    00416957     6A 04                        PUSH 4
    00416959     68 00100000                  PUSH 1000
    0041695E     68 00100000                  PUSH 1000
    00416963     6A 00                        PUSH 0
    00416965     E8 78FFFFFF                  CALL CyberPro.004168E2                                ; JMP to kernel32.VirtualAlloc
    0041696A     C600 C3                      MOV BYTE PTR DS:[EAX],0C3
    0041696D     8985 9F029D01                MOV DWORD PTR SS:[EBP+19D029F],EAX
    00416973     8D85 9B029D01                LEA EAX,DWORD PTR SS:[EBP+19D029B]
    00416979     50                           PUSH EAX
    0041697A     68 20010000                  PUSH 120
    0041697F     6A 10                        PUSH 10
    00416981     FFB5 9F029D01                PUSH DWORD PTR SS:[EBP+19D029F]
    00416987     E8 50FFFFFF                  CALL CyberPro.004168DC                                ; JMP to kernel32.VirtualProtect
    0041698C     8B85 9F029D01                MOV EAX,DWORD PTR SS:[EBP+19D029F]
    00416992     FFD0                         CALL EAX
    00416994     0F31                         RDTSC
    00416996     50                           PUSH EAX
    00416997     C3                           RETN
    
    (с) 0x0c0de 2007
     
    #1 0x0c0de, 18 Oct 2007
    Last edited: 21 Oct 2007
    9 people like this.
  2. genom--

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

    Joined:
    9 Jul 2006
    Messages:
    668
    Likes Received:
    416
    Reputations:
    288
    хм не очень секу в асме -- но вродебы сам писал старался и не боян - в общем наконецто норм статья -- молодцом