google-ctf-2018-title
I recently discovered the world of hacking CTF competitions. These competitions often contain a selection of individual challenges that exercise a whole range of skills related to hacking and computer security, but they all have one common goal… find the flag… which is usually just a recognisable, but hard to guess text string.

It all started after Youtube recommended a few videos from the LiveOverflow channel. That channel belongs to a German guy who’s created a large collection of short and easy to follow videos that give an excellent introduction to the subject. Then more recently I was watching another Youtube video where someone else was walking through the process of solving some of the challenges from the 2018 Google CTF competition. This video started by describing the Wired CSV challenge, where you’re given a photograph of a logic analyser connected to a chip and a CSV file containing the readings.

I immediately knew that i could solve this and had to stop the video before I got any spoilers. So I set aside a weekend and decided to see how much I could do in the same 48 hour period that I’d have had if I’d entered the real competition.

Feel It

Manufacturer Confidential

Before I started on Wired CSV, I had a look around at some of the other problems, and quickly found another one that I thought I could do. For the Feel It challenge you’re given a pcap file containing some captured USB traffic and told “I have a feeling there is a flag there somewhere”.

Opening the file in Wireshark, the first thing I looked for was the name of the USB device, hoping it’d give me an idea of how to decode the traffic, but it wasn’t going to be that easy. They’d censored a few fields… “Manufacturer is confidential”, “and so is product string”, “not to mention serial number”.

Scrolling through the messages that the host and device were exchanging I could see that it was a HID class device, so my first guess was that it was a keyboard, and maybe the flag was being typed on it. Working on the assumption that a keyboard would deliver key presses via interrupts I took a look at the data from the interrupt packets.

Interrupt Packets

Interrupt Packets

To do this I set a filter to only show inbound interrupts and added a column to the Wireshark packet list for the “usb.capdata” field, which allowed me to quickly scroll through and see what the data looked like. However, as can be seen in the image above, the data from the interrupts wasn’t really changing. The only part that was changing was the first byte, which was just incrementing.

With the interrupts ruled out I removed the filter and took a look to see what other types of packets there were. The most common packets were UDBHID SET_REPORT packets. The ones coming from the device to the host didn’t have any data, but the ones from the host to the device did. The payloads in these packets were alternating between a fixed payload which consisted of mostly 0x55 bytes (‘U’), and a second payload that was more random, and which did change slightly.

The Flag?

The Flag?

Comparing the data in these packets I could see that the length of the payload was increasing by one byte per packet, and that the new byte was the only change, the value of the earlier bytes all remained the same. This seemed like a good candidate for the flag. The data could be building up the flag one byte at a time, which would mean that the last packet likely contained the whole flag, but what was the encoding? It certainly wasn’t anything that I recognised.

Product ID

Product ID

While I was wondering how I was going to work out how to decode the data, I had another hunt around in the other packets, and eventually I found the break I needed. The censorship of the device descriptor data hadn’t removed everything, hiding in packet 2 were the vendor (0xc251) and product (0x1126) IDs. A quick google for this pair of numbers then revealed that the device was a Eurobraille Esys braille display.

That fit perfectly with the way the data was changing. Someone was probably typing the flag and it was appearing one character at a time on the display. The first hit when searching for the IDs was the code for brltty on github, so I dug around in there looking for info on the encoding, eventually stumbling across a set of header files that defined the dot patterns for various alphabets. Using the data in en-nabcc.ttb, I tried to match it to the USB payload. The hex values in that header didn’t match, but I tried matching the dot info to the bits in the USB data.

The data had two NULL bytes in it, so assuming that these might be spaces, I thought that the flag was likely to start straight after one of them. From the header file I could see that a capital C should have dots 1, 4 and 7, so I compared that to the bits for the 0x18 after the first NULL… no match, only two bits set. Then I tried comparing with the 0x49 after the second NULL… bits 1, 4 and 7 were set! Next byte… 2,3,4,5,7… T! Then 1,2,4,7… F! Found it!

Wired CSV

Wired CSV

Wired CSV

With one problem solved it was now time to move on to the Wired CSV challenge that had first attracted me. For this challenge you’re given two things, the image that you can see to the right, and a 230MB CSV file.

I’d watched enough of Gynvael’s video to have gotten one spoiler… the chip that the logic analyser is connected to is an Atari POKEY keyboard controller, which was apparently used in all the old 8-bit Atari machines. So it seemed like a pretty good bet that the flag was being typed on the keyboard and the challenge was to decode the logic analyser output to get the key presses.

A quick bit of googling revealed that the Atari’s used a standard keyboard scanning method. The keys are wired in an 8×8 grid and the key switches have one contact connected to a common wire for the row, and the other contact connected to a common wire for the column. Then when a key is pressed it shorts the row and column wires together.

Atari Keyboard Circuit

Atari Keyboard Circuit

To scan the keys the controller connects the first row to +5V via a pull-up resistor, then one by one, it connect each column to ground. If the key at the intersection of the row and column is pressed then it will short the row to ground counteracting the pull-up and presenting 0V to the controller. If the key is not pressed then the pull-up will present 5V to the controller. After scanning the first row the controller will repeat the procedure for each of the other rows, eventually testing all 64 keys.

To ensure that the keyboard is responsive, this scanning happens quite fast, testing a new key approximately every 63 microseconds (I think it’s actually using the 15.7KHz horizontal sync clock from the video display). The K0, K1 and K2 pins of the controller are used to select the rows (via a 4051 multiplexer), and the K3, K4 and K5 pins are used to select the columns. The resulting high or low voltage for all the main keys comes back to the controller on pin KR1, and the modifier keys (CNTL, SHFT, BRK) are fed back to pin KR2.

Because it’s testing each key in turn, the outputs on pins K0-K5 are simply acting as a 6-bit counter, counting from 0 to 63, and then wrapping around and starting again, over and over. This count directly translates to the scan-code for each key, which can be converted to ASCII using a lookup table. So, all I needed to do was take the value of this counter once every clock tick, then check if KR1 was high or low, which would tell me whether that key was pressed at that time. Then I could remember the state of each key and whenever I saw the state change from not-pressed to pressed I would have a key-down event and could print a character. The only minor complication is that I didn’t have a clock signal, but that was easily solved. I could just use K0 as the clock, then sample the value of the signals half a clock cycle later when they’d all be stable.

The data in the CSV file looked like this:

 Time [s],Wire6-Analog,Wire7-Analog, Time [s],Wire0-Digital, Time [s],Wire1-Digital, Time [s],Wire2-Digital, Time [s],Wire3-Digital, Time [s],Wire4-Digital, Time [s],Wire5-Digital, Time [s],Wire6-Digital, Time [s],Wire7-Digital
0.000000000000000, 4.768121242523193, 4.773899555206299, 0.000000000000000, 0, 0.000000000000000, 0, 0.000000000000000, 0, 0.000000000000000, 1, 0.000000000000000, 0, 0.000000000000000, 0, 0.000000000000000, 1, 0.000000000000000, 1
0.000008000000000, 4.768121242523193, 4.773899555206299, 0.000000990000000, 1, 0.000065560000000, 1, 0.000194380000000, 1, 0.000451750000000, 0, 0.000452070000000, 1, 0.001480790000000, 1, 1.468471380000000, 0, 2.503182740000000, 0
0.000016000000000, 4.773141384124756, 4.778934478759766, 0.000065230000000, 0, 0.000194070000000, 0, 0.000451450000000, 0, 0.000965990000000, 1, 0.001480440000000, 0, 0.003537600000000, 0, 1.468535670000000, 1, 2.503689840000000, 1

If we ignore the first three columns, then the remainder are in pairs. For each capture channel we have a timestamp of when the value changed in one column and then the new value in the other column. On each row, the data from one channel has no relation to the other channels, they are all from completely different times, so before starting on a script to decode the data, I first wrote one to reformat it into something more usable. I wanted the data in time order, so I used the following script to write out a new CSV file that had the columns: timestamp, channel_number, value

#!/usr/bin/python
import sys
# reformat to timestamp,channel,value
with open(sys.argv[1]) as fd:
        fd.readline()
        while True:
                line = fd.readline()
                if not line:
                        break
                cols = [x.strip() for x in line.split(',')][3:]
                for i in range(len(cols)/2):
                        if cols[i*2+0] != '':
                                print "%s,%s,%s" % (cols[i*2+0], i, cols[i*2+1])

Then once I had the right columns, I ran a quick sort command to get everything in the right order:

sort -k1n < events.csv > sorted-events.csv

Not only did that get things in a more useful format, but it also reduced the file size from 230MB to 14MB. Now it was a simple matter to write the decoder script:

#!/usr/bin/python
import sys
scancodes = ['L',     'J',    ';',     '[F1]',  '[F2]',   'K',     '+',     '*',
             'O',     '[XX]',  'P',    'U',     '\n',    'I',      '-',     '=',
             'V',     '[HLP]', 'C',    '[F3]',  '[F4]',  'B',      'X',     'Z',
             '4',     '[XX]',  '3',     '6',    '[ESC]', '5',     '2',      '1',
             ',',     ' ',     '.',     'N',    '[XX]',  'M',     '/',     '[INV]',
             'R',     '[XX]',  'E',     'Y',    '\t',    'T',    'W',      'Q',
             '9',     '[XX]',  '0',     '7',    '\b',    '8',     '<',      '>',
             'F',     'H',      'D',    '[XX]', '[CAP]', 'G',     'S',     'A']
HALF_TICK = (1.0/15700)/2
next_sample_time = 0.0
state = [0, 0, 0, 0, 0, 0, 0, 0]
key_state = [0 for x in range(64)]
with open(sys.argv[1]) as fd:
        while True:
                line = fd.readline()
                if not line:
                        break
                timestamp, channel, value = line.split(',')
                timestamp = float(timestamp)
                channel = int(channel)
                value = int(value)
                if timestamp > next_sample_time:
                        scancode = 0
                        for i in range(6):
                                scancode |= (1 ^ state[i]) << i
                        if key_state[scancode] != state[6]:
                                key_state[scancode] = state[6]
                                if not key_state[scancode]:
                                        sys.stdout.write(scancodes[scancode])
                                        sys.stdout.flush()
                state[channel] = value
                if channel == 0:
                        next_sample_time = timestamp + HALF_TICK

Then out popped the flag and another one was solved!

Back To The Basics

Back To The Basics

Back To Basics

Sticking with the retro 8-bit theme, the next problem I tried was Back To The Basics.

This time we get given the file crackme.prg and are told “You won’t find any assembly in this challenge, only C64 BASIC. Once you get the password, the flag is CTF{password}. P.S. The challenge has been tested on the VICE emulator.”

Not being even remotely familiar with C64 stuff, the first thing I tried was simply opening the file in vim. This revealed that it was a binary file, but with a lot of readable stuff spread all the way through it. Probably the basic keywords had been replaced with tokens to save space, so I then started googling for something that would decode it. I can’t remember exactly what I was searching for, but I must have been using the wrong keywords because this took a ridiculously long time, but eventually I found “petcat”, which comes with the VICE emulator.

Running petcat on the file produced a remarkably short listing:

;crackme.prg ==0801==
    1 rem ======================
    2 rem === back to basics ===
    3 rem ======================
   10 printchr$(155):printchr$(147)
   20 poke 53280, 6:poke 53281, 6:
   25 print"loading..."
   30 data 2,1,3,11,32,32,81,81,81,32,32,32,32,81,32,32,32,32,81,81,81,81,32,81,81,81,81,81,32,32,81,81,81,81,32,32,87,87,87,87
   31 data 32,32,32,32,32,32,81,32,32,81,32,32,81,32,81,32,32,81,32,32,32,32,32,32,32,81,32,32,32,81,32,32,32,32,32,87,32,32,32,32
   32 data 20,15,32,32,32,32,81,81,81,32,32,81,32,32,32,81,32,32,81,81,81,32,32,32,32,81,32,32,32,81,32,32,32,32,32,32,87,87,87,32
   33 data 32,32,32,32,32,32,81,32,32,81,32,81,81,81,81,81,32,32,32,32,32,81,32,32,32,81,32,32,32,81,32,32,32,32,32,32,32,32,32,87
   34 data 20,8,5,32,32,32,81,81,81,32,32,81,32,32,32,81,32,81,81,81,81,32,32,81,81,81,81,81,32,32,81,81,81,81,32,87,87,87,87,32
   40 for i = 0 to 39: poke 55296 + i, 1: next i
   41 for i = 40 to 79: poke 55296 + i, 15: next i
   42 for i = 80 to 119: poke 55296 + i, 12: next i
   43 for i = 120 to 159: poke 55296 + i, 11: next i
   44 for i = 160 to 199: poke 55296 + i, 0: next i
   50 for i = 0 to 199
   51 read c : poke 1024 + i, c
   52 next i
   60 print:print:print:print:print
   70 poke 19,1: print"password please?" chr$(5): input ""; p$: poke 19,0
   80 print:print:printchr$(155) "processing... (this might take a while)":print"[                    ]"
   90 chkoff = 11 * 40 + 1
  200 if len(p$) = 30 then goto 250
  210 poke 1024 + chkoff + 0, 86:poke 55296 + chkoff + 0, 10
  220 goto 31337
  250 poke 1024 + chkoff + 0, 83:poke 55296 + chkoff + 0, 5
 2000 rem never gonna give you up
 2001 rem
 2010 poke 03397, 00199 : poke 03398, 00013 : goto 2001
 31337 print:print"verdict: nope":goto 31345
 31345 goto 31345

Considering that the size of the file was around 64KB, this was tiny, and just looking at it in vim I could see that there was more code after this fragment. Then looking at a hexdump around the position that the listing stopped, I could see a run of NULL bytes, maybe they were making the listing stop?

Next I downloaded the source code for petcat, where I found the p_expand function, which contained this outer loop:

    while ((fread(line, 1, 2, source) == 2) && (line[1]) && fread(line + 2, 1, 2, source) == 2) {

The middle condition would stop on a NULL, so I commented it out:

    while ((fread(line, 1, 2, source) == 2) && /*(line[1]) &&*/ fread(line + 2, 1, 2, source) == 2) {

That did the trick. Now I got a much, much longer listing, but it still wasn’t right. This new listing continued slightly further than the first one, but then there was a big block of junk, then readable code, then junk, then readable code, and so on, to the end of the file. The new code before the first block of junk looked like this:

 2010 poke 03397, 00199 : poke 03398, 00013 : goto 2001
 31337 print:print"verdict: nope":goto 31345
 31345 goto 31345
    0 rem
 2001 poke 03397, 00069 : poke 03398, 00013
 2002 poke 1024 + chkoff + 1, 81:poke 55296 + chkoff + 1, 7
 2004 es = 03741 : ee = 04981 : ek = 148
 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 2009 poke 1024 + chkoff + 1, 87

The other blocks of code that were between the blocks of junk all looked very similar to each other. Here’s the first one:

 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 2905 poke 1024 + chkoff + 1, a:poke 55296 + chkoff + 1, b
 2910 poke 03397, 00029 : poke 03398, 00020 : goto 2001
    0 rem
 2001 poke 03397, 00069 : poke 03398, 00013
 2002 poke 1024 + chkoff + 2, 81:poke 55296 + chkoff + 2, 7
 2004 es = 05363 : ee = 06632 : ek = 152
 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 2009 poke 1024 + chkoff + 2, 87

Then at the end of the listing was this bit:

 31337 t = t0 + t1 + t2 + t3 + t4 + t5 + t6 + t7 + t8 + t9 + ta + tb + tc + td + te + tf + tg + th + tj
 31338 if t = -19 then goto 31340
 31339 print:print"verdict: nope":goto 31345
 31340 print:print"verdict: correct"
 31345 goto 31345

Now that I had these extra fragments of code, the next question was how did they get called? The beginning of the code was still the same as the initial short listing, and all the paths stopped in infinite loops. The only potential candidate for getting to this new code was via the POKEs, which are used to write to arbitrary memory locations. What were they doing?

There were a few different areas of memory being written to:

  • 53280, 53281: Used to set the background and border colour
  • 55296 – 55295: Colour RAM, used to set the colour of the characters on the screen
  • 1024 – 2023: Screen RAM, used to set the individual characters on the screen
  • 3397, 3398: Apparently undocumented, memory map indicates “default basic area”

Even though I had no idea what it was doing, the last one was obviously the prime candidate. It was showing up once in the first short listing, and also in each of the fragments between the junk in the longer listing. What values were being written? In the short listing it was writing 199 (0xc7) and 13 (0x0d) to two consecutive memory locations. What if these were interpreted as a single 16-bit number? That would be 3527 (0xdc7). Did that have any relation to the unreachable code fragments?

Next I modified petcat again, this time making it print out the memory address of each line. Now I had this listing:

 [0d45] 2000 rem never gonna give you up
 [0d63] 2001 rem
 [0d69] 2010 poke 03397, 00199 : poke 03398, 00013 : goto 2001
 [0d96] 31337 print:print"verdict: nope":goto 31345
 [0db5] 31345 goto 31345
 [0dc1]    0 rem
 [0dc7] 2001 poke 03397, 00069 : poke 03398, 00013
 [0deb] 2002 poke 1024 + chkoff + 1, 81:poke 55296 + chkoff + 1, 7
 [0e1f] 2004 es = 03741 : ee = 04981 : ek = 148
 [0e46] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [0e81] 2009 poke 1024 + chkoff + 1, 87

And yes, that first pair of pokes is writing the address of the first block of unreachable code. Checking the addresses of the other pokes and code blocks also got perfect matches. Each block of code linked to the next, so maybe these pokes were setting the basic interpreters instruction pointer and doing the equivalent of a goto?

This is where I wasted a lot of time. I initially read these blocks of code wrong. I read them as “peek(i + ek) and 255” instead of the “(peek(i) + ek) and 255” that’s actually there. Then after getting it wrong initially I kept automatically reading what I thought was there, not what was actually there, so I thought these blocks were moving memory from one location to another. Eventually after looking again to try and work out why the pointless “and 255” was there, I spotted my mistake, at which point it became obvious that it’s decrypting a block.

es is the start address of the encrypted block, ee is the end address, and ek is the key for the block. Where do these addresses point? At the blocks of junk in my listing!

So I collected these addresses and keys and then made one last modification to petcat to get it to decrypt these blocks. Click expand to see the diff of my modifications to petcat (build with: gcc -I. -I arch/sdl/ -o petcat petcat.c):

--- vice-3.3/src/petcat.c	2018-12-17 18:45:18.000000000 +0000
+++ vice-3.3-modified/src/petcat.c	2019-03-30 15:25:50.668430935 +0000
@@ -81,6 +81,7 @@
 #include "util.h"
 #include "vice-event.h"
+#define DEBUG
 #ifdef DEBUG
 #define DBG(x)  printf x
 #else
@@ -926,7 +927,7 @@
     int fil = 0, outf = 0, overwrt = 0, textmode = 0;
     int flg = 0;                            /* files on stdin */
-    archdep_init(&argc, argv);
+    //archdep_init(&argc, argv);
     /* Parse arguments */
     progname = argv[0];
@@ -1638,6 +1639,61 @@
  * load address included on program files. That way it can list from
  * RAM dump.
  */
+struct crypt_range
+{
+	unsigned int start;
+	unsigned int end;
+	unsigned int key;
+} crypt_ranges[] =
+{
+	{ 3741,  4981,  148 },
+	{ 5363,  6632,  152 },
+	{ 7014,  8219,  165 },
+	{ 8601,  9868,  184 },
+	{ 10250, 11483, 199 },
+	{ 11865, 13107, 240 },
+	{ 13489, 14760, 249 },
+	{ 15142, 16351, 132 },
+	{ 16733, 17975, 186 },
+	{ 18360, 19633, 214 },
+	{ 20020, 21265, 245 },
+	{ 21652, 22923, 203 },
+	{ 23310, 24584, 223 },
+	{ 24971, 26178, 237 },
+	{ 26565, 27837, 192 },
+	{ 28224, 29471, 157 },
+	{ 29858, 31101, 158 },
+	{ 31488, 32761, 235 },
+	{ 33148, 34367, 143 },
+	{ 0, 0, 0 }
+};
+
+static int mygetc(FILE* fp)
+{
+	unsigned int key = 0;
+	off_t pos = ftell(fp) + 0x801 - 2;
+	struct crypt_range* r = crypt_ranges;
+	while(r->start != 0)
+	{
+		if(pos >= r->start && pos <= r->end)
+		{
+			key = r->key;
+			break;
+		}
+		++r;
+	}
+
+	return (fgetc(fp) + key) & 0xff;
+}
+
+size_t myfread(void* ptr, size_t size, size_t nmemb, FILE* stream)
+{
+	char* p = ptr;
+	size_t i;
+	for(i = 0; i < size * nmemb; ++i)
+		*p++ = mygetc(stream);
+	return nmemb;
+}
 static int p_expand(int version, int addr, int ctrls)
 {
@@ -1654,9 +1710,10 @@
      * next file on stdin intact.
      */
-    while ((fread(line, 1, 2, source) == 2) && (line[1]) && fread(line + 2, 1, 2, source) == 2) {
+    while ((myfread(line, 1, 2, source) == 2) && /*(line[1]) &&*/ myfread(line + 2, 1, 2, source) == 2) {
+        off_t filepos = ftell(source) - 6;
         quote = 0;
-        fprintf(dest, " %4d ", (spnum = (line[2] & 0xff) + ((line[3] & 0xff) << 8)));
+        fprintf(dest, " [%04lx] %4d ", addr+filepos, (spnum = (line[2] & 0xff) + ((line[3] & 0xff) << 8)));
         if (directory) {
             if (spnum >= 100) {
@@ -1670,7 +1727,7 @@
         /* prevent list protection from terminating listing */
-        while ((c = getc(source)) != EOF && !c) {
+        while ((c = mygetc(source)) != EOF && !c) {
         }
         if (c == 0x12 && !line[2] && !line[3]) {  /* 00 00 12 22 */
@@ -1688,7 +1745,7 @@
              */
             if (!quote && (c == 0x64)) {
-                if (((c = getc(source)) < 0x80) && basic_list[version - 1].tokens) {
+                if (((c = mygetc(source)) < 0x80) && basic_list[version - 1].tokens) {
                     fprintf(dest, "%s", basic_list[version - 1].tokens);
                     continue;
                 } else {
@@ -1710,7 +1767,7 @@
                 }
                 if (version != B_35 && version != B_FC3) {
                     if (c == 0xce && basic_list[version - 1].prefixce) {            /* 'rlum' on V3.5*/
-                        if ((c = getc(source)) <= MAX_KWCE) {
+                        if ((c = mygetc(source)) <= MAX_KWCE) {
                             fprintf(dest, "%s", (version == B_10) ? kwce10 : kwce);
                         } else {
                             fprintf(dest, "($ce%02x)", c);
@@ -1718,13 +1775,13 @@
                         continue;
                     } else if (c == 0xfe && basic_list[version - 1].prefixfe) {
                         if (version == B_SXC) {
-                            if ((c = getc(source)) <= basic_list[B_SXC - 1].max_token) {
+                            if ((c = mygetc(source)) <= basic_list[B_SXC - 1].max_token) {
                                 fprintf(dest, "%s", basic_list[version - 1].tokens);
                             } else {
                                 fprintf(dest, "($fe%02x)", c);
                             }
                         } else {
-                            if ((c = getc(source)) <= basic_list[B_10 - 1].max_token) {
+                            if ((c = mygetc(source)) <= basic_list[B_10 - 1].max_token) {
                                 fprintf(dest, "%s", (version == B_71) ? kwfe71 : kwfe);
                             } else {
                                 fprintf(dest, "($fe%02x)", c);
@@ -1786,7 +1843,7 @@
             }
             _p_toascii((int)c, version, ctrls, quote);  /* convert character */
-        } while ((c = getc(source)) != EOF && c);
+        } while ((c = mygetc(source)) != EOF && c);
         fprintf(dest, "\n");
     }      /* line */
@@ -2296,7 +2353,7 @@
         if (codesnocase) {
             for (p = wordlist[token], q = (char *)line, j = 0;
-                 *p && *q && (util_tolower(*p) == util_tolower(*q));
+                 *p && *q && (tolower(*p) == tolower(*q));
                  p++, q++, j++) {}
         } else {
             for (p = wordlist[token], q = (char *)line, j = 0;

Running this new version successfully revealed all of the code:

;../../../crackme.prg ==0801==
 [0801]    1 rem ======================
 [081e]    2 rem === back to basics ===
 [083a]    3 rem ======================
 [0857]   10 printchr$(155):printchr$(147)
 [086b]   20 poke 53280, 6:poke 53281, 6:
 [0886]   25 print"loading..."
 [0898]   30 data 2,1,3,11,32,32,81,81,81,32,32,32,32,81,32,32,32,32,81,81,81,81,32,81,81,81,81,81,32,32,81,81,81,81,32,32,87,87,87,87
 [0913]   31 data 32,32,32,32,32,32,81,32,32,81,32,32,81,32,81,32,32,81,32,32,32,32,32,32,32,81,32,32,32,81,32,32,32,32,32,87,32,32,32,32
 [0991]   32 data 20,15,32,32,32,32,81,81,81,32,32,81,32,32,32,81,32,32,81,81,81,32,32,32,32,81,32,32,32,81,32,32,32,32,32,32,87,87,87,32
 [0a0f]   33 data 32,32,32,32,32,32,81,32,32,81,32,81,81,81,81,81,32,32,32,32,32,81,32,32,32,81,32,32,32,81,32,32,32,32,32,32,32,32,32,87
 [0a8d]   34 data 20,8,5,32,32,32,81,81,81,32,32,81,32,32,32,81,32,81,81,81,81,32,32,81,81,81,81,81,32,32,81,81,81,81,32,87,87,87,87,32
 [0b09]   40 for i = 0 to 39: poke 55296 + i, 1: next i
 [0b2f]   41 for i = 40 to 79: poke 55296 + i, 15: next i
 [0b57]   42 for i = 80 to 119: poke 55296 + i, 12: next i
 [0b80]   43 for i = 120 to 159: poke 55296 + i, 11: next i
 [0baa]   44 for i = 160 to 199: poke 55296 + i, 0: next i
 [0bd3]   50 for i = 0 to 199
 [0be5]   51 read c : poke 1024 + i, c
 [0bfd]   52 next i
 [0c05]   60 print:print:print:print:print
 [0c13]   70 poke 19,1: print"password please?" chr$(5): input ""; p$: poke 19,0
 [0c4a]   80 print:print:printchr$(155) "processing... (this might take a while)":print"[                    ]"
 [0c9e]   90 chkoff = 11 * 40 + 1
 [0cb7]  200 if len(p$) = 30 then goto 250
 [0cd0]  210 poke 1024 + chkoff + 0, 86:poke 55296 + chkoff + 0, 10
 [0d05]  220 goto 31337
 [0d11]  250 poke 1024 + chkoff + 0, 83:poke 55296 + chkoff + 0, 5
 [0d45] 2000 rem never gonna give you up
 [0d63] 2001 rem
 [0d69] 2010 poke 03397, 00199 : poke 03398, 00013 : goto 2001
 [0d96] 31337 print:print"verdict: nope":goto 31345
 [0db5] 31345 goto 31345
 [0dc1]    0 rem
 [0dc7] 2001 poke 03397, 00069 : poke 03398, 00013
 [0deb] 2002 poke 1024 + chkoff + 1, 81:poke 55296 + chkoff + 1, 7
 [0e1f] 2004 es = 03741 : ee = 04981 : ek = 148
 [0e46] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [0e81] 2009 poke 1024 + chkoff + 1, 87
 [0e9d] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [0eeb] 2020 ba = asc( mid$(p$, 1, 1) )
 [0f05] 2021 bb = asc( mid$(p$, 2, 1) )
 [0f1f] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [0f7e] 2030 if ba and 1 then p0 = 0.062500000001818989403545856475830078125
 [0fbc] 2031 if ba and 2 then p1 = 0.0156250000004547473508864641189575195312
 [0ffb] 2032 if ba and 4 then p2 = 0.0039062500001136868377216160297393798828
 [103a] 2033 if ba and 8 then p3 = 0.0009765625000284217094304040074348449707
 [1079] 2034 if ba and 16 then p4 = 0.0002441406250071054273576010018587112427
 [10b9] 2035 if ba and 32 then p5 = 0.0000610351562517763568394002504646778107
 [10f9] 2036 if ba and 64 then p6 = 0.0000152587890629440892098500626161694527
 [1139] 2037 if ba and 128 then p7 = 0.0000038146972657360223024625156540423632
 [117a] 2040 if bb and 1 then p8 = 0.0000009536743164340055756156289135105908
 [11b9] 2031 if bb and 2 then p9 = 0.0000002384185791085013939039072283776477
 [11f8] 2032 if bb and 4 then pa = 0.0000000596046447771253484759768070944119
 [1237] 2033 if bb and 8 then pb = 0.000000014901161194281337118994201773603
 [1275] 2034 if bb and 16 then pc = 0.0000000037252902985703342797485504434007
 [12b5] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [1300] 2060 g = 0.671565706376017
 [131a] 2100 t0 = k = g : a = 86 : b = 10
 [133b] 2200 if t0 = -1 then a = 83 : b = 5
 [135a] 2210 poke 1024 + chkoff + 1, 90
 [1376] 2500 rem
 [137c] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [13b7] 2905 poke 1024 + chkoff + 1, a:poke 55296 + chkoff + 1, b
 [13ea] 2910 poke 03397, 00029 : poke 03398, 00020 : goto 2001
 [1417]    0 rem
 [141d] 2001 poke 03397, 00069 : poke 03398, 00013
 [1441] 2002 poke 1024 + chkoff + 2, 81:poke 55296 + chkoff + 2, 7
 [1475] 2004 es = 05363 : ee = 06632 : ek = 152
 [149c] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [14d7] 2009 poke 1024 + chkoff + 2, 87
 [14f3] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [1541] 2020 ba = asc( mid$(p$, 2, 1) )
 [155b] 2021 bb = asc( mid$(p$, 3, 1) )
 [1575] 2022 bc = asc( mid$(p$, 4, 1) )
 [158f] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [15ee] 2030 if ba and 32 then p0 = 0.062500000001818989403545856475830078125
 [162d] 2031 if ba and 64 then p1 = 0.0156250000004547473508864641189575195312
 [166d] 2032 if ba and 128 then p2 = 0.0039062500001136868377216160297393798828
 [16ae] 2033 if bb and 1 then p3 = 0.0009765625000284217094304040074348449707
 [16ed] 2034 if bb and 2 then p4 = 0.0002441406250071054273576010018587112427
 [172c] 2035 if bb and 4 then p5 = 0.0000610351562517763568394002504646778107
 [176b] 2036 if bb and 8 then p6 = 0.0000152587890629440892098500626161694527
 [17aa] 2037 if bb and 16 then p7 = 0.0000038146972657360223024625156540423632
 [17ea] 2040 if bb and 32 then p8 = 0.0000009536743164340055756156289135105908
 [182a] 2031 if bb and 64 then p9 = 0.0000002384185791085013939039072283776477
 [186a] 2032 if bb and 128 then pa = 0.0000000596046447771253484759768070944119
 [18ab] 2033 if bc and 1 then pb = 0.000000014901161194281337118994201773603
 [18e9] 2034 if bc and 2 then pc = 0.0000000037252902985703342797485504434007
 [1928] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [1973] 2060 g = 0.682612358126820
 [198d] 2100 t1 = k = g : a = 86 : b = 10
 [19ae] 2200 if t1 = -1 then a = 83 : b = 5
 [19cd] 2210 poke 1024 + chkoff + 2, 90
 [19e9] 2500 rem
 [19ef] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [1a2a] 2905 poke 1024 + chkoff + 2, a:poke 55296 + chkoff + 2, b
 [1a5d] 2910 poke 03397, 00144 : poke 03398, 00026 : goto 2001
 [1a8a]    0 rem
 [1a90] 2001 poke 03397, 00069 : poke 03398, 00013
 [1ab4] 2002 poke 1024 + chkoff + 3, 81:poke 55296 + chkoff + 3, 7
 [1ae8] 2004 es = 07014 : ee = 08219 : ek = 165
 [1b0f] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [1b4a] 2009 poke 1024 + chkoff + 3, 87
 [1b66] 2010 v = 0.6666666666612316235641 : g = 0
 [1b8f] 2020 ba = asc( mid$(p$, 4, 1) )
 [1ba9] 2021 bb = asc( mid$(p$, 5, 1) )
 [1bc3] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [1c22] 2030 if ba and 4 then p0 = 0.062500000001818989403545856475830078125
 [1c60] 2031 if ba and 8 then p1 = 0.0156250000004547473508864641189575195312
 [1c9f] 2032 if ba and 16 then p2 = 0.0039062500001136868377216160297393798828
 [1cdf] 2033 if ba and 32 then p3 = 0.0009765625000284217094304040074348449707
 [1d1f] 2034 if ba and 64 then p4 = 0.0002441406250071054273576010018587112427
 [1d5f] 2035 if ba and 128 then p5 = 0.0000610351562517763568394002504646778107
 [1da0] 2036 if bb and 1 then p6 = 0.0000152587890629440892098500626161694527
 [1ddf] 2037 if bb and 2 then p7 = 0.0000038146972657360223024625156540423632
 [1e1e] 2040 if bb and 4 then p8 = 0.0000009536743164340055756156289135105908
 [1e5d] 2031 if bb and 8 then p9 = 0.0000002384185791085013939039072283776477
 [1e9c] 2032 if bb and 16 then pa = 0.0000000596046447771253484759768070944119
 [1edc] 2033 if bb and 32 then pb = 0.000000014901161194281337118994201773603
 [1f1b] 2034 if bb and 64 then pc = 0.0000000037252902985703342797485504434007
 [1f5b] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [1fa6] 2060 g = 0.682552023325146
 [1fc0] 2100 t2 = k = g : a = 86 : b = 10
 [1fe1] 2200 if t2 = -1 then a = 83 : b = 5
 [2000] 2210 poke 1024 + chkoff + 3, 90
 [201c] 2500 rem
 [2022] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [205d] 2905 poke 1024 + chkoff + 3, a:poke 55296 + chkoff + 3, b
 [2090] 2910 poke 03397, 00195 : poke 03398, 00032 : goto 2001
 [20bd]    0 rem
 [20c3] 2001 poke 03397, 00069 : poke 03398, 00013
 [20e7] 2002 poke 1024 + chkoff + 4, 81:poke 55296 + chkoff + 4, 7
 [211b] 2004 es = 08601 : ee = 09868 : ek = 184
 [2142] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [217d] 2009 poke 1024 + chkoff + 4, 87
 [2199] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [21e7] 2020 ba = asc( mid$(p$, 5, 1) )
 [2201] 2021 bb = asc( mid$(p$, 6, 1) )
 [221b] 2022 bc = asc( mid$(p$, 7, 1) )
 [2235] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [2294] 2030 if ba and 128 then p0 = 0.062500000001818989403545856475830078125
 [22d4] 2031 if bb and 1 then p1 = 0.0156250000004547473508864641189575195312
 [2313] 2032 if bb and 2 then p2 = 0.0039062500001136868377216160297393798828
 [2352] 2033 if bb and 4 then p3 = 0.0009765625000284217094304040074348449707
 [2391] 2034 if bb and 8 then p4 = 0.0002441406250071054273576010018587112427
 [23d0] 2035 if bb and 16 then p5 = 0.0000610351562517763568394002504646778107
 [2410] 2036 if bb and 32 then p6 = 0.0000152587890629440892098500626161694527
 [2450] 2037 if bb and 64 then p7 = 0.0000038146972657360223024625156540423632
 [2490] 2040 if bb and 128 then p8 = 0.0000009536743164340055756156289135105908
 [24d1] 2031 if bc and 1 then p9 = 0.0000002384185791085013939039072283776477
 [2510] 2032 if bc and 2 then pa = 0.0000000596046447771253484759768070944119
 [254f] 2033 if bc and 4 then pb = 0.000000014901161194281337118994201773603
 [258d] 2034 if bc and 8 then pc = 0.0000000037252902985703342797485504434007
 [25cc] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [2617] 2060 g = 0.667647300753773
 [2631] 2100 t3 = k = g : a = 86 : b = 10
 [2652] 2200 if t3 = -1 then a = 83 : b = 5
 [2671] 2210 poke 1024 + chkoff + 4, 90
 [268d] 2500 rem
 [2693] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [26ce] 2905 poke 1024 + chkoff + 4, a:poke 55296 + chkoff + 4, b
 [2701] 2910 poke 03397, 00052 : poke 03398, 00039 : goto 2001
 [272e]    0 rem
 [2734] 2001 poke 03397, 00069 : poke 03398, 00013
 [2758] 2002 poke 1024 + chkoff + 5, 81:poke 55296 + chkoff + 5, 7
 [278c] 2004 es = 10250 : ee = 11483 : ek = 199
 [27b3] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [27ee] 2009 poke 1024 + chkoff + 5, 87
 [280a] 2010 v = 0.6666666666612316235641 : g = 0
 [2833] 2020 bc = asc( mid$(p$, 9, 1) )
 [284d] 2021 ba = asc( mid$(p$, 7, 1) )
 [2867] 2022 bb = asc( mid$(p$, 8, 1) )
 [2881] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [28e0] 2030 if ba and 16 then p0 = 0.062500000001818989403545856475830078125
 [291f] 2031 if ba and 32 then p1 = 0.0156250000004547473508864641189575195312
 [295f] 2032 if ba and 64 then p2 = 0.0039062500001136868377216160297393798828
 [299f] 2033 if ba and 128 then p3 = 0.0009765625000284217094304040074348449707
 [29e0] 2034 if bb and 1 then p4 = 0.0002441406250071054273576010018587112427
 [2a1f] 2035 if bb and 2 then p5 = 0.0000610351562517763568394002504646778107
 [2a5e] 2036 if bb and 4 then p6 = 0.0000152587890629440892098500626161694527
 [2a9d] 2037 if bb and 8 then p7 = 0.0000038146972657360223024625156540423632
 [2adc] 2040 if bb and 16 then p8 = 0.0000009536743164340055756156289135105908
 [2b1c] 2031 if bb and 32 then p9 = 0.0000002384185791085013939039072283776477
 [2b5c] 2032 if bb and 64 then pa = 0.0000000596046447771253484759768070944119
 [2b9c] 2033 if bb and 128 then pb = 0.000000014901161194281337118994201773603
 [2bdc] 2034 if bc and 1 then pc = 0.0000000037252902985703342797485504434007
 [2c1b] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [2c66] 2060 g = 0.682310803327740
 [2c80] 2100 t4 = k = g : a = 86 : b = 10
 [2ca1] 2200 if t4 = -1 then a = 83 : b = 5
 [2cc0] 2210 poke 1024 + chkoff + 5, 90
 [2cdc] 2500 rem
 [2ce2] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [2d1d] 2905 poke 1024 + chkoff + 5, a:poke 55296 + chkoff + 5, b
 [2d50] 2910 poke 03397, 00131 : poke 03398, 00045 : goto 2001
 [2d7d]    0 rem
 [2d83] 2001 poke 03397, 00069 : poke 03398, 00013
 [2da7] 2002 poke 1024 + chkoff + 6, 81:poke 55296 + chkoff + 6, 7
 [2ddb] 2004 es = 11865 : ee = 13107 : ek = 240
 [2e02] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [2e3d] 2009 poke 1024 + chkoff + 6, 87
 [2e59] 2010 v = 0.6666666666612316235641 - 0.00000000046566128730773925781250 : g = 0
 [2ea7] 2020 ba = asc( mid$(p$, 9, 1) )
 [2ec1] 2021 bb = asc( mid$(p$, 10, 1) )
 [2edc] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [2f3b] 2030 if ba and 2 then p0 = 0.062500000001818989403545856475830078125
 [2f79] 2031 if ba and 4 then p1 = 0.0156250000004547473508864641189575195312
 [2fb8] 2032 if ba and 8 then p2 = 0.0039062500001136868377216160297393798828
 [2ff7] 2033 if ba and 16 then p3 = 0.0009765625000284217094304040074348449707
 [3037] 2034 if ba and 32 then p4 = 0.0002441406250071054273576010018587112427
 [3077] 2035 if ba and 64 then p5 = 0.0000610351562517763568394002504646778107
 [30b7] 2036 if ba and 128 then p6 = 0.0000152587890629440892098500626161694527
 [30f8] 2037 if bb and 1 then p7 = 0.0000038146972657360223024625156540423632
 [3137] 2040 if bb and 2 then p8 = 0.0000009536743164340055756156289135105908
 [3176] 2031 if bb and 4 then p9 = 0.0000002384185791085013939039072283776477
 [31b5] 2032 if bb and 8 then pa = 0.0000000596046447771253484759768070944119
 [31f4] 2033 if bb and 16 then pb = 0.000000014901161194281337118994201773603
 [3233] 2034 if bb and 32 then pc = 0.0000000037252902985703342797485504434007
 [3273] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [32be] 2060 g = 0.670638734940470
 [32d8] 2100 t5 = k = g : a = 86 : b = 10
 [32f9] 2200 if t5 = -1 then a = 83 : b = 5
 [3318] 2210 poke 1024 + chkoff + 6, 90
 [3334] 2500 rem
 [333a] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [3375] 2905 poke 1024 + chkoff + 6, a:poke 55296 + chkoff + 6, b
 [33a8] 2910 poke 03397, 00219 : poke 03398, 00051 : goto 2001
 [33d5]    0 rem
 [33db] 2001 poke 03397, 00069 : poke 03398, 00013
 [33ff] 2002 poke 1024 + chkoff + 7, 81:poke 55296 + chkoff + 7, 7
 [3433] 2004 es = 13489 : ee = 14760 : ek = 249
 [345a] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [3495] 2009 poke 1024 + chkoff + 7, 87
 [34b1] 2010 v = 0.6666666666612316235641 - 0.00000000046566128730773925781250 : g = 0
 [34ff] 2020 ba = asc( mid$(p$, 10, 1) )
 [351a] 2021 bb = asc( mid$(p$, 11, 1) )
 [3535] 2022 bc = asc( mid$(p$, 12, 1) )
 [3550] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [35af] 2030 if ba and 64 then p0 = 0.062500000001818989403545856475830078125
 [35ee] 2031 if ba and 128 then p1 = 0.0156250000004547473508864641189575195312
 [362f] 2032 if bb and 1 then p2 = 0.0039062500001136868377216160297393798828
 [366e] 2033 if bb and 2 then p3 = 0.0009765625000284217094304040074348449707
 [36ad] 2034 if bb and 4 then p4 = 0.0002441406250071054273576010018587112427
 [36ec] 2035 if bb and 8 then p5 = 0.0000610351562517763568394002504646778107
 [372b] 2036 if bb and 16 then p6 = 0.0000152587890629440892098500626161694527
 [376b] 2037 if bb and 32 then p7 = 0.0000038146972657360223024625156540423632
 [37ab] 2040 if bb and 64 then p8 = 0.0000009536743164340055756156289135105908
 [37eb] 2031 if bb and 128 then p9 = 0.0000002384185791085013939039072283776477
 [382c] 2032 if bc and 1 then pa = 0.0000000596046447771253484759768070944119
 [386b] 2033 if bc and 2 then pb = 0.000000014901161194281337118994201773603
 [38a9] 2034 if bc and 4 then pc = 0.0000000037252902985703342797485504434007
 [38e8] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [3933] 2060 g = 0.729427094105661
 [394d] 2100 t6 = k = g : a = 86 : b = 10
 [396e] 2200 if t6 = -1 then a = 83 : b = 5
 [398d] 2210 poke 1024 + chkoff + 7, 90
 [39a9] 2500 rem
 [39af] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [39ea] 2905 poke 1024 + chkoff + 7, a:poke 55296 + chkoff + 7, b
 [3a1d] 2910 poke 03397, 00080 : poke 03398, 00058 : goto 2001
 [3a4a]    0 rem
 [3a50] 2001 poke 03397, 00069 : poke 03398, 00013
 [3a74] 2002 poke 1024 + chkoff + 8, 81:poke 55296 + chkoff + 8, 7
 [3aa8] 2004 es = 15142 : ee = 16351 : ek = 132
 [3acf] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [3b0a] 2009 poke 1024 + chkoff + 8, 87
 [3b26] 2010 v = 0.6666666666612316235641 : g = 0
 [3b4f] 2020 ba = asc( mid$(p$, 12, 1) )
 [3b6a] 2021 bb = asc( mid$(p$, 13, 1) )
 [3b85] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [3be4] 2030 if ba and 8 then p0 = 0.062500000001818989403545856475830078125
 [3c22] 2031 if ba and 16 then p1 = 0.0156250000004547473508864641189575195312
 [3c62] 2032 if ba and 32 then p2 = 0.0039062500001136868377216160297393798828
 [3ca2] 2033 if ba and 64 then p3 = 0.0009765625000284217094304040074348449707
 [3ce2] 2034 if ba and 128 then p4 = 0.0002441406250071054273576010018587112427
 [3d23] 2035 if bb and 1 then p5 = 0.0000610351562517763568394002504646778107
 [3d62] 2036 if bb and 2 then p6 = 0.0000152587890629440892098500626161694527
 [3da1] 2037 if bb and 4 then p7 = 0.0000038146972657360223024625156540423632
 [3de0] 2040 if bb and 8 then p8 = 0.0000009536743164340055756156289135105908
 [3e1f] 2031 if bb and 16 then p9 = 0.0000002384185791085013939039072283776477
 [3e5f] 2032 if bb and 32 then pa = 0.0000000596046447771253484759768070944119
 [3e9f] 2033 if bb and 64 then pb = 0.000000014901161194281337118994201773603
 [3ede] 2034 if bb and 128 then pc = 0.0000000037252902985703342797485504434007
 [3f1f] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [3f6a] 2060 g = 0.683334092143953
 [3f84] 2100 t7 = k = g : a = 86 : b = 10
 [3fa5] 2200 if t7 = -1 then a = 83 : b = 5
 [3fc4] 2210 poke 1024 + chkoff + 8, 90
 [3fe0] 2500 rem
 [3fe6] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [4021] 2905 poke 1024 + chkoff + 8, a:poke 55296 + chkoff + 8, b
 [4054] 2910 poke 03397, 00135 : poke 03398, 00064 : goto 2001
 [4081]    0 rem
 [4087] 2001 poke 03397, 00069 : poke 03398, 00013
 [40ab] 2002 poke 1024 + chkoff + 9, 81:poke 55296 + chkoff + 9, 7
 [40df] 2004 es = 16733 : ee = 17975 : ek = 186
 [4106] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [4141] 2009 poke 1024 + chkoff + 9, 87
 [415d] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [41ab] 2020 ba = asc( mid$(p$, 14, 1) )
 [41c6] 2021 bb = asc( mid$(p$, 15, 1) )
 [41e1] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [4240] 2030 if ba and 1 then p0 = 0.062500000001818989403545856475830078125
 [427e] 2031 if ba and 2 then p1 = 0.0156250000004547473508864641189575195312
 [42bd] 2032 if ba and 4 then p2 = 0.0039062500001136868377216160297393798828
 [42fc] 2033 if ba and 8 then p3 = 0.0009765625000284217094304040074348449707
 [433b] 2034 if ba and 16 then p4 = 0.0002441406250071054273576010018587112427
 [437b] 2035 if ba and 32 then p5 = 0.0000610351562517763568394002504646778107
 [43bb] 2036 if ba and 64 then p6 = 0.0000152587890629440892098500626161694527
 [43fb] 2037 if ba and 128 then p7 = 0.0000038146972657360223024625156540423632
 [443c] 2040 if bb and 1 then p8 = 0.0000009536743164340055756156289135105908
 [447b] 2031 if bb and 2 then p9 = 0.0000002384185791085013939039072283776477
 [44ba] 2032 if bb and 4 then pa = 0.0000000596046447771253484759768070944119
 [44f9] 2033 if bb and 8 then pb = 0.000000014901161194281337118994201773603
 [4537] 2034 if bb and 16 then pc = 0.0000000037252902985703342797485504434007
 [4577] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [45c2] 2060 g = 0.729182238224924
 [45dc] 2100 t8 = k = g : a = 86 : b = 10
 [45fd] 2200 if t8 = -1 then a = 83 : b = 5
 [461c] 2210 poke 1024 + chkoff + 9, 90
 [4638] 2500 rem
 [463e] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [4679] 2905 poke 1024 + chkoff + 9, a:poke 55296 + chkoff + 9, b
 [46ac] 2910 poke 03397, 00223 : poke 03398, 00070 : goto 2001
 [46d9]    0 rem
 [46df] 2001 poke 03397, 00069 : poke 03398, 00013
 [4703] 2002 poke 1024 + chkoff + 10, 81:poke 55296 + chkoff + 10, 7
 [4739] 2004 es = 18360 : ee = 19633 : ek = 214
 [4760] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [479b] 2009 poke 1024 + chkoff + 10, 87
 [47b8] 2010 v = 0.6666666666612316235641 + 0.00000000046566128730773925781250 : g = 0
 [4806] 2020 bc = asc( mid$(p$, 17, 1) )
 [4821] 2021 ba = asc( mid$(p$, 15, 1) )
 [483c] 2022 bb = asc( mid$(p$, 16, 1) )
 [4857] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [48b6] 2030 if ba and 32 then p0 = 0.062500000001818989403545856475830078125
 [48f5] 2031 if ba and 64 then p1 = 0.0156250000004547473508864641189575195312
 [4935] 2032 if ba and 128 then p2 = 0.0039062500001136868377216160297393798828
 [4976] 2033 if bb and 1 then p3 = 0.0009765625000284217094304040074348449707
 [49b5] 2034 if bb and 2 then p4 = 0.0002441406250071054273576010018587112427
 [49f4] 2035 if bb and 4 then p5 = 0.0000610351562517763568394002504646778107
 [4a33] 2036 if bb and 8 then p6 = 0.0000152587890629440892098500626161694527
 [4a72] 2037 if bb and 16 then p7 = 0.0000038146972657360223024625156540423632
 [4ab2] 2040 if bb and 32 then p8 = 0.0000009536743164340055756156289135105908
 [4af2] 2031 if bb and 64 then p9 = 0.0000002384185791085013939039072283776477
 [4b32] 2032 if bb and 128 then pa = 0.0000000596046447771253484759768070944119
 [4b73] 2033 if bc and 1 then pb = 0.000000014901161194281337118994201773603
 [4bb1] 2034 if bc and 2 then pc = 0.0000000037252902985703342797485504434007
 [4bf0] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [4c3b] 2060 g = 0.682352954987467
 [4c55] 2100 t9 = k = g : a = 86 : b = 10
 [4c76] 2200 if t9 = -1 then a = 83 : b = 5
 [4c95] 2210 poke 1024 + chkoff + 10, 90
 [4cb2] 2500 rem
 [4cb8] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [4cf3] 2905 poke 1024 + chkoff + 10, a:poke 55296 + chkoff + 10, b
 [4d28] 2910 poke 03397, 00091 : poke 03398, 00077 : goto 2001
 [4d55]    0 rem
 [4d5b] 2001 poke 03397, 00069 : poke 03398, 00013
 [4d7f] 2002 poke 1024 + chkoff + 11, 81:poke 55296 + chkoff + 11, 7
 [4db5] 2004 es = 20020 : ee = 21265 : ek = 245
 [4ddc] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [4e17] 2009 poke 1024 + chkoff + 11, 87
 [4e34] 2010 v = 0.6666666666612316235641 - 0.00000000046566128730773925781250 : g = 0
 [4e82] 2020 ba = asc( mid$(p$, 17, 1) )
 [4e9d] 2021 bb = asc( mid$(p$, 18, 1) )
 [4eb8] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [4f17] 2030 if ba and 4 then p0 = 0.062500000001818989403545856475830078125
 [4f55] 2031 if ba and 8 then p1 = 0.0156250000004547473508864641189575195312
 [4f94] 2032 if ba and 16 then p2 = 0.0039062500001136868377216160297393798828
 [4fd4] 2033 if ba and 32 then p3 = 0.0009765625000284217094304040074348449707
 [5014] 2034 if ba and 64 then p4 = 0.0002441406250071054273576010018587112427
 [5054] 2035 if ba and 128 then p5 = 0.0000610351562517763568394002504646778107
 [5095] 2036 if bb and 1 then p6 = 0.0000152587890629440892098500626161694527
 [50d4] 2037 if bb and 2 then p7 = 0.0000038146972657360223024625156540423632
 [5113] 2040 if bb and 4 then p8 = 0.0000009536743164340055756156289135105908
 [5152] 2031 if bb and 8 then p9 = 0.0000002384185791085013939039072283776477
 [5191] 2032 if bb and 16 then pa = 0.0000000596046447771253484759768070944119
 [51d1] 2033 if bb and 32 then pb = 0.000000014901161194281337118994201773603
 [5210] 2034 if bb and 64 then pc = 0.0000000037252902985703342797485504434007
 [5250] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [529b] 2060 g = 0.745769257191599
 [52b5] 2100 ta = k = g : a = 86 : b = 10
 [52d6] 2200 if ta = -1 then a = 83 : b = 5
 [52f5] 2210 poke 1024 + chkoff + 11, 90
 [5312] 2500 rem
 [5318] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [5353] 2905 poke 1024 + chkoff + 11, a:poke 55296 + chkoff + 11, b
 [5388] 2910 poke 03397, 00187 : poke 03398, 00083 : goto 2001
 [53b5]    0 rem
 [53bb] 2001 poke 03397, 00069 : poke 03398, 00013
 [53df] 2002 poke 1024 + chkoff + 12, 81:poke 55296 + chkoff + 12, 7
 [5415] 2004 es = 21652 : ee = 22923 : ek = 203
 [543c] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [5477] 2009 poke 1024 + chkoff + 12, 87
 [5494] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [54e2] 2020 ba = asc( mid$(p$, 18, 1) )
 [54fd] 2021 bb = asc( mid$(p$, 19, 1) )
 [5518] 2022 bc = asc( mid$(p$, 20, 1) )
 [5533] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [5592] 2030 if ba and 128 then p0 = 0.062500000001818989403545856475830078125
 [55d2] 2031 if bb and 1 then p1 = 0.0156250000004547473508864641189575195312
 [5611] 2032 if bb and 2 then p2 = 0.0039062500001136868377216160297393798828
 [5650] 2033 if bb and 4 then p3 = 0.0009765625000284217094304040074348449707
 [568f] 2034 if bb and 8 then p4 = 0.0002441406250071054273576010018587112427
 [56ce] 2035 if bb and 16 then p5 = 0.0000610351562517763568394002504646778107
 [570e] 2036 if bb and 32 then p6 = 0.0000152587890629440892098500626161694527
 [574e] 2037 if bb and 64 then p7 = 0.0000038146972657360223024625156540423632
 [578e] 2040 if bb and 128 then p8 = 0.0000009536743164340055756156289135105908
 [57cf] 2031 if bc and 1 then p9 = 0.0000002384185791085013939039072283776477
 [580e] 2032 if bc and 2 then pa = 0.0000000596046447771253484759768070944119
 [584d] 2033 if bc and 4 then pb = 0.000000014901161194281337118994201773603
 [588b] 2034 if bc and 8 then pc = 0.0000000037252902985703342797485504434007
 [58ca] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [5915] 2060 g = 0.666743217501820
 [592f] 2100 tb = k = g : a = 86 : b = 10
 [5950] 2200 if tb = -1 then a = 83 : b = 5
 [596f] 2210 poke 1024 + chkoff + 12, 90
 [598c] 2500 rem
 [5992] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [59cd] 2905 poke 1024 + chkoff + 12, a:poke 55296 + chkoff + 12, b
 [5a02] 2910 poke 03397, 00053 : poke 03398, 00090 : goto 2001
 [5a2f]    0 rem
 [5a35] 2001 poke 03397, 00069 : poke 03398, 00013
 [5a59] 2002 poke 1024 + chkoff + 13, 81:poke 55296 + chkoff + 13, 7
 [5a8f] 2004 es = 23310 : ee = 24584 : ek = 223
 [5ab6] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [5af1] 2009 poke 1024 + chkoff + 13, 87
 [5b0e] 2010 v = 0.6666666666612316235641 + 0.00000000023283064365386962890625 : g = 0
 [5b5c] 2020 ba = asc( mid$(p$, 20, 1) )
 [5b77] 2021 bb = asc( mid$(p$, 21, 1) )
 [5b92] 2022 bc = asc( mid$(p$, 22, 1) )
 [5bad] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [5c0c] 2030 if ba and 16 then p0 = 0.062500000001818989403545856475830078125
 [5c4b] 2031 if ba and 32 then p1 = 0.0156250000004547473508864641189575195312
 [5c8b] 2032 if ba and 64 then p2 = 0.0039062500001136868377216160297393798828
 [5ccb] 2033 if ba and 128 then p3 = 0.0009765625000284217094304040074348449707
 [5d0c] 2034 if bb and 1 then p4 = 0.0002441406250071054273576010018587112427
 [5d4b] 2035 if bb and 2 then p5 = 0.0000610351562517763568394002504646778107
 [5d8a] 2036 if bb and 4 then p6 = 0.0000152587890629440892098500626161694527
 [5dc9] 2037 if bb and 8 then p7 = 0.0000038146972657360223024625156540423632
 [5e08] 2040 if bb and 16 then p8 = 0.0000009536743164340055756156289135105908
 [5e48] 2031 if bb and 32 then p9 = 0.0000002384185791085013939039072283776477
 [5e88] 2032 if bb and 64 then pa = 0.0000000596046447771253484759768070944119
 [5ec8] 2033 if bb and 128 then pb = 0.000000014901161194281337118994201773603
 [5f08] 2034 if bc and 1 then pc = 0.0000000037252902985703342797485504434007
 [5f47] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [5f92] 2060 g = 0.682352764997662
 [5fac] 2100 tc = k = g : a = 86 : b = 10
 [5fcd] 2200 if tc = -1 then a = 83 : b = 5
 [5fec] 2210 poke 1024 + chkoff + 13, 90
 [6009] 2500 rem
 [600f] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [604a] 2905 poke 1024 + chkoff + 13, a:poke 55296 + chkoff + 13, b
 [607f] 2910 poke 03397, 00178 : poke 03398, 00096 : goto 2001
 [60ac]    0 rem
 [60b2] 2001 poke 03397, 00069 : poke 03398, 00013
 [60d6] 2002 poke 1024 + chkoff + 14, 81:poke 55296 + chkoff + 14, 7
 [610c] 2004 es = 24971 : ee = 26178 : ek = 237
 [6133] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [616e] 2009 poke 1024 + chkoff + 14, 87
 [618b] 2010 v = 0.6666666666612316235641 : g = 0
 [61b4] 2020 ba = asc( mid$(p$, 22, 1) )
 [61cf] 2021 bb = asc( mid$(p$, 23, 1) )
 [61ea] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [6249] 2030 if ba and 2 then p0 = 0.062500000001818989403545856475830078125
 [6287] 2031 if ba and 4 then p1 = 0.0156250000004547473508864641189575195312
 [62c6] 2032 if ba and 8 then p2 = 0.0039062500001136868377216160297393798828
 [6305] 2033 if ba and 16 then p3 = 0.0009765625000284217094304040074348449707
 [6345] 2034 if ba and 32 then p4 = 0.0002441406250071054273576010018587112427
 [6385] 2035 if ba and 64 then p5 = 0.0000610351562517763568394002504646778107
 [63c5] 2036 if ba and 128 then p6 = 0.0000152587890629440892098500626161694527
 [6406] 2037 if bb and 1 then p7 = 0.0000038146972657360223024625156540423632
 [6445] 2040 if bb and 2 then p8 = 0.0000009536743164340055756156289135105908
 [6484] 2031 if bb and 4 then p9 = 0.0000002384185791085013939039072283776477
 [64c3] 2032 if bb and 8 then pa = 0.0000000596046447771253484759768070944119
 [6502] 2033 if bb and 16 then pb = 0.000000014901161194281337118994201773603
 [6541] 2034 if bb and 32 then pc = 0.0000000037252902985703342797485504434007
 [6581] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [65cc] 2060 g = 0.670634204987467
 [65e6] 2100 td = k = g : a = 86 : b = 10
 [6607] 2200 if td = -1 then a = 83 : b = 5
 [6626] 2210 poke 1024 + chkoff + 14, 90
 [6643] 2500 rem
 [6649] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [6684] 2905 poke 1024 + chkoff + 14, a:poke 55296 + chkoff + 14, b
 [66b9] 2910 poke 03397, 00236 : poke 03398, 00102 : goto 2001
 [66e6]    0 rem
 [66ec] 2001 poke 03397, 00069 : poke 03398, 00013
 [6710] 2002 poke 1024 + chkoff + 15, 81:poke 55296 + chkoff + 15, 7
 [6746] 2004 es = 26565 : ee = 27837 : ek = 192
 [676d] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [67a8] 2009 poke 1024 + chkoff + 15, 87
 [67c5] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [6813] 2020 bc = asc( mid$(p$, 25, 1) )
 [682e] 2021 ba = asc( mid$(p$, 23, 1) )
 [6849] 2022 bb = asc( mid$(p$, 24, 1) )
 [6864] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [68c3] 2030 if ba and 64 then p0 = 0.062500000001818989403545856475830078125
 [6902] 2031 if ba and 128 then p1 = 0.0156250000004547473508864641189575195312
 [6943] 2032 if bb and 1 then p2 = 0.0039062500001136868377216160297393798828
 [6982] 2033 if bb and 2 then p3 = 0.0009765625000284217094304040074348449707
 [69c1] 2034 if bb and 4 then p4 = 0.0002441406250071054273576010018587112427
 [6a00] 2035 if bb and 8 then p5 = 0.0000610351562517763568394002504646778107
 [6a3f] 2036 if bb and 16 then p6 = 0.0000152587890629440892098500626161694527
 [6a7f] 2037 if bb and 32 then p7 = 0.0000038146972657360223024625156540423632
 [6abf] 2040 if bb and 64 then p8 = 0.0000009536743164340055756156289135105908
 [6aff] 2031 if bb and 128 then p9 = 0.0000002384185791085013939039072283776477
 [6b40] 2032 if bc and 1 then pa = 0.0000000596046447771253484759768070944119
 [6b7f] 2033 if bc and 2 then pb = 0.000000014901161194281337118994201773603
 [6bbd] 2034 if bc and 4 then pc = 0.0000000037252902985703342797485504434007
 [6bfc] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [6c47] 2060 g = 0.733381925616444
 [6c61] 2100 te = k = g : a = 86 : b = 10
 [6c82] 2200 if te = -1 then a = 83 : b = 5
 [6ca1] 2210 poke 1024 + chkoff + 15, 90
 [6cbe] 2500 rem
 [6cc4] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [6cff] 2905 poke 1024 + chkoff + 15, a:poke 55296 + chkoff + 15, b
 [6d34] 2910 poke 03397, 00103 : poke 03398, 00109 : goto 2001
 [6d61]    0 rem
 [6d67] 2001 poke 03397, 00069 : poke 03398, 00013
 [6d8b] 2002 poke 1024 + chkoff + 16, 81:poke 55296 + chkoff + 16, 7
 [6dc1] 2004 es = 28224 : ee = 29471 : ek = 157
 [6de8] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [6e23] 2009 poke 1024 + chkoff + 16, 87
 [6e40] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [6e8e] 2020 ba = asc( mid$(p$, 25, 1) )
 [6ea9] 2021 bb = asc( mid$(p$, 26, 1) )
 [6ec4] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [6f23] 2030 if ba and 8 then p0 = 0.062500000001818989403545856475830078125
 [6f61] 2031 if ba and 16 then p1 = 0.0156250000004547473508864641189575195312
 [6fa1] 2032 if ba and 32 then p2 = 0.0039062500001136868377216160297393798828
 [6fe1] 2033 if ba and 64 then p3 = 0.0009765625000284217094304040074348449707
 [7021] 2034 if ba and 128 then p4 = 0.0002441406250071054273576010018587112427
 [7062] 2035 if bb and 1 then p5 = 0.0000610351562517763568394002504646778107
 [70a1] 2036 if bb and 2 then p6 = 0.0000152587890629440892098500626161694527
 [70e0] 2037 if bb and 4 then p7 = 0.0000038146972657360223024625156540423632
 [711f] 2040 if bb and 8 then p8 = 0.0000009536743164340055756156289135105908
 [715e] 2031 if bb and 16 then p9 = 0.0000002384185791085013939039072283776477
 [719e] 2032 if bb and 32 then pa = 0.0000000596046447771253484759768070944119
 [71de] 2033 if bb and 64 then pb = 0.000000014901161194281337118994201773603
 [721d] 2034 if bb and 128 then pc = 0.0000000037252902985703342797485504434007
 [725e] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [72a9] 2060 g = 0.667648012284220
 [72c3] 2100 tf = k = g : a = 86 : b = 10
 [72e4] 2200 if tf = -1 then a = 83 : b = 5
 [7303] 2210 poke 1024 + chkoff + 16, 90
 [7320] 2500 rem
 [7326] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [7361] 2905 poke 1024 + chkoff + 16, a:poke 55296 + chkoff + 16, b
 [7396] 2910 poke 03397, 00201 : poke 03398, 00115 : goto 2001
 [73c3]    0 rem
 [73c9] 2001 poke 03397, 00069 : poke 03398, 00013
 [73ed] 2002 poke 1024 + chkoff + 17, 81:poke 55296 + chkoff + 17, 7
 [7423] 2004 es = 29858 : ee = 31101 : ek = 158
 [744a] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [7485] 2009 poke 1024 + chkoff + 17, 87
 [74a2] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [74f0] 2020 ba = asc( mid$(p$, 27, 1) )
 [750b] 2021 bb = asc( mid$(p$, 28, 1) )
 [7526] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [7585] 2030 if ba and 1 then p0 = 0.062500000001818989403545856475830078125
 [75c3] 2031 if ba and 2 then p1 = 0.0156250000004547473508864641189575195312
 [7602] 2032 if ba and 4 then p2 = 0.0039062500001136868377216160297393798828
 [7641] 2033 if ba and 8 then p3 = 0.0009765625000284217094304040074348449707
 [7680] 2034 if ba and 16 then p4 = 0.0002441406250071054273576010018587112427
 [76c0] 2035 if ba and 32 then p5 = 0.0000610351562517763568394002504646778107
 [7700] 2036 if ba and 64 then p6 = 0.0000152587890629440892098500626161694527
 [7740] 2037 if ba and 128 then p7 = 0.0000038146972657360223024625156540423632
 [7781] 2040 if bb and 1 then p8 = 0.0000009536743164340055756156289135105908
 [77c0] 2031 if bb and 2 then p9 = 0.0000002384185791085013939039072283776477
 [77ff] 2032 if bb and 4 then pa = 0.0000000596046447771253484759768070944119
 [783e] 2033 if bb and 8 then pb = 0.000000014901161194281337118994201773603
 [787c] 2034 if bb and 16 then pc = 0.0000000037252902985703342797485504434007
 [78bc] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [7907] 2060 g = 0.749690691474855
 [7921] 2100 tg = k = g : a = 86 : b = 10
 [7942] 2200 if tg = -1 then a = 83 : b = 5
 [7961] 2210 poke 1024 + chkoff + 17, 90
 [797e] 2500 rem
 [7984] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [79bf] 2905 poke 1024 + chkoff + 17, a:poke 55296 + chkoff + 17, b
 [79f4] 2910 poke 03397, 00039 : poke 03398, 00122 : goto 2001
 [7a21]    0 rem
 [7a27] 2001 poke 03397, 00069 : poke 03398, 00013
 [7a4b] 2002 poke 1024 + chkoff + 18, 81:poke 55296 + chkoff + 18, 7
 [7a81] 2004 es = 31488 : ee = 32761 : ek = 235
 [7aa8] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [7ae3] 2009 poke 1024 + chkoff + 18, 87
 [7b00] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [7b4e] 2020 ba = asc( mid$(p$, 28, 1) )
 [7b69] 2021 bb = asc( mid$(p$, 29, 1) )
 [7b84] 2022 bc = asc( mid$(p$, 30, 1) )
 [7b9f] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [7bfe] 2030 if ba and 32 then p0 = 0.062500000001818989403545856475830078125
 [7c3d] 2031 if ba and 64 then p1 = 0.0156250000004547473508864641189575195312
 [7c7d] 2032 if ba and 128 then p2 = 0.0039062500001136868377216160297393798828
 [7cbe] 2033 if bb and 1 then p3 = 0.0009765625000284217094304040074348449707
 [7cfd] 2034 if bb and 2 then p4 = 0.0002441406250071054273576010018587112427
 [7d3c] 2035 if bb and 4 then p5 = 0.0000610351562517763568394002504646778107
 [7d7b] 2036 if bb and 8 then p6 = 0.0000152587890629440892098500626161694527
 [7dba] 2037 if bb and 16 then p7 = 0.0000038146972657360223024625156540423632
 [7dfa] 2040 if bb and 32 then p8 = 0.0000009536743164340055756156289135105908
 [7e3a] 2031 if bb and 64 then p9 = 0.0000002384185791085013939039072283776477
 [7e7a] 2032 if bb and 128 then pa = 0.0000000596046447771253484759768070944119
 [7ebb] 2033 if bc and 1 then pb = 0.000000014901161194281337118994201773603
 [7ef9] 2034 if bc and 2 then pc = 0.0000000037252902985703342797485504434007
 [7f38] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [7f83] 2060 g = 0.682356773410023
 [7f9d] 2100 th = k = g : a = 86 : b = 10
 [7fbe] 2200 if th = -1 then a = 83 : b = 5
 [7fdd] 2210 poke 1024 + chkoff + 18, 90
 [7ffa] 2500 rem
 [8000] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [803b] 2905 poke 1024 + chkoff + 18, a:poke 55296 + chkoff + 18, b
 [8070] 2910 poke 03397, 00163 : poke 03398, 00128 : goto 2001
 [809d]    0 rem
 [80a3] 2001 poke 03397, 00069 : poke 03398, 00013
 [80c7] 2002 poke 1024 + chkoff + 19, 81:poke 55296 + chkoff + 19, 7
 [80fd] 2004 es = 33148 : ee = 34367 : ek = 143
 [8124] 2005 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [815f] 2009 poke 1024 + chkoff + 19, 87
 [817c] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [81ca] 2020 ba = asc( mid$(p$, 30, 1) )
 [81e5] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [8244] 2030 if ba and 4 then p0 = 0.062500000001818989403545856475830078125
 [8282] 2031 if ba and 8 then p1 = 0.0156250000004547473508864641189575195312
 [82c1] 2032 if ba and 16 then p2 = 0.0039062500001136868377216160297393798828
 [8301] 2033 if ba and 32 then p3 = 0.0009765625000284217094304040074348449707
 [8341] 2034 if ba and 64 then p4 = 0.0002441406250071054273576010018587112427
 [8381] 2035 if ba and 128 then p5 = 0.0000610351562517763568394002504646778107
 [83c2] 2036 if bx and 64 then p6 = 0.0000152587890629440892098500626161694527
 [8402] 2037 if bx and 128 then p7 = 0.0000038146972657360223024625156540423632
 [8443] 2040 if bx and 1 then p8 = 0.0000009536743164340055756156289135105908
 [8482] 2031 if bx and 2 then p9 = 0.0000002384185791085013939039072283776477
 [84c1] 2032 if bx and 4 then pa = 0.0000000596046447771253484759768070944119
 [8500] 2033 if bx and 8 then pb = 0.000000014901161194281337118994201773603
 [853e] 2034 if bx and 16 then pc = 0.0000000037252902985703342797485504434007
 [857e] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [85c9] 2060 g = 0.670817057136476
 [85e3] 2100 tj = k = g : a = 86 : b = 10
 [8604] 2200 if tj = -1 then a = 83 : b = 5
 [8623] 2210 poke 1024 + chkoff + 19, 90
 [8640] 2500 rem
 [8646] 2900 for i = es to ee : k = ( peek(i) + ek ) and 255 : poke i, k : next i
 [8681] 2905 poke 1024 + chkoff + 19, a:poke 55296 + chkoff + 19, b
 [86b6] 2910 poke 03397, 00239 : poke 03398, 00134 : goto 2001
 [86e3]    0 rem
 [86e9] 2000 rem
 [86ef] 2001 rem
 [86f5] 31337 t = t0 + t1 + t2 + t3 + t4 + t5 + t6 + t7 + t8 + t9 + ta + tb + tc + td + te + tf + tg + th + tj
 [875a] 31338 if t = -19 then goto 31340
 [8772] 31339 print:print"verdict: nope":goto 31345
 [8791] 31340 print:print"verdict: correct"
 [87ab] 31345 goto 31345
 [87b7]    0 rem

Now I had all of the code and I could see that the previously encrypted blocks each contained code similar to this:

 [0e9d] 2010 v = 0.6666666666612316235641 - 0.00000000023283064365386962890625 : g = 0
 [0eeb] 2020 ba = asc( mid$(p$, 1, 1) )
 [0f05] 2021 bb = asc( mid$(p$, 2, 1) )
 [0f1f] 2025 p0 = 0:p1 = 0:p2 = 0:p3 = 0:p4 = 0:p5 = 0:p6 = 0:p7 = 0:p8 = 0:p9 = 0:pa = 0:pb = 0:pc = 0
 [0f7e] 2030 if ba and 1 then p0 = 0.062500000001818989403545856475830078125
 [0fbc] 2031 if ba and 2 then p1 = 0.0156250000004547473508864641189575195312
 [0ffb] 2032 if ba and 4 then p2 = 0.0039062500001136868377216160297393798828
 [103a] 2033 if ba and 8 then p3 = 0.0009765625000284217094304040074348449707
 [1079] 2034 if ba and 16 then p4 = 0.0002441406250071054273576010018587112427
 [10b9] 2035 if ba and 32 then p5 = 0.0000610351562517763568394002504646778107
 [10f9] 2036 if ba and 64 then p6 = 0.0000152587890629440892098500626161694527
 [1139] 2037 if ba and 128 then p7 = 0.0000038146972657360223024625156540423632
 [117a] 2040 if bb and 1 then p8 = 0.0000009536743164340055756156289135105908
 [11b9] 2031 if bb and 2 then p9 = 0.0000002384185791085013939039072283776477
 [11f8] 2032 if bb and 4 then pa = 0.0000000596046447771253484759768070944119
 [1237] 2033 if bb and 8 then pb = 0.000000014901161194281337118994201773603
 [1275] 2034 if bb and 16 then pc = 0.0000000037252902985703342797485504434007
 [12b5] 2050 k = v + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + pa + pb + pc
 [1300] 2060 g = 0.671565706376017
 [131a] 2100 t0 = k = g : a = 86 : b = 10
 [133b] 2200 if t0 = -1 then a = 83 : b = 5

Each of these blocks was reading a character (or two, or three) of the entered password, preforming a bit of maths and setting the value of the t0, t1, t2, etc. variables. These variables are then used in the very last bit of code that wasn’t encrypted, which tests that adding up all 19 of the t variables results in an answer of -19. So the correct password is the one that makes each of these blocks set its t variable to -1.

These blocks initially looked quite complicated, but essentially they just start with a seed value, then test each bit of the input characters, adding a unique value for each bit. Then if the result equals the hard coded value it will set t to -1.

Then, taking the seed and expected values from each block, I wrote a little python script to calculate the password. This script walks through each bit of the password, setting it to 0 if adding the value for that bit would push the accumulated total over the target, and setting it to 1 if it would not.

#!/usr/bin/python
import sys
bitvals = [
	0.062500000001818989403545856475830078125,
	0.0156250000004547473508864641189575195312,
	0.0039062500001136868377216160297393798828,
	0.0009765625000284217094304040074348449707,
	0.0002441406250071054273576010018587112427,
	0.0000610351562517763568394002504646778107,
	0.0000152587890629440892098500626161694527,
	0.0000038146972657360223024625156540423632,
	0.0000009536743164340055756156289135105908,
	0.0000002384185791085013939039072283776477,
	0.0000000596046447771253484759768070944119,
	0.000000014901161194281337118994201773603,
	0.0000000037252902985703342797485504434007
]
data = [
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.671565706376017
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.682612358126820
	},
	{
		'seed': 0.6666666666612316235641,
		'target': 0.682552023325146
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.667647300753773
	},
	{
		'seed': 0.6666666666612316235641,
		'target': 0.682310803327740
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000046566128730773925781250,
		'target': 0.670638734940470
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000046566128730773925781250,
		'target': 0.729427094105661
	},
	{
		'seed': 0.6666666666612316235641,
		'target': 0.683334092143953
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.729182238224924
	},
	{
		'seed': 0.6666666666612316235641 + 0.00000000046566128730773925781250,
		'target': 0.682352954987467
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000046566128730773925781250,
		'target': 0.745769257191599
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.666743217501820
	},
	{
		'seed': 0.6666666666612316235641 + 0.00000000023283064365386962890625,
		'target': 0.682352764997662
	},
	{
		'seed': 0.6666666666612316235641,
		'target': 0.670634204987467
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.733381925616444
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.667648012284220
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.749690691474855
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.682356773410023
	},
	{
		'seed': 0.6666666666612316235641 - 0.00000000023283064365386962890625,
		'target': 0.670817057136476
	}
]
bits = ''
for d in data:
	total = d['seed']
	for bitval in bitvals:
		if total + bitval <= d['target']:
			total += bitval
			bits += '1'
		else:
			bits += '0'
byte = 0
for i, b in enumerate(bits):
	if b == '1':
		byte |= 1 << (i % 8)
	if i > 0 and i % 8 == 7:
		sys.stdout.write(chr(byte))
		byte = 0
sys.stdout.write('\n')

Running it gave me: LINKD-LHSTS´┐ŻAND.40-BHd-FLOATS, and while that’s not the correct answer, it was close enough for me to guess the real password, and another one was solved!

Phrack

PHRACKIDX.TXT

After solving Back To The Basics I had a look at a few of the other challenges. Proprietary Format looked like it’s probably a custom image format, possibly with some kind of run-length encoding… Tape looked like it probably involves error correcting codes to reconstruct the part of the file where the flag is supposed to be… But I kept being drawn back to Phrack.

For this challenge we’re given an ARJ archive file that contains a single text file, and told “I heard Phrack is still kinda relevant. But since when do they include ctf flags?”.

There really aren’t all that many places that you can hide in a text file, especially one that’s quite structured like this index file. I started by looking at the white space using “:set syntax=whitespace” in vim. That showed that there was nothing funny going on with invisible characters. I then managed to totally rule out the text file by finding the original and running a diff, which showed that no changes had been made. So that left the ARJ file as the hiding place for the flag.

The other copy of the file that I’d found was also in an ARJ archive, and comparing the two showed that the one from the challenge was noticeably larger. The challenge file was 10,779 bytes, while the original was only 9,031 bytes. Thinking that the extra data might be hiding in extra headers or between sections in the file, I threw together a quick ARJ file parser in python, but this showed that there weren’t any extra parts to the file. The extra size was all within the compressed data for the one and only file in the archive.

One thing that the parser did show is that the compression method was different. The original ARJ archive used maximum compression, but the challenge file used minimum. Could the lower compression setting account for the difference in file size? I tried re-compressing the text file using minimum compression and produced a 9,826 byte file, so the challenge file was still bigger than it should have been, even with it using minimum compression.

When using minimum compression, ARJ archives use a simple LZ77 style compression, maintaining a sliding window of decompressed data and using references into that window to eliminate repeated data. The compressed data is a mixture of literal data and offset/length pairs that specify bytes that should be copied from the sliding window. Knowing this, I started to think about how extra data could be hidden in this encoding.

The first thing I thought of was to shorten the length of a reference by one character, so instead of referencing a string of X bytes, the compressed data would be modified to reference a string of X-1 bytes, and then the missing byte would be written as a literal byte. Then if we only modified references with the two most common last characters, the value of that last character that was removed from the reference could be used to encode 0 or 1.

To test this idea I grabbed the arj source code and added code to the decompressor to look for sub-optimal references into the sliding window. This looked promising. It showed that the challenge file had a lot of sub-optimal references, and the other arj archives I had did not have any, so I was going in the right direction. However, although there were a lot of sub-optimal references, they weren’t only short by a single character, so it wasn’t using the encoding scheme that I’d thought of. It must be modifying the references in a different way.

Here are a couple of examples of the output that I got from my modified decompressor:

last ref was suboptimal: off: 272, len: 9, char: 32:
last ref: .  How to
last nxt:  Pi
this txt:  Ma

This first example shows the most common sub-optimal reference, the reference was one byte short and the missing character was a space. In this output “last ref” is the text of the sub-optimal reference, “last nxt” is the text immediately following the reference in the sliding window that could have been part of the reference, and “this txt” is the text being reference afterwards. In this case we can see that “ Pi” was available in the window, which starts with a space, and we’re now processing the following reference that started with a space. The last reference should have been one byte longer and included the space.

last ref was suboptimal: off: 1728, len: 6, char: 32:
last ref: Phrack
last nxt:  World News Issue 3 Part
this txt:  World News Issue 3 Part

This second example shows a sub-optimal reference with a lot more characters missing. The first reference was “Phrack“, but why wasn’t the “ World News Issue 3 Part” text also part of that reference? This following reference is totally redundant, all of the text was available in the window and could have been part of the first reference.

This is where I really started to get stuck. I knew I was definitely on the right track, but there were just so many different ways that the hidden data could be encoded using modified reference lengths. There were exactly 128 sub-optimal references that were followed by a literal character, which seemed really promising, it’s divisible by 8 and would result in a 16 byte flag, but every variation of encoding that I tried using these just output garbage. Also, there were a lot of sub-optimal references that were followed by another reference instead of a literal, why were they there?

In total there were 1050 sub-optimal references of all types. This isn’t divisible by 8… but it is divisible by 7… could the flag be encoded with 7-bit characters? Could the number of missing characters from the references be used to encode the data? There were just too many possibilities.

I started googling for existing LZ77 steganography techniques and found a couple of existing implementations:

Although Jan’s technique shouldn’t result in any sub-optimal references like those I was seeing, I was starting to get a bit desperate, so I modified the decompressor to look for cases where there was more than one possibility in the window, and to check which one was used. There were many cases where there were multiple possibilities, but the challenge file was always using the last (and closest) one. So that was that one ruled out too, and google had no more suggestions, so I was on my own again.

I was starting to think that I wasn’t going to complete this one. It was Sunday evening and I’d been working on this for a whole day, using up half of the time I had for the challenges, and I was totally out of ideas. I’d been scrolling up and down in the list of sub-optimal references failing to spot any pattern when I stopped to eat. Then while I was eating a thought came to me… a lot of the references to Phrack were exactly six characters long… wasn’t that a bit of a coincidence?

So I took one more look through the sub-optimal reference list, and it wasn’t just a lot of the references to Phrack. It was all of them! “Phrack” never occurred as part of any other reference, it only occurred in a six character reference that contained nothing but “Phrack”. Hmm… how could data be encoded in that? Then I wondered if “Phrack” ever occurred outside of a reference? In theory it should only happen for the first occurrence. Another quick modification to the decompressor to get it to check and yes, there were many literal occurrences of “Phrack”.

That naturally led to the thought that a reference to “Phrack” in the window could encode a one bit, and a literal “Phrack” could encode a zero. Another quick modification to the decompressor and… garbage again! Now I wasn’t too hopeful, but there was one last thing to try, reverse the encoding, window = 0, literal = 1, and… there it is… I couldn’t quite believe my eyes, but it just printed the flag!

Here’s the diff to the decoder:

diff --git a/decode.c b/decode.c
index b474cef..96f27be 100644
--- a/decode.c
+++ b/decode.c
@@ -536,6 +536,20 @@ static short decode_len()
 /* Decodes the entire file, using method 4 */
+void outputbit(int bit)
+{
+       static unsigned char byte = 0;
+       static int pos = 0;
+
+       byte |= bit << pos;
+       if(++pos == 8)
+       {
+               printf("%c", byte);
+               pos = 0;
+               byte = 0;
+       }
+}
+
 void decode_f(int action)
 {
  int i;
@@ -544,6 +558,7 @@ void decode_f(int action)
  int r;
  static unsigned long ncount;
+printf("\n");
  if(ntext==NULL)
   ntext=malloc_msg(FDICSIZ);
  decode_start_stub();
@@ -557,6 +572,8 @@ void decode_f(int action)
   {
    ncount++;
    ntext[r]=(unsigned char)(bitbuf>>8);
+   if(r - 5 >= 0 && memcmp(&ntext[r-5], "Phrack", 6) == 0)
+    outputbit(1);
    fillbuf(8);
    if(++r>=FDICSIZ)
    {
@@ -572,6 +589,10 @@ void decode_f(int action)
    ncount+=(unsigned long)j;
    if((i=r-decode_ptr()-1)<0)
     i+=FDICSIZ;
+
+   if(j == 6 && memcmp(ntext+i, "Phrack", 6) == 0)
+    outputbit(0);
+
    while(j-->0)
    {
     ntext[r]=ntext[i];

That Was Fun!

With Phrack finally solved I decided to call it a day. I only had an hour or so left, so it was unlikely I’d solve another one, and I’d already done a lot better than I expected. If they hold another competition this year then I might have to enter it properly.

Leave a Reply

Your email address will not be published. Required fields are marked *

Post Navigation