Исследование Enigma Protector 1.35 build20071012
Начнем разбирать представленный агрегат
[Инструменты]
1.OllyDbg+PhantOm plugin+OllyAdvanced
2.Imprec
3.LordPE
4.CFF Explorer
5.Resource Binder
[Обман анализаторов]
Ерундово достаточно. Просто Borland C++ старт-ап=\
Код:
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-регистров
Код:
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
Код:
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 в таких местах
Код:
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]
а нужно нам только последнее
Код:
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 выглядит так
Код:
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) и прикрутить к экзешнику в виде отдельной секции, а при загрузке спроецировать в память по тому же адресу. Так и сделаем
[Импорт]
Смотрим
Код:
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
Поясняю. Все адреса указывают на выделенную память. Посмотрим, что представляют из себя переходники…
Код:
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
Опа.. Это сама функция, полностью украденная. Честно говоря, что конкретно это за функция я определить не могу, поэтому надо поискать среди украденных такую, определить название которой не составит труда. И такая функция нашлась
Код:
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. Перезапускаем программу. Останавливаемся тут
Код:
008F8F4C 8A06 MOV AL,BYTE PTR DS:[ESI]
008F8F4E 3D FF000000 CMP EAX,0FF
Трассируем с помощью F7 (или Ctrl+F9 сразу) до первого ret. Возврат происходит сюда
Код:
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 возвращается длина инструкции. Запись очередной инструкции происходит следующим образом
Код:
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
…
Код:
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
…
Код:
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 выпоняется этот фрагмент
Код:
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
Инструкция
Код:
MOV DWORD PTR DS:[EDX+EAX*4+4],ECX
Самая интересная. В ecx адрес по которому записана сэмулированная функция, а по адресу [EDX+EAX*4+4] лежит адрес АПИ. Значит нужно просто пропустить эту инструкцию и импорт останется без изменений. Нопить нежелательно – программа вылетает. Просто поставить на нее хардвар на исполнение и запустить cкрипт
Код:
var save
start:
run
cmp eip,0090119D
jne ret__
mov eip,009011A1
jmp start
ret__:
ret
После того, как скрипт отработал, остаются невосстановленные функции. Смотрим
Код:
01001104 . 7609817C DD kernel32.CreateFileW
01001108 . 23A8807C DD kernel32.lstrcmpiW
0100110C . 90169000 DD 00901690
01001110 . B4139000 DD 009013B4
01001114 . FB6C817C DD kernel32.GetCommandLineW
Посмотрим что у нас по этим адресам
Код:
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
Код:
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