четверг, 23 декабря 2010 г.

Чистка бинарного кода от морфинга

Хотел продемонстрировать простоту использования 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.

среда, 22 декабря 2010 г.

Скрипт для нахождения id сисколов

Знакомый попросил о IDA скрипте, который находит сисколы в ntdll.dll, генерит текстовый файл с парами: имя сервисной ф-ции + id, я такой нашел у себя, и подумал, что скрипт может пригодиться кому-нибудь еще, поэтому выкладываю тут(формат вывода легко можно подстроить для себя).

#include <idc.idc>

//
// Info: This script gets pair: "Service Function Name + Service Function Id" and save it into the file("c:\ServiceFuncNames.txt").
// HowToUse: Just run script in IDA.
// Version: 1.0
// Author: TSS
// Data: 18.05.10
//

static Processing( currentAddress, startAddress, reportFile )
{
    auto functionName;
    auto functionId;
    auto movInstructionAddress;

    //
    // get "mov eax, index" instruction
    //

    movInstructionAddress = PrevHead( currentAddress, startAddress );

    //
    // make some validation
    //
   
    if ( GetOpType( movInstructionAddress, 1 ) != 5 || GetMnem( movInstructionAddress ) != "mov" )
    {
        Message( "Error: before syscall wrong instruction\n" );
        return;
    }

    //
    // get service function id
    //

    functionId = GetOperandValue( movInstructionAddress, 1 );

    //
    // get function name
    //

    functionName = GetFunctionName( currentAddress );

    //
    // save info into the file
    //

    fprintf( reportFile, "%s, Id = %x\n", functionName, functionId );
}

static EnumSyscall( startAddress, endAddress, reportFile )
{
    auto currentAddress;
   
    currentAddress = startAddress;

    while ( currentAddress != BADADDR )
    {
        if ( GetMnem( currentAddress ) == "syscall" )
            Processing( currentAddress, startAddress, reportFile );
       
        currentAddress = NextHead( currentAddress, endAddress );
    }

    return 0;
}

static main()
{
    auto addressInCodeSegment;
    auto codeSegmentStartAddress;
    auto codeSegmentEndAddress;
    auto reportFile;

    Message( "Script started. Wait a couple of seconds...\n" );

    //
    // choosing first ordinal, cause it placed in code segment anyway
    //

    addressInCodeSegment = GetEntryPoint( 1 );

    //
    // we will search in entire code section
    //

    codeSegmentStartAddress = SegStart( addressInCodeSegment );
    codeSegmentEndAddress = SegEnd( addressInCodeSegment );

    //
    // open file for report
    //

    reportFile = fopen( "c:\\ServiceFuncNames.txt", "wt" );

    //
    // find and process all syscall's
    //

    EnumSyscall( codeSegmentStartAddress, codeSegmentEndAddress, reportFile );

    //
    // close report file
    //

    fclose( reportFile );

    Message( "GetServiceFunctions script complete work, check file c:\ServiceFuncNames.txt for result...\n" );
}

четверг, 2 декабря 2010 г.

Python и реверсинг

Казалось бы - какая связь? А она есть =]



Прежде всего нужно отметить тот факт, что есть средства работать с той же IDA, это и скриптовый язык IDC и плагины которые можно писать на чем угодно. Однако, IDC хоть и удобен в каких-то мелких задачах, но тем не менее его возможности довольно ограничены.

Что же касается плагинов, то их возможности почти безграничны, но есть один недостаток и он довольно серьезен ( по крайней мере для меня ): невозможность без перезагрузки IDA поменять что-то в плагине, и если проект большой - эта мелочь настолько тормозит разработку, что возникают мысли заменить это на чтото иное. И после непродолжительных поисков и тестов, лучшим выбором оказался Python встроенный в IDA.

Страничка проекта idapython: http://code.google.com/p/idapython/

С Python'ом до этого я не встречался, но основы языка учаться сверх быстро.
В итоге профит - написание графа code flow ( то есть то, что показывает нам WinGraph32 в IDA ) на питоне занимает вечер, скорость разработки очень радует, по сравнению с плагинами.

Да и понаписано довольно много на питоне, включая такие интересные штуки как, например, PaiMei - целый фреймворк для реверсера, включающий в себя кучу компонентов для статического и динамического анализа.


Теперь что касается WinDbg, у него тоже есть скриптовый язык и плагины, до плагинов у меня руки не дошли( но думается, что проблемы там могут возникнуть теже, что и с плагинами IDA ), а вот скриптовый язык... О нем без мата я говорить не могу, код такого типа приходится писать:


.for (r $t4 = poi(@$t3); (@$t4 != 0) & (@$t4 != @$t3); r $t4 = poi(@$t4))
{
                r? $t5 = #CONTAINING_RECORD(@$t4, nt!_ETHREAD, ThreadListEntry)

                r? $t6 = @$t5->Cid
                r? $t6 = (nt!_CLIENT_ID *)&@$t6
                r $t6 = @@c++(@$t6->UniqueThread)

                r? $t7 = (nt!_KTHREAD *)@$t5
                r $t8 = @@c++(@$t7->Alertable)

Вобщем юзать _это_ просто противно, однако и тут нам чип и дейл Python спешит на помощь.

Добрые люди интегрировали его и в WinDbg: http://pykd.codeplex.com/

И теперь скрипты на том же питоне пишуться уже с превеликим удовольствием.