Well, this is a really interesting essay! A disgusting censorware
reversed... what could be more interesting than this... light on the dark paths of all
enemies of knowledge! IMHO the idea that somebody should 'know' -and impose- what should
be 'good' for me to see (or feel, or drool, or watch, or read) and what should not be 'so
good', borders to Goebbel, or '50 american times... as you know I don't like very much the
vulgarity of many younger crackers... yet this time I enjoy it so much I won't edit it:
Jawohl: fuck you, stupid censors!
And thank a lot Saruman, you'r a good reverser!
Index
Introduction
Tools used
Target URL
Program history
Essay
Installation - "Evidence of sucking"
The Crack - "So where's the challenge?"
Reversing the encryption - "Why security through obscurity
/doesn't/ work"
Appendix 1 - Commented disassembly of decryption-loop (from
IDA)
Appendix 2 - HLL implementations of decryption-loop (ANSI-C and
Borland Pascal)
Introduction
Cybersitter is one of those disgusting censorwares, created and maintained by fractions
within the christian right movement.
The software blocks access to sites deemed 'immoral' by the fraction behind it. Not only
does CS (why do I find myself thinking of BS, bullshit?) block access to 'immoral'
world-wide-web pages, but it also does it's best to filter 'immoral' words and phrases
anywhere within a datastream (HTTP, IRC, FTP).
The company behind this product, Solid Oak Software (SOS), is just as disgusting as Microsoft, if not more so. They have a documented history of emailbombing their critics, but not only that, they also favour taking legal actions, or more correctly - threatening with legal action, against /anyone/ criticizing their line of products (even if they happen to be journalists, doing their job).
Why this essay then? (I'll try to keep it short, but I'm a written-word kind of guy). If people want to use blocking software at home, isn't that fine by you? Well, no. Not when the company behind the blocking software doesn't want to reveal just /what/ sites and /what/ keywords they are using. This is the reason for this essay - we will show you what Solid Oak Software doesn't want you to see, what they doesn't want you to /know/.
For more information on this, visit http://www.peacefire.org,
an organization working to stop net censorship, or http://www.softdisk.com/comp/dan/cybersitter/
. Together they give you a pretty complete view of this this whole SOS/CS ordeal.
Now, let's get this township rebellion on the road...
Tools used
SoftICE
W32dasm
IDA
HIEW
ANSI-C Compiler
Borland Pascal
Turbo Assembler
Target URL
http://www.cybersitter.com /cyb97t.exe
Program History
Bloody, just like christian history. For the full story please visit one of the sites mentioned earlier. They (SOS) are constantly mutating the protectionscheme in vain attempts to 'defeat' reverse engineering.
Installation - "Evidence of sucking"
When running the installation you will notice that you are not allowed to specify where the program is to be installed. Yes, it do seem like SOS sucks just as much as I've been telling you, no? The installer will spread it's 'wares' all over your harddrive, specifically it'll target your %windir% and %windir%\system paths.
Let's start the program. Here, we're presented with a MessageBox() with the text '"4.0" is not valid floating point value.' but your mileage may vary. If you do get this box, you will notice that it contains a famous Delphi-resource. So now we know that.
What's first on the menue? Let's crack this bitch. I usually crack for intellectual stimulation, but as we soon shall see the people behind this software doesn't want anyone to receive any kind of stimulation ;-)
The Crack - "So where's the challenge?"
Open the "Enter Unlock Code" registration-box (under the 'register' menu). You are asked to enter your unlock-code. No username asked for? They must get the material for the serial-calculation from somewhere.. let's see... Let's check out the 'on-line order form' alternative. We're informed that we can order the full retail version for only $39.95. Oh, that's soo tempting, but I digress. Now, let's see what we can learn from the 'order by phone' alternative. Ah, now we're getting somewhere. We are presented with a key, mine is '0184C1ACD'. Looks like HEX to me, let's do some probing. Back to the unlock-code thingy. Let's enter something, say.. '1223344' and break in on hmemcpy (works well in Delphi applications).
We'll snap back to SoftICE after a call to CallWindowProcA. We're nested pretty deep inside the kernel or something, so let's trace back to CS. After stepping back thru seven or eight ret's you'll find yourself in the 'CYB97!code' module, looking good.
:0045FDF4 call 0041F8B0 ; hmemcpy ... :0045FDF9 mov eax, dword ptr [ebp-24] ; ... we land here :0045FDFC lea edx, dword ptr [ebp-20]
Checking eax and edx we'll find our code at eax. Let's step further down the road..
:0045FDFF call 004074FC :0045FE04 mov eax, dword ptr [ebp-20] :0045FE07 mov edx, 0045FF9C
Again, checking eax and edx we'll find the text 'extend' at edx. So if one would want to extend ones trialperiod, use 'extend' as the serial. (. This is sooo hard .)
:0045FE0C call 00403D80 ; check if 'extend' :0045FE11 jne 0045FEB1
We're looking for the correct serial, so let's hitch a ride with that jump...
:0045FEB1 33C0 xor eax, eax [ silly initcode snipped ] :0045FEC2 mov al, 43 :0045FEC4 call 00444BD8 :0045FEC9 lea edx, dword ptr [ebp-24] :0045FECC mov eax, dword ptr [ebx+000001E8]
Let's see what that call did. We 'd edx' and find something interresting. The C:\ volume label, the text 'FAT32'. I instantly recognized it as the result of a filesystem-info-query, very interresting indeed.
:0045FED2 call 0041F8B0 ; This we know is hmemcpy. 'bd *' and step over. :0045FED7 mov eax, dword ptr [ebp-24]
eax points to the unlock-code we entered. We're getting close here.
:0045FEDA call 00407850
What happened to eax there? (We see that it was modified thanks to SoftICE)
:? eax 00012AAB0 0001223344 '<character-representation>'
So, we have our unlock-code in eax, as a number...
:0045FEDF sub eax, 00001424 :0045FEE4 mov esi, dword ptr [ebp-1A]
When my eyes fell on 'esi' after this move I was certain I'd cracked this program. What I had only suspected a minute or two ago had now been confirmed.
:0045FEE7 cmp eax, esi
They subtract $1424 and compares it esi, which holds the correct serial+$1424.
:0045FEE9 jne 0045FF2B
Just another good/bad-guy jump. So what did I see in esi above? What else but $184C1ACD :-)
In short: The correct unlock-code is the VOLUMEID of 'C:\' (in decimal) increased by $1424. I don't know if you find this obvious - it may look like I skipped a step here - but I really had this 'hunch' groving since that call which resulted in the pointer to the structure holding the string 'FAT32'...
I made a 'keymaker' for this. All one need is:
.radix 16 mov bl,3 mov ax,6900 mov dx,offset BUFFER int 21 ; Get disk serial number. mov edx,dword ptr [BUFFER+2] add edx,1424 <output edx here>
Done. :-) One can conclude that the live approach worked very well in this case. I have also tried the dead listing approach (just now), but it don't think I would have found out the basic 'algorithm' as fast that way (I'm kind of slow :-)
That's it for cracking, now let's get going with the "tough-stuff"...
Reversing the encryption - "Why security through obscurity /doesn't/ work"
While we (moving from 'I' to 'we' form here. I made the crack, we reversed the encryption) certainly had nothing against reversing the serial scheme of this disgusting software, that was in no way our primary goal. The primary goal was to extract all those oh-so-secret keywords and lists of sites that are blocked, those lists that are so loved by the authors behind this program that they almost threw a lawsuit at the guy doing an earlier reversal (in a time where the program had no 'do not reverse-engineer' notice, the protection-scheme as changed since then). As usual, if you want the full story, search the net.
First a little behind-the-scene knowledge is required. Question: Where do they store the keywords/list of blocked sites? Let's find out.
I knew the program were using one or more encrypted files that it had hid away
somewhere under the %windir% path. Well, knew and knew, I assumed this because that was
how the older version worked (had I read). So, I pretty much launched Volkov Commander,
set it to sort on filesize and started looking through the files in %windir%\. When I
found nothing but shards of old installations I progressed to check the .\system\
directory too. This is where I found the *fil.dll files, which certainly didn't look like
any DLL I've ever seen. It was obvious these files were encrypted. (Looking back, using
regmon or examining the string-resources of CYB97.EXE would have paid off faster,
obviously. Let's try and remember that for the future :-)
ADWFIL DLL 32,410 98-02-16 12.16 adwfil.dll BNRFIL DLL 2,322 98-02-16 12.16 bnrfil.dll CULTFIL DLL 1,450 97-11-20 12.33 cultfil.dll HIWFIL DLL 502 97-11-20 12.33 hiwfil.dll IAWFIL DLL 2,138 97-11-20 12.33 iawfil.dll LGWFIL DLL 1,982 97-11-20 12.33 lgwfil.dll PICSFIL DLL 744 97-11-20 12.33 picsfil.dll USRFIL DLL 42 98-02-16 0.52 usrfil.dll 8 file(s) 41 590 bytes
Actually, I found the file "WFILEU.DRV" first, but it was too short to contain all the data. We'll get back to that file later.
Looking at the filenames and the "options\filter files" menu of CS one can puzzle out which file holds which kind of filters/keywords - which could be important if we were to choose to launch a cryptanalysis attack - however, inspection of the files showed that such an attack would be out of our league (ie: it wasn't a singlepass XOR or Ceasar-chiper :-) (note: I've been informed that earlier versions used a static XOR-key of $96 or some such. How's that for security?)
The first line of ADWFIL.DLL look like this.
1{9})b#b!b4g(f1a8g%f9c5c=a-n&n<o0o&l=i3m!j"j.j1i.h&h-h+r<w1w5u!t t-v r6s,p)p8q?q$z6&127;0&127;&|5}4})z!~8{ x=y:y=e
Now, going thru the files, pondering, trying to see patterns, etc. We came to the conclusion that the encryption is a 16->8 bit transformation (all lines contain an even number of bytes), possibly with an add or xor operation entwined (the beginning of the first line seemed to hold far more low-ascii characters than the end).
So we progress by setting up a bpx on CreateFileA (bpx createfilea do "d ecx"), tracing until the program tries to open one of those o-so-interresting "DLL" files.
Starting CS we're pulled in to SoftIce a couple of times. Somewhere around the fifth break he's trying to open WFILEU.DRV. That file was encrypted too, so let's check it out. We add a bpx to 'ReadFile' and let it run.
Softice will break in again in a module called STCP!.text, obviously this is a DLL used
by CS,
and thus worthy of closer inspection.
:00B0CCA3 call [kernel32!ReadFile] :00B0CCA9 test eax,eax ; We'll land here
Now, let's trace out of this subroutine and we'll eventually find ourself in a loop, like this:
:00B0CD45 mov al,[esi] :00B0CD47 cmp al,1A :00B0CD4A jz B0CE21 :00B0CD4F cmp al,0D :00B0CD51 jz B0CD55 :00B0CD53 inc esi :00B0CD54 mov [edi],al
esi and edi point to a buffer holding encrypted data. Ah, we're getting real close here. He seem to be doing some sort of preprocessing, maybe stripping end-of-line characters or some such. Looking at the code at B0CD55 and B0CE21 (the two exit paths) we decide that this routine isn't doing anything we need to understand fully, at least not now, so we scroll down and do a 'HERE' on the ret terminating the subroutine. We could set a bpm on the buffer holding the encrypted data, but because we feel real close now we refrain ourselves from such an drastic move - we may miss something important if we let the program run wild.
Instead, let's continue stepping over code, it's worked real well so far, hasn't it?
Take your time and examine the contents of memory and registers after suspicious moves
(like subproc arguments, EBP-xx). Not too many instructions later you'll find the address
of the encryption buffer being loaded and pushed to the stack. This is how it looks:
:100023B1 lea eax, dword ptr [ebp+FFFFFBD8] :100023B7 push eax :100023B8 lea eax, dword ptr [ebp-220] :100023BE push eax :100023BF lea ecx, dword ptr [ebp-1C] :100023C5 call 10003740 :100023CA test eax,eax (note: the EBP's may differ, depending on what file you are currently decrypting)
Examination of the memory areas addressed showed that the last address pushed is the buffer holding encrypted data. The topmost push pointed to something that I first thought to be a key (In my case it held the string '09') but we were later able to puzzle it all together using the magnificent program IDA and it's function for renaming stack-variables (in other words, I was going down the wrong path and my friend rescued me :-)
It dawned on us that what is pushed is the address of the buffer holding the soon-to-be decrypted data, and ecx is loaded with a pointer to a buffer holding a 16-bit decryption key.
Now, back to the encryption-algorithm which is to be found under that "call
xxxx3740".
This would be a good time for you to examine Appendix 1 - our commented disassembly. The
decryption loop is rather lengthy, and examining it at the bit-level would be overkill,
instead I will assume you've taken a casual glance at the code, and will proceed by
outlining the overall algorithm.
First, the key. They use a dword to hold the decryption-key, but only use the lower 16-bits for actual decryption. First they create two keys by shifting and anding out a five-bit value (0-31d) from both the upper and lower bits of the 16-bit key. like this:
key1 = (key SHR 4) AND $1F key2 = (key SHR 8) AND $1F
The two-bytes-for-one scheme (do you remember?) comes from dividing the byte to be encrypted into two 4-bit parts which are then encrypted individually using the two keys. Here we want to take those two bytes and reverse the encryption, to form the one decrypted character.
ch1 = inbyte1 XOR key1 ch2 = inbyte2 XOR key2
The 'decrypted' bits are then shifted some more, and OR'ed together to form the one decrypted character.
ch2 = (ch2 AND $F) SHL 4 chout = (ch1 AND $F) OR ch2
Here chout is the fully decrypted character, which is written to the decryptionbuffer.
Now the key (which I call a 'cyclic' key) is updated using the following formula:
key = key + ($100 - chout)
And the process repeats.
One important ingredient is missing. What are the initial value of the key?! Further analysis of the disassembled specimen will reveal a mysterious push $DEAD ;-). However, again the live approach paid off as we got this magic key automatically while tracing the code. Some more testing showed that this key only is reseeded upon opening a file to decrypt. In other words, the cyclic key above shall not be reset for each line decrypted, but just as with a CRC you feed the old (previous) key to the decryption-subroutine each pass through it.
All in all, this work took us many hours, counting a couple of hours spent by us individually to get to know the program and the encryption. It was first when we sat together in front of IDA, really _looking_ at the code, filling in the blanks and tossing ideas about that we were able to create a program in C to partially (more on this later) decode a file. We had the advantage of having access to multiple machines, so that we could run IDA and do the documentation on one, while running CS under SoftICE on another, examining the live specimen at our leisure.
It's hard to document something like this, it's easy to go into too much detail in some places, or forget to fully explain, or be fuzzy about the greater picture, just how one reached a certain conclusion and why one did what one did.
One thing is clear to us after researching and writing this one essay. We now understand better just how much effort is put into work like this by all our colleagues over the world. Take for instance our biggest slip: Our first try at decrypting a file fail short of okay. The result was that only the first few lines were decrypted okay, then came garbage for a few screens, and then it seemed like the decryptor 'came in sync' again and some more lines were decoded. We thought we had missed a key-update somewhere. So of course we (in this case it was mostly I, I must admit) went about looking in all the wrong places. To sum it up (to experience): After a couple of hours I compared our HLL code to the actual assembler and found we'd missed one little instruction when converting the assembler-decryptor. Now, checking this should have been the number one prio. The lesson is: Do not assume something is more complex or advanced than it has to be. I'm prone to that error. Instead, apply logic, the Zen way of doing things. Ah, but you knew that already, didn't you? ;-)
Please refer to Appendix 2 - "HLL implementations of decryption-loop" if you
need help understanding/implementing this decryption.
Final notes - "I've got a right to
rant"
This was researched by Bobban and Saruman of DFR Research & Engineering, in
cooperation.
The essay was written by me, Saruman. I'm solely responsible for the presentation, which
may not be all one could wish for, but there it is.
So what /are/ in those encrypted files then? Well, apart from the obvious smut-sites and smut-words they also block perfectly legit sites, such as www.c2.org and www.linkexchange.com and usegroups such as alt.crackers (!), alt.censorship and soc.women. They seem to have listened somewhat to their critics, because the used to block a whole range of sites containing criticism (such as www.peacefire.org). In the past they've even gone so far as to let the installation-program scan the harddrive for references to peacefire.org (the searched the http-cache) or the old decryptor program and refuse to install if traces of these were detected. It's our moral right to continue the reversal of censorwares to make sure that they do not try to push their bent morals down the throat of us, their customers - IN SECRET. To me it's obvious that all keywords and sites blocked should be public knowledge, and editable through the standard user interface of the censorware in question. Anything less than that is unacceptable.
If you want to contact me, well.. use your brain, or shout my name in the fidonet echo 'HOLYSMOKE' :-)
Good bye, and happy cracking. I do hope that others will pick up from here, there are a lot of censorwares out there, all waiting to be throughly reversed. A cool project would be one in-memory-disabler for every one of these censorwares? I can't do it on my own though, but together...
/%)+Saruman - food for thought, anyway. Bye.
PS.
The 'master password' (if any) is stored in the registry:
HKLM\System\CurrentControlSet\control\SecurityProviders\NetSet\MSwcf\EMP\
The surf's up!
PPS
For compiled binaries and more, download the cs97hack.zip file. The
file contains the binaries for the serial-generatior, the file-decryptor and an
WIN32-application for fetching and showing the master-password, if any, from the registry.
Appendix 1 - Commented disassembly of
decryption-loop (from IDA)
10003740 Decrypt proc near 10003740 10003740 keybuf = dword ptr -18h 10003740 cryptkey2 = byte ptr -14h 10003740 cryptkey1 = byte ptr -10h 10003740 out_count = dword ptr -0Ch 10003740 in_count = dword ptr -08h 10003740 bufsize = dword ptr -04h 10003740 cryptbuf = dword ptr 08h 10003740 decryptbuf = dword ptr 0Ch 10003740 10003740 push ebp 10003741 mov ebp, esp 10003743 sub esp, 18h 10003746 push ebx 10003747 push esi 10003748 push edi 10003749 mov [ebp+keybuf], ecx ; on entry ECX = ptr to key 1000374C mov eax, [ebp+cryptbuf] 1000374F push eax 10003750 call _strlen 10003755 add esp, 4 10003758 mov [ebp+bufsize], eax 1000375B mov [ebp+out_count], 0 10003762 mov [ebp+in_count], 0 10003769 jmp loc_10003771 1000376E ; ------------------------------------------- 1000376E 1000376E loc_1000376E: 1000376E inc [ebp+in_count] 10003771 10003771 loc_10003771: 10003771 mov eax, [ebp+bufsize] 10003774 inc eax 10003775 cmp eax, [ebp+in_count] 10003778 jle loc_10003824 1000377E mov eax, [ebp+in_count] 10003781 mov ecx, [ebp+cryptbuf] 10003784 xor edx, edx 10003786 mov dl, [eax+ecx] 10003789 cmp edx, 20h 1000378C jl loc_1000380A 10003792 mov eax, [ebp+keybuf] 10003795 mov eax, [eax+4] 10003798 shr eax, 4 1000379B and al, 1Fh 1000379D mov [ebp+cryptkey2], al 100037A0 mov eax, [ebp+keybuf] 100037A3 mov eax, [eax+4] 100037A6 shr eax, 8 100037A9 and al, 1Fh 100037AB mov [ebp+cryptkey1], al 100037AE mov eax, [ebp+in_count] 100037B1 mov ecx, [ebp+cryptbuf] 100037B4 xor edx, edx 100037B6 mov dl, [eax+ecx+1] 100037BA xor eax, eax 100037BC mov al, [ebp+cryptkey1] 100037BF xor edx, eax 100037C1 and edx, 0Fh 100037C4 shl edx, 4 100037C7 mov eax, [ebp+in_count] 100037CA mov ecx, [ebp+cryptbuf] 100037CD xor ebx, ebx 100037CF mov bl, [eax+ecx] 100037D2 xor eax, eax 100037D4 mov al, [ebp+cryptkey2] 100037D7 xor ebx, eax 100037D9 and bl, 0Fh 100037DC or dl, bl 100037DE mov eax, [ebp+out_count] 100037E1 mov ecx, [ebp+decryptbuf] 100037E4 mov [eax+ecx], dl 100037E7 mov eax, 100h 100037EC mov ecx, [ebp+decryptbuf] 100037EF mov edx, [ebp+out_count] 100037F2 xor ebx, ebx 100037F4 mov bl, [ecx+edx] 100037F7 sub eax, ebx 100037F9 mov ecx, [ebp+keybuf] 100037FC add [ecx+4], eax 100037FF inc [ebp+out_count] 10003802 inc [ebp+in_count] 10003805 jmp loc_1000381F 1000380A ; ------------------------------------------------------------- 1000380A 1000380A loc_1000380A: ; CODE XREF: Decrypt+4C 1000380A mov eax, [ebp+in_count] 1000380D mov ecx, [ebp+cryptbuf] 1000380D mov ecx, [ebp+cryptbuf] 10003810 mov al, [eax+ecx] 10003813 mov ecx, [ebp+out_count] 10003816 mov edx, [ebp+decryptbuf] 10003819 mov [ecx+edx], al 1000381C inc [ebp+out_count] 1000381F 1000381F loc_1000381F: ; CODE XREF: Decrypt+C5 1000381F jmp loc_1000376E 10003824 ; ------------------------------------------------------------- 10003824 10003824 loc_10003824: ; CODE XREF: Decrypt+38 10003824 mov eax, 1 10003829 jmp $+5 1000382E pop edi 1000382F pop esi 10003830 pop ebx 10003831 leave 10003832 retn 8 10003832 Decrypt endp
Appendix 2 - HLL implementations of
decryption-loop
ANSI-C
// by Bobban / DFR Research & Engineering #include <stdio.h> #include <stdlib.h> #include <conio.h>
typedef unsigned long ulong; typedef unsigned char byte;
ulong key = 0xDEADL;
void decrypt_line(char *inbuff, char *outbuff) { int in_count , out_count = 0,len; byte ch1,ch2; ulong subkey1,subkey2; len = strlen(inbuff)/2;
for (in_count = 0; in_count < len ; in_count++) { subkey1 = key; subkey1 = (subkey1 >> 4) & 0x1fL; subkey2 = key; subkey2 = (subkey2 >> 8) & 0x1fL; ch2 = *(inbuff+in_count*2+1); ch2 ^= subkey2; ch2 = (ch2 & 0x0f) << 4; ch1 = *(inbuff + in_count*2); ch1 ^= subkey1; ch1 &= 0x0f; ch1 |=ch2; outbuff[out_count] = ch1; key += 0x100L - (ulong)ch1; ++out_count; } outbuff[out_count] = NULL; }
int main (void) { FILE *infile; char inbuff[400]; char outbuff[400]; clrscr();
if ((infile = fopen(".\\usrfil.txt","r")) == NULL) { printf ("file not found \n"); exit(1); } while(fgets(inbuff,400,infile) != NULL) { decrypt_line(inbuff,outbuff); printf("%s\n",outbuff); }
close(infile); return 0; }
Borland Pascal
Program CSDecode; { By Saruman / DFR Research & Engineering } Uses CRT,Utils,ASMUtils;
CONST Version :String[5]='1.0.0'; {$I UPDATE.INC }
VAR KEY :LongInt;
T :Text; S1 :String; S2 :String;
Function Decrypt(const S: String): String; var loop :Word; uts :String; key1,key2 :LongInt; ch1,ch2 :Byte; begin uts[0]:=chr(Length(s) div 2); for loop:=0 to (length(S) div 2)-1 do begin { create two keys } key1:=(key SHR 4) AND $1F; key2:=(key SHR 8) AND $1F; { decode high bits } ch2:=ord(S[(loop*2)+2]) XOR key2; ch2:=(ch2 AND $F) SHL 4; { decode low bits and create final output character (ch1+ch2) } ch1:=ord(S[(loop*2)+1]) XOR key1; ch1:=(ch1 AND $F) OR ch2; { Write to output } uts[loop+1]:=char(ch1); { Update cyclic key } Inc(key,$100-ch1); end; Decrypt:=uts; end;
BEGIN ClrScr; TextColor(14); Write('CyberSitter''97 Decryptor v'+Version+' ú Crackware ú Compiled '+Compiled+#10+#13); TextColor(12); Write('Copyright (C)1998 Saruman / DFR Research & Engineering ú FREEWARE'+#10+#13+#10+#13); NormVideo;
if ParamCount=0 then begin WriteLn('Usage: CSDEC <file-to-decrypt> [$seedkey] [>redirect]'); Halt; end;
if NOT Exist(ParamStr(1)) then begin WriteLn('Error: File "',ParamStr(1),'" not found.'); Halt(1); end;
if va(ParamStr(2))=0 then key:=$DEAD else key:=va(ParamStr(2)); WriteLn(' Infile: ',ParamStr(1)); WriteLn('Seed key: $',HexW(key)); WriteLn;
if CheckRedirectOUT then begin WriteLn(' redirecting output ... '); Assign(output,''); ReWrite(output); end;
Assign(T,ParamStr(1)); Reset(T);
While NOT EoF(T) do begin ReadLn(T,S1); WriteLn(Decrypt(S1)); end; Close(T); END.
[version 1.1]