Авторские статьи Распаковка. Общий подход.

Discussion in 'Статьи' started by taha, 22 Nov 2006.

  1. taha

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

    Joined:
    20 Aug 2006
    Messages:
    399
    Likes Received:
    330
    Reputations:
    251

    Содержание:

    1. Вступление
    2. Нам понадобится
    3. Ликбез
    Стек
    Несколько слов о RVA
    Способы адресации​
    4. Приступим к распаковке
    В поисках OEP
    Сбрасываем дамп
    Восстановление импорта​
    5. Ссылки


    1. Вступление
    Попав внутрь запакованной программы, начинающий cracker начинает волноваться и паникивать, ведь там так одиноко без WinAPI, а кругом вражески настроенные опкоды. И вроде незачто зацепиться, однако это не так, и распокавать программу на самом деле очень просто. Сегодня я расскажу о том как это делается.

    2. Нам понадобится
    а) Отладчик OllyDbg и плагины к нему (CommandBar, OllyDump, OllyScript).
    б) Редактор PE-файлов - LordPE
    в) Программа для восстановления импорта - ImpRec

    3. Ликбез
    а) Стек
    Чего я не знаю о стеке? - спросит читатель. Push - pop и все дела, ан нет, им можно пользоваться и более умело.
    В регистре esp храниться адрес вершины стека, тоесть параметра помещённого в стек последним. К примеру:
    Code:
    push 5h
    push 6h
    mov eax,dword ptr [esp] - после выполнения в eax будет 6h
    А как обратится к 5 через esp? Опятьтаки спросит читатель.
    Для начала мы сохраним esp в ebp, но так как ebp используем не мы одни, то нужно сохранить его.
    Code:
    push ebp
    mov ebp,esp - теперь в ebp адрес вершины стека
    Также необходимо понимать, что стек растёт от старших адресов к младшим. Это очень важно!!!
    Так как 5h было помещено раньше чем все остальное, то и адрес у него будет больше.
    Также мы знаем, что размер одного кадра стека равен 4h.
    Следовательно чтобы обратится к пяти надо к ebp прибавить 2h*4h.
    Code:
    push 5h
    push 6h
    push ebp
    mov ebp,esp
    mov eax, dword ptr [ebp+8] - в eax 5h
    А параметры помещенные после сохранения ebp?
    Просто теперь вычетаем номер желаемого параметра помноженный на размер кадра.
    mov eax,dword ptr [ebp-1*4h] - первый параметр, запушенный после ebp
    И во всём этом легко убедится.
    Напишем простую программу на MASM, где будет следующий код:
    Code:
    push 5h
    push 6h
    push ebp
    mov ebp,esp; <- сохраняем esp
    mov eax, dword ptr [ebp+4]; <- в eax 6h
    mov eax, dword ptr [ebp+8]; <- в eax 5h
    push 7h
    mov eax,dword ptr [ebp-4h]; <- в eax 7h
    add esp,4h; <- увеличиваем esp на 4h.В данный момент esp=ebp-4h (тоесть по этому адресу находится семёрка)
    ;после увеличения по адресу esp будет находится, сохранённое значение ebp
    pop ebx <- востонавливаем ebp
    push 0
    call ExitProcess
    Открываем в Olly, убеждаемся в моей правоте, читаем далее.

    б) Несколько слов о RVA
    Термины:
    Entry Point (EP) - точка входа в программу
    Original Entry Point (OEP) - это адрес, с которого бы начала выполняться программа если бы не была упакованна
    Virtual Address (VA) - виртуальный адрес элемента в памяти
    Relative Virtual Adress (RVA) - относительный виртуальный адрес. Адрес относительно ImageBase.
    Image Base - это адрес в памяти, начиная с которого программа загружена в память​
    Те самые несколько слов:
    Очевидно, что OEP не равно EP, иначе вы бы эту статью не читали. Его мы будем искать. Но дело не в этом, нам понадобится RVA OEP. Как вы, думаю, догадались нужно просто из VA OEP вычесть ImageBase. Из полного виртуалного адреса VA вычитаем адрес самого начала (даже не программы) файла, и получется RVA.
    RVA OEP = VA OEP - ImageBase
    Ну вот к примеру, мы нашли OEP равный 00402000, а ImageBase равно 00400000, тогда RVA OEP будет равно 2000.
    Где мы найдём ImageBase - это второстепенный вопрос, о котором я расскажу позже.

    в) Способы адресации
    Сейчас я хочу поговорить с вами о том, как передать управление в другую часть кода.
    Первый способ:
    Code:
    jmp metka
    metka:
    mov eax,metka
    jmp eax
    
    Code:
    Втрой способ:
    push metka
    retn
    metka:
    Третий способ:
    Code:
    call metka
    metka:
    Четвёртый способ:
    Code:
    stc
    jc metka
    metka:
    Пятый способ:
    Code:
    mov cl,1
    loop metka
    metka:
    Эти примеры могут нам пригодится при нахождении OEP.

    4. Приступим к распаковке
    а) В поисках OEP
    Общая часть:
    Резонно будет предположить, что код, распаковывающий прогамму, во время распаковки тоже использует стек, следовательно после его выполнения он будет восстанавливать esp. И неприменно обратится к адресу esp-4, на него мы и ставим break. В OllyDbg это делается так: в CommandBar пишем "hr esp-4" enter. А дальше? А дальше, после восстановления регистров и стека, перейдёт на OEP, который мы подсмотим.

    Пример с UPX 0.89.6 - 1.02 / 1.05 - 1.24 -> Markus & Laszlo:
    Запускаем программу по F9. Break срабатывает, и мы видим следующие инструкции:
    Code:
     popad
     jmp xxxxxxxx
    В этом месте программа восстанавливает все регистры и передаёт управление коду по адресу xxxxxxxx. Скорее всего это и есть переход на OEP. Проверим. Жмем F8 и попадаем сюда.
    Code:
     PUSH 0
     CALL Crackme.0040109C                   ; JMP to kernel32.GetModuleHandleA
     MOV DWORD PTR DS:[404094],EAX 
     CALL Crackme.00401096                   ; JMP to kernel32.GetCommandLineA
     MOV DWORD PTR DS:[404098],EAX
    Явно не код распоковщика, а следовательно распакованной программы. Тоесть jmp - это переход на OEP программы.
    Обращение к esp-4 может встерчаться много раз в программе, вот для чего я привел способы адресации. Вам придётся нераз трасировать программу (F7), чтобы распознать где заканчивается код распаковщика и начинается код программы.

    upx_script1.txt
    Code:
    /* Transfer execution to some label on next breakpoint. */
    	eob Break:
    /* Мы заметели, что прога использут popad(61h) для восстановления регистров.  Найдём его. */
    	findop eip, #61#
    /* Поставим бряк на найденый адрес */
    	bphws $RESULT, "x"
    /* Executes F9 in OllyDbg */
    	run
    Break:
    /* Execute F8 in OllyDbg */
    	sto
    /* Execute F8 in OllyDbg */
    	sto
    /* Снимаем брейк */
    	bphwc $RESULT
    /* Выходим */
    	ret
    Можно короче(если убрать коменты и cmt, конечно):

    upx_sript2.txt
    Code:
    findop eip, #61#
    /* go: Executes to specified address (like G in SoftIce) */
    /* Выполнять до адреса $RESULT*/
    go $RESULT
    sto
    sto
    /* cmt - комментировать строку, по адресу (в данном случае - eip) */
    cmt eip," <------------- OEP"
    ret
    Пример с ASPack 2.12 -> Alexey Solodovnikov:
    Запускаем программу по F9. Break срабатывает, и мы видим следующие инструкции:
    Code:
    JNZ SHORT Crackme.004053BA
    MOV EAX,1
    RETN 0C
    PUSH Crackme.00402000
    RETN
    Жмём по F8, jnz срабатывает и передаёт управление на push, конструкция push-retn представлена в способах адресации.

    push xxxxxx
    retn
    можно представить так:
    jmp xxxxxx

    После перехода на xxxxxx, мы видим стандартный код начала программы на MASM (На C, Delphi посмотрите в отладчике. Меняться будут ток адреса да параметры.):

    Code:
     PUSH 0
     CALL Crackme.0040109C                   ; JMP to kernel32.GetModuleHandleA
     MOV DWORD PTR DS:[404094],EAX 
     CALL Crackme.00401096                   ; JMP to kernel32.GetCommandLineA
     MOV DWORD PTR DS:[404098],EAX
    aspack.txt
    Code:
    	eob Break
    /* 61h=popad, 75h=jnz, дальше всё как в предыдущем примере. */
    	findop eip, #6175#
    	bphws $RESULT, "x"
    	run
    Break:
    	bphwc $RESULT
    	sto
    	sto
    	sto
    	log eip
    ret
    Пример с FSG 2.0 -> bart/xt:
    Запускаем программу по F9. Break срабатывает, на этот раз всё сложнее прога остановилась на каком то mov, явно не конец распаковки, ещё F9 и ещё пока не окажемся здесь:

    Code:
    PUSH EAX
    CALL DWORD PTR DS:[EBX+10]
    XCHG EAX,EBP
    MOV EAX,DWORD PTR DS:[EDI]
    INC EAX
    JS SHORT Crackme#.004001C2
    JNZ SHORT Crackme#.004001D4
    JMP DWORD PTR DS:[EBX+C]
    Мне так кажется, что переход на OEP будет тут (JMP DWORD PTR DS:[EBX+C]). Ставим бряк на этот jump, а старый на esp-4 снимаем, это можно опятьтаки сделать в CommandBar'е. Command: hd esp-4. Жмём F9 и останавливаемся на jump, F8. Да, так оно и есть, и мы на OEP.

    FSG.txt
    Code:
    /* FF630C - это наш jump на OEP*/
    	findop eip, #FF630C#
    	go $RESULT
    	sto
    	cmt eip, "OEP Reached !"
    	ret
    Пример с PECompact 2.x -> Jeremy Collake:
    Запускаем программу по F9. Однако бряк не срабатывает, прога останавливается, возникает исключительная ситуация, возможно это антиотладка( в данной статье нас это не интересует). Смотрим на вершину стека:

    Code:
    0012FFC0   0012FFF0
    0012FFC4   7C816D4F  RETURN to kernel32.7C816D4F
    0012FFC8   7C910738  ntdll.7C910738
    Просто прога ставит SEH. Не думаю, что нас что-либо может остановить. Shift-F9, Shift-F9, F9 и т.д, пока не найдём что-нибудь интересное. Меня заинтересовал следующий код:

    Code:
    POP EDX
    POP ESI
    POP EDI
    POP ECX
    POP EBX
    POP EBP
    JMP EAX     ; Crackme#.00402000
    Трейсим и действительно в eax OEP. Готово!!

    PECompact.txt
    Code:
    // Author: hacnho/VCT2k4
    // Website: http://nhandan.info/hacnho
    // Что могу сказать !!? Читайте readme к OllyScript !!! =)
    var CS
    var CB
    var Temp
    
    sto
    findop eax, #C3#
    bp $RESULT
    esto
    esto
    
    gmi eip, CODEBASE
    mov CB, $RESULT
    log CB
    
    gmi eip, CODESIZE
    mov CS, $RESULT
    log CS
    
    bpwm CB, CS
    esto
    sto
    bpmc
    findop eip, #FFE0#
    mov Temp, $RESULT
    bp $RESULT
    esto
    jmp exit
    
    Return:
    esto
    jmp exit
    
    exit:
    cmp eip, Temp
    jne Return
    sto
    log eip
    cmt eip, "This is the OEP! Found by hacnho/VCT2k4"
    MSG "Dumped and fix IAT now! Thanx for using my Script...!"
    ret
    Пример с winupack_029b
    С ним всё было как то необычно, бряк сробатывал в ненужных местах. Затем прога вылетела. Shift-F9 и я оказался в какой то функции, F9 опять вылетела, далее опять в функцию, я посмотрел на стек и увидел там адрес функции GetCommandLineA. Проделал эти операции ещё раз, там появилась следущая функция, распаковывает или востанавливает (не разбирался) IAT, блеснуло у меня в голове, Жал Shift-F9,F9 пока не дошёл до ExitProcess, далее тассировал более аккуратно и вследствии вышел на OEP.

    Можно было ещё разобрать пару пакеров, но у меня на компе больше нет. Но идея, думается мне, понятна.
    Если Вы не поняли как находится OEP, то просто юзайте скрипты для Olly, благо в инете их много, для различных пакеров и протектеров.

    б) Сбрасываем дамп
    Итак мы перешли на OEP, что делать дальше?
    Пока ничего не трогаем. Просто заходим в плагины > OllyDump > dump debugged process.
    Жмём "Dump".
    Всё готово!!! Можете пользоваться.

    в) Восстановление импорта
    Для восстановления импорта нам нужно узнать ImageBase. Запускаем LordPE, жмём PE Editor и выбираем нашу программу, смотрим ImageBase. Закрываем LordPE.
    Запускаем нашу программу(всмысле запакованную), открываем ImpRec, выбираем нужный процес. В поле OEP пишем RVA OEP, подсчитаный по формуле представленной выше. Жмём IAT AutoSearch для автоматического поиска таблицы импорта, и видим сообщение, что скорее всего адрес найден. Теперь жмём Get Imports и видим, что в списке появлись используемые dll и функции. Жмём Fix Dump и выбираем наш дамп. Ну вот и всё.

    Возможные вопросы:
    Q: Половина функций в библиотеке опознана, а другая нет!! Что делать?
    A: Выбери неопределённую функцию, и нажми на правую клавишу. Выбери "Disassembly / HexView", выделяй, например "comctl32.dll/002D/ImageList_Destroy", и теперь на обе клавиши. Выберай Get Import. Всё, функция определена, библиотека тоже.
    Q: А если много таких функций?
    A: Выбираем все, на правую клавишу, Trace level1 (Disassm). Результат налицо.
    Q: А если всётаки не хочет определяться?
    A: Выбираем любую функцию, на правую клавишу, Advanced commands > Get API Call > OK, теперь отсекаем всё не нужное.

    5. Ссылки
    cracklab.ru
    wasm.ru (Об упаковщиках в последний раз)
    _____________________________________________
    Author: Taha
    Date: 22.11.2006

     
    #1 taha, 22 Nov 2006
    Last edited: 22 Nov 2006
    6 people like this.