My life as an hacker
Notice:
I am not someone who breaks into computers and such.
I am someone that likes to play with computers and write programs.
It is sad that nowadays almost everybody gives the term hacker
a different meaning from its original
definition.
It is useless to ask me for help with breaking passwords and such.
I have no experience with such things, and even if I could, I will
not help you, for the simple reason that I am not for hire for this
kind of work.
|
A picture
of me in my
cyberpunk
t-shirt
|
As a young boy I already displayed a great interest in
electricity and electronics,
and did I dream about having my own computer center in
the future, as some drawings indicated.
My very first contact with real computers came through my cousin
Frans Faase, who gave me
a paper tape with one of his programs. At home I tried to decode this
tape by hand, and got some of the lines decoded.
I think it was part of an Algol 60 program.
And because we happen to have the same (calling) name, I also adopted
his account name at the university: FAAF, and used it whenever
I dreamed about doing something with computers.
My life as a hacker really started in high school,
when on December 9, 1977, during an outing with our class, I met
Henk Koppelaar through one of my teachers.
It was during a dinner in a Chinese restaurant that he concluded that
I must be good with computers, after he heard I was good in everything
in school except languages. Later that evening, at his home, he showed
me some books about Artificial Intelligence. In March 1978, I got a book
about Algol 60, and I started to write some programs.
On May 5, I went to the Academic Computer Centre Utrecht (ACCU), where
I met with Henk. He shows me around and taught me how to use the Cyber
main frame (using NOS/BE 1.4 LVL 518*10). I had to use punch cards to
run programs. Henk was using a simulation program, where each line contains
a simulation rule. Henk told me that he sometimes shuffles the punch cards
just before putting them in the card reader, just to baffle the others,
because the order of the rules does not matter. I tried to run one of my
own Algol 60 programs, but not which much success.
In the months that follow, I receive several letters for Henk, and also
got some more books (including A Comparative
study of Programming Languages). He also told me about recursive
functions. On June 13, I designed my first programming language, which was
a functional programming language using recursion. The next Monday, I also
got the idea of writing a program for a "artificial brain" (neural network)
with 84 nodes.
Summer of 78: Fortran on a Cyber main frame
On Tuesday, June 20, 1978, Henk phoned me up to tell me had a program,
called SOCPAC, in IBM Fortran that he wanted to be ported to the Cyber.
The next day, I went to his office to collect the box with approximately
1500 punch cards and found my way to the ACCU, and started working.
At first I would load the whole deck of cards repeatedly. Soon I
found I could store them, and edit them by giving commands that
would remove or add lines at a time. On the next Friday, I also used
a terminal to make some edits, and I showed my parents around the ACCU.
Every time when I ran a job, I would sit down in front of a monitor
showing the different queues of the main frame; the input, the running
and the printing queue. I used the FIBMCDC conversion program to
convert the program from IBM Fortran to CDC Fortran dialect, which
costed 900000 Kws (Kilo word seconds??).
That summer I learned Fortran, the batch language and the editing
language Update mostly by myself. After having gone to
England, I got the program running
in August. Henk gave me the book `Finite Mathematics'
by Seymour Lipschutz as a reward for my work.
4th World Congress of Cybernetics and Systems
This congress was held at Amsterdam, August 22-25, 1978. When Henk Koppelaar
had to present a talk there, he invited me to join him on August 24. This became the
first computer science conference I attended.
At the end of the day, I also attend a chess match between Roland Lever and
the Russian chess program Kaissa.
LISP 1.5
When I was in England that summer, on a language course, I bought the LISP 1.5 Programmer's Manual by
John McCarthy from Dillons in London on July 21,
because Henk had told me that it was the language for Artificial
Intelligence. (Remember Eliza?) Of course, I read this book, and that is how I learned LISP.
(Read a discussion about the origin of CAR and CDR, if you like.)
Because Henk had a budget on the computer
that he was afraid to lose, he let me use it freely. So in the years
that followed, I often went to Utrecht to either try out some kind of Fortran
program, or to work with LISP.
In Fortran I wrote my first program
that could be used to count
Hamilton paths in a square grid graph. (Of course, I did not
know that they were called Hamilton paths at that time.)
I wrote my own shortest path LISP function before I saw Dijkstra's
algorithm for this. (I also wrote a program to find all the shortest paths
in Fortran of order n^2 log(n)
, which is also known
as Floyd's algorithm.)
I wrote some list matching functions in LISP,
as an extension to string matching ideas that I found in a language
called METEOR that Henk and a student of him worked on.
Finally, I managed to write a recursive-decent
LL(1) compiler-compiler in LISP that generated
LISP. This program consisted of about one thousand lines of LISP. If
you wanted to use it you had to make a file that would contain:
- The compiler-compiler.
- The grammar rules
(rather primitive, but it worked).
- The input to be compiled.
The compiler-compiler would read the grammar rules, and based
on these define some LISP functions that would form the compiler.
These functions would then read the remaining of the input according
the grammar rules and execute the generated code, consisting
of some LISP functions.
I did this without any books about compilers or compiler-compilers.
Sadly enough, I lost the code.
I also wrote a set of functions for formatting text and (a primitive
form of) plotting to an ASCII file.
(And of course, I tried to write a LISP interpreter in Fortran.)
In my freshman year I learned Algol 68 (the most academical
program language, ever made) and Pascal (which was initially
only intended as a toy-language for a compiler construction course).
(Algol 68 compilers are still being developed, I found out recently.)
One of my room mates bought a Acorn Atom,
a home computer build around a 6502 processor.
It is a fairly unknown machine made by the British Acorn company, which
had a very nice dialect of Basic, that was designed rather
orthogonal. It also had a build in assembler, which I started to use
soon after I discovered it.
I wrote a Hamilton path counting
program in machine language,
which took (at that time) almost a complete day to count the
number of Hamilton paths in a grid of 7 by 7 that started in
one of the corners. It found around 1.5 million paths.
The computer was also used for some serious work. I developed a
dorm administration program, which was
used for calculating we had to pay each other with respect to
the use of the telephone, coffee and joint meals.
Ssomewhere in my third year, I also wrote
a cross-assembler that generated code for a
6809 processor.
The Atari 800 XL
The Atari 800 XL is another home computer build around the 6502.
On this we (a roommate and me) wrote our first complete compiler
for a small language to 6502 machine code.
It was written in Basic.
The first thing we did when it was ready, was writing the compiler
in the language and compile it with the compiler written in Basic.
At the end this took around 20 minutes, but we got our compiler
written in machine code. (A classical example of boots-trapping.)
This we got at our dorm, only at the end of my study.
As it had colour graphics I invested a lot of time in
writing a program in GfA-Basic that could generated Mandelbrot
fractals, in a way that you quickly get an impression of how
the picture was going to look, without having to calculate
all dots (line by line).
Another project I did was writing the software for an installation
of an artist, which consisted of three televisions. These three
televisions where put side-by-side, where the middle one could spin
around the centre of the screen. The sequence of events would start
with showing a Dutch soccer player (Koeman) kicking a ball in the
direction of the centre screen. Then the centre screen would start
spinning and display a bouncing ball. At some moment this ball would
fly out to one of the screens on the outside, where a reverse slide
show of the soccer player would catch the ball.
An Atari ST was used to read the position of the middle television
by means of the joystick interface, and control the speed and
direction of the motor attached to the middle television.
The video output of the Atari was switched between the three televisions.
Both the sensors and the accurators were rather primitive: only 16
different positions could be detected, and the motor control used
only 4 levels (including off), which were not linear. The motor would
not start running at the lower levels.
The most difficult part turned out to be the controlling of the turning
speed of the motor.
The installation was shown at several art-displays in Holland.
With all my previous experiences with compilers and compiler-compilers,
it was to be expected that I would do something like that for my
master thesis. My master thesis got the (horrible) title:
An attribute evaluator generator.
It was intended to become part of a compiler-compiler system,
and consisted of a Pascal program that would generate Pascal code for attribute evaluation in Abstract Program
Trees.
In the three years after my graduation, I worked part-time at
the University of Twente on finishing the program and writing
the documentation for it.
Up to now, its my best documented program, which sadly enough, has
never been used, or read by someone, as far as I know.
It was the first big program that I wrote, which consisted of
about 500K of source code. The documentation consist of a
technical report describing the functional
specification of the program, and a technical report containing the source code and it documentation.
I also worked on a variant called Propertied Abstract Trees or PAT for short.
Email and Relay
When I started to work at the University, it was also the first
time I came in contact, what is now known as the Internet. At that
time it consisted of a number of loosely connected networks, such
as ARPA and EARN. The oldest email I have kept was sent to me
on February 27, 1987 by Albert Nymeyer from Australia. My UUCP email
address from that time was mcvax!utrcu1!utrinu!fransjf.
My EARN email address was faase@hentht5.bitnet.
Besides email, I also made use of Relay, an early predecessor of IIRC.
It was a simple command-line oriented interface. I did spend
some time writing a Turbo Pascal program implementing a little better interface.
This program would divide the screen into three parts. The top half
would contain the list of participants, the lower half the lines
that people wrote, and the bottom line was used as a command line.
In coming status change messages (people joining or leaving the channel)
were not displayed in the lower half but used to update the list
in the top half of the screen.
PostScript
During this time, I also discovered that PostScript is a programming
language. Here are some of my PostScript programs based on the ones
that I wrote then:
- The Henon curve.
- A more interesting curve found by
a friend of me.
- An ever extending cloud of points.
View in Landscape mode, if using ghostview.
(Please, modify these before printing, as they contain endless loops!)
Working
Only three years after I graduated, I started working in the real
world (with PC's), which was a refreshing experience, because I had to learn
many new things. So far, I had only used Pascal, now I learned C,
which since than became my prime programming language, and which I
still use for all my private hacking. In the four years that
I worked at the software company, I learned a lot about hacking.
Because I was working at many different projects, I often had to
change the config.sys and the autoexec.bat files.
Not soon, I wrote a the project.bat
batch file, which would do this for me. It makes us of the program
getjn, which asks a Yes/No question.
AutoLISP
One time I had to write a program in AutoLISP (part of AutoCAD)
that had to read many small tables with information from external
files. I decided to only read this files on demand, and use
a LISP function that would redefine itself after having read
the table, in such a way that it would just return the contents
of the table as a single value. Such a function would look like:
(defun read_table_a ( / v)
(setq v (read_table 'format_fn "a.txt"))
(eval (list 'defun 'read_table_a '() (list 'quote v)))
)
And because I had many of these tables, I defined a LISP function
that could define them, based on a file name, and a format description
(in the form of a LISP function). To get the above definition
one would call:
(def_read_table 'read_table "a.txt" 'format_fm)
This function is a function that defined functions that can
redefine themselves. The function was loaded with quote, list and eval
functions, but it worked. It looks like:
(defun def_read_table (fn filename format)
(eval
(list 'defun fn '( / v)
(list 'setq 'v (list 'read_table (list 'quote format) filename))
(list 'eval (list 'list ''defun (list 'quote fn) ''() '(list 'quote v)))
) ))
The LISP function read_table was a function that read the
input file, line by line, ignoring commented lines starting with
percent-sign. Each line was converted to list of strings with
the `words' on the line. The format function would then be applied
to this list of strings to return a row in the table.
I leave it up to the reader to define this LISP function.
Export/import SQL script
One day a colleague of mine, needed to make some test input, based
on the contents of an SQL database. So, he wanted to have an
SQL select statement that would return insert statements with the
contents of a table. So, I wrote something like:
select 'insert into TAB1
values('||decode(KEY,NULL,'NULL',KEY)
||','||decode(NAME1,NULL,'NULL',''''||NAME1||'''')
||','||decode(NAME2,NULL,'NULL',''''||NAME2||'''')
||','||decode(NUM1,NULL,'NULL',NUM1)
||');' from TAB1;
This SQL script when executed will output for each row of the table
TAB1 an insert statement, assuming that the table has the
four columns: KEY, NAME1, NAME2 and
NUM1, where KEY and NUM1 are numeric and
the other fields strings.
And when I had written this, he wanted to have
a SQL statement that would return this SQL statement, given the
name of the table (TAB1 in this example),
using the contents of the data-dictionary tables.
So, I wrote:
select decode(column_id,
1, 'select ''insert into TAB1 values(''||',
'||'',''||') ||
'decode(' ||
column_name ||
',NULL,''NULL'',' ||
decode(data_type,
'CHAR', '''''''''||'||column_name||'||''''''''',
column_name) ||
')'
from user_tab_columns
where table_name = TAB1
order by column_id;
select '||'');'' from TAB1;' from dual;
If this script is executed it will output the first select statement
I wrote, assuming the table user_tab_columns contains
the table definition of the above given definition of table TAB1.
And when we got this, he wanted to have a script that would
return the above two select statements for each table in the
database (again using the data dictionary tables).
Finally, we managed to do this using a single
SQL*Plus (of Oracle) script, using the ability of
SQL*Plus to
redirect its output to a file, and to execute files.
The final script looked like this:
set heading off
set feedback off
set linesize 90
set pagesize 2000
spool e__.sql
select 'spool exps.sql ' from dual;
select
'select decode(column_id, 1,'||
'''select ''''insert into '||table_name||' values(''''||'',',
'''||'''',''''||'')||'||
'''decode(''||column_name||'',NULL,''''NULL'''',''||',
'decode(data_type,''CHAR'','||
'''''''''''''''''''||''||column_name||''||'''''''''''''''''',',
'column_name)||'')'''||
'from user_tab_columns '||
'where table_name = '''||table_name||'''',
' order by column_id; ',
'select ''||'''');'''' from '||table_name||';'' from dual;'
from user_tables;
select 'spool off' from dual;
spool off
set linesize 120
start e__
If this script is executed the output of the select statements will
to spooled to the file e__.sql, which will contain two select
statements for each table name mentioned in the table user_tables.
Secondly, the script will execute the e__.sql script.
The e__.sql script will generate one select statement
for each table in the file exps.sql.
To generate a script import.sql containing an insert statement for
each single row in the database, we have to execute the following
commands:
spool import.sql
start exps
spool off
Thus you only need SQL*Plus to make a dump of a database, and
import it in another instance of the database, with the added functionality
of being able to edit the dump.
Implementation
of a fast variant of the bubble sort
Bubble sort, can be considered as the most simple sort algorithm,
but it is often characterised as not being very optimal. C-libraries
often contain the function qsort() which is (in most cases -- don't let
the name fool you) an implementation of the faster Quick-sort algorithm.
What many people don't know is that this is a recursive algorithm, which
can behave bad on an (almost) sorted array, and could lead to unwanted
stack-overflows. Of course, proper implementations should not cause
stack-overflows, and would not show bad behaviour on an (almost) sorted
array by selecting a good `pivot' element.
As this once happened to me (with the qsort() of an old version
of MSC), I wrote a faster bubble sort function, based on some ideas
that I read in some magazine somewhere. (Don't ask me which magazine.
I have a good memory for algorithms, but a bad memory for data.)
First of all bubble sort can be improved by using alternating
passes. Secondly, it can be improved by starting comparing pairs of
elements that are further away from each other.
The algorithm I implemented here, starts of with comparing pairs in
each half of the array, and reduces the distance after each pass.
The distance is reduced faster, depending how many elements were
exchanged. Although this algorithm is not n log(n)
,
which is the optimal lower bound for sorting, it comes quite close
in practice, which makes it much better than traditional bubble
sort, which is n^2
.
Gareth McCaughan compared
my program with some other sorting algorithms, wrote
a small test report about it.
I wrote a modified version of bsort.c,
bsort_s.c, which work for arrays
of simple types (like char, int and double)
with the default compare operator `<'.
When Lawrence Kirby made a request on comp.lang.c
for (Quick) sort algorithm on list, I realized that my algorithm can
be adapted for lists: bsort_l.c
(Has not been tested yet). Later on, I wrote another version,
which sorts the list instead of swapping the contents:
bsort_rl.c.
My favourite editor on the DOS platform is q
from SemWare. The latest
version that I worked with was renamed into e.
(Nice names for editors, as hackers do not like to type much.)
For the last one, I wrote some extra
functions in its macro language.
During this time I also started decoding the
DWG-format
that is being used by AutoCAD (Release 12). My boss had told me that
some other company had taken a year to decode it. Within a month
I decode half of it, the remaining only being some stupid work.
A binary compare program that I had written (bfc.c) turned
out to be very help full to find the main structure.
So far, I have found nobody that is interested, or that would like
to finish the work.
JavaScript
Lately, I have discovered JavaScript.
When I wanted to convert some old MS Word 5.0 (on MSDOS) documents to
the Word for Windows 97, I discovered that these old file formats are
no longer recognized. Of course, I searched the web for this, but
failed to find anything about this. Thus, I felt there was no other
option left but to reverse engineer the Word 5.0 file format. (I
vaguely remembered having seen a description of the format in some
book about five years ago. I blamed myself for not buying the book
then.) After some hours of puzzling, I discovered that the structure
is actually not so complicated. It is even rather redundant in many
ways, which made it easier to reverse engineer it for the matter.
I finally managed to write a program for
converting the documents into HTML. I have to admit that this program
is not one of my best designed one, but what do you expect for a
one time conversion program.
Because there are no free tools to export the contents a Quark Xpress file, I worked on reverse engineering the format by myself.
Previous projects
Currently, I do not have time to embark on any large projects, although
I am often thinking about software engineering systems based on some
ideas I described on the page "The Art of Programming".
Some of the ideas presented there, I am trying to implement in a
project, that I named the "MetaMeta" project.
- The 12 coin problem.
- A program for finding the word `KERST'
(Dutch for Christmas) in a diamond.
An diamond filled with as many
words `KERST' (?).
- Hacked together my own implementation of a Non-Networked File System.
- A small program to split up a string into
a given set of strings.
- A more forgiving uudecode program,
that can be used to process multi-part uuencode postings collected
in a single file, without having to edit the file.
- A small program that finds the
maximal lowest common multiplier of the numbers a_1, .., a_k
that sum up to n, for a given n. Or in other words:
the largest order of a permutation of length n
(already published in
`Bulletin de la Société Mathématique de France',
Vol. 97, page 187, 1969).
The results for n upto 100.
- The You.cgi-script that fingers
the persons who want to know what I know about them (many want to
know this). It also keeps a log, so I can know who wants to know
what I know about them. The script uses
txt2pre.c.
- Something about sparse rulers.
- syncd.c, a program for
synchronizing the contents of a Unix directory and a DOS
directory on a floppy disk.
- Working out my own LaTeX style file
for my online diary.
- go2ps.c, a simple filter
for generating a PostScript file from a Go game record. Each page
of the generated file shows the board for the move with the same
number.
- parse.c, a program to
parse expressions and evaluate expressions.
Someone needed it, and I wrote it in
about an hour. It is not complete, and also not correct.
- add_footer.c, a filter
which adds footers to PostScript files generated by Netscape
containing a (optional) URL, and a page number.
- digits.c
- Mixer: a program which slowly
changes the image on the root window of an X-terminal.
- BrainF*** interpreters and compilers
- Machine independent implementation of Cooperative
Multi-threading in C
- How to crack a Binary File Format