Как стать автором
Обновить

Комментарии 53

Хороший инструмент. Помню нам один из поставщиков, присылал документы в формате frp (20-30 листов ежемесячно). Нам из него надо было вытащить таблицы для дальнейшей работы. Программа designfr умеет конвертировать frp в разные форматы, но если в поле таблицы есть перенос строки, то при конверте всё ломается, часть поля может встать выше или ниже, вариантов много. Вначале конвертированный файл исправлялся с помощью регулярок (шутка по 99 проблем), но со временем число вариантов росло, и добавлялся ручной труд по вычистке. Следующей идеей было парсить frp файл самостоятельно, и вытаскивать оттуда таблицу, но это очень трудозатратно. Остановился на промежуточном варианте, склеивая поля с переносами, соответственно корректируя размер документа, страницы и разные смещения, а после чего штатными средствами конвертируя. Писал скрипт на PowerShell с «магическими цифрами» смещений. Поставщик теперь уже сменился.
Установил пакет kaitai-struct-compiler, но там нет визуализатора =(. Где его взять?
Присоединяюсь к вопросу.
Ох %) Я не автор статьи, зато как бы немножко автор KS :) До сих пор немножко в офигевшем состоянии.

Уважаемый товарищ mnakamura каким-то непостижимым образом нашел KS, когда мы его только начинали выкладывать на github и по сути была выложена только первая версия компилятора. Написал мне, я ответил, завязалась переписка — всегда же здорово, когда обращают внимание и когда по делу. Общались по-английски. И я, разумеется, в жизнь бы не подумал, что товарищ с именем Nakamura Matsuo и японским e-mail'ом может писать по-русски, да и еще отгрохать такую статью на хабр. Накамура, блин. Ну ладно я. Но у меня-то в почте вполне русские имя-фамилия видны, да и на github'е я особенно не скрываюсь. Неужели было сложно посмотреть? И уж тем более если статью на уважаемый ресурс пишешь — хоть спросить?

Сворачивая драму и возвращаясь к исходному вопросу и статье, позволю себе немножко прокомментировать:

1. На самом деле современная версия компилятора написана на Scala, а не на Java.
2. Для запуска на самом деле есть варианта: либо под JRE, либо под node.js (т.к. Scala может компилироваться в JavaScript).
3. Визуализатор действительно есть и он действительно не выложен. Мы планировали выкладывать его на следующей неделе, но раз события так подстегивают — дайте хоть полчаса-час времени, приведу его в более-менее приличное состояние и выложу.

Т.е. у автора статьи визуализатор, которого нет в паблике?
И вправду интересно получилось :)

Еще раз виноват. Признаю, меру, степень, глубину. Я как-то не догадался посмотреть — уже привык, что преимущественно вся infosec-тусовка в штатах и общается по-английски. У меня сейчас в контактах, например, Feodor Fitzman и Stacy Krymsky — с обоими знакомился на конференциях, про обоих я достоверно знаю, что люди родились в штатах и по-русски не говорят (хотя, вероятно, семейные корни из xUSSR).

Пользуясь случаем, хочу поблагодарить тогда еще раз уже по-русски за отличный проект. Извините, что так получилось с визуализатором.
Большое спасибо
Да, хотелось бы по-подробнее о том, где взять визуализатор или как включить, если он есть в стандартной сборке?
Посыпаю голову пеплом, мой промах. Я узнал о KS пару недель назад, списался с автором — и он мне много чем помогал, в том числе прислал кучу примеров форматов, вот этот самый визуализатор, дампер в YAML/JSON и т.п. утилиты. Я, собственно, увидел анонс на opennet, что у них релиз таки состоялся и подумал, что это все уже опубликовали, а, оказывается, пока опубликовали только компилятор.

Сейчас посмотрел внимательнее — в компиляторе явно есть файл LICENSE, что он GPL, а вот в визуализаторе нигде упоминаний о лицензии нет — соответственно, я немножко в ступоре — могу ли я его куда-то выкладывать или как… Сейчас напишу и спрошу.
Всё, теперь я знаю, к кому обращаться для того, чтобы вскрыть никому не известную визуалку на непонятно каком движке.
Обращайтесь, на самом деле, 何故なの ;) У меня в коллекции пока 4 расковырянных более-менее хорошо движка, хотелось бы еще собрать.
Вскройте двиг фирмы eushully ;)
Там своя ВМка с опкодами (коих я насчитал около 300). Больше года колупал — так и не смог понять принципа работы, настолько там всё завернуто. Все скрипты являются бинарниками с секциями кода и прочих данных, включая даже некие таблицы смещений. Все тексты упрятаны там же и не правятся без изменения кучи смещений в коде и т.п.

PS: Я пока колупаю двиг Overflow (School Days & Co). Уже давно 95%, даже свой двиг готов на его основе. осталось только пара фитч нереализованных.
О, сейчас как раз идёт перевод Kami no Rhapsody от них, бедные ребята переводят через Visual Novel Reader, так как скрипты никто не смог вскрыть :(
Я частично вскрыл, но все зависло на всех этих смещениях. Я даже сам движок пропатчил (ASMом, в HEX-редакторе), переведя весь вывод текста на экране на Юникод. В итоге даже отвязал игру от японской локали вовсе (даже поиск шрифтов перебил, что бы показывал именно Юникодовские шрифты, а не ShifJS).
Давайте объединять усилия? У меня по Eushully ряд наработок есть (я Arterial начинал, но он дико нудный и длинный — в итоге так и не прошел, да и интерес ковырять поугас — VMка там действительно одна из самых странных, что я видел). VMки, кстати, в KS разбираются относительно неплохо, я уже штуки 3 сделал в нем.
Я сейчас другой ВНкой занят.А точнее 2-мя на одном движке…
Вы меня заинтриговали, скачал, посмотрел. А что там принципиально сложного? Ну, помимо очевидного замечания, что при количестве команд в несколько сотен выяснение всех деталей того, что делает каждая, потребует некоторой усидчивости?
Таблицы смещений в скриптах в количестве трёх штук — просто адреса команд с опкодами 0x71, 3, 0x8F. Первая — список возможных сообщений для хранения флагов прочитанности/непрочитанности, вторая — список возможных внешних вызовов из скрипта, третья — список возможных вызовов процедур. Подозреваю, что так сделано для большей стабильности сейвов: когда в сейвах стек вызовов хранится как массив индексов в отдельной таблице, а не прямо адресами внутри файла, это не будет плыть при каждом изменении скриптов.
Для правки текстов кучу смещений изменять не нужно, достаточно дописывать исправленные тексты в конец файла и править только одно смещение — собственно адрес текста.
Примитивный дизассемблер
from __future__ import print_function
import io, sys, struct
#table generated automatically, minor mistakes are possible
cmdsizes = (
0,1,1,3,3,1,5,3,3,1,5,23,1,9,25,3,	#0x0
9,19,3,9,1,11,5,5,0,0,0,0,0,0,17,25,	#0x10
13,5,5,5,5,7,9,9,9,0,9,11,11,25,11,9,	#0x20
11,9,21,13,25,23,7,23,25,0,0,0,0,0,0,0,	#0x30
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x40
7,7,7,7,7,5,7,7,7,7,7,7,7,7,7,7,	#0x50
5,7,7,5,5,5,7,7,7,7,7,7,5,1,5,3,	#0x60
11,3,3,21,3,3,3,3,3,7,7,5,1,5,3,3,	#0x70
3,3,11,7,3,1,3,1,3,9,13,3,3,5,3,3,	#0x80
15,3,5,1,1,5,1,11,0,0,0,0,0,0,0,0,	#0x90
7,1,5,5,0,0,0,0,0,0,5,5,19,1,1,1,	#0xa0
3,3,5,1,5,3,3,3,1,3,3,3,3,3,3,3,	#0xb0
3,1,5,3,3,5,5,5,3,1,1,3,5,1,7,1,	#0xc0
3,1,3,1,9,3,13,3,5,1,13,0,0,0,0,0,	#0xd0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0xe0
0,0,0,0,0,0,0,0,0,0,1,5,1,5,3,1,	#0xf0
1,1,7,3,1,3,3,5,3,5,5,5,5,3,5,3,	#0x100
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x110
0,0,0,0,0,0,0,0,0,0,0,0,11,15,17,9,	#0x120
3,3,3,5,7,5,5,3,5,7,13,15,3,7,5,7,	#0x130
9,3,3,1,5,3,3,13,3,3,15,3,5,13,0,0,	#0x140
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x150
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x160
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x170
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x180
5,5,5,7,7,7,7,3,7,1,3,1,1,5,5,5,	#0x190
19,5,3,3,5,3,5,3,1,3,3,5,7,1,7,7,	#0x1a0
7,3,3,1,1,3,3,3,5,5,5,3,1,3,5,1,	#0x1b0
3,7,5,5,3,9,5,3,5,7,3,3,3,5,3,3,	#0x1c0
7,11,5,11,9,1,5,5,7,5,0,0,0,0,0,0,	#0x1d0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x1e0
0,0,0,0,1,1,1,5,9,7,3,17,3,9,11,9,	#0x1f0
3,3,11,9,9,13,15,17,7,11,3,15,1,3,1,7,	#0x200
3,3,5,7,5,5,5,9,9,9,9,3,1,5,13,15,	#0x210
13,9,5,17,1,5,11,13,11,11,7,9,7,11,13,11,	#0x220
3,9,9,11,11,11,9,5,3,13,5,15,1,1,5,5,	#0x230
9,11,5,1,1,5,5,3,3,7,7,5,5,25,3,21,	#0x240
21,25,3,5,11,13,11,11,5,1,3,3,17,7,11,9,	#0x250
9,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x260
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x270
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x280
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x290
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x2a0
0,0,0,0,0,0,0,0,0,0,0,0,23,3,3,7,	#0x2b0
7,3,13,5,1,5,5,9,9,7,3,3,3,3,3,3,	#0x2c0
7,7,7,7,7,5,5,5,7,5,17,3,3,5,5,7,	#0x2d0
7,7,7,7,7,3,5,5,3,3,3,3,5,3,3,23,	#0x2e0
19,15,13,13,7,9,3,3,5,15,3,3,11,13,0,0,	#0x2f0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x300
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	#0x310
21,7,9,11,1,5,9,3,7,5,3,1,13,5,23,3,	#0x320
5,9,9,11,3,9,11,9,13,15,13,9,5,7,11,7	#0x330
)
# list created manually, may be incomplete
# (cmd in oplabels[i]) == (i-th operand of cmd is an offset)
oplabels = (
(0x7B,0x8C,0x8F,0xD5),
(0x7B,0x8D,0x92,0x95,0xA0,0xA2,0xA3,0xCC,0xCE,0xFB),
(0xA0,0xCE,0xD4,0x102),
(0xD4,),
(0xD6,0x90),
(0xD6,0x90),
(0x90,),
)
with io.open(sys.argv[1], 'rb') as f:
	header = struct.unpack('<4s4s6I', f.read(0x20))
	if header[0] != b'SYS3' and header[0] != b'SYS4':
		print("invalid signature")
		sys.exit(1)
	header2size = struct.unpack('<I', f.read(4))[0]
	header2 = struct.unpack('<' + str(header2size // 4 - 1) + 'I', f.read(header2size - 4))
	data = f.read()
# pass 1: prepare labels
pos = 0
maxAddr = 0
labels = set()
while pos < len(data) // 4:
	cmd = struct.unpack_from('<I', data, pos * 4)[0]
	if cmd >= len(cmdsizes) or cmdsizes[cmd] == 0:
		break
	for i in xrange((cmdsizes[cmd] - 1) // 2):
		optype, operand = struct.unpack_from('<ii', data, (pos + 1 + 2 * i) * 4)
		if i < len(oplabels) and cmd in oplabels[i] and optype == 0 and operand >= 0:
			labels.add(operand)
			maxAddr = max(maxAddr, operand)
	pos += cmdsizes[cmd]
	if cmd in (1,2,5) and pos > maxAddr:
		break
# pass 2: print
print("\theader '%s', %d, %d, %d, %d, %d, %d" % header[1:])
pos = 0
unprintedLabels = labels
firstStringOffs, lastStringOffs = -1, -1
firstArrayOffs, lastArrayOffs = -1, -1
messageStarts = []
externalCalls = []
internalCalls = []
while pos < len(data) // 4:
	if pos in labels:
		print("loc_%08X:" % pos)
		unprintedLabels.remove(pos)
	cmd = struct.unpack_from('<I', data, pos * 4)[0]
	if cmd >= len(cmdsizes) or cmdsizes[cmd] == 0:
		print("\t[invalid command 0x%X]" % cmd)
		break
	if cmd == 0x71:
		messageStarts.append(pos)
	if cmd == 3:
		externalCalls.append(pos)
	if cmd == 0x8F:
		internalCalls.append(pos)
	cmdsize = cmdsizes[cmd]
	cmdargs = struct.unpack_from('<' + str(cmdsize - 1) + 'i', data, (pos + 1) * 4)
	print("\tcmd%03X" % cmd, end='')
	for i in xrange((cmdsize - 1) // 2):
		if i:
			print(",", end='')
		print(" ", end='')
		if cmdargs[i * 2] == 0:
			if i < len(oplabels) and cmd in oplabels[i] and cmdargs[i * 2 + 1] >= 0:
				print("loc_%08X" % cmdargs[i * 2 + 1], end='')
			elif cmd == 0x64 and i == 1:
				# second arg of cmd064 is offset of array in data
				offs = cmdargs[i * 2 + 1]
				if lastArrayOffs == -1:
					firstArrayOffs = offs
				elif lastArrayOffs != offs:
					print("[Warning: out-of-order array]");
				arrsize = struct.unpack_from('<I', data, offs * 4)[0]
				arr = struct.unpack_from('<' + str(arrsize) + 'i', data, (offs + 1) * 4)
				lastArrayOffs = offs + 1 + arrsize
				print('<', end='')
				for j in xrange(arrsize):
					if j:
						print(",", end='')
					print('%d' % arr[j], end='')
				print('>', end='')
			else:
				print('%d' % cmdargs[i * 2 + 1], end='')
		elif cmdargs[i * 2] == 2:
			offs = cmdargs[i * 2 + 1] * 4
			if lastStringOffs == -1:
				firstStringOffs = offs // 4
			elif lastStringOffs != offs // 4:
				print("[Warning: out-of-order string: %08X instead of %08X]" % (offs // 4, lastStringOffs), end='');
			decoded = b''
			while True:
				s = ord(data[offs]) ^ 0xFF
				if s == 0:
					break
				decoded += chr(s)
				offs += 1
			lastStringOffs = (offs + 1) // 4 + 1
			print(b'"' + decoded + b'"', end='')
		else:
			print('op%X[0x%X]' % (cmdargs[i * 2], cmdargs[i * 2 + 1]), end='')
	print()
	pos += cmdsize
	# command 5 can be a normal command with size=1 or nofollow-command depending on ???
	# commands 4,9,0x7C are actually nofollow, but codegen seems to treat them as normal ones
	if cmd in (1,2,5) and pos > maxAddr:
		break
if len(unprintedLabels):
	print("Warning: not all labels were printed");
expectedEndAddr = min(len(data) // 4, header2[1], header2[3], header2[5])
if firstArrayOffs != -1:
	if lastArrayOffs != expectedEndAddr:
		print("Warning: range [%08X,%08X) was not printed" % (lastArrayOffs, expectedEndAddr))
	expectedEndAddr = min(expectedEndAddr, firstArrayOffs)
if firstStringOffs != -1:
	if lastStringOffs != expectedEndAddr:
		print("Warning: range [%08X,%08X) was not printed" % (lastStringOffs, expectedEndAddr))
	expectedEndAddr = min(expectedEndAddr, firstStringOffs)
if pos != expectedEndAddr:
	print("Warning: range [%08X,%08X) was not printed" % (pos, expectedEndAddr))
if messageStarts != list(struct.unpack_from('<' + str(header2[0]) + 'i', data, header2[1] * 4)):
	print("Warning: unexpected messageStarts array")
if externalCalls != list(struct.unpack_from('<' + str(header2[2]) + 'i', data, header2[3] * 4)):
	print("Warning: unexpected externalCalls array")
if internalCalls != list(struct.unpack_from('<' + str(header2[4]) + 'i', data, header2[5] * 4)):
	print("Warning: unexpected internalCalls array")


В принципе, при некотором желании выдачу дизассемблера выше можно даже скомпилировать назад с помощью fasmg, если пошаманить с его макросами.

Интересно. А cmdsizes вы доставали откуда-то из exe-шника?

Да. В движке обработчики команд довольно любезно начинают с того, что выставляют размер команды типа mov dword ptr [esi+ecx*4+5D804h], 5, так что
примерно такой скрипт IDA
auto a,b,aprev,anext,s,f;
f=fopen("c:\\temp\\eushully_vm_cmdsize.txt","w");
for (b=0x415303;b!=BADADDR;b=NextHead(b,0x416713)){
  // установка обработчиков: mov dword ptr [esi+0xA4F24+i*4], offset cmd_i
  if (GetOpType(b,0)!=4) continue;
  fprintf(f,"0x%X\t",(GetOperandValue(b,0) - 0xA4F24)/4);
  s=GetOperandValue(b,1);
  aprev=BADADDR;
  for (a=s;a!=BADADDR;a=anext){
    anext=NextHead(a,BADADDR);
    if(!isCode(GetFlags(a))){fprintf(f,"[not a code at %X]\n",a);break;}
    if (GetOpType(a,0) == 4 && GetOperandValue(a,0) == 0x5D804) {
      if (GetOpType(a,1) == 5)fprintf(f,"%d\n",GetOperandValue(a,1));
      else if (GetOpType(a,1) == 1 &&
        GetMnem(aprev) == "mov" &&
        GetOpType(aprev,0) == 1 &&
        GetOperandValue(aprev,0) == GetOperandValue(a,1) &&
        GetOpType(aprev,1) == 5)fprintf(f,"%d\n",GetOperandValue(aprev,1));
      else fprintf(f,"[unknown write type at %x]\n",a);
      break;
    }
    // если обнаружили ветвление, на всякий случай выходим
    if(Rfirst(a)!=anext || Rnext(a,anext)!=BADADDR){a=BADADDR;break;}
    aprev=a;
  }
  if (a==BADADDR) fprintf(f,"[unresolved: %x]\n",s);
}
fclose(f);


заполняет таблицу, за исключением нескольких сложных случаев, которые уже можно добить руками.

Ох. Это уже как-то неспортивно получается и уже совсем не clean-room, но в целом — почему бы и нет ;)


Я все пытаюсь делать максимально без залезания внутрь, кроме совсем уж тяжких случаев — так и интереснее, и куда ближе к боевым условиям. Ну и банально потому, что есть туча новелл типа под всякие PC98, которые даже запустить негде, не то, что дизассемблировать или дебагать.


А 0x415303 и 0xa4f24 — это в age.exe от какой игры?

У меня в анамнезе слишком много копания в коде, чтобы думать о clean-room, и слишком много программирования, чтобы руками заполнять таблицу, которую может сделать скриптик на десяток строчек кода. Но ваш подход тоже заслуживает внимания.
Тут выше назвали одну конкретную игру, Kami no Rhapsody, это оттуда. Движок версии 4.46.

Ну, ее в любом случае всю вручную заполнять, придумывая имена командам и разбирая, что они делают — если речь не о просто выдирании строчек :)

Да, так оно проще. Но на тот момент я в IDA разбирался очень слабо, в итоге большую часть делал «наобум».
Очень интересно, спасибо!
Ну вот, на самом интересном месте.
А то как-то пытался расковырять формат хранения диалогов в одной игре (ресурсы не запакованы), но так ничего и не вышло (нет опыта в реверсе).
Максимум, что выжал — название движка, сайт, где можно его скачать и собственно, триал движка и вроде даже какого-то инструментария. Но он, разумеется, даже не на английском. На этом я остановился и бросил затею.
010Editor. Работает без явы. Правда, платный.
Угу. И написать сначала темплейт для него, а потом все переписать по второму разу, уже в виде кода. Собственно, статья ровно про то, что можно так не делать и экономить время и силы.
Выскажусь в защиту предыдущего автора. Новый инструмент достаточно интересный, и мне самому захотелось попробовать, как понадобится разобрать какой-то формат.

Однако я бы не был столь категоричен насчёт 010. Распаковщик можно написать даже в рамках его скриптового языка, и мне приходилось вполне успешно это делать (хотя количество багов что в 010, что в питоновой реализации его шаблонизатора убивает).

Более того, лично мне представление данных в 010 очень удобно, так что уйти от него будет нелегко. Да и много больше времени при разборе формата уходит на алгоритм сжатия, чем на написание небольшого куска кода для i/o. Тем более, что почти всегда это копипаст с других утилит.

К конкретно 010 у меня есть несколько крупных претензий:


  1. Он закрытый и стоит денег.
  2. Его язык шаблонов только на первый взгляд декларативный. Думаю, вы как раз знаете, что там шаг влево-шаг вправо — привет, старые добрые циклы while (!FEof()) { ... } или ручные ReadBytes(...), ReadUInt(...) и т.д.
  3. Как следствие императивности — на любых более-менее больших файлах (гигабайты хотя бы) — 010 превращается в улитку и жрет память, как не в себя. Нельзя даже просто разметить области, не вдаваясь в подробности: приходится искусственно городить в шаблоне некое подобие LOD, только для того, чтобы каждая итерация не занимала по 5-7 минут.

Насчет алгоритма сжатия — с одной стороны соглашусь, с другой — как раз, субъективно, это куда более банальная задача. Алгоритм сжатия особенно нечего угадывать и исследовать — он зачастую выдирается буквально как есть из бинарника, ассемблер транслируется в эквивалентный C, скажем, после чего его можно просто запускать и особенно в нем не копаться.

Стоит порадоваться, что в отличие от конкурентов даже после выжирания гигабайтов памяти он не вылетает и не зависает наглухо. Но претензии по делу, достаточно какого-нибудь скрипта с несколькими миллионами инструкций. Хотя для меня императивность — это особенность/возможность, а не недостаток.
А вот со сжатием — если дело с популярной платформой типа x86, то часто легче выдрать, в консольных же движках подлезть далеко не всегда проще ручного анализа.
отличие от конкурентов даже после выжирания гигабайтов памяти он не вылетает и не зависает наглухо

Данупрям. Вот только что попробовал загрузить свою разбиралку формата VNки 3-летней давности: исходный бинарник 4 гига (под 1 DVD подгоняли), выжрало 16 гигабайт памяти и 8 с чем-то свопа, потормозило минут 15 и умерло.


А вот со сжатием — если дело с популярной платформой типа x86, то часто легче выдрать, в консольных же движках подлезть далеко не всегда проще ручного анализа.

radare чудесно делает из фактически кода любой платформы C-подобный код, который минимально надо обработать напильником и можно хоть в C, хоть в Java, хоть в JavaScript.

Допускаю, хотя 24 гига всего на 1 DVD — я бы задумался, а как оно написано. Однако тот же нео или синалайзис, которые являлись прямыми (и чуть ли не единственными) конкурентами, вылетят много раньше.

Лукавство лукавством, но как реверсер вы должны понимать, что сложность не столько в декомпиляторах, сколько в возможности подобраться к функции распаковки, начиная с возможности чтения бинарника вообще. Отладчика может не быть, а XREF-ы на библиотеки не находиться.

Впрочем, мы по-моему отошли от темы, к предложенному инструменту конкуренты не относятся; спасибо за дискуссию.

Да ответ, в общем, простой: все эти штуки создают разметку не on demand, а сразу проходя всё. При том, что в реальности читающая процедура делает не так: ей незачем загружать в память (и тем более распаковывать сразу) все содержимое DVD, она зачитает три с половиной нужных прямо сейчас файла, остальное зачитает, когда будет нужно.


Впрочем, мы по-моему отошли от темы, к предложенному инструменту конкуренты не относятся; спасибо за дискуссию.

Вполне интересная тема, я на такие могу часами разговаривать :) На одном небезызвестном сайте, обсуждая эту статью, сейчас подкинули новую интересную задачку — я уже второй вечер над ней голову ломаю. Пока заткнулся на том, что есть 16-байтовые строчки, из которой надо извлечь позицию и размер файла в архиве. Еще пару вечеров помедитирую, а потом, наверное, сдамся, и перейду на тяжелую артиллерию типа Olly или IDA.

radare чудесно делает из фактически кода любой платформы C-подобный код, который минимально надо обработать напильником и можно хоть в C, хоть в Java, хоть в JavaScript.

Не поделитесь как от r2 получить такой C-подобный код?
Мне все это напомнило " бесконечное лето", очень популярную игру, причем популярную у очень странных и смешных людей
«Бесконечное лето» — визуалка, написанная странным людьми для странных людей под впечатлением от качественных японских визуалок.
Это офигительно. Хорошие статьи про реверс-инженеринг читаются как технодетектив. А есть ли в природе контесты с подобными заданиями «расковырять непонятно что»?
Да берёте любой бинарь и в путь, не? Правда расковыривать те же исполняемые файлы — это вам не парсить простенький не зашифрованный и даже не сжатый том с обычными файлами.

Периодически проводятся всякие CTF-конкурсы (типа такого).

для распаковки архивов т.н. «визуальных новелл» есть несколько инструментов, например, вот этот. в нём, правда, нет функциональности для описания неизвестных форматов по типу kaitai, но всё же список известных довольно обширен, в частности, есть и поддержка Yuka (движок, описанный в заметке).
Их, на самом деле много, если интересно. Из больших есть Crass, есть [vn-tools](https://github.com/vn-tools/arc_unpacker/), есть [ae](http://wks.arai-kibou.ru/ae.php), есть приснопамятный ExtractData (автора которого засудили). Я на самом деле ковыряю визуальные новеллы уже довольно давно (примерно как переехал сюда и начал язык учить) — у меня внушительная коллекция собралась.
примерно как переехал сюда и начал язык учить

Т.е. вы переехали в Японию. Я правильно понял? А с какой целью и чем занимаетесь, если не секрет?
В статье вы, кажется, не упоминали.

Правильно. В статье не упоминал, потому что статья все-таки не про меня, да и я относительно шифруюсь. Как показала ситуация с minori и ExtractData — местные копирасты весьма агрессивно относятся к тому, что продаваемые в их магазинах визуальные новеллы распаковывают. Если читаете по-японски — то http://mitiku3.jugem.jp/?eid=5959 — автор ExtractData вроде бы до сих пор settlement им выплачивает, ему что-то в районе 2 миллионов йен выкатили.

А с какой целью и чем занимаетесь, если не секрет?

Конечно же, скупить все фигурки на Акихабаре (*•̀ᴗ•́*)
А если серьезно — ну, нравится мне страна, нравится культура. Сакура-но-кайка вон неделю назад буквально закончилась.

А так все примерно как у 90% уезжающих: минимально выучил язык по аниме, сдал на JLPT n4, поступил учиться, за год освоился, в итоге остался. Живу, работаю. Компанию, наверное, напрямую писать от греха подальше не буду, но, с другой стороны гуглится это все за две минуты.
minori и ExtractData — местные копирасты весьма агрессивно относятся к тому, что продаваемые в их магазинах визуальные новеллы распаковывают. Если читаете по-японски — то http://mitiku3.jugem.jp/?eid=5959 — автор ExtractData вроде бы до сих пор settlement им выплачивает, ему что-то в районе 2 миллионов йен выкатили.

По-японски к сожалению не читаю. Но то, что жестко относятся к ковыряниям продуктов — тут бы и я не сильно-то распространялся про себя, в таком случае.


Любопытно. Чтож, успехов вам :)


P.S. И большое спасибо за статью. Хорошая статья про реверс инженеринг — это большая редкость.

Спасибо :)
О, как интересно… Не планируете работать в эроге-компаниях программистом, к случаю? :)
Хотя я думаю, они там и без вас свои движки вовсю клепать будут.
Не планирую, а зачем? В год выпускается, наверное, несколько тысяч VNок. На ту же vndb, по ощущениям, попадает процентов 60-70. «Эроге-компании», как вы их назвали — зачастую полтора человека (сценарист и иногда художник). Большая часть выпускающегося — демки и всякие пробы пера новичков — далеко они не уходят. Денег там платят весьма скромно, особенно если ты не сценарист, а программист на день-два фриланса.

Эдак с половина индустрии лицензирует так или иначе kirikiri, вторая половина пишет свое. Как правило, по одной из двух причин: либо требование издателя, либо некий юношеский максимализм и гордость не позволяют освоить готовое, а хочется обязательно написать свое. У мейджоров, опять же, как правило свои уже готовые и десятилетиями отлаженные решения.

Написание движков — по большому счету, относительно простой и нудный процесс. Каких-то программистских сверхзадач там нет, все весьма рутинно: загрузи картинки-музыку-звук-шрифты-текст да показывай/запускай их в нужном порядке. Встроенные минигеймы, если они и есть, опять же, обычно в лучшем случае добавляют к вышеописанному чуть более сложную математику и все.

В общем, не, не нужны там программисты. Там творческие люди нужны, а из меня писатель никакой, тем более на японском.
Спасибо автору за отличное введение в KS, а тов. GreyCat за саму утилиту. Надо будет взять на вооружение, а то я так и продолжаю хранить все форматы в виде С-стуруктур и комментариев к ним, а парсеры писать вручную (вкривь и вкось).
Пожалуйста :) Будем надеяться, раскачаюсь на еще туториалы по реверсингу.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории