Хотел продемонстрировать простоту использования IDA Python, но както не попадалась интересной цели. И вот наконец, нашел кое-что интересное:
http://cracklab.ru/f/index.php?action=vthread&forum=6&topic=17494
Автор описывает это так:
"Вот семпл выводящий сообщение. Над исходным бинарем выполнен  автоматический ресерч и морфинг, при котором выполнена генерация  Gs-серий для линейных блоков http://indy-vx.narod.ru/Temp/GsTest.zip"
Загрузив в GsTest в IDA, можно увидеть интересный способ морфинга бинарного кода с использованием segment override prefix'ов. Выглядит это так:
start        proc near
        push    ebp
        mov    ebp, esp
        add    esp, 0FFFFFFD4h
        push    fs
        pop    gs
        mov    eax, large gs:30h
        push    ds
        pop    gs
        assume gs:_text
        mov    eax, gs:[eax+0Ch]
        push    ds
        pop    gs
        mov    eax, gs:[eax+0Ch]
        push    ds
        pop    gs
        mov    eax, gs:[eax]
        push    ds
        pop    gs
        mov    ebx, gs:[eax+18h]
        push    ss
        pop    gs
        assume gs:nothing
        mov    dword ptr gs:[ebp-10h],    59B88A67h
В OllyDbg это не трассируется, но цель данной заметки совсем в другом - показать, как просто можно получить чистый код из морфированного, используя IDA Python.
Скрипт для каждой ф-ции перечисляет все инструкции, с целью поиска pop gs, затем получает код префикса (в данном ехе используются лишь segment override prefixes), затем в базу данных IDA вносятся изменения( nop's и соответствующие коды префиксов там где юзается gs) для получения чистого кода. Так как у IDA немного едет крыша и она прячет часть кода в assume gs:xxx, в скрипте делается принудительный undefine. После работы скрипта IDA database будет содержать практически готовый код, за исключением нопов. В принципе ничего не мешает сохранить эту базу и сделать вывод уже без нопов.
Конечный результат выглядит так:
401000        push    ebp
401001        mov     ebp, esp
401003        add     esp, 0FFFFFFD4h
40100a        mov     eax, large fs:30h
401014        mov     eax, [eax+0Ch]
40101b        mov     eax, [eax+0Ch]
401022        mov     eax, [eax]
401028        mov     ebx, [eax+18h]
40102f         mov     [ebp+var_10], 59B88A67h
40103a        mov     [ebp+var_C], 0DB164279h
401045        mov     [ebp+var_8], 9E1E35CEh
401050        mov     [ebp+var_4], 0
401057        lea     eax, [ebp+var_10]
40105a        push    eax
40105b        push    0
40105d        push    ebx
40105e        call    sub_401103
По моему вполне читабельно, и скорее всего компилябельно.
Сам скрипт:
from idaapi import *
from idc import *
from idautils import *
def GetPreviousInstructionAddress( address ):
    prevInstructionAddress = PrevNotTail( address )
    return prevInstructionAddress
def GetNextInstructionAddress( address ):
    nextInstructionAddress = NextNotTail( address )
    return nextInstructionAddress
def GetSegmentOverridePrefixAndEliminatePush( address ):
    nopOpcode = 0x90
    if GetMnem( address ) == "push":
        if GetOpnd( address, 0 ) == "cs":
            PatchByte( address, nopOpcode ) # 0Eh
            return nopOpcode                 
        elif GetOpnd( address, 0 ) == "ss":
            PatchByte( address, nopOpcode ) # 16h
            return nopOpcode                 
        elif GetOpnd( address, 0 ) == "ds":
            PatchByte( address, nopOpcode ) # 1Eh
            return nopOpcode
        elif GetOpnd( address, 0 ) == "es":
            PatchByte( address, nopOpcode ) # 06h
            return nopOpcode                 
        elif GetOpnd( address, 0 ) == "fs":
            PatchWord( address, 0x9090 )     # 0FA0h
            return 0x64
        elif GetOpnd( address, 0 ) == "gs":
            PatchWord( address, 0x9090 )    # 0FA8h    
            return nopOpcode                 
        else:
            return 0
def EliminatePopSegmentOverridePrefix( address ):
    PatchWord( address, 0x9090 )
def SetSegmentOverridePrefix( address, segmentOverridePrefix ):
    PatchByte( address, segmentOverridePrefix )
def ProcessGs( address ):
    prevInstructionAddress = GetPreviousInstructionAddress( address )
    segmentOverridePrefix = GetSegmentOverridePrefixAndEliminatePush( prevInstructionAddress )
    nextInstructionAddress = GetNextInstructionAddress( address )
    MakeUnkn(nextInstructionAddress, DOUNK_SIMPLE)
    nextByte = nextInstructionAddress
    MakeCode( nextByte + 1 )
    SetSegmentOverridePrefix( nextInstructionAddress, segmentOverridePrefix )
def RemoveGsInFunction( functionStart, functionEnd ):
    for address in Heads( functionStart, functionEnd ):
        if GetMnem( address ) == "pop" and GetOpnd( address, 0 ) == "gs":
            ProcessGs( address )
            EliminatePopSegmentOverridePrefix( address )
def PostProcessMakeCode( functionStart, functionEnd ):
    start = functionStart
    end = functionEnd
    for address in Heads( functionStart, functionEnd ):
        prevByte = address
        prevByte = prevByte - 1
        MakeUnkn( address, DOUNK_SIMPLE )
    MakeCode( start )
#       
# entry point
#
ea = ScreenEA()
for func in Functions( SegStart(ea), SegEnd(ea) ):
    functionStart = func
    functionEnd   = FindFuncEnd( functionStart )
    RemoveGsInFunction( functionStart, functionEnd )
    PostProcessMakeCode( functionStart, functionEnd )
Данный скрипт собранный на коленке за 20 мин, естественно не претендует на generic деобфускатор( в нем не обрабатываются chunks, не убираются jxx следующие друг за другом и т.д., скрипт вобще не содержит никакой обработки control flow ), он лишь показывает, как быстро можно писать обработку бинарного кода, используя IDA Python.
Комментариев нет:
Отправить комментарий