Задание: 67k.zip

Дано 67139 exe-файлов (Чооооооо), которые имеют общую структуру. Каждый файл на вход принимает число, которое сравнивается с числом полученным от некоторой арифметической операции над двумя константами. Если они равны, то делаем сдвиг этого значения на какое-то количество бит, которое также для каждого файла свое и выводим младшие 8 бит полученного значения.

/ (fcn) entry0 97
|   entry0 ();
|           0x00402006      685e304000     push 0x40305e
|           0x0040200b      ff1544104000   call dword [sym.imp.msvcrt.dll_puts]
|           0x00402011      58             pop eax
|           0x00402012      686c304000     push 0x40306c
|           0x00402017      6804304000     push 0x403004
|           0x0040201c      ff1548104000   call dword [sym.imp.msvcrt.dll_scanf]
|           0x00402022      83c408         add esp, 8
|           0x00402025      a100304000     mov eax, dword [0x403000]
|           0x0040202a      b9eda7a8a1     mov ecx, 0xa1a8a7ed
|           0x0040202f      e8cfffffff     call 0x402003
|           0x00402034      3b056c304000   cmp eax, dword [0x40306c]
|       ,=< 0x0040203a      751e           jne 0x40205a
|       |   0x0040203c      8a0d07304000   mov cl, byte [0x403007]
|       |   0x00402042      d3f8           sar eax, cl
|       |   0x00402044      25ff000000     and eax, 0xff
|       |   0x00402049      50             push eax
|       |   0x0040204a      6834304000     push 0x403034
|       |   0x0040204f      ff154c104000   call dword [sym.imp.msvcrt.dll_printf]
|       |   0x00402055      83c408         add esp, 8
|      ,==< 0x00402058      eb0c           jmp 0x402066
|      |`-> 0x0040205a      6808304000     push 0x403008
|      |    0x0040205f      ff1544104000   call dword [sym.imp.msvcrt.dll_puts]
|      |    0x00402065      58             pop eax
\      `--> 0x00402066      c3             ret

Обрабатывать все это количество файлов вручную неблагодарное дело да и не выполнимо за приемлемое время.

И я решил что эмуляция в r2 должна справится с этой задачей. И я был прав, накидал быстренько скрипт, который через r2pipe будет мне виндовые файлы на маке эмулить :)

Скрипт работал отлично, но из-за того что файлов очень много и больше всего времени тратится скорей всего на создание новой сессии для каждого файла он работал больше часа. Но это не было проблемой, т.к. было еще много заданий, которые можно было решать пока он выполняет свою работу. Но у организаторов появился дурная привычка убирать задание (при этом задания рабочие и не забагованые), немного его менять и сново выкладывать с обнулением очков за предыдущее решение. И было принято решение распаралелить обработку файлов, т.к. ждать часами, а потом еще надеется на то, что его снова не поменяют мне не хотелось. И эта задача отлично распаралелилась. И теперь на все тратилось порядка 16 минут.

Но как и поведено в r2 - нельзя просто так взять и заставить работать его так как тебе хочется. ;)

После обработки всех файлов, результат их выполнения сформировал большой javascript обфусцированный jsfuck’ом. Для деобфуфскации заюзал JSUNFuck (все таки могут на .NET писать годные тулзы ;) ) И получил следующий код

alert("The flag is easyctf{double_you_tee_eff?so_mAny_b1ns}");

Скрипт для обработки файлов

import r2pipe
from glob import iglob
from Queue import Queue
from threading import Thread, Lock


def get_char(fname):
    r2 = r2pipe.open(fname)

    entry = int(r2.cmd('s'), 16)
    start_addr = entry + 31
    before_cond_addr = entry + 46
    after_cond_addr = entry + 54
    end_addr = entry + 67

    r2.cmd('s 0x%x' % start_addr)
    r2.cmd('aei; aeim; aeip')

    r2.cmd('aecu 0x%x' % before_cond_addr)
    r2.cmd('ar eip = 0x%x' % after_cond_addr)

    r2.cmd('aecu 0x%x' % end_addr)
    res = int(r2.cmd('ar eax'), 16)

    r2.quit()

    return res


class MyThread(Thread):
    def __init__(self, q):
        super(MyThread, self).__init__()
        self.q = q

    def run(self):
        while True:
            fname = self.q.get()
            idx = int(fname[-9:-4], 16)
            res = get_char(fname)
            lock.acquire()
            l[idx] = res
            lock.release()
            self.q.task_done()


n = 67139
l = [0] * n

queue = Queue()
lock = Lock()

for i in range(16):
    worker = MyThread(queue)
    worker.setDaemon(True)
    worker.start()

for fname in iglob('*.exe'):
    queue.put(fname)

print 'Wait'
queue.join()
print 'Done'

s = ''.join(map(chr, l[12:]))

with open('res.js', 'w') as f:
    f.write(s)