papers
Back to the HCU Papers


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!


 

The Penetration of CyberSitter'97

as told by members of DFR Research & Engineering

 

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"

Final notes

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.


Essay

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]