[EasyCTF 2017] 67k (rev 400)
Задание: 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)