четверг, 24 февраля 2011 г.
среда, 23 февраля 2011 г.
MicroXP - легковесная верcия Windows XP
Задача - есть необходимость подключаться к некоторой удаленной сети по RDP. Для осуществления такого соединения на машине должен быть установлен специальный софт, создающий закрытый канал между клиентской машиной и удаленной сетью. После установки этого софта можно уже запускать не только клиент RDP (mstsc.exe), но и другие разрешенные программы (telnet/ssh, ftp, radmin и т.д.), которым нужен доступ в удаленную сеть.
И все бы ничего, когда клиентская машина работает на Windows. В этом случае ты просто заходишь на страничку, логин/пароль, прямо со страницы стартует activex, который ставит и запускает все что нужно, и можно работать.
Сложности начинаются, когда клиентская машина - это не Windows. Конечно, клиенты RDP уже есть не только под Windows, но это не особо помогает, так как без того особого софта для туннеля ничего работать все равно не будет. И как назло, нет возможности докупить серверную часть этого VPN'а для поддержки клиентов не только под Windows.
Что люди, пользователи Linux и Mac, вынуждены делать? Ставить Windows на виртуальной машине и запускать RDP в ней. Хорошее решение, но с одним недостатком. Виртуальная машина с обычными виндами расползается на пару гигов как минимум, и загружается не очень быстро.
Я немного погуглил, и наткнулся на MicroXP. Это сильно урезанная версия XP SP3. Дистрибутив занимает ~100 мегов, а в установленном виде ~250. Под VirtualBox'ом на Mac Air установка занимает минут пять, а уже установленная система статует секунд за 10. Виртуальная машина минимальна - динамический диск на 300 мегов (реально будет ~250) и 64 мегов памяти.
После установки MicroXP надо доустановить Virtual Box Guest Additions (~200кб) для возможности расшаривать каталоги и не мучаться с мышкой, и добавить клиента RDP (его, как раз, можно перенести через расшареный каталог).
В общем, если вам нужны минималистические ультрабыстрые XP, то MicroXP отличный кандидат.
P.S. Вопросы легальности этой сборки XP оставим на бортом. Поэтому ссылки ведут на торренты. Кстати, не ходите на сайт microxp.org. Это fake, для сбора почтовых адресов, видимо.
среда, 16 февраля 2011 г.
Анализ результатов взлома программы однокомандного процессора NORCPU
Анализ результатов взлома программы однокомандного процессора NORCPU
Итак, публикуя анонс задачи по взлому программы, работающей на однокомандном процессоре NORCPU, я наивно думал, что хотя бы до конца марта я получу какое-нибудь решение.
Замечание: все цитируемые ниже фрагменты приводятся как есть, с минимальными правками в плане форматирования на странице.
В реальности все было иначе. Буквально через пару часов после поста на Хабре пришло письмо от Vasiliy Artemev:
Secret code: 139471
Это было правильным ответом. Василий официально стал первым и получил законные 100$ призовых. Он любезно рассказал, что взломал методом грубой силы, перебором. Действительно, простейшая переборная атака заставляла программу выдать секретное слово уже на 3-х символьном пароле. Увы, моя реализация хеш-функции для пароля стала уязвимым звеном.
Но полного анализа не было, и к тому же Василий предложил спонсировать дальнейший анализ задачи в виде 50$ из начального призового фонда. В общем, анализ продолжался.
Тем временем я сделал новую версию задачи, где уже не было хеширования, а был просто зашифрованный пароль. Я думал, это усложнит взлом. Но вечером того же дня приходит письмо от Anton Bukov:
Ответ на NORCPU hackme challenge, Version 2: "R0und2 D0ne!" ?
Это правильный ответ. Я недоумевал. Как можно было так быстро разобраться?
Антон также любезно рассказал, как ему удалось получить ответ:
Мой взлом основан на строке:
mem = mem_0.slice(0);
Тут массив копируется, если дважды вызвать функцию calc на одном массиве, то для любого пароля будет отображен ответ. У меня в коде это выглядело так:
wcout << calc(L"0") << endl << calc(L"0") << endl;
Я так догадываюсь для предотвращения этого и осуществлялось копирование. Боюсь это не тот способ, которого вы ждали. Я и сам удивился, когда заметил ответ в output-е. Ну а после уже разобрался из-за чего он вылез.
Антон случайно нашел баг, из-за которого программа опять сама выдавала секрет. Надо было запустить программу повторно без приведения памяти эмулятора в исходное состояния.
Корень проблемы:
1 2 3 4 5 6 7 | ...
IS_0(flag)
JZ okay
EXIT(1)
okay:
; print the secret
...
|
После первого неправильного прогона и отказа в выводе секрета программа останавливалась в строке 4, и регистр комманд "ip" уже указывал на метку "okay". Поэтому если интерпретатор просто запустить еще раз без инициализации памяти (а все регистры, включая "ip" находятся в памяти), он продолжит выполнение с метки "okay" и выведет секрет.
Досадно. Я быстро исправил проблему и выложил версию 2.1, в котором этого эффекта уже не было.
Прошло пару дней.
И вот приходит письмо от пользователя a5b с Хабра:
- Хотпатчинг переходов и ответ Pw:abcd resp:R0und2 D0ne!
- пароль естественно неверный
- инвертировал записываемые данные в ((i==27692)||(i==31712))
Формально, это решение, но нет пароля, а значит полного анализа тоже нет.
Но через час от a5b приходит довесок:
h1cKmE1fUsAn
input data:
chr conI LEET xor
CHR1 13417 13313 104 h
CHR2 39953 39968 49 1
CHR3 54302 54397 99 c
CHR4 32223 32148 75 K
CHR5 30900 30937 109 m
CHR6 27373 27304 69 E
CHR7 16420 16405 49 1
CHR8 49210 49244 102 f
CHR9 16740 16689 85 U
CHR10 50115 50096 115 s
CHR11 19308 19245 65 A
CHR12 57802 57764 110 n
static init:
CHRi 59609= 59651
CONi 59610= 59634
CNTR 59611=12
SUM 59608=0
LEET 59607 = 13313
1:
[59609]+1 -> [59609] // select next chr
[59610]+1 -> [59610] // select next con
SUM |= CHRi ^ LEET ^ CONi
LEET = LEET * 3 + 29
[59611]: if([59611] != 0) Loop 1
...
if(SUM != 0) exit
else print R0und2 D0ne // эту часть подробно не смотрел.
// Судя по модификациям кода (продвижение индекса), загружает 12 констант:
// 29528 22899 2971 9089 27542 17353 52278 25635 11626 34909 39131 51838,
// над каждой колдует и пишет в вывод
А вот это уже анализ. "a5b" стал первым, кто прислал алгоритм задачи номер 2.
В тот же день вечером приходит письмо от Max Filippov:
Algorithm that was used to check password correctness in the first round was the following:
bool check(const char *p)
{
int v = 0x1040;
for(; *p; ++p)
{
v ^= *p;
for (int i = 0; i < 8; ++i)
{
int f = v & 1;
v >>= 1;
if (f)
v ^= 0x1408;
}
}
return v == 0x1c89;
}
that is, sort of CRC.
To discover it I've collected NORCPU execution trace and "disassembled" it.
Modified NORCPU source and disassembler are attached, and also may be found there: http://jcmvbkbc.spb.ru/git/?p=dumb/norcpu.git;a=summary
И довесок:
The method used is pretty straightforward:
- collect execution trace;
- recognize instruction patterns and collapse sequences of primitive instructions to more complex ones;
- analyze disassembled trace.
So, first I needed trace: I copied javascript text into cpp source, fixed lingual differences and inserted the following printf:
while (1) {
int i = mem[ip];
printf("%04x:NOR(%04x, %04x => %04x) ", i, mem[i], mem[i + 1], mem[i + 2]);
int a = mem[i + 0];
so that I got a long line (about 8Mb) of primitive instruction execution trace.
Then I started constructing sed script that would make it readable.
First, it broke the trace linewise, one instruction per line (288323 lines, will read it in case of insomnia). I took a look at processed trace and recorded several obvious instruction patterns into sed. Then reran script, took next look, recorded more patterns, ...
This way I figured out all boolean logic commands and jumps. Then rotations left. Each time new command got recognized, new filtered processed trace was suggesting next step, e.g. 15 ROTL equals ROTR etc.
Then I looked into your article at http://easy-coding.blogspot.com/2010/03/blog-post_26.html. And found addition pattern in disassembly. And recorded it in sed script.
After that I was able to just read the trace (which shrinked to 1035 lines). Its inner loop fit into one page, I just made some notes on a scratchpad:
[f1ba]: current in-T index (i)
[f1b4]: LEN
[f1b5]: 8
0012-0035:[f1b9] ^= (T[i] & 0xff)
006d-007b:[f1b8] = [f1b9] & 1
008a-0158:[f1b9] >>= 1
0167-10c7:[f1aa] = [f1b8] + -1, [f1ab] = !carry
10ca-10e6:jmp on carry to 1145:110d
110d-111b:[f1b9] ^= 1408
1145-1f4f:--[f1b5]
20a5-3005:[f1aa] = [f1b5] + -1, f1ab = !carry
3008-3024:jmp on carry to 006d:304b
304b-304b:++i
3fab-3fab:--LEN
4f0b-5e8a:jmp on carry to 5eb1:6
then I browsed through the repetitions of this inner loop and found the end of the outer loop.
5eb1-6e74:check 1c89
Then just translated it into C. It all took me three evenings.
Затем от Max Filippov пришло решение и второй задачи.
Ответ на второй тур -- h1cKmE1fUsAn
Результат -- R0und2 D0ne!
Алгоритм проверки пароля такой:
bool check(const char *p)
{
static const int xor_array[] = {
0x3469,
0x9c11,
0xd41e,
0x7ddf,
0x78b4,
0x6aed,
0x4024,
0xc03a,
0x4164,
0xc3c3,
0x4b6c,
0xe1ca,
};
int v = 0;
int x = 0x3401;
for (int i = 0; i < 12; ++i)
{
int f = p[i] ^ x ^ xor_array[i];
// printf("x: %04x, f: %04x\n", x, f);
v |= f;
x = (x * 3 + 0x1d) & 0xffff;
}
return !v;
}
Закомментированный printf выводит ключевую фразу по ходу.
Методика анализа -- как и в первом туре -- дизассемблирование трассы выполнения.
Первый тур был откровенно интереснее.
И, наконец, последнее полученное решение от Salo Kril для первой задачи.
Особых пояснений нет -- просто исходники.
// Генерация паролей
// Brute_force(3);
WORD ks_f(char *buff, int len)
{
int i, j;
WORD ks = 0x1040;
for (i = 0; i < len; i++)
{
ks ^= buff[i];
for (j = 0; j < 8; j++)
{
if((ks & 1) == 0)
ks = ks >> 1;
else
ks = (ks >> 1) ^ 0x1408;
}
}
return ks;
}
void Brute_force(int n)
{
int i;
static char alphabet[] =
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F"
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F"
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F"
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E";
if(n == 0)
{
if(ks_f(buff_bf, BF_N) == 0x1c89)
printf("%s\n",buff_bf);
return;
}
n--;
for (i = 0; alphabet[i]; i++)
{
buff_bf[n] = alphabet[i];
Brute_force(n);
}
}
и реконструированный код:
#define DEST_COUNT 0xF1FE
extern WORD mem[];
/*
Secret code: 139471
*/
void reconstructed_fn(void)
{
int i, j;
WORD src, dest, key, count, hash, hash_OK, key_const;
src = mem[0xF1BA]; // input string
count = mem[0xF1ED]; // input string length
dest = mem[0xF1BB]; // 0xf1ff
hash_OK = mem[0xF1BC]; // 0x1c89
hash = mem[0xF1B9]; // 0x1040
for(i = 0; i < count; i++)
{
hash ^= mem[src + i] & 0xFF;
for (j = 0; j < 8; j++)
{
if ((hash & 1) == 0)
hash = hash >> 1;
else
hash = (hash >> 1) ^ 0x1408;
}
}
if (hash == hash_OK)
{
src = mem[0x6EB6]; // "Secret code: 139471"
count = mem[0xF1C7]; // 19
key = ((hash >> 8) ^ hash) + 1;
key_const = mem[0x6EA8]; // 11
}
else
{
src = mem[0x5EBE]; // "Wrong password!"
count = mem[0xF1DC]; // 15
key = mem[0xF1BD];
key_const = mem[0xF1BE]; // 17
}
mem[DEST_COUNT] = count;
for (i = 0; i < count; i++)
{
mem[dest + i] = mem[src + i] ^ key;
key = key * 3 + key_const;
}
}
/*
--------------------------------------------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--------------------------------------------------------------------------------------------
*/
WORD and(WORD w1, WORD w2)
{
return w1 & w2;
}
WORD or(WORD w1, WORD w2)
{
return w1 | w2;
}
WORD xor(WORD w1, WORD w2)
{
return w1 ^ w2;
}
WORD rol(WORD w1, WORD n)
{
return (w1 << n) | (w1 >> (16 - n));
}
WORD ror(WORD w1, WORD n)
{
return (w1 >> n) | (w1 << (16 - n));
}
WORD extend_16(WORD k1)
{
int i, k2;
for (i = 0, k2 = 0; i < 16; i++)
{
k2 = or(k2, k1);
k1 = rol(k1, 1);
}
return k2;
}
WORD add(WORD *kk1, WORD a, WORD b)
{
WORD mask_bit, aa0, aa1, tmp1, i, k1, k2;
k1 = 0;
k2 = 0;
mask_bit = 1;
for (i = 0; i < 16; i++)
{
k1 = and(k1, mask_bit);
aa0 = xor(a, b);
tmp1 = xor(aa0, k1);
tmp1 = and(tmp1, mask_bit);
k2 = or(tmp1, k2);
aa1 = and(a, b);
aa0 = and(k1, aa0);
k1 = or(aa1, aa0);
k1 = rol(k1, 1);
mask_bit = rol(mask_bit, 1);
}
k1 = and(k1, mask_bit);
*kk1 = extend_16(k1);
return k2;
}
void reconstr(void)
{
WORD k1, k2, src, dest, tmp1, key, count, tmp2, tmp3, i, ks, ks_OK, key_a;
WORD mask_bit, aa1, aa0;
k1 = mem[0xF1AF]; // 0x88
k2 = mem[0xF1A3]; // 0x47
src = mem[0xF1BA]; // input string
count = mem[0xF1ED]; // input string length
dest = mem[0xF1BB]; // 0xf1ff
ks_OK = mem[0xF1BC]; // 0x1c89 "w":ks=0x0ACC "WWW":0x0FCE "123456789":ks=0x05E3
ks = mem[0xF1B9]; // 0x1040
l_0006:
ks = xor(ks, and(mem[src], 0xFF));
i = 8;
l_006D:
tmp3 = and(ks, 1);
ks = ror(ks, 1);
ks = and(ks, 0x7FFF);
tmp2 = add(&k2, tmp3, -1);
if(k2 != 0)
{
ks = xor(ks, 0x1408);
}
l_1145:
i = add(&k1, i, -1);
tmp2 = add(&k2, i, -1);
if(k2 != 0)
goto l_006D;
l_304B:
src = add(&k1, src, 1);
count = add(&k1, count, -1);
tmp2 = add(&k2, count, -1);
if(k2 != 0)
goto l_0006;
src = mem[0x5EBE]; // Wrong password!
count = mem[0xF1DC]; // 15
key = mem[0xF1BD];
key_a = mem[0xF1BE];
ks_OK = xor(ks_OK, ks);
tmp2 = add(&k2, ks_OK, -1);
if(k2 != 0)
goto l_8552;
l_6E9B:
src = mem[0x6EB6]; // Secret code: 139471
count = mem[0xF1C7]; // 19
key = ks;
key_a = mem[0x6EA8];
key = ror(key, 8);
key = and(key, 0xFF);
key = xor(ks, key);
key = and(key, 0x7FFF);
key = add(&k1, key, 1);
l_8552:
mem[DEST_COUNT] = count;
l_8558:
mem[dest] = xor(mem[src], key);
tmp3 = add(&k1, key, key);
key = add(&k1, key, tmp3);
key = add(&k1, key, key_a);
src = add(&k1, src, 1);
dest = add(&k1, dest, 1);
count = add(&k1, count, -1);
tmp2 = add(&k2, count, -1);
if(k2 != 0)
goto l_8558;
l_F186:
mem[0xF1B2] = mem[0xF193]; // nc
}
Итак, как вы уже поняли, решения были полностью исчерпывающими.
Всем приславшим огромное спасибо за проявленное внимание.
Ниже привожу оригинальные исходники обоих задач. Надо просто запустить питоновский скрипт, он скомпилирует код, написанный через функции-макросы, сделает тестовый прогон и сгенерирует html-страничку (нужен файл-шаблон "template.html").
Весь архив вместе c решениями-взломами доступны в виде git репозитория.
Задача 1
Файл norcpu.py (template.html).
import sys, re, time, string, binascii
verbose = False
verbose_cpu = False
scramble = True
test_wrong_crc = 0
secret_code = "Secret code: 139471"
password = "h0cKmE1fUsAn"
guess = "123456789012"
guess = password
# Secret code message encryption mask.
secret_coef_add = 17
message_text = "Wrong password!"
# Wrong password message encryption mask.
message_mask = 0x6301
message_coef_add = 11
# Non-standard CRC initial value (should be 0xFFFF).
crc16_initial_value = 0x1040
# Non-standard CRC constant (should be 0x8401).
crc16_constant = 0x1408
code_segment = []
data_segment = []
label_count = 0
def dump(data, length = 8):
result = []
for i in xrange(0, len(data), length):
line = data[i:i + length]
hex_line = ' '.join(["%04X" % x for x in line])
result.append("%04X: %-*s\n" % (i, length*5, hex_line))
return ''.join(result)
def dump_js(data, length = 8):
result = []
for i in xrange(0, len(data), length):
line = data[i:i + length]
hex_line = ' '.join(["0x%04X," % x for x in line])
result.append("%-*s\n" % (length*5, hex_line))
return ''.join(result)
def calc_crc16(data):
global crc16_initial_value
global crc16_constant
crc16 = crc16_initial_value
for i in range(0, len(data)):
ch = ord(data[i]) & 0xff
crc16 = crc16 ^ ch
for j in range(0, 8):
if ((crc16 & 1) != 0):
crc16 = (crc16 >> 1) ^ crc16_constant
else:
crc16 = crc16 >> 1
return crc16
crc16 = calc_crc16(password)
def encode_string(data, name, mask, coef_add):
global mem, names
offset = names[name]
offset_sz = names[name + "_sz"]
for i in range(0, len(data)):
mem[offset + i] = ord(data[i]) ^ mask
mask = (mask * 3 + coef_add) & 0xffff
mem[offset_sz] = len(data)
def put_string(data, name):
global mem, names
offset = names[name]
offset_sz = names[name + "_sz"]
for i in range(0, len(data)):
mem[offset + i] = ord(data[i])
mem[offset_sz] = len(data)
def save_mem(name, size = -1):
f = open(name, "w")
if size == -1: size = len(mem)
for i in (mem[0:size]):
hex = "%04X" % i
bin = binascii.a2b_hex(hex)
f.write(bin)
f.close()
def next_label():
global label_count
label_count = label_count + 1
return "label_%04d" % label_count
def code_rem(comment):
code_segment.append('; ' + comment)
def data_rem(comment):
data_segment.append('; ' + comment)
def data_label(name):
data_segment.append(name + ":")
def code_label(name):
code_segment.append(name + ":")
def code(value):
printed = value
if type(value).__name__ == 'int':
printed = "%d" % value
code_segment.append(" dw %s" % printed)
scramble_counter = 0x27
def next_scramble_counter():
global scramble_counter
scramble_counter = scramble_counter * 3 + 7
return scramble_counter & 0xff
def word(value):
if value == -1:
if scramble:
value = next_scramble_counter()
else:
value = 0
printed = value
if type(value).__name__ == 'int':
printed = "%d" % value
data_segment.append(" dw %s" % printed)
def buffer(length, value = -1):
for i in range(0, length):
word(value)
def var(name, value = -1):
data_label(name);
word(value);
def NOR(a, b, r):
code_rem('NOR ' + str(a) + ' ' + str(b) + ' ' + str(r))
code(a)
code(b)
code(r)
def NOT(a, r):
NOR(a, a, r);
def OR(a, b, r):
NOR(a, b, "or_reg")
NOT("or_reg", r)
var("or_reg")
def AND(a, b, r):
NOT(a, "and_reg_a")
NOT(b, "and_reg_b")
OR("and_reg_a", "and_reg_b", "and_reg_a")
NOT("and_reg_a", r)
var("and_reg_a")
var("and_reg_b")
def ANDi(a, imm, r):
MOVi(imm, "and_i_reg")
AND(a, "and_i_reg", r)
var("and_i_reg")
def XOR(a, b, r):
NOT(a, "xor_reg_a")
NOT(b, "xor_reg_b")
AND(a, "xor_reg_b", "xor_reg_b")
AND(b, "xor_reg_a", "xor_reg_a")
OR("xor_reg_a", "xor_reg_b", r)
var("xor_reg_a")
var("xor_reg_b")
def XORi(a, imm, r):
MOVi(imm, "xor_i_reg")
XOR(a, "xor_i_reg", r)
var("xor_i_reg")
def MOV(a, b):
code_rem('MOV ' + str(a) + ' ' + str(b))
NOT(a, "move_reg")
NOT("move_reg", b)
code_rem('MOV END')
var("move_reg")
def JMP(a):
code_rem('JMP ' + str(a))
MOV(a, "ip")
def JMPi(a):
code_rem('JMPi ' + str(a))
label = next_label()
JMP(label)
code_label(label)
code(a)
def MOVi(imm, a):
code_rem('MOVi #' + str(imm) + ' ' + str(a))
label_data = next_label()
label_jump = next_label()
MOV(label_data, a)
JMPi(label_jump)
code_label(label_data)
code(imm)
code_label(label_jump)
# [a] -> b
def PEEK(a, b):
label1 = next_label()
label2 = next_label()
MOV(a, label1)
MOV(a, label2)
code_label(label1) # NOT(0, 0, move_reg)
code(0) # <- a
code_label(label2) #
code(0) # <- a
code("move_reg") #
NOT("move_reg", b)
# a -> [b]
def POKE(a, b):
code_rem('POKE ' + str(a) + ' [' + str(b) + ']')
label = next_label()
MOV(b, label)
NOT(a, "move_reg") # +3 (three operations)
code("move_reg") # +4
code("move_reg") # +5
code_label(label)
code(0) # <- b
# imm -> [a]
def POKEi(imm, a):
MOVi(imm, "poke_i_reg")
POKE("poke_i_reg", a)
var("poke_i_reg")
def EXIT(a):
MOV(a, "exit_reg")
def EXITi(a):
MOVi(a, "exit_reg")
def FADD(mask, carry, a, b, r):
AND(a, mask, "fadd_reg_a") # zero bits in 'a' except mask'ed
AND(b, mask, "fadd_reg_b") # zero bits in 'b' except mask'ed
AND(carry, mask, carry) # zero bits in 'carry' except mask'ed
# SUM = (a ^ b) ^ carry
XOR(a, b, "fadd_reg_t1")
XOR("fadd_reg_t1", carry, "fadd_reg_bit_r")
# Leave only 'mask'ed bit in bit_r.
AND("fadd_reg_bit_r", mask, "fadd_reg_bit_r")
# Add current added bit to the result.
OR("fadd_reg_bit_r", r, r)
# CARRY = (a & b) | (carry & (a ^ b))
AND(a, b, "fadd_reg_t2")
AND(carry, "fadd_reg_t1", "fadd_reg_t1")
# CARRY is calculated, and 'shift_reg' contains the same value
# but shifted the left by 1 bit.
OR("fadd_reg_t2", "fadd_reg_t1", carry)
# CARRY is shifted the left by 1 bit to be used on the next round.
MOV("shift_reg", carry)
# shift_reg = mask << 1
MOV(mask, mask)
# mask = shift (effectively "mask = mask << 1")
MOV("shift_reg", mask)
AND(carry, mask, carry)
var("fadd_reg_a")
var("fadd_reg_b")
var("fadd_reg_bit_r")
var("fadd_reg_t1")
var("fadd_reg_t2")
def ZERO(a):
XOR(a, a, a)
def FADC(a, b, r):
ZERO("fadc_reg_t")
MOV("const_1", "fadc_reg_mask")
for i in range(0, 16):
FADD("fadc_reg_mask", "carry_reg", a, b, "fadc_reg_t")
MOV("fadc_reg_t", r)
ZERO("fadc_reg_t")
for i in range(0, 16):
OR("fadc_reg_t", "carry_reg", "fadc_reg_t")
MOV("carry_reg", "carry_reg")
MOV("shift_reg", "carry_reg")
MOV("fadc_reg_t", "carry_reg")
var("fadc_reg_mask")
var("fadc_reg_t")
def ADD(a, b, r):
ZERO("carry_reg")
FADC(a, b, r)
def ADDi(a, imm, r):
MOVi(imm, "add_i_reg")
ADD(a, "add_i_reg", r)
var("add_i_reg")
def PUSH(a):
ADD("stack_reg", "const_minus_1", "stack_reg")
POKE(a, "stack_reg")
def PUSHi(imm):
MOVi(imm, "push_i_reg")
PUSH("push_i_reg")
var("push_i_reg")
def POP(a):
PEEK("stack_reg", a)
ADD("stack_reg", "const_1", "stack_reg")
def CALL(a):
label = next_label()
PUSHi(label)
JMP(a)
code_label(label)
def CALLi(a):
label = next_label()
PUSHi(label)
JMPi(a)
code_label(label)
def RET():
POP("ip")
# Jump 'a', if cond = FFFF, and 'b' if conf = 0000
def BRANCH(a, b, cond):
AND(a, cond, "branch_reg_a") # reg_a = a & cond
NOT(cond, "branch_reg_b") # reg_b = !cond
AND(b, "branch_reg_b", "branch_reg_b") # reg_b = b & reg_b = b & !cond
OR("branch_reg_a", "branch_reg_b", "ip") # ip = (a & cond) | (b & !cond)
var("branch_reg_a")
var("branch_reg_b")
# Jump 'a', if cond = FFFF, and 'b' if conf = 0000
def BRANCHi(a, b, cond):
MOVi(a, "branch_i_reg_a")
MOVi(b, "branch_i_reg_b")
BRANCH("branch_i_reg_a", "branch_i_reg_b", cond)
var("branch_i_reg_a")
var("branch_i_reg_b")
# if a != 0 -> carry = FFFF else carry = 0000
def IS_0(a):
ZERO("carry_reg")
FADC(a, "const_minus_1", "is_0_reg")
NOT("carry_reg", "zero_reg")
var("is_0_reg")
var("zero_reg")
# ip = (zero_reg == FFFF ? a : ip)
def JZi(a):
label = next_label()
BRANCHi(a, label, "zero_reg")
code_label(label)
# ip = (zero_reg == FFFF ? a : ip)
def JNZi(a):
label = next_label()
BRANCHi(label, a, "zero_reg")
code_label(label)
def ROL(a, b):
MOV(a, a) # shift_reg = a << 1
MOV("shift_reg", b)
def ROR(a, b):
MOV(a, "ror_reg")
for i in range(0, 15):
ROL("ror_reg", "ror_reg")
MOV("ror_reg", b)
var("ror_reg")
def SHL(a, b):
ROL(a, b)
ANDi(b, 0x0001, b)
def SHR(a, b):
ROR(a, b)
ANDi(b, 0x7FFF, b)
# NORCPU code
var("ip", "start")
var("shift_reg")
var("carry_reg")
var("const_1", 1)
var("const_minus_1", 0xFFFF)
var("exit_reg")
var("stack_reg", "stack")
code_label("start")
var("i")
var("j")
var("ch")
var("mask")
var("t")
var("crc16", crc16_initial_value)
var("ptr", "password")
MOV("password_sz", "i")
crc_loop = next_label()
code_label(crc_loop) # crc_loop
# ch = *ptr
PEEK("ptr", "ch")
# ch &= 0xFF
ANDi("ch", 0xFF, "ch")
# crc16 ^= ch
XOR("crc16", "ch", "crc16")
MOVi(8, "j")
crc_loop_j = next_label()
code_label(crc_loop_j) # crc_loop_j
# t = crc16 & 1
ANDi("crc16", 1, "t")
# crc16 >>= 1
SHR("crc16", "crc16")
IS_0("t")
crc_loop_1 = next_label()
JZi(crc_loop_1)
# crc16 ^= crc16_constant
XORi("crc16", crc16_constant, "crc16")
code_label(crc_loop_1) # crc_loop_1
ADD("j", "const_minus_1", "j")
IS_0("j")
JNZi(crc_loop_j)
# ptr += 1
ADD("ptr", "const_1", "ptr")
# i = i - 1
ADD("i", "const_minus_1", "i")
IS_0("i")
JNZi(crc_loop)
var("ptr2", "result")
correct_crc = crc16 + test_wrong_crc
var("correct_crc", correct_crc)
# By default we're going to decrypt 'Wrong...' message.
MOVi("message", "ptr")
MOV("message_sz", "i")
var("message_mask", message_mask)
MOV("message_mask", "mask")
var("coef_add", message_coef_add)
wrong_label = next_label()
XOR("correct_crc", "crc16", "correct_crc")
IS_0("correct_crc")
JNZi(wrong_label)
# Now we switch to descrypt the secret message.
MOVi(secret_coef_add, "coef_add")
MOVi("secret", "ptr")
MOV("secret_sz", "i")
# mask = ((crc16 & 0xff) | ((crc16 >> 8) & 0xff)) + 1
MOV("crc16", "mask")
for i in range(0, 8):
SHR("mask", "mask")
XOR("crc16", "mask", "mask")
ANDi("mask", 0xff, "mask")
ADD("mask", "const_1", "mask")
code_label(wrong_label)
MOV("i", "result_sz")
loop = next_label()
code_label(loop) # loop
# ch = *ptr
PEEK("ptr", "ch")
# ch ^= mask
XOR("ch", "mask", "ch")
POKE("ch", "ptr2")
# mask = mask * 3 + 11
ADD("mask", "mask", "t")
ADD("mask", "t", "mask")
ADD("mask", "coef_add", "mask")
# ptr += 1
ADD("ptr", "const_1", "ptr")
# ptr2 += 1
ADD("ptr2", "const_1", "ptr2")
# i = i - 1
ADD("i", "const_minus_1", "i")
IS_0("i")
JNZi(loop)
EXITi(0x00)
buffer(8)
data_label("stack")
var("secret_sz", len(secret_code))
data_label("secret")
buffer(len(secret_code) + 1)
var("message_sz")
data_label("message")
buffer(16)
var("password_sz")
data_label("password")
buffer(16)
# The buffer holding the result string.
var("result_sz")
data_label("result")
buffer(32)
# Compiler
text = code_segment
text.extend(data_segment)
if verbose:
print "\n".join(text)
# Phase 1. Calculate names.
addr = 0
names = {}
for line in text:
if line[0] == ';': continue
if line[0] != ' ':
name = line.partition(':')[0]
names[name] = addr
else:
addr = addr + 1
if verbose:
print names
raw_text = "\n".join(text)
# Resolve names.
for name in names:
if verbose:
print name, names[name], type(names[name])
name_re = re.compile(r'dw ' + name + '$', re.M)
value = "%d" % names[name]
raw_text = name_re.sub('dw ' + value, raw_text)
text = raw_text.split("\n")
if verbose:
print "\n".join(text)
# Phase 2. Compilation.
addr = 0
comment = ""
mem = []
for line in text:
if line[0] == ';' or line[0] != ' ':
comment = comment + line + ' '
else:
value = int(line.strip().partition(" ")[2])
if verbose:
print "%04X: %04X ; %s" % (addr, value, comment)
mem.append(value)
addr = addr + 1
comment = ""
# Interpretation
ip = names["ip"]
exit_reg = names["exit_reg"]
shift_reg = names["shift_reg"]
carry_reg = names["carry_reg"]
def nor(a, b):
r = a | b
r = r ^ 0xFFFF
return r & 0xFFFF
def norcpu():
while 1:
i = mem[ip];
a = mem[i + 0]
b = mem[i + 1]
r = mem[i + 2]
mem[ip] = i + 3
f = nor(mem[a], mem[b])
mem[r] = f
mem[shift_reg] = ((f >> 15) & 1) | ((f & 0x7FFF) << 1)
if verbose_cpu:
print "%04X: %04X [%04X] %04X [%04X] -> %04X [%04X]" % \
(i, a, mem[a], b, mem[b], r, mem[r])
if r == exit_reg:
break
print "Starting from [%04X]" % mem[ip]
# Encrypt the secret code.
secret_mask = ((crc16 & 0xff) ^ ((crc16 >> 8) & 0xff)) + 1
encode_string(secret_code, "secret", secret_mask, secret_coef_add);
# Encrypt 'Wrong...' message.
encode_string(message_text, "message", message_mask, message_coef_add);
mem_js = dump_js(mem)
save_mem("norcpu-1-before.bin")
mem_sz = len(mem)
if len(mem) >= 0x10000:
print "Too much code (%08X, %04X)" % (len(mem), len(mem) - 0x10000)
sys.exit()
# Inject plain password in the last moment (for testing).
put_string(guess, "password")
save_mem("norcpu-2-before-with-password.bin")
if verbose:
print "Original memory:"
print dump(mem)
start_time = time.time()
norcpu()
end_time = time.time()
save_mem("norcpu-3-after.bin", mem_sz)
if verbose:
print "Memory after:"
dump(mem)
print
print "Size: %X" % len(mem)
print "Time: %d" % (end_time - start_time)
print "Exit: %04X" % mem[exit_reg]
print("CRC : %04X (%04X)" % (crc16, correct_crc))
result = names["result"]
result_value = ""
for i in range(0, mem[names["result_sz"]]):
result_value = result_value + chr(mem[result + i] & 0xff)
if result_value != secret_code:
print "ERROR: [%s] != [%s]" % (secret_code, result_value)
js = string.Template(open('template.html', 'r').read())
js = js.substitute( \
ip = names["ip"],
exit_reg = names["exit_reg"],
shift_reg = names["shift_reg"],
password = names["password"],
password_sz = names["password_sz"],
result = names["result"],
result_sz = names["result_sz"],
mem_js = mem_js
)
f = open("norcpu.html", "w")
f.write(js)
f.close()
Задача 2
Файл norcpu.py (template.html).
import sys, re, time, string, binascii
verbose = False
verbose_cpu = False
scramble = True
secret_password = "h1cKmE1fUsAn"
secret_password_xor_mask = 0x3401
secret_password_add = 29
secret_code = "R0und2 D0ne!"
secret_code_xor_mask = 0x730A
secret_code_add = 37
guess = "123456789012"
guess = secret_password
code_segment = []
data_segment = []
label_count = 0
def dump(data, length = 8):
result = []
for i in xrange(0, len(data), length):
line = data[i:i + length]
hex_line = ' '.join(["%04X" % x for x in line])
result.append("%04X: %-*s\n" % (i, length*5, hex_line))
return ''.join(result)
def dump_js(data, length = 8):
result = []
for i in xrange(0, len(data), length):
line = data[i:i + length]
hex_line = ' '.join(["0x%04X," % x for x in line])
result.append("%-*s\n" % (length*5, hex_line))
return ''.join(result)
def encode_string(data, name, mask, coef_add):
global mem, names
offset = names[name]
offset_sz = names[name + "_sz"]
for i in range(0, len(data)):
mem[offset + i] = ord(data[i]) ^ mask
mask = (mask * 3 + coef_add) & 0xffff
mem[offset_sz] = len(data)
def put_string(data, name):
global mem, names
offset = names[name]
offset_sz = names[name + "_sz"]
for i in range(0, len(data)):
mem[offset + i] = ord(data[i])
mem[offset_sz] = len(data)
def save_mem(name, size = -1):
f = open(name, "w")
if size == -1: size = len(mem)
for i in (mem[0:size]):
hex = "%04X" % i
bin = binascii.a2b_hex(hex)
f.write(bin)
f.close()
def next_label():
global label_count
label_count = label_count + 1
return "label_%04d" % label_count
def code_rem(comment):
code_segment.append('; ' + comment)
def data_rem(comment):
data_segment.append('; ' + comment)
def data_label(name):
data_segment.append(name + ":")
def code_label(name):
code_segment.append(name + ":")
def code(value):
printed = value
if type(value).__name__ == 'int':
printed = "%d" % value
code_segment.append(" dw %s" % printed)
scramble_counter = 0x2743
def next_scramble_counter():
global scramble_counter
scramble_counter = scramble_counter * 3 + 7
return scramble_counter & 0xffff
def word(value):
if value == -1:
if scramble:
value = next_scramble_counter()
else:
value = 0
printed = value
if type(value).__name__ == 'int':
printed = "%d" % value
data_segment.append(" dw %s" % printed)
def buffer(length, value = -1):
for i in range(0, length):
word(value)
def var(name, value = -1):
data_label(name);
word(value);
# Macros
def NOR(a, b, r):
code_rem('NOR ' + str(a) + ' ' + str(b) + ' ' + str(r))
code(a)
code(b)
code(r)
def NOT(a, r):
NOR(a, a, r);
def OR(a, b, r):
NOR(a, b, "or_reg")
NOT("or_reg", r)
var("or_reg")
def AND(a, b, r):
NOT(a, "and_reg_a")
NOT(b, "and_reg_b")
OR("and_reg_a", "and_reg_b", "and_reg_a")
NOT("and_reg_a", r)
var("and_reg_a")
var("and_reg_b")
def ANDi(a, imm, r):
MOVi(imm, "and_i_reg")
AND(a, "and_i_reg", r)
var("and_i_reg")
def XOR(a, b, r):
NOT(a, "xor_reg_a")
NOT(b, "xor_reg_b")
AND(a, "xor_reg_b", "xor_reg_b")
AND(b, "xor_reg_a", "xor_reg_a")
OR("xor_reg_a", "xor_reg_b", r)
var("xor_reg_a")
var("xor_reg_b")
def XORi(a, imm, r):
MOVi(imm, "xor_i_reg")
XOR(a, "xor_i_reg", r)
var("xor_i_reg")
def MOV(a, b):
code_rem('MOV ' + str(a) + ' ' + str(b))
NOT(a, "move_reg")
NOT("move_reg", b)
code_rem('MOV END')
var("move_reg")
def JMP(a):
code_rem('JMP ' + str(a))
MOV(a, "ip")
def JMPi(a):
code_rem('JMPi ' + str(a))
label = next_label()
JMP(label)
code_label(label)
code(a)
def MOVi(imm, a):
code_rem('MOVi #' + str(imm) + ' ' + str(a))
label_data = next_label()
label_jump = next_label()
MOV(label_data, a)
JMPi(label_jump)
code_label(label_data)
code(imm)
code_label(label_jump)
# [a] -> b
def PEEK(a, b):
label1 = next_label()
label2 = next_label()
MOV(a, label1)
MOV(a, label2)
code_label(label1) # NOT(0, 0, move_reg)
code(0) # <- a
code_label(label2) #
code(0) # <- a
code("move_reg") #
NOT("move_reg", b)
# a -> [b]
def POKE(a, b):
code_rem('POKE ' + str(a) + ' [' + str(b) + ']')
label = next_label()
MOV(b, label)
NOT(a, "move_reg") # +3 (three operations)
code("move_reg") # +4
code("move_reg") # +5
code_label(label)
code(0) # <- b
# imm -> [a]
def POKEi(imm, a):
MOVi(imm, "poke_i_reg")
POKE("poke_i_reg", a)
var("poke_i_reg")
def EXIT(a):
MOV(a, "exit_reg")
def EXITi(a):
MOVi(a, "exit_reg")
def FADD(mask, carry, a, b, r):
AND(a, mask, "fadd_reg_a") # zero bits in 'a' except mask'ed
AND(b, mask, "fadd_reg_b") # zero bits in 'b' except mask'ed
AND(carry, mask, carry) # zero bits in 'carry' except mask'ed
# SUM = (a ^ b) ^ carry
XOR(a, b, "fadd_reg_t1")
XOR("fadd_reg_t1", carry, "fadd_reg_bit_r")
# Leave only 'mask'ed bit in bit_r.
AND("fadd_reg_bit_r", mask, "fadd_reg_bit_r")
# Add current added bit to the result.
OR("fadd_reg_bit_r", r, r)
# CARRY = (a & b) | (carry & (a ^ b))
AND(a, b, "fadd_reg_t2")
AND(carry, "fadd_reg_t1", "fadd_reg_t1")
# CARRY is calculated, and 'shift_reg' contains the same value
# but shifted the left by 1 bit.
OR("fadd_reg_t2", "fadd_reg_t1", carry)
# CARRY is shifted the left by 1 bit to be used on the next round.
MOV("shift_reg", carry)
# shift_reg = mask << 1
MOV(mask, mask)
# mask = shift (effectively "mask = mask << 1")
MOV("shift_reg", mask)
AND(carry, mask, carry)
var("fadd_reg_a")
var("fadd_reg_b")
var("fadd_reg_bit_r")
var("fadd_reg_t1")
var("fadd_reg_t2")
def ZERO(a):
XOR(a, a, a)
def FADC(a, b, r):
ZERO("fadc_reg_t")
MOV("const_1", "fadc_reg_mask")
for i in range(0, 16):
FADD("fadc_reg_mask", "carry_reg", a, b, "fadc_reg_t")
MOV("fadc_reg_t", r)
ZERO("fadc_reg_t")
for i in range(0, 16):
OR("fadc_reg_t", "carry_reg", "fadc_reg_t")
MOV("carry_reg", "carry_reg")
MOV("shift_reg", "carry_reg")
MOV("fadc_reg_t", "carry_reg")
var("fadc_reg_mask")
var("fadc_reg_t")
def ADD(a, b, r):
ZERO("carry_reg")
FADC(a, b, r)
def ADDi(a, imm, r):
MOVi(imm, "add_i_reg")
ADD(a, "add_i_reg", r)
var("add_i_reg")
def PUSH(a):
ADD("stack_reg", "const_minus_1", "stack_reg")
POKE(a, "stack_reg")
def PUSHi(imm):
MOVi(imm, "push_i_reg")
PUSH("push_i_reg")
var("push_i_reg")
def POP(a):
PEEK("stack_reg", a)
ADD("stack_reg", "const_1", "stack_reg")
def CALL(a):
label = next_label()
PUSHi(label)
JMP(a)
code_label(label)
def CALLi(a):
label = next_label()
PUSHi(label)
JMPi(a)
code_label(label)
def RET():
POP("ip")
# Jump 'a', if cond = FFFF, and 'b' if conf = 0000
def BRANCH(a, b, cond):
AND(a, cond, "branch_reg_a") # reg_a = a & cond
NOT(cond, "branch_reg_b") # reg_b = !cond
AND(b, "branch_reg_b", "branch_reg_b") # reg_b = b & reg_b = b & !cond
OR("branch_reg_a", "branch_reg_b", "ip") # ip = (a & cond) | (b & !cond)
var("branch_reg_a")
var("branch_reg_b")
# Jump 'a', if cond = FFFF, and 'b' if conf = 0000
def BRANCHi(a, b, cond):
MOVi(a, "branch_i_reg_a")
MOVi(b, "branch_i_reg_b")
BRANCH("branch_i_reg_a", "branch_i_reg_b", cond)
var("branch_i_reg_a")
var("branch_i_reg_b")
# if a != 0 -> carry = FFFF else carry = 0000
def IS_0(a):
ZERO("carry_reg")
FADC(a, "const_minus_1", "is_0_reg")
NOT("carry_reg", "zero_reg")
var("is_0_reg")
var("zero_reg")
# ip = (zero_reg == FFFF ? a : ip)
def JZi(a):
label = next_label()
BRANCHi(a, label, "zero_reg")
code_label(label)
# ip = (zero_reg == FFFF ? a : ip)
def JNZi(a):
label = next_label()
BRANCHi(label, a, "zero_reg")
code_label(label)
def ROL(a, b):
MOV(a, a) # shift_reg = a << 1
MOV("shift_reg", b)
def ROR(a, b):
MOV(a, "ror_reg")
for i in range(0, 15):
ROL("ror_reg", "ror_reg")
MOV("ror_reg", b)
var("ror_reg")
def SHL(a, b):
ROL(a, b)
ANDi(b, 0x0001, b)
def SHR(a, b):
ROR(a, b)
ANDi(b, 0x7FFF, b)
def MUL3(a, b):
ADD(a, a, "mul3_reg") # mul3_reg = a + a
ADD("mul3_reg", a, b) # b = mul3_reg + a
var("mul3_reg")
# NORCPU code
var("ip", "start")
var("shift_reg")
var("carry_reg")
var("const_1", 1)
var("const_minus_1", 0xFFFF)
var("exit_reg")
var("stack_reg", "stack")
code_label("start")
var("ch")
var("t")
var("xor_mask")
var("cmp_flag")
var("ptr")
var("ptr2")
var("i")
MOVi("exchange", "ptr")
MOVi("secret_password", "ptr2")
MOVi(secret_password_xor_mask, "xor_mask")
MOVi(0, "cmp_flag")
MOVi(len(secret_password), "i")
cmp_loop = next_label()
code_label(cmp_loop) # cmp_loop:
PEEK("ptr", "ch") # ch = *ptr
XOR("ch", "xor_mask", "ch") # ch ^= xor_mask
PEEK("ptr2", "t") # t = *ptr2
XOR("ch", "t", "ch") # ch = ch ^ t
OR("cmp_flag", "ch", "cmp_flag") # cmp_flag |= ch
ADD("ptr", "const_1", "ptr") # ptr += 1
ADD("ptr2", "const_1", "ptr2") # ptr2 += 1
MUL3("xor_mask", "xor_mask") # xor_mask *= 3
ADDi("xor_mask", secret_password_add, "xor_mask") # xor_mask += add_const
ADD("i", "const_minus_1", "i") # i -= 1
IS_0("i")
JNZi(cmp_loop)
MOVi(0, "exchange_sz")
ok_label = next_label()
IS_0("cmp_flag")
JZi(ok_label)
exit_label = next_label()
JMPi(exit_label)
code_label(ok_label)
MOVi("secret_code", "ptr")
MOV("secret_code_sz", "i")
MOVi(secret_code_xor_mask, "xor_mask")
MOVi("exchange", "ptr2")
MOV("i", "exchange_sz")
loop = next_label()
code_label(loop) # loop:
PEEK("ptr", "ch") # ch = *ptr
XOR("ch", "xor_mask", "ch") # ch ^= xor_mask
POKE("ch", "ptr2") # *ptr2 = ch
MUL3("xor_mask", "xor_mask") # xor_mask *= 3
ADDi("xor_mask", secret_code_add, "xor_mask") # xor_mask += add_const
ADD("ptr", "const_1", "ptr") # ptr += 1
ADD("ptr2", "const_1", "ptr2") # ptr2 += 1
ADD("i", "const_minus_1", "i") # i = i - 1
IS_0("i")
JNZi(loop)
code_label(exit_label) # exit_label:
EXITi(0x00)
buffer(8)
data_label("stack")
var("secret_code_sz", len(secret_code))
data_label("secret_code")
buffer(len(secret_code))
var("secret_password_sz")
data_label("secret_password")
buffer(16)
var("exchange_sz", 0)
data_label("exchange")
buffer(32)
# Compiler
text = code_segment
text.extend(data_segment)
if verbose:
print "\n".join(text)
# Phase 1. Calculate names.
addr = 0
names = {}
for line in text:
if line[0] == ';': continue
if line[0] != ' ':
name = line.partition(':')[0]
names[name] = addr
else:
addr = addr + 1
if verbose:
print names
raw_text = "\n".join(text)
# Resolve names.
for name in names:
if verbose:
print name, names[name], type(names[name])
name_re = re.compile(r'dw ' + name + '$', re.M)
value = "%d" % names[name]
raw_text = name_re.sub('dw ' + value, raw_text)
text = raw_text.split("\n")
if verbose:
print "\n".join(text)
# Phase 2. Compilation.
addr = 0
comment = ""
mem = []
for line in text:
if line[0] == ';' or line[0] != ' ':
comment = comment + line + ' '
else:
value = int(line.strip().partition(" ")[2])
if verbose:
print "%04X: %04X ; %s" % (addr, value, comment)
mem.append(value)
addr = addr + 1
comment = ""
# Interpretation
ip = names["ip"]
exit_reg = names["exit_reg"]
shift_reg = names["shift_reg"]
carry_reg = names["carry_reg"]
def nor(a, b):
r = a | b
r = r ^ 0xFFFF
return r & 0xFFFF
def norcpu():
while 1:
i = mem[ip];
a = mem[i + 0]
b = mem[i + 1]
r = mem[i + 2]
mem[ip] = i + 3
f = nor(mem[a], mem[b])
mem[r] = f
mem[shift_reg] = ((f >> 15) & 1) | ((f & 0x7FFF) << 1)
if verbose_cpu:
print "%04X: %04X [%04X] %04X [%04X] -> %04X [%04X]" % \
(i, a, mem[a], b, mem[b], r, mem[r])
if r == exit_reg:
break
print "Starting from [%04X]" % mem[ip]
encode_string(secret_code, "secret_code", secret_code_xor_mask, secret_code_add);
encode_string(secret_password, "secret_password", secret_password_xor_mask, secret_password_add);
mem_js = dump_js(mem)
save_mem("norcpu-1-before.bin")
mem_sz = len(mem)
if len(mem) >= 0x10000:
print "Too much code (%08X, %04X)" % (len(mem), len(mem) - 0x10000)
sys.exit()
# Inject plain password in the last moment (for testing).
put_string(guess, "exchange")
save_mem("norcpu-2-before-with-password.bin")
if verbose:
print "Original memory:"
print dump(mem)
start_time = time.time()
norcpu()
end_time = time.time()
save_mem("norcpu-3-after.bin", mem_sz)
if verbose:
print "Memory after:"
dump(mem)
print
print "Size: %X" % len(mem)
print "Time: %d" % (end_time - start_time)
print "Exit: %04X" % mem[exit_reg]
exchange = names["exchange"]
result_value = ""
for i in range(0, mem[names["exchange_sz"]]):
result_value = result_value + chr(mem[exchange + i] & 0xff)
print "Result: [%s]" % result_value
if len(result_value) == 0:
print "ERROR: Wrong password"
js = string.Template(open('template.html', 'r').read())
js = js.substitute( \
ip = names["ip"],
exit_reg = names["exit_reg"],
shift_reg = names["shift_reg"],
exchange = names["exchange"],
exchange_sz = names["exchange_sz"],
mem_js = mem_js
)
f = open("norcpu2.html", "w")
f.write(js)
f.close()