Исследование 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