Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 12 ноября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

Распаковка ASProtect 1.23 RC4

Обсудить статью на форуме

Массу крэкерских инструментов, видеоуроков и статей вы сможете найти на видеокурсе от нашего сайта. Подробнее здесь.

Автор: -= ALEX =- <alex@kpnemo.ru>

Цель

crackme - http://www.alex2kx.nm.ru/aspr123crackme.rar

Инструменты

 SoftIce for NT
 SuperBpm for NT
 IceExt
 Loader 2.0 by Syd
 Hiew
 Imprec 1.6
 LordPE
 PEeditor 1.7
 

Вступление

В этой статье я расскажу как распаковать столь популярный в наше время пакер ASProtect, а именно версию 1.23 RC 4. Для большей наглядности я разделю статью на 2 части: восстановления импорта, восстановления спертых байтов (Stolen Bytes) и нахождение OEP. Целю нашей распаковки будет мною созданный crackme, и упакованный ASProtect 1.23 RC 4 build 08.07. Итак приступим....

Восстановление импорта.

Я начал распаковку программы именно с восстановления импорта, а не с получения дампа и поиска OEP. Это связано с тем, что существует простой способ нахождения создания импорта, а затем уже в нужном месте можно брать дамп. Как всегда грузим программу под отладчиком, для этого я использую Loader by Syd, но это только если у вас 2000 или XP. Этот загрузчик, в отличии от загрузчиков типа break'n'enter не требует восстановления затертого байта. Для тех, кто им ни разу не пользовался объясню как его использовать. Для начала скачайте его, он входит в комплект stripper'a by syd и его можно скачать на сайте http://www.wasm.ru/. Затем необходимо открыть окно sice'a, и написать I3HERE ON, потом открыть файл и нажать Break on Oep. Чтобы не возникло проблем с обнаружением отладчика и с несрабатыванием бряков рекомендуется включить IceExt и SuperBpm. Дальше используем загрузчик. Как только мы оказались в отладчике на EP программы, ставим такой бряк: bpm mapviewoffile x, почему - объясню ниже. Как вы наверное уже знаете, я надеюсь, что для получения адреса функции служит GetProcAddress. Эта функция используется много раз в аспре. После распаковки программы в память функция GetProcAddress используется как раз для определения адресов апи функций и заполнения этими адресами таблицу импорта. После проверки и определения CRC программы, с помощью чего юзаются конструкции CreateFileA, CreateFileMapping, MapViewOfFile.... можно уже ставить бряк на GetProcAddress, тогда прога уже распакована в память. После того как сработает бряк на MapViewOfFile, а он может сработать два раза для обладателей XP c дополнительными темами XP, удаляем этот бряк и ставим бряк на GetProcAddress. Бряк на GetProcAddress сработает несколько раз, нас интересует срабатывание именно в таком месте:
 001B:008E2F34  E88722FFFF          CALL      KERNEL32!GetProcAddress
 001B:008E2F39  8945F8              MOV       [EBP-08],EAX  SS:0012FE04=00000000
 001B:008E2F3C  837DF800            CMP       DWORD PTR [EBP-08],00
 001B:008E2F40  0F85DA000000        JNZ       008E3020
 
Далее нажимаем F12 до тех пор, пока не окажемся в таком месте:
 001B:008E32B4  E847FCFFFF          CALL      008E2F00  <- От сюда мы вышли
 001B:008E32B9  E87EFEFFFF          CALL      008E313C
 001B:008E32BE  8B17                MOV       EDX,[EDI]
 001B:008E32C0  8902                MOV       [EDX],EAX
 001B:008E32C2  EB7E                JMP       008E3342
 
Мы оказались в процедуре создания импорта. Тепер можем посмотреть, что творится в eax. А там как раз адрес АПИ функции. Следующий call, который находиться по адресу 008E32B9, принимает адресс апи функции и по нему возвращает адрес соответствующего переходника. Чтобы нам получить относительно чистый импорт мы должны обойти этот call, а именно за'nop'ить. Выглядит это так:
 001B:008E32B4  E847FCFFFF          CALL      008E2F00
 001B:008E32B9  90                  NOP
 001B:008E32BA  90                  NOP
 001B:008E32BB  90                  NOP
 001B:008E32BC  90                  NOP
 001B:008E32BD  90                  NOP
 001B:008E32BE  8B17                MOV       EDX,[EDI]
 001B:008E32C0  8902                MOV       [EDX],EAX
 
Если внимательно приглядеться, то по адресу 008E32C0 происходит заполнение таблицы импорта адресами АПИ функций. Нам необходимо определить начало этой таблицы импорта, для этого становимся на 008E32BE и смотрим, что находиться в edx. В edx как раз начало нашего импорта: 004420F0. Теперь жмем F12, оказываемся в таком месте:
 001B:008E3541  B904000000          MOV       ECX,00000004
 001B:008E3546  01CE                ADD       ESI,ECX
 001B:008E3548  E8E7FCFFFF          CALL      008E3234
 001B:008E354D  5B                  POP       EBX
 001B:008E354E  EBCE                JMP       008E351E
 001B:008E3550  61                  POPAD
 
Как мы видим, здесь цикл. На необходимо поставить бряк на 008E3550. Дальше трейсим, по пути встретиться SEH трюк :
 001B:008E359C  648920              MOV       FS:[EAX],ESP  FS:00000000=817CDE84
 001B:008E359F  3100                XOR       [EAX],EAX
 001B:008E35A1  EB01                JMP       008E35A4
 
Дак вот, дойдя до 008E359C, необходимо ставить сразу бряк на 008E35A1, чтоб обойти XOR [EAX],EAX , иначе произойдет exception. Дальше доходим до такого места:
 001B:008E35FC  60                  PUSHAD
 001B:008E35FD  68AC638E00          PUSH      008E63AC
 001B:008E3602  8D45F4              LEA       EAX,[EBP-0C]
 001B:008E3605  FF35A47A8E00        PUSH      DWORD PTR [008E7AA4]
 001B:008E360B  FF10                CALL      [EAX]  DS:0012FF7C=008FA164
 001B:008E360D  61                  POPAD
 
По адресу 008E360B, происходит эмуляция таких функций как: GetModuleHandleA, GetProcAddress, GetVersion и других. Вам может показаться, что за'nop'ив этот call мы получим чистый импорт, без переходников. Но реально же по адресам в таблице импорта лежат не адреса этих апи функций, а левые адреса. Так что быдем ручками чистить импорт :) Ставим бряк на 008E360D и зацикливаем прогу. (a [enter] jmp eip [enter] - для тех кто в танке). Теперь можем сделать два дела сразу: сдампить прогу и получить таблицу импорта. Для начала сдампим прогу, я использовал LordPe. Выбираем в списке процессов нашу зацикленную программу и нажимаем правой кнопкой мыши. Далее выбираем Dump Full и сохраняем наш дамп. Теперь будем разбираться с импортом. Запускаем ImpRec, выбираем в процессах нашу зацикленную прогу, далее в поле RVA пишем адрес начала нашей таблицы импорта с учетом imagebase, т.е. мы впишем 0420F0. Размер таблицы импорта мы не знаем поэтому рекомендуется ставить значения побольше, лишнее потом можно удалить. Так как мой crackme программа относительно небольшая по размеру, я оставил значение по умолчанию - 1000. Теперь жмем GetImports. Что-то получили :) теперь будем чистить этот импорт. Жмем на ShowInvalid, оказываемся в таком месте:

Неизвестные адреса, это адреса аспровских переходников о которых я говорил выше. Дальше необходимо определять, что эта за апи функция. Для упрощения и наглядности я ниже покажу вам как выглядят эти переходники/эмуляторы и объясню почему именно такой-то переходник есть такая-то функция.
 GetProcAddress:
 008E17A4    push ebp
 008E17A5    mov ebp,esp
 008E17A7    mov edx,[ebp+C]
 008E17AA    mov eax,[ebp+8]
 008E17AD    mov ecx,[8E6484]    // DWORD value: 008E6304
 008E17B3    mov ecx,[ecx]
 008E17B5    cmp ecx,eax
 008E17B7    jnz short 008E17C2
 008E17B9    mov eax,[edx*4+8E63D8]    // DWORD value: 00000000
 008E17C0    jmp short 008E17C9
 008E17C2    push edx
 008E17C3    push eax
 008E17C4    call 008D51C0    // = kernel32.dll/018A/GetProcAddress
 008E17C9    pop ebp
 008E17CA    retn 8
 
Понять, что это за эмулируемая функция, можно, если прерваться на адресе 008E17A4 и проследить, как будет выполняться программа. В конце концов, я приду на адрес 008E17C4 и выполнится функция GetProcAddress, которая вернет адрес запрашиваемой функции в регистре eax и с тех пор, eax больше меняться не будет. Значит это так и есть функция GetProcAddress.
 GetModuleHandleA:
 008E1C64    push ebp
 008E1C65    mov ebp,esp
 008E1C67    mov eax,[ebp+8]
 008E1C6A    test eax,eax
 008E1C6C    jnz short 008E1C81
 008E1C6E    cmp dword ptr [8E7AA4],400000
 008E1C78    jnz short 008E1C81
 008E1C7A    mov eax,[8E7AA4]    // DWORD value: 00400000
 008E1C7F    jmp short 008E1C87
 008E1C81    push eax
 008E1C82    call 008D51B8    // = kernel32.dll/0168/GetModuleHandleA
 008E1C87    pop ebp
 008E1C88    retn 4
 
Аналогично как и с GetProcAddress. Замечу, что значение 00400000 было получено где-то заранее, и по адресу 008E1C6A проверяется первый переданный параметр функции, если он равен 0, то в eax заносится значение 00400000, так как GetModuleHandleA(0)=00400000.
 GetCommandLineA:
 008E1CD8    push 0
 008E1CDA    call 008D51B8    // = kernel32.dll/0168/GetModuleHandleA
 008E1CDF    push dword ptr [8E7E14]    // DWORD value: 0A280105
 008E1CE5    pop eax
 008E1CE6    mov eax,[8E7E24]    // DWORD value: 00142378
 008E1CEC    retn
 
По адресу 008E1CDA вызывается некая функция-пустышка, так как никакой роли она не играет. По адресу 008E1CE6 в eax заноситься какое-то значение. Если в отладчике посмотреть, что находиться по этому адресу (d eax), то мы увидем полный путь к файлу. Следовательно это и есть функция GetCommandLineA.
 LockResource:
 008E1CC8    push ebp
 008E1CC9    mov ebp,esp
 008E1CCB    mov eax,[8E7E24]    // DWORD value: 00142378
 008E1CD1    mov eax,[ebp+8]
 008E1CD4    pop ebp
 008E1CD5    retn 4
 
Эта функция возвращает то же самое значение, что и принимает. Т.е. когда происходит вызов функции call, то в esp помещается адрес возврата. Когда происходит команда push ebp, в esp уже заноситься значение ebp, и указатель стека смещается. Следовательно адрес возврата будет находиться по адресу [esp+4], а передаваемый параметр по адресу [esp+8]. Команда mov ebp,esp помещает начало стэка в ebp, поэтому значение [ebp+8] будет аналогично [esp+8]. А по этому адресу лежит передаваемый параметр, он же возвращается функцией. Так хитро аспр эмулирует LockResource
 GetVersion:
 008E1C8C    push dword ptr [8E7E14]    // DWORD value: 0A280105
 008E1C92    pop eax
 008E1C93    retn
 
 GetCurrentProcessId:
 008E1CC0    mov eax,[8E7E20]    // DWORD value: 00000658
 008E1CC5    retn
 
Можем заметить, что в eax заносится небольшое значение. А если посмотреть в ImpRec к примеру, то в списках процессов, напротив нашего файла будет находиться как раз такое значение. Это есть идентификатор текущего процесса. Значит этот код эмулирует GetCurrentProcessId
 FreeResource:
 008E1CF0    push ebp
 008E1CF1    mov ebp,esp
 008E1CF3    mov eax,[8E7E24]    // DWORD value: 00142378
 008E1CF9    pop ebp
 008E1CFA    retn 4
 
Сначала вам может показаться, что это еще одна эмуляция GetCommandLineA. Но функция GetCommandLineA не имеет параметров ! Аспротект так хитро эмулирует FreeResource. Для того, чтобы продемонстрировать еще одну эмуляцию, я в своем crackme сделал наг, окошко из ресурсов. Поэтому следующая функция эмулируется аспром вот так:
 DialogBoxParamA:
 008E1D14    push ebp
 008E1D15    mov ebp,esp
 008E1D17    push ebx
 008E1D18    mov ebx,[ebp+8]
 008E1D1B    mov eax,[ebp+18]
 008E1D1E    push eax
 008E1D1F    mov eax,[ebp+14]
 008E1D22    push eax
 008E1D23    mov eax,[ebp+10]
 008E1D26    push eax
 008E1D27    push 5
 008E1D29    mov eax,[ebp+C]
 008E1D2C    push eax
 008E1D2D    push ebx
 008E1D2E    call 008D5158    // = kernel32.dll/00D5/FindResourceA
 008E1D33    push eax
 008E1D34    push ebx
 008E1D35    call 008D51F8    // = kernel32.dll/0234/LoadResource
 008E1D3A    push eax
 008E1D3B    call 008D5200    // = kernel32.dll/0242/LockResource
 008E1D40    push eax
 008E1D41    push ebx
 008E1D42    call 008D5228    // = user32.dll/009C/DialogBoxIndirectParamA
 008E1D47    pop ebx
 008E1D48    pop ebp
 008E1D49    retn 14
 
Если в отладчике набрать u DialogBoxParamA, то мы увидем точно такой же код, как и использует аспротект при умуляции.
Еще есть эмуляция, которой в нашем случае не было, это эмуляция GetCurrentProcess. Там в эмуляторе заноситься в eax DWORD value: FFFFFFFF. Запомните !
Чтобы самому посмотреть, как выглядит код переходника/эмулятора, жмем правой кнопкой мыши по адресу и выбираем Dissassemler / Hex View. Как только определили функцию, кликаем два раза по этой строке и выбираем в списке функцию. Еще один момент. Вы можете увидеть такое:

Т.е. F49C293 никак на адрес не смахивает, в данном месте это является "разделителем" между адресами апи функций разных библиотек. Выбираем, жмем Cut thunk(s). У нас появилась новая ветка :) Вот в таком духе делаем всё. В конце вы встретите кучу мусора, т.к. адрес мы не точный ставили, а примерный. Поэтому после последней определившейся апи функции жмем Cut thunk(s), а потом самая последняя ветвь и будет мусором, выбираем, жмем Delete thunk(s). В итоге вы получите чистый импорт, как и я получил:

Теперь жмем Fix Dump, выбираем наш дамп. Теперь мы получили уже почти полную распакованную прогу. Нам осталось только восстановить спертые байты и найти OEP. Итак часть вторая...

Восстановление спертых байтов и поиск OEP

Грузим еще раз прогу, на EP проги ставим всем знакоый бряк bpm esp-4. На третий раз бряк сработает в нужном нам месте, дальше пойдет мусорный код, в котором и выполняются спертые команды. Для начала хотелось бы маленьки обратить внимание на термин "спертые байты". В нашем случае аспр забирает начало нашей проги, которая написана на Delphi. Начало у Delphi прог почти всегда такое, ну по крайней мере первые три инструкции:
 push ebp
 mov ebp,esp
 add esp, xxxx или sub esp, xxxxx
 mov eax, yyyy
 call zzzzz // InitExe
 
В большинстве случаев, где-то 90 %, аспр забирает байты до первого call'a. В нашем случае именно так и происходит. Мусорный код выглядит примерно так:
 001B:008F5A89  61                  POPAD
 001B:008F5A8A  3EEB02              JMP       008F5A8F
 001B:008F5A8D  CD20                INT       20 VXDJmp CD02,6B26
 001B:008F5A93  20EB                AND       BL,CH
 001B:008F5A95  02CD                ADD       CL,CH
 001B:008F5A97  2068A8              AND       [EAX-58],CH
 001B:008F5A9A  8EC8                MOV       CS,AX
 001B:008F5A9C  185389              SBB       [EBX-77],DL
 001B:008F5A9F  7424                JZ        008F5AC5
 001B:008F5AA1  0464                ADD       AL,64
 001B:008F5AA3  EB01                JMP       008F5AA6
 001B:008F5AA5  9A8D642404EB01      CALL      01EB:0424648D
 001B:008F5AAC  F081DEA3FCBE4E      LOCK SBB  ESI,4EBEFCA3
 
Некоторые не любят копаться в этом мусорном коде, потому что он бывает очень большим и напутанным. Но мы попробуем это сделать. Трейсим по F8 немного этот код пока сразу же в глаза нам не попадется вот это:
 001B:008F5AE6  E8558BEC81          CALL      827BE640
 001B:008F5AE7  55                  PUSH      EBP
 001B:008F5AE8  8BEC                MOV       EBP,ESP
 001B:008F5AEA  81EC0C000000        SUB       ESP,0000000C
 001B:008F5AF0  F2EB01              REPNZ JMP 008F5AF4
 001B:008F5AF3  E8668135FD          CALL      FDC4DC5E
 
Здесь как раз выполняются наши спертые команды. Замечу, что команда sub esp, 0000000C слишком большая, т.е. занимает целых 5 байт ! На деле эта команда выглядит так add esp, -0C ! и занимает всего 3 байта. Теперь нам осталось только узнать yyyy, трейсим наш мусорный код до ret и смотрим что у нас хранится в eax, а в eax у нас как раз нужное нам значение yyyyy. Т.е. в мусорном коде мы не найдем инструкцию mov eax, yyyyy. Значение yyyyy генерится теким способом, но нам это впринципе не важно, т.к. если уж аспр забрал команды, то он должен и также их восстановить, и как раз у нас при выходе из мусорного кода в eax будет нужный адрес. После мусорного кода мы попадем на переходник GetModuleHandleA. Аспр - первый пакер, который при краже байт стал заходить в call, в нашем случае call zzzzz. Теперь жмем F12, чтобы оказаться в главной ветви программы, т.е. рядом с OEP. Это выглядит так:
 001B:0043F50B  E8B469FCFF          CALL      00405EC4
 001B:0043F510  6A00                PUSH      00       <- мы здесь
 001B:0043F512  68F4F24300          PUSH      0043F2F4
 001B:0043F517  6A00                PUSH      00
 001B:0043F519  6A64                PUSH      64
 
Мы вышли как раз из call zzzzz. Выше этого call'a есть пустые байты, на месте которых должны были находиться спертые команды. Туда мы и будем вписывать спертые байты. Размер спертых байт составляет: 1 (push ebp)+2 (mov ebp,esp)+3 (add esp,-0C)+5 (mov eax,0043F3C8)=11 или B (hex) байт. Поэтому 0043F50B-B=0043F500, это и есть наше OEP ! Открываем файл в HIEW, по адресу 0043F500 начинаем писать:
 push ebp
 mov ebp, esp
 add esp, -0C
 mov eax, 0043F3C8
 
Теперь подправляем EP нашего дампа на 0003F500, например с помощью PEeditor, и можете смело запускать дамп. Если вы все сделали правильно, могу вас поздравить, вы теперь умеете распаковывать ASProtect 1.23 :). От вас осталось убрать наг :) :)
На последок хотелось бы передать приветы этим людям: MozgC[TSRh], Kerghan, Hex, Angel_aka_K$, Madness, YMY, Bad_Guy и многим другим.

Автор: -=ALEX=-
E-Mail: alex@kpnemo.ru

Обсуждение статьи: Распаковка ASProtect 1.23 RC4 >>>


Комментарии к статье: Распаковка ASProtect 1.23 RC4

..::Winset::. 13.04.2004 04:47:24
Неплохо !!
---
Zer0 15.04.2004 20:12:33
Браво-браво :)
---
-= ALEX =- 17.04.2004 16:04:29
Пасибо вам ;)
---
Weq 18.05.2004 19:19:11
Отличная статейка !
---
Said 28.11.2004 17:16:17
Очень неплохая статья
---
oops 16.12.2004 02:38:56
Не работоспособная на все сто . :(
---
Alexey 04.02.2005 04:44:46
Не качается пример нормально, архив поврежден и все тут!
---
quant 27.07.2009 04:25:28
Жаль,что всё уже покрылось пылью...
---

Материалы находятся на сайте https://exelab.ru



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS