воскресенье, 31 июля 2011 г.

Конкурс от журнала ПРОграммист. Начало отладчика для VM

Функцию проверки пароля я успешно изучил, и привел к такому виду:


В "Show Final Titles" я не вполне уверен, но коль скоро туда ведет проверка пароля на финальные титры, то буду думать что это так. Теперь буду смотреть, что бывает, когда функция проверки пароля возвращает 1. Кто ее вызывает, проверяет, и что после этого делает. Здесь уже не обойтись без отладчика. Так как, идущий в комплекте с эмулятором - довольно убогий, а трассировка с помощью OllyDbg довольно медленная, то придется писать свой. Конечно, полноценный отладчик для моих целей совсем не нужен, будут реализована только трассировка, и точки останова.
Ядро отладчика будет DLL, которую я внедрю в процесс эмулятора с помощью microsoft detours. Управляться отладчик будет из консоли. Обмен консоли с ядром будет осуществляться через named pipe.
Нужно реализовать следующие функции :
1) Создать точку останова по адресу (bpx hexaddr)
2) Проверить точки останова (bl)
3) Удалить точку останова (bc index)
4) Уведомление о достижении программой точки останова (инициируется ядром)
5) Трассировка от одной точки останова до другой (t b1 b2)
5) Пошаговое выполнение

Конкурс от журнала ПРОграммист. Проверка пароля

Беру следующий кусок кода:

loc_8E90:
move.w #$10,d1 ; d1 = 16
bsr.w sub_93AA ;вызов подпрограммы
sub_93AA:
clr.w d0 ;d0 = 0
subq.w #1,d1 ;d1 = d1 - 1
loc_93AE:
asl $7742(a4) ;подпрограмма сдвигает 2 старших байта пароля
roxl $7740(a4) ;приведенного по таблице к 5-битовым значениям
roxl $773E(a4) ;и 2 младших байта пароля записывает в регистр d0
roxl $773C(a4) ;
roxl.w #1,d0 ;
dbf d1,loc_93AE ; повторить 16 раз
move.w d0,$3A56(a4) ;записать по адресу в память 2 младших знака пароля
bsr.w sub_9372 ;вызов подпрограммы
move.w $773C(a4),d0 ;подпрограмма складывает между собой по модулю 2
move.w $773E(a4),d1 ;символы пароля, приведенные по
eor.w d1,d0 ; таблице к 5-битовым значениям
eor.w d1,d0 ;
move.w $7742(a4),d1 ;мне кажется, что здесь должен быть 0
eor.w d1,d0 ;и прибавляет к ним число 0x6573
eori.w #$6573,d0 ;
rts
cmp.w $3A56(a4),d0 ;результат подпрограммы сравнивается с младшими байтами пароля
bne.s loc_8EB0

Таким образом, у нас есть 4 слова пароля.
W1 W2 W3 W4

Должно выполняться условие
W1 = W2 W3 W4 0x6573

Чтобы проверить - напишу тестовую программу, которая будет генерировать правильный пароль. W2, W2, W3 будут выбираться случайным образом. W1 будет получаться из формулы выше.

Конкурс от журнала ПРОграммист. Подбор кода. Начало

А дальше идут ветвления. Здесь бы в нормальном отладчике просмотреть трассировку, но нормального отладчика не дали. Значит буду трассировать через OllyDbg. Сначала, с помощью hardware breakpoint нахожу место, где изменяется память (0x4791F5) командного регистра виртуальной машины(ВМ). Ставлю Conditional log breakpoint. В поле Condition пишу (EAX > 00008DF6) && (EAX< 00008EAE), это означает что точка останова будет срабатывать только когда значение командного регистра ВМ будет в пределах нашей функции. В поле Explanation(описание) пишу PC (название командного регистра). В поле Expression пишу [5E4994] чтобы показывало значение регистра. Pause programm Never. Log Value of expression - allways.
Работает! К сожалению, ужасно медленно, но что имеем - то имеем.

по фрагменту лога (ассемблерный код я подставил чтобы понятно было)
004791F5 LOG: PC = 8E38 (36408.) ;cmpi.w #$DBB7,$773C(a4)
004791F5 LOG: PC = 8E90 (36496.) ;bne.s loc_8E90
004791F5 LOG: PC = 8E94 (36500.) ;move.w #$10,d1
видно, что выполнился условный переход. Что-то мне говорит, что это как раз и есть контроль правильности пароля. Сейчас проверим. Нужно подбрать пароль, чтобы соответствовал условию.

Выбираю из таблицы все символы, которые соответствуют буквам пароля.
B C D F G H J K L M N P Q R S T V W X Y Z
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14
0 1 2 3 4 5 6 7 8 9 !
15 16 17 18 19 1A 1B 1C 1D 1E 1F

Как видно из таблицы, для кодирования пароля в памяти, используются все варианты перестановки 5-ти бит. В результате работы цикла loc_8E14, по адресу 773C оказывается 64-битовая последовательность двенадцати 5-битовых символов. Младшие 4 бита этой последовательности всегда равны 0.
Затем она маскируется, с помощью сложения по модулю 2, этим числом
B2 9A 56 B2 B2 EB B2 00.

Чтобы переход не выполнился, нам нужно найти такие числа, чтобы получить из B29A DBB7 сложением по модулю 2. Дальше идут переходы, смысл которых такой-же, поэтому запишу все вместе.

B 1011 D 1101 0110 6
2 0010 B 1011 1001 9
9 1001 B 1011 0010 2
A 1010 7 0111 1101 B

5 0101 6 0110 0011 3
6 0110 B 1011 1101 D
B 1011 A 1010 0001 1
2 0010 7 0111 0101 5

B 1011 C 1100 0111 7
2 0010 4 0100 0110 6
E 1110 5 0101 1011 B
B 1011 3 0011 1000 8

B 1011 4 0100 1111 F
2 0010 D 1101 1111 F
0 0000 F 1111 1111 F
0 0000 0 0000 0000 0

разобью на наборы по 5 бит и посмотрю что получится
R G 1 Y 5 H P 1 2 F ! !
01101 00100 10110 10011 11010 00101 01011 10110 10111 00011 11111 11111

Теперь, когда я по можно упростить граф дизассемблера, сгруппировав его узлы. Если я не ошибся ни с чем, то теперь он будет выглядеть так:

Конкурс от журнала ПРОграммист. Изучаем кодЪ.

Теперь пытаюсь разобраться что в коде, попутно изучая ассемблер М68К

movem.l d2-d3/a2,-(sp) ; сохраняет значение регистров в памяти
clr.w $773C(a4) ;очищает память по адресу FFf73C (773C + a4)
clr.w $773E(a4) ;очищает память по адресу FFf73E
clr.w $7740(a4) ;очищает память по адресу FFf740
clr.w $7742(a4) ;очищает память по адресу FFf742
lea $7754(a4),a2 ;a2 = указатель на пароль
move.w #$B,d2 ;d2 = длина пароля
move.w a0,d3 ;d3 = номер текущего символа пароля

loc_8E14:
clr.w d0 ;D0 = 0
move.b (a2,d3.w),d0 ;из некоторой таблицы
lea unk_93E0,a0 ;выбирается число
move.b (a0,d0.w),d0 ;индексом выступает символ пароля
moveq #5,d1 ;d1 = 5
bsr.w sub_938E ;вызов подпрограммы

subq.w #1,d1 ;d1 = d1 - 1

loc_9390: ;

lsr.w #1,d0 ;символ сдвигается вправо, младший бит попадает в регистр переноса

roxr $773C(a4) ; и движется по памяти

roxr $773E(a4) ; в 8-ми байтах

roxr $7740(a4) ;

roxr $7742(a4) ;

dbf d1,loc_9390 ;повторить 4 раза

clr.w d1 ;очистить d1

rts ;вернуться из подпрограммы

subq.b #1,d3 ;d3 = d3 - 1
dbf d2,loc_8E14 ;повторить 12 раз

bsr.w sub_90DE

eori.l #$B2 9A 56 B2,$773C(a4) ; сложение по модулю 2 с какими-то

eori.l #$B2 EB B2 00,$7740(a4) ; маской

cmpi.w #$DBB7,$773C(a4)
bne.s loc_8E90

Конкурс от журнала ПРОграммист. Мотивация и подготовка

Решение принимать участие в конкурсе пришло по нескольким причинам. Во-первых занятная тема. Идея подбирать пароль к игрушке для консоли устаревшей лет эдак 10 назад, могла посетить только довольно извращенное сознание. Это внушает уважение. Во-вторых, одной из моих первых побед над машиной - было осознание принципа генерации паролей в игре metal gear для NES. Это была радость, и торжество интеллекта над машиной. Хорошо было бы повторить. И, наконец, сега была моей недостижимой мечтой в детстве. Вскрытие игры под нее должно стать прекрасной местью этой злой платформе. Тратить я буду около часа в сутки, прогресс записывать в этот документ.