+ Reply to Thread
Results 1 to 4 of 4

Thread: Pwnium CTF [reverse300] breakpoint

  1. #1
    ARCHANGEL's Avatar

    Message Pwnium CTF [reverse300] breakpoint


    Добрый день, уважаемые форумчане. В этот раз мы с вами будем исследовать задание breakpoint из категории «реверсинг», взятое с pwnium CTF:
    re300
    Зеркало 1
    Зеркало 2



    Сам таргет особо непримечателен. Можно даже назвать эту цель лёгкой. Но я в этой статье попытаюсь рассмотреть возможности дизассемблера IDA Pro вообще, и плагина IDA Python в частности. Не то, чтоб это была какая-то закрытая информация – нет, всё это можно найти в общем доступе в документации к IdaPython. Проблема в том, что эта самая документация структурирована из рук вон плохо, и найти в ней что-то, если не знаешь, что искать, часто бывает весьма затруднительно. Поэтому мы с вами, так сказать, в «боевых условиях», попробуем пойти дальше, чем множество уже доступных writeup’ов и рассмотрим, как применение этого плагина может упростить статический анализ.

    Мне часто приходиться слышать мнение, будто реверс-инженерия, это «долгий вдумчивый взгляд в дизассемблерный код». Но я считаю иначе. Настоящий реверсинг – это не только понимание имеющегося у нас кода, но и способность манипулировать его разнообразными представлениями так, чтоб эти преобразования способствовали пониманию. Теперь попробуем воплотить эти красивые (или не очень) слова в реальность.

    Для начала перейдём на точку входа приложения. Это стандартная точка входа для программ, написанных на gcc.



    Переименуем функцию по адресу 0x40661Ah на main, и именно с неё мы и начнём наше исследование:



    В самом начале нас ждёт один простой антиотладочный приём:



    Здесь видно, что в случае обнаружения отладчика переменная stateValue будет увеличена на 1. Ниже легко найти функцию, которая выполнит проверку серийного номера, и в случае ввода «правильного» номера, выведет на экран смайлик :



    Зайдя внутрь функции CheckSerialInput, видим что-то длинное и, на первый взгляд, сложное. На самом деле, эту функцию можно условно разделить на 2 части: часть модификации состояний и часть проверки символов. Рассмотрим первую часть:



    Как только переменная stateValue становится равной какой-то из констант состояний, делается переход на базовый блок, в каждом базовом блоке переменная stateValue получает новое значение, а мы узнаём один из символов флага. Пример одного такого базового блока:



    Странно видеть здесь термин «базовый блок», но это так. Если будет выполняться условный переход, то это значит, что введенное значение не соответствует флагу.
    Давайте подытожим. Идея в том, что каждому состоянию соответствует некий базовый блок. Внутри базового блока происходит сравнение текущего символа флага с некоторой предопределённой константой. Если текущий символ равен этой константе, значит переменная stateValue получает другое значение (происходит переключение состояния), и всё повторяется. Если всё так красиво и понятно, почему бы не автоматизировать этот процесс?
    В IdaPython есть неоспоримое преимущество – помимо возможности дизассемблера, у нас появляются ещё и возможности python’a. Для начала рассмотрим API IdaPython, который нам тут пригодится.

    • Byte(address) – вернёт байт, расположенный по указанному адресу.
    • Dword(address) – то же, только dword.
    • GetDisasm(address) – вернёт строку дизассемблированного текста. Что интересно, строка вернётся из базы, поэтому перед использование следует убедиться, что возвращаемое значение действительно соответствует вашим ожиданиям:
    • DecodeInstruction (address) – выполняет декодирование инструкции, возвращая некий объект insn_t. В общем, на этом – всё. Вот так немного нам понадобится, чтоб осуществить задуманное.

    Code:
    beginRawStatePos = 0x000000000040064D
    endRawStatePos = 0x0000000000400EF0 #after the last one
    currentStatePos = beginRawStatePos
    allStates = {}
    pattern1 = 'cmp     eax, '
    pattern2 = 'jz      loc_'
    
    while currentStatePos < endRawStatePos:
      currentInstruction = idc.GetDisasm(currentStatePos)
      StateMatch = currentInstruction.find(pattern1)
      if StateMatch != -1:
        stateValue = int('0x' + currentInstruction[len(pattern1):-1],16)
      else:
        StateMatch = currentInstruction.find(pattern2)
        if StateMatch != -1:
          addressValue = int('0x' + currentInstruction[len(pattern2):],16)
          allStates[stateValue] = addressValue
      currentStatePos += GetInstructionSize(currentStatePos)
    Эта часть скрипта создаст словарь, ключами в котором будут значения состояний, а значениями – адреса базовых блоков. Потом определим несколько служебных функций:

    Code:
    def GetInstructionSize(address):
      return idautils.DecodeInstruction(address).__get_size__()
    
    def GetStateSymbol(blockAddress):
      byteAddress = blockAddress + 8
      return Byte(byteAddress)
    
    def GetStateValue(blockAddress):
      stateAddress = blockAddress + 17
      return Dword(stateAddress)
    Первая – определит размер инструкции в байтах, вторая – считает значение символа флага (то, которое должно быть), третья – считает из текущего блока новое значение состояния.
    И последний цикл восстановит значение флага:

    Code:
    State = 0x0FE129837
    flag = array.array('B','')
    try:
      while 1:
        currentBlock = allStates[State]
        State = GetStateValue(currentBlock)
        flag.append(GetStateSymbol(currentBlock))
    except: pass
    
    print(flag.tostring())
    Таким образом, в консоли плагина появляется значение флага:

    D3bugG1nG_Th1s_ObfuSc4t3d_C0d3_1s_R34lly_H4rD

    Полный текст скрипта приведен здесь
    Добрым быть просто - достаточно обратить свой гнев на негодяев...

  2. 4 пользователя(ей) сказали cпасибо:
    Dark Koder (14-07-2014) Darwin (15-07-2014) OKOB (14-07-2014) ximera (15-07-2014)
  3. #2

    Default Re: Pwnium CTF [reverse300] breakpoint

    Элегантное решение. Автор за кого играл?? Реверса на pwnium было на удивление много, но имхо он был гораздо проще чем обычно. Во время игры поднял все 10+100+150+200+300, чем помог RDot прыгнуть на 3е место.

    Мне так понравилась идея автоматизации, что не удержался от проверки по старинке на IDC:
    Code:
    auto eaB, eaE, ea, xval, value, addr, str_out, i;
    eaB = 0x40064d;
    eaE = 0x400ee5;
    str_out = "";
    xval = 0xFE129837;
    for(i=0;i<45;i=i+1) {
      ea = eaB-1;
      for(;;) {
        ea = FindBinary(ea+1, 3, "3D ? ? ? ? 0F 84");
        if (ea==BADADDR)
          break;
        value = Dword(ea+1);
        addr = ea+Dword(ea+7)+11;     
        if(ea == eaE)
          break;
        if(value == xval)
          break;  
      }
      Message("%c", Byte(addr+8));  
      xval = Dword(addr+17);
    }
    Message("\n");
    Last edited by OKOB; 14-07-2014 at 23:21.

  4. 3 пользователя(ей) сказали cпасибо:
    Dark Koder (15-07-2014) Darwin (15-07-2014) ximera (15-07-2014)
  5. #3
    ARCHANGEL's Avatar

    Default Re: Pwnium CTF [reverse300] breakpoint

    На IDC компактно получилось. Тоже так симпатично.
    Добрым быть просто - достаточно обратить свой гнев на негодяев...

  6. 2 пользователя(ей) сказали cпасибо:
    Dark Koder (15-07-2014) ximera (15-07-2014)
  7. #4

    Default Re: Pwnium CTF [reverse300] breakpoint

    А что там с павном случилось? Он вроде был сначала доступен, а потом убрали таск.

+ Reply to Thread

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
All times are GMT. The time now is 01:29
vBulletin® Copyright ©2000 - 2018
www.reverse4you.org