→ ructf2012 - Reverse300 $1/\/\PL3

| No TrackBacks
В трех словах:
com файл, подмена прерываний, самораспаковка, считываем с клавиатуры пароль, расшифровываем строку
Ответ: Bi11_i5_pr0|_|d_0|=_y0|_|

В много слов:
Выполнение начинается с вызова функции
seg000:0245                 call    sub_1028D
которая является своеобразным шлюзом для вызова других функций из таблицы, которая расположена по адресу 27Fh
seg000:02A8                 mov     ax, [bx+27Fh]
seg000:02AC                 push    ax
сама таблица содержит 7 функций, но часть кода зашифровано
seg000:027F off_1027F       dw offset f1_save_int1c
seg000:0281                 dw offset f2_save_int9
seg000:0283                 dw offset f3_save_int20
seg000:0285                 dw offset f4_restore_int1c
seg000:0287                 dw offset f5_restore_int20
seg000:0289                 dw offset f6_restore_int9
seg000:028B                 dw offset f7_print_string
Вначале управление передается на функцию:
seg000:0179 proc            f1_save_int1c near
Действия её сводится к следующему:
  1. вызов sub_1028D, которая в свою очередь вызовет уже вторую функцию f2_save_int9 (сохранит вектор 9го прерывания)
  2. сохранит вектор 1C прерывания
  3. вызов sub_1028D -> f3_save_int20
  4. установка нового вектора прерывания таймера

Давайте посмотрим новую функцию таймера
seg000:0104 proc            int_1C near 
Первым делом она устанавливает новые обработчики прерываний 0x9 и 0x20, далее она считает контрольную сумму самого себя с адреса 0x248 по 0x27B.
seg000:0115                 mov     bx, offset sub_10248
seg000:0118                 mov     [check_sum], 0
seg000:011D
seg000:011D loc_1011D:                              ; CODE XREF: int_1C+26j
seg000:011D                 cmp     bx, offset unk_1027B
seg000:0121                 jz      short loc_1012C
seg000:0123                 mov     al, [bx]
seg000:0125                 add     [check_sum], al
seg000:0129                 inc     bx
seg000:012A                 jmp     short loc_1011D
seg000:012C                 mov     bl, [check_sum]
seg000:0130                 and     bl, 0Fh         ; 8
seg000:0133                 mov     [check_sum], bl
Полученной контрольной суммой расшифровываются (0x248,0x27B), (0x1AA,0x1E1) (0x205,0x214) ниже скрипт для расшифровки:
sum = 0
for ea in range(0x10248,0x1027B):
   sum += Byte(ea)
sum &= 0xF

for ea in range(0x10248,0x1027B):
  PatchByte(ea,Byte(ea)^sum)
for ea in range(0x101AA,,0x101E1):
  PatchByte(ea,Byte(ea)^sum)
for ea in range(0x10205,0x10214):
  PatchByte(ea,Byte(ea)^sum)
Дальше выполнение программы вернется на 0x248, где в цикле с клавиатуры считывается 16 символьный пароль, считается сумма всех символов и этим ключем расшифровывается строка расположенная по адресу 0x02C0. Если сумма расшифрованной программы равно 0ADh, то мы выводим её.
seg000:0260 loc_10260:                              ; CODE XREF: sub_10248+23j
seg000:0260                 mov     bx, offset unk_102BE
seg000:0263                 add     bx, cx
seg000:0265                 xor     [bx+1], dh
seg000:0268                 add     dl, [bx+1]
seg000:026B                 loop    loc_10260
seg000:026D                 cmp     dl, 0ADh
seg000:0270                 jnz     short locret_10279
seg000:0272                 mov     ah, 9
seg000:0274                 mov     dx, 2BEh
seg000:0277                 int     21h             ; DOS - PRINT STRING
seg000:0277                                         ; DS:DX -> string terminated by "$"
Поскольку размер ключа у нам всего 1байт, а последний символ должен быть '$' мы можем перебрать все значения (на самом деле его можем просто вычислить) и найти верный ключ, которым расшифровав строку получим '90 |)33P3r$'

Идем дальше, смотрим прерывания int9, прерывание от клавиатуры. Что оно делает? Читает с контроллера прерываний сканкод нажатой клавишы, после этого суммирует значение первых 16 нажатых клавиш и вызывает функцию расшифровки второй для второй строки, расположеной по адресу 0x02CD.
seg000:01AA proc            decrypt_str2 near       ; CODE XREF: seg000:01DEp
seg000:01AA                                         ; DATA XREF: int_1C:loc_1014Bo
seg000:01AA                 mov     cx, 19h
seg000:01AD
seg000:01AD loc_101AD:                              ; CODE XREF: decrypt_str2+16j
seg000:01AD                 mov     bx, 2CDh
seg000:01B0                 add     bx, cx
seg000:01B2                 mov     al, [bx]
seg000:01B4                 xor     al, [key1]
seg000:01B8                 rol     al, cl          ; rol(b[i]^key,i)
seg000:01BA                 mov     [bx], al
seg000:01BC                 add     [key2], al
seg000:01C0                 loop    loc_101AD
seg000:01C2                 retn
seg000:01C2 endp            decrypt_str2
def decrypt(s,key):
   r = []
   for i in range(0x19,-1,-1):
      r.append( rol(s[i]^key,i%8) )
   return r

s = []
for ea in range(0x102CD,0x102CD+26):
   s.append(Byte(ea))

for i in range(256):
  a = map(chr,decrypt(s,i))
  if( a[0] == '$'):
    a.reverse()
    print "".join(a)
На выходе получаем строку: '\x19i11_i5_pr0|_|d_0|=_y0|_|$'
Однако, asm код не расшифровает первый символ строки, поэтому оставив его без изменений получим правильный ответ: Bi11_i5_pr0|_|d_0|=_y0|_|

No TrackBacks

TrackBack URL: http://smokedchicken.org/m/mt-tb.cgi/71

About this Entry

This page contains a single entry by snk published on March 18, 2012 9:21 PM.

Codegate Quals - Network 200 was the previous entry in this blog.

ructf2012 - Reverse400 What I do? is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.