Авторские статьи Исследование Enigma Protector 1.35

Discussion in 'Статьи' started by 0x0c0de, 17 Nov 2007.

  1. 0x0c0de

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

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    Исследование Enigma Protector 1.35 build20071012

    Начнем разбирать представленный агрегат

    [Инструменты]

    1.OllyDbg+PhantOm plugin+OllyAdvanced
    2.Imprec
    3.LordPE
    4.CFF Explorer
    5.Resource Binder

    [Обман анализаторов]

    Ерундово достаточно. Просто Borland C++ старт-ап=\
    Code:
      
    01003532 > /EB 10           JMP SHORT notepad_.01003544
    01003534   |66:623A         BOUND DI,DWORD PTR DS:[EDX]
    01003537   |43              INC EBX
    01003538   |2B2B            SUB EBP,DWORD PTR DS:[EBX]
    0100353A   |48              DEC EAX
    0100353B   |4F              DEC EDI
    0100353C   |4F              DEC EDI
    0100353D   |4B              DEC EBX
    0100353E   |90              NOP                                                                            ; Switch (cases 10..11)
    0100353F  -|E9 00104000     JMP 01404544
    01003544  -\E9 47670700     JMP notepad_.01079C90
    01003549    41              INC ECX
    
    Вообще там вариантов в настройках много чем можно прикинуться (Visual C++, Visual Basic, MASM etc etc). Но это не особо интересно. Die определяет все нормально, а peid действительно выдает, что программа незапакована.

    [Антиотладка]

    Антиотладка в протекторе не особо сильная. Обнуление dr-регистров

    Code:
    009BB652    3100            XOR DWORD PTR DS:[EAX],EAX ; эксепт 
    009BB654  ^ E9 EB67F9FF     JMP 00951E44
    009BB659    8B4424 0C       MOV EAX,DWORD PTR SS:[ESP+C] ; seh-обработчик 
    009BB65D    8B4C24 04       MOV ECX,DWORD PTR SS:[ESP+4]
    009BB661    C740 04 0000000>MOV DWORD PTR DS:[EAX+4],0
    009BB668    C740 08 0000000>MOV DWORD PTR DS:[EAX+8],0
    009BB66F    C740 0C 0000000>MOV DWORD PTR DS:[EAX+C],0
    009BB676    C740 10 0000000>MOV DWORD PTR DS:[EAX+10],0
    009BB67D    8160 14 F00FFFF>AND DWORD PTR DS:[EAX+14],FFFF0FF0
    009BB684    8160 18 00DC000>AND DWORD PTR DS:[EAX+18],0DC00
    009BB68B    C780 B8000000 9>MOV DWORD PTR DS:[EAX+B8],9BB698
    009BB695    31C0            XOR EAX,EAX
    009BB697    C3              RET
    
    Решение: в плагине PhantOm ставим опцию защиты др регистров..
    А еще протектор эмулирует IsDebuggerPresent

    Code:
    00902899    31C9            XOR ECX,ECX
    0090289B    64:8B05 1800000>MOV EAX,DWORD PTR FS:[18]
    009028A2    8B40 30         MOV EAX,DWORD PTR DS:[EAX+30]
    009028A5    0FB648 02       MOVZX ECX,BYTE PTR DS:[EAX+2]
    009028A9    E3 01           JECXZ SHORT 009028AC
    009028AB    40              INC EAX
     
    [OEP]

    OEP как это ни странно находится с помощью древнего метода hr esp-4. Но! Срабатываний будет 6 или 7 в таких местах

    Code:
    008FD3C2    60              PUSHAD
    008FD3C3    8B65 FC         MOV ESP,DWORD PTR SS:[EBP-4]
    008FD3C6    61              POPAD
    008FD3C7    8D25 4CD69800   LEA ESP,DWORD PTR DS:[98D64C]
    008FD3CD    61              POPAD
    008FD3CE    8B25 48D69800   MOV ESP,DWORD PTR DS:[98D648]
    
    а нужно нам только последнее

    Code:
    009FE46A    61              POPAD
    009FE46B    8B05 B8D69800   MOV EAX,DWORD PTR DS:[98D6B8]
    009FE471    50              PUSH EAX
    009FE472    8B05 B4D69800   MOV EAX,DWORD PTR DS:[98D6B4]
    009FE478    010424          ADD DWORD PTR SS:[ESP],EAX
    009FE47B    8BC4            MOV EAX,ESP
    009FE47D    81E8 7C000000   SUB EAX,7C
    009FE483    68 00000000     PUSH 0
    009FE488    3BE0            CMP ESP,EAX
    009FE48A  ^ 0F85 F3FFFFFF   JNZ 009FE483
    009FE490    81C4 7C000000   ADD ESP,7C
    009FE496    58              POP EAX
    009FE497    C78424 04000000>MOV DWORD PTR SS:[ESP+4],0
    009FE4A2    FFE0            JMP NEAR EAX ; переход на oep 
    
    Данный фрагмент легко идентифицировать во всех запакованных программах

    А сама oep выглядит так
    Code:
    009FDEEC    6A 70           PUSH 70
    009FDEEE    68 98180001     PUSH 1001898
    009FDEF3    E8 70966000     CALL notepad_.01007568
    009FDEF8    33DB            XOR EBX,EBX
    009FDEFA    53              PUSH EBX
    009FDEFB    8B3D CC100001   MOV EDI,DWORD PTR DS:[10010CC]
    009FDF01    FFD7            CALL NEAR EDI
    009FDF03    66:8138 4D5A    CMP WORD PTR DS:[EAX],5A4D
    009FDF08    0F85 30000000   JNZ 009FDF3E
    009FDF0E    8B48 3C         MOV ECX,DWORD PTR DS:[EAX+3C]
    009FDF11    03C8            ADD ECX,EAX
    009FDF13    8139 50450772   CMP DWORD PTR DS:[ECX],72074550
    009FDF19    0F85 1F000000   JNZ 009FDF3E
    009FDF1F    0FB781 18000000 MOVZX EAX,WORD PTR DS:[ECX+18]
    009FDF26    81F8 0B010000   CMP EAX,10B
    009FDF2C    0F84 34000000   JE 009FDF66
    009FDF32    81F8 0B020000   CMP EAX,20B
    009FDF38    0F84 0B000000   JE 009FDF49
    009FDF3E    899D E4FFFFFF   MOV DWORD PTR SS:[EBP-1C],EBX
    009FDF44    E9 3E000000     JMP 009FDF87
    009FDF49    81B9 84000000 0>CMP DWORD PTR DS:[ECX+84],0E
    009FDF53  ^ 0F86 E5FFFFFF   JBE 009FDF3E
    009FDF59    33C0            XOR EAX,EAX
    
    Неприятно удивили адреса. Выделенная память. Что с этим делать? Можно сдампить регион (Alt+M -в OllyDbg - выбираем нужный регион и жмем Dump Memory Area) и прикрутить к экзешнику в виде отдельной секции, а при загрузке спроецировать в память по тому же адресу. Так и сделаем

    [Импорт]

    Смотрим
    Code:
    010010A8   .  6606A000      DD 00A00666
    010010AC   .  E806A000      DD 00A006E8
    010010B0   .  7607A000      DD 00A00776
    010010B4   .  8108A000      DD 00A00881
    010010B8   .  4E09A000      DD 00A0094E
    010010BC   .  7609A000      DD 00A00976
    010010C0   .  18179000      DD 00901718
    010010C4   .  9209A000      DD 00A00992
    010010C8   .  B409A000      DD 00A009B4
    010010CC      88            DB 88
    010010CD      14            DB 14
    010010CE      90            NOP
    010010CF      00            DB 00
    010010D0      33            DB 33                                    ;  CHAR '3'
    010010D1      0A            DB 0A
    010010D2      A0            DB A0
    010010D3      00            DB 00
    010010D4   .  590BA000      DD 00A00B59
    010010D8   .  000CA000      DD 00A00C00
    010010DC   .  3C0DA000      DD 00A00D3C
    010010E0   .  7D0DA000      DD 00A00D7D
    010010E4   .  E70DA000      DD 00A00DE7
    010010E8   .  170EA000      DD 00A00E17
    
    Поясняю. Все адреса указывают на выделенную память. Посмотрим, что представляют из себя переходники…

    Code:
    00A006E8    6A 14           PUSH 14
    00A006EA    68 0801817C     PUSH 7C810108
    00A006EF    E8 D71DE07B     CALL kernel32.7C8024CB
    00A006F4    C745 E4 0100000>MOV DWORD PTR SS:[EBP-1C],1
    00A006FB    8B5D 08         MOV EBX,DWORD PTR SS:[EBP+8]
    00A006FE    F6C3 04         TEST BL,4
    00A00701  - 0F84 F6F9E07B   JE kernel32.7C8100FD
    00A00707    FF35 A433887C   PUSH DWORD PTR DS:[7C8833A4]
    00A0070D    FF15 9C12807C   CALL NEAR DWORD PTR DS:[<&ntdll.RtlLockH>; ntdll.RtlLockHeap
    00A00713    33FF            XOR EDI,EDI
    00A00715    897D FC         MOV DWORD PTR SS:[EBP-4],EDI
    00A00718    8D73 FC         LEA ESI,DWORD PTR DS:[EBX-4]
    00A0071B    8975 DC         MOV DWORD PTR SS:[EBP-24],ESI
    00A0071E    56              PUSH ESI
    00A0071F    68 E030887C     PUSH 7C8830E0
    00A00724    FF15 AC12807C   CALL NEAR DWORD PTR DS:[<&ntdll.RtlIsVal>; ntdll.RtlIsValidHandle
    00A0072A    84C0            TEST AL,AL
    00A0072C  - 0F84 DDFAE37B   JE kernel32.7C84020F
    00A00732    33C0            XOR EAX,EAX
    00A00734    66:8B46 02      MOV AX,WORD PTR DS:[ESI+2]
    00A00738    8D48 FF         LEA ECX,DWORD PTR DS:[EAX-1]
    00A0073B    66:894E 02      MOV WORD PTR DS:[ESI+2],CX 
    
    Опа.. Это сама функция, полностью украденная. Честно говоря, что конкретно это за функция я определить не могу, поэтому надо поискать среди украденных такую, определить название которой не составит труда. И такая функция нашлась

    Code:
    009ED424    6A 20           PUSH 20
    009ED426    68 D89B807C     PUSH 7C809BD8
    009ED42B    E8 9B50E17B     CALL kernel32.7C8024CB
    009ED430    81F3 37F4B4E7   XOR EBX,E7B4F437
    009ED436    81F3 37F4B4E7   XOR EBX,E7B4F437
    009ED43C    33DB            XOR EBX,EBX
    009ED43E    8B4D 14         MOV ECX,DWORD PTR SS:[EBP+14]
    009ED441    3BCB            CMP ECX,EBX
    009ED443  - 0F84 DC43E17B   JE kernel32.7C801825
    009ED449    8919            MOV DWORD PTR DS:[ECX],EBX
    009ED44B    64:A1 18000000  MOV EAX,DWORD PTR FS:[18]
    009ED451    8B40 30         MOV EAX,DWORD PTR DS:[EAX+30]
    009ED454    8B7D 08         MOV EDI,DWORD PTR SS:[EBP+8]
    009ED457    83FF F4         CMP EDI,-0C
    009ED45A  - 0F84 8245E17B   JE kernel32.7C8019E2
    009ED460    83FF F5         CMP EDI,-0B
    009ED463  - 0F84 6E45E17B   JE kernel32.7C8019D7
    009ED469    83FF F6         CMP EDI,-0A
    009ED46C  - 0F84 2944E17B   JE kernel32.7C80189B
    009ED472    8BC7            MOV EAX,EDI
    009ED474    25 03000010     AND EAX,10000003
    009ED479    83F8 03         CMP EAX,3
    009ED47C  - 0F84 2144E17B   JE kernel32.7C8018A3
    009ED482    8B75 18         MOV ESI,DWORD PTR SS:[EBP+18]
    009ED485    3BF3            CMP ESI,EBX
    009ED487  - 0F85 D144E17B   JNZ kernel32.7C80195E
    009ED48D    53              PUSH EBX
    009ED48E    53              PUSH EBX
    009ED48F    FF75 10         PUSH DWORD PTR SS:[EBP+10]
    009ED492    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
    009ED495    8D45 D8         LEA EAX,DWORD PTR SS:[EBP-28]
    009ED498    50              PUSH EAX
    009ED499    53              PUSH EBX
    009ED49A    53              PUSH EBX
    009ED49B    53              PUSH EBX
    009ED49C    57              PUSH EDI
    009ED49D    FF15 8C11807C   CALL NEAR DWORD PTR DS:[<&ntdll.NtReadFi>; ntdll.ZwReadFile
    
    Украдена ReadFile. Теперь ставим на ее начало (начало оригинальной функции в kernel32.dll) hardware breakpoint on access. Перезапускаем программу. Останавливаемся тут

    Code:
    008F8F4C    8A06            MOV AL,BYTE PTR DS:[ESI]
    008F8F4E    3D FF000000     CMP EAX,0FF
    
    Трассируем с помощью F7 (или Ctrl+F9 сразу) до первого ret. Возврат происходит сюда

    Code:
    00900D07    8D55 E6         LEA EDX,DWORD PTR SS:[EBP-1A]
    00900D0A    8BC6            MOV EAX,ESI
    00900D0C    E8 1382FFFF     CALL 008F8F24 ; дизассемблер длин, мы только были в нем 
    00900D11    8BD8            MOV EBX,EAX ; в eax длина инструкции
    00900D13    85DB            TEST EBX,EBX
    
    Вызов дизассемблера длин, в eax возвращается длина инструкции. Запись очередной инструкции происходит следующим образом

    Code:
    008F15C0    8B08            MOV ECX,DWORD PTR DS:[EAX]
    008F15C2    C601 6A         MOV BYTE PTR DS:[ECX],6A ; если у нас push число 
    008F15C5    FF00            INC DWORD PTR DS:[EAX]
    008F15C7    8B08            MOV ECX,DWORD PTR DS:[EAX]
    008F15C9    8811            MOV BYTE PTR DS:[ECX],DL
    008F15CB    FF00            INC DWORD PTR DS:[EAX]
    008F15CD    B8 02000000     MOV EAX,2
    008F15D2    C3              RET
    

    Code:
    008F15AC    8B08            MOV ECX,DWORD PTR DS:[EAX]
    008F15AE    C601 68         MOV BYTE PTR DS:[ECX],68 ; тоже разновидность push 
    008F15B1    FF00            INC DWORD PTR DS:[EAX]
    008F15B3    8B08            MOV ECX,DWORD PTR DS:[EAX]
    008F15B5    8911            MOV DWORD PTR DS:[ECX],EDX
    008F15B7    8300 04         ADD DWORD PTR DS:[EAX],4
    008F15BA    B8 05000000     MOV EAX,5
    008F15BF    C3              RET
    

    Code:
    008F1525    8D40 00         LEA EAX,DWORD PTR DS:[EAX]
    008F1528    8B08            MOV ECX,DWORD PTR DS:[EAX]
    008F152A    C601 E8         MOV BYTE PTR DS:[ECX],0E8 ; call
    008F152D    FF00            INC DWORD PTR DS:[EAX]
    008F152F    83EA 05         SUB EDX,5
    008F1532    8B08            MOV ECX,DWORD PTR DS:[EAX]
    008F1534    8911            MOV DWORD PTR DS:[ECX],EDX
    008F1536    8300 04         ADD DWORD PTR DS:[EAX],4
    008F1539    B8 05000000     MOV EAX,5
    008F153E    C3              RET
    
    И так для каждой инструкции в отдельности…. Когда записаны все инструкции данной функции до ret выпоняется этот фрагмент

    Code:
    00901194    8D047F          LEA EAX, DWORD PTR DS:[EDI+EDI*2]
    00901197    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]
    0090119A    8B4D F0         MOV ECX,DWORD PTR SS:[EBP-10]
    0090119D    894C82 04       MOV DWORD PTR DS:[EDX+EAX*4+4],ECX
    009011A1    47              INC EDI
    009011A2    FF4D D0         DEC DWORD PTR SS:[EBP-30]
    009011A5  ^ 0F85 CDFAFFFF   JNZ 00900C78
    
    Инструкция

    Code:
     
    MOV DWORD PTR DS:[EDX+EAX*4+4],ECX
    
    Самая интересная. В ecx адрес по которому записана сэмулированная функция, а по адресу [EDX+EAX*4+4] лежит адрес АПИ. Значит нужно просто пропустить эту инструкцию и импорт останется без изменений. Нопить нежелательно – программа вылетает. Просто поставить на нее хардвар на исполнение и запустить cкрипт

    Code:
    var save
    
    
    start:
    run
    cmp eip,0090119D 
    jne ret__
    mov eip,009011A1  
    jmp start
    
    ret__:
    ret
    
    После того, как скрипт отработал, остаются невосстановленные функции. Смотрим

    Code:
     
    01001104   .  7609817C      DD kernel32.CreateFileW
    01001108   .  23A8807C      DD kernel32.lstrcmpiW
    0100110C   .  90169000      DD 00901690
    01001110   .  B4139000      DD 009013B4
    01001114   .  FB6C817C      DD kernel32.GetCommandLineW
    
    Посмотрим что у нас по этим адресам
    Code:
    009013B4    55              PUSH EBP
    009013B5    8BEC            MOV EBP,ESP
    009013B7    53              PUSH EBX
    009013B8    56              PUSH ESI
    009013B9    57              PUSH EDI
    009013BA    A1 54739700     MOV EAX,DWORD PTR DS:[977354]
    009013BF    8B00            MOV EAX,DWORD PTR DS:[EAX]
    009013C1    3B45 08         CMP EAX,DWORD PTR SS:[EBP+8]
    009013C4    75 3A           JNZ SHORT 00901400
    009013C6    BE 0C000000     MOV ESI,0C
    …
    00901473    8B45 0C         MOV EAX,DWORD PTR SS:[EBP+C]
    00901476    50              PUSH EAX
    00901477    8B45 08         MOV EAX,DWORD PTR SS:[EBP+8]
    0090147A    50              PUSH EAX
    0090147B    E8 284EFEFF     CALL 008E62A8                    ; JMP to kernel32.GetProcAddress
    00901480    5F              POP EDI
    00901481    5E              POP ESI
    00901482    5B              POP EBX
    00901483    5D              POP EBP
    00901484    C2 0800         RET 8
    
    Это вызов GetProcAddress….
    Энигма обрабатывает по-другому эти функции. Перейдем по адресу 008E62A8

    Code:
    008E6288  - FF25 40339900   JMP NEAR DWORD PTR DS:[993340]                                                           ; kernel32.GetLocaleInfoA
    008E628E    8BC0            MOV EAX,EAX
    008E6290  - FF25 3C339900   JMP NEAR DWORD PTR DS:[99333C]                                                           ; kernel32.GetModuleFileNameA
    008E6296    8BC0            MOV EAX,EAX
    008E6298  - FF25 38339900   JMP NEAR DWORD PTR DS:[993338]                                                           ; kernel32.GetModuleHandleA
    008E629E    8BC0            MOV EAX,EAX
    008E62A0  - FF25 34339900   JMP NEAR DWORD PTR DS:[993334]                                                           ; kernel32.GetPrivateProfileStringA
    008E62A6    8BC0            MOV EAX,EAX
    008E62A8  - FF25 30339900   JMP NEAR DWORD PTR DS:[993330]                                                           ; kernel32.GetProcAddress
    008E62AE    8BC0            MOV EAX,EAX
    008E62B0  - FF25 2C339900   JMP NEAR DWORD PTR DS:[99332C]                                                           ; kernel32.GetStdHandle
    008E62B6    8BC0            MOV EAX,EAX
    008E62B8  - FF25 28339900   JMP NEAR DWORD PTR DS:[993328]                                                           ; kernel32.GetStringTypeExA
    008E62BE    8BC0            MOV EAX,EAX
    008E62C0  - FF25 24339900   JMP NEAR DWORD PTR DS:[993324]                                                           ; kernel32.GetSystemDirectoryA
    
    Это таблица импорта самого протектора… Поскольку невостановленных функции всего 4 можно восстановить их вручную. Импреком восстанавливаем импорт, добавляем секцию с oep, добавляем еще одну секцию (используя CFF Explorer) с нашим кодом, который будет проецировать oep в память по тому же адресу. Resource Binder-ом восстанавливаем ресурсы…
    Велкам ту pm при обнаружении ошибок, буду рада конструктивной критике.

    (c) 0x0c0de 2007
     
    #1 0x0c0de, 17 Nov 2007
    Last edited: 18 Nov 2007
    4 people like this.