Simple unix busting by Dr. Fuhrball

I have read all of the student essays, and none refer
to unix and only one refers to dongles. This essay refers
to both and indicates some techniques.

The product in question is Aries which is a 3d graphics
package that runs on Sco openserver5. Similar to autocad
in function, its about 100 times more powerful. And about
5 times as expensive. It drives SLA's (stereo lithography
aparatus) directly. (argon ion laser writes on nasty
chemical which turns solid creating 3d shapes). It also
drives my hobby mill in the basement.

Anyway this program comes with a microphar (french) dongle
which is basically a few gates and a national semiconducor
eeprom nmc9306 (32 words). A lot of the dongles out there
use this part, because of its extremely low consumption of
power.

There are a number of ways to break this thing. Since I have
an MSEE, my first inclination is to hook up the logic analizer
and look at whats going on. I have now seen at least 5 different
dongles that all use the national semiconductor eeprom, in various
creative ways, but its basically the same thing.

(The new Rainbow technologies software sentinel pro is just a fancied
version of same, with the addition of their 8 shift register with
adjustable feedback of old), 100% proprietary of course.

Of Course I also have a MSCS, and that lends me to the software
crack. Which is elegant because it does not touch the origional
aries code. And thats good, because the main executable is over
10mb. And with their update of the month club, you could be cracking
this thing for the rest of your life. This crack once installed, lasts
forever.

Back to the story;


When you install this program it adds a device driver that
talks to the dongle.

running strings on Driver.o in the /etc/conf/pack.d/mp shows
among others the following symbols.

mpopen, mpclose, mpread, mpwrite...

So now we have the names to look at in kernel space.

Fire up adb on /unix and disassemble starting at mpopen and you get



dumpfile = /dev/mem, namelist = /unix, outfile = stdout
> 
mpopen              pushl  %ebp
mpopen+1            movl   %esp,%ebp
mpopen+3            subl   $0x0,%esp
mpopen+9            pushl  %ebx
mpopen+a            pushl  %edi
mpopen+b            pushl  %esi
mpopen+c            testb  $0x1,0xd00ee570         [-,valeur_port +1c]
mpopen+13           je     0x0c <d0093559>         [mpopen+25]
mpopen+19           movb   $0x10,0xe00010e9        [-,u+10e9]
mpopen+20           jmp    0x012 <d009356b>        [mpopen+37]
mpopen+25           data16 
mpopen+26           movw   $0x1,0xd00ee570         [-,valeur_port +1c]
mpopen+2e           data16 
mpopen+2f           movw   $0x0,0xd00ee572         [valeur_port +1e]
mpopen+37           popl   %esi
mpopen+38           popl   %edi
mpopen+39           popl   %ebx
mpopen+3a           leave  
mpopen+3b           ret    

mpclose             pushl  %ebp
mpclose+1           movl   %esp,%ebp
mpclose+3           subl   $0x0,%esp
mpclose+9           pushl  %ebx
mpclose+a           pushl  %edi
mpclose+b           pushl  %esi
mpclose+c           data16 
mpclose+d           movw   $0x0,0xd00ee570         [valeur_port +1c]
mpclose+15          popl   %esi
mpclose+16          popl   %edi
mpclose+17          popl   %ebx
mpclose+18          leave  
mpclose+19          ret    

The rest has been deleted for brievity
Anyway, its obviously C code, virtually
all unix drivers are. So lets dis-compile
the whole thing, which once you get the
knack, goes quite quick.

Here is the dis-compiled code

It took about 3 hours with my favorite drink
which is 50 year old single malt north highlands
SCOTCH.

Notice that its still in french. They did not even
have the brains to strip the debugging symbols out
of the final Driver.o module which made dis-compilation
that much easier.


/* origional microphar dongle driver */
/* I assume they own a copyright on this thing */

#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/dir.h>
#include <sys/seg.h>
#include <sys/page.h>
#include <sys/user.h>

struct valeur_port {
unsigned char p;
unsigned short p8;
unsigned char pa,pb,pc,pd;
unsigned short p10,p12,p14,p16,p18,p1a,p1c;
short p1e,p20;
unsigned short p22;
short p24;
}valeur_port;

extern char fvect;
extern struct xnet{
unsigned char p14,p15,p16,p17,p18;
}xnet;


mpopen(dev,flag)
short int dev,flag;
{
if (valeur_port.p1c & 0x1)
	u.u_error=EBUSY;
else
{
	valeur_port.p1c=1;
	valeur_port.p1e=0;
}
}
mpclose(dev,flag)
short int dev,flag;
{
	valeur_port.p1c=0;
}
mpwrite(dev,flag)
short int dev,flag;
{
while ((valeur_port.p20 = cpass()) >= 0)
{
switch (valeur_port.p1e) {
case 0 :
	if(valeur_port.p20 == 'P')
	{
	valeur_port.p1e=1;
	valeur_port.p22=0;
	}	
	break;
case 1 :
	w1();

default:
	break;
}
}
}

w1()
{
unsigned int temp;
	if (valeur_port.p20 != '*')
	{
	if (valeur_port.p20 >= '0')
		{
		if (valeur_port.p20 <= '9')
		{
		valeur_port.p22=valeur_port.p22*10;
		valeur_port.p22=valeur_port.p22+(valeur_port.p20-0x30);
		return;
		}
		}
		return;
	}
	else
	{
	valeur_port.p1e = 0;
	temp = spl5();
	valeur_port.p24=valeur_port.p22;
	if (valeur_port.p24 == 0)
 	valeur_port.p24 += 1;
	driver();	
	splx(temp);
	}	
}
mpread(dev,flag)
short int dev,flag;
{
unsigned int temp;
	temp=0x2710;   /* 10000 */
	valeur_port.p8 += valeur_port.p22;
	valeur_port.p8 ^= 0x79b;  /*1947*/
	valeur_port.p8 -= (valeur_port.p22 | 0xfedc);
	passc(0x30);
	while (temp > 0)
	{
	passc((valeur_port.p8/temp)+'0');
	valeur_port.p8= valeur_port.p8 % temp;
	temp=temp/10;
	}
	passc('\n');
}
sort(number)
unsigned char number;
{
unsigned short temp;
temp=valeur_port.p1a;
iooutb(temp,number);
tempo();
}
alimentation()

/* this is the one that mixes up the output lines */

{
unsigned int temp;
valeur_port.p=0;
sort(0);
for (temp=0;temp < 0x28; temp++)
alea();
valeur_port.p=valeur_port.pa;
sort(valeur_port.p);
}
_fin()
{
valeur_port.p &= ~valeur_port.pb;
sort(valeur_port.p);
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pc;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pd;
sort(valeur_port.p);
valeur_port.p=0;
sort(0);
}
tempo()
{
short temp;
for (temp=0;temp < 0xa;temp++);
}
lecturemot(var1,var2)
unsigned short var1,var2;
{
unsigned short temp;
char temp1;
char temp1a;
unsigned short temp2;
short temp3;
temp2=0;
temp=0;
fvect=ioinb(valeur_port.p1a);
alimentation();
do
{
operation(0x80,var1);
valeur_port.p |=valeur_port.pc;
sort(valeur_port.p);
temp1=lecture_bit();
valeur_port.p &= ~valeur_port.pc;
sort(valeur_port.p);
lecture_bit();
temp=temp<<1;
if (temp1 ==0)
++temp;
++temp2;
}
while (temp2 < 0x10);
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pc;
sort(valeur_port.p);
_fin();
sort(fvect);
return(temp);
}

operation(oper,oper2)
unsigned short oper,oper2;
{
unsigned short temp1;
unsigned short temp;
valeur_port.p |= valeur_port.pb;
sort(valeur_port.p);
valeur_port.p |= valeur_port.pd;
sort(valeur_port.p);
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pc;
sort(valeur_port.p);
switch (oper) {
case 0 :
case 0x10:
case 0x20:
case 0x30:
oper2=oper;
break;
default:
oper2 |=oper;
}
for (temp=0;temp<8;temp++)
{
if (temp1 & 0x80)
{
valeur_port.p |= valeur_port.pd;
sort(valeur_port.p);
}
else
{
valeur_port.p |= ~valeur_port.pd;
sort(valeur_port.p);
}
tempo();
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pc;
sort(valeur_port.p);
temp1=temp1<<1;
}
valeur_port.p &= ~valeur_port.pd;
sort(valeur_port.p);
}

lecture_bit()
{
unsigned char temp1;
unsigned char temp2;
unsigned short temp3;
temp3=valeur_port.p1a;
temp1=0xff;
temp3++;
tempo();
temp2=ioinb(temp3);
temp1 &= temp2;
temp1 &= 0x40;
return(temp1);
}
etatci()
{
unsigned short temp1,temp2,temp3,temp4;
temp3=0x3e8;
temp4=0;
valeur_port.p &= ~valeur_port.pb;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pd;
sort(valeur_port.p);
tempo();
valeur_port.p|= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pc;
sort(valeur_port.p);
valeur_port.p |= valeur_port.pb;
sort(valeur_port.p);
tempo();
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
do {
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
temp1=(unsigned char)lecture_bit();
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
lecture_bit();
if (temp4++ >0x10)
{
if (temp1 ==0)
goto more;
} 
}
while (--temp3 !=0);
more:
temp2=0x3e8-temp3;
tempo();
valeur_port.p &= ~valeur_port.pb;
sort(valeur_port.p);
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pc;
sort(valeur_port.p);
valeur_port.p |= valeur_port.pb;
sort(valeur_port.p);
return(temp2);
}

ewactif()
{
operation(0x30,0);
valeur_port.p &= ~valeur_port.pb;
sort(valeur_port.p);
tempo();
}
ewinactif()
{
operation(0,0);
valeur_port.p &= ~valeur_port.pb;
sort(valeur_port.p);
tempo();
}

effacemot(oper)
unsigned short oper;
{
unsigned short temp1,temp2;
alimentation();
ewactif();
operation(0xc0,oper);
temp1=etatci();
if(temp1!=0);
ewinactif();
_fin();
return(temp1);
}

ecritcode(oper,oper1)
unsigned short oper,oper1;
{
unsigned short temp1,temp2;
temp2=0x10;
alimentation();
ewactif();
operation(0xc0,oper);
temp1=etatci();
if (temp1==0)
{
_fin();
return(temp1);
}
operation(0x40,oper);
do {
if ((oper1 & 0x8000))
{
valeur_port.p |= valeur_port.pd;
sort(valeur_port.p);
goto more2;
}
valeur_port.p &= ~valeur_port.pd;
sort(valeur_port.p);
more2:
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p |= valeur_port.pc;
sort(valeur_port.p);
valeur_port.p &= ~valeur_port.pc;
sort(valeur_port.p);
oper1=oper1<<1;
}
while (--temp2 !=0);
temp1=etatci();
if (temp1 !=0)
ewinactif();
_fin();
return(temp1);
}

choixnumero()
{
valeur_port.p16=0;
calcul_masque();
valeur_port.p1a=0x3bc;
valeur_port.p14=lecturemot(0x3f);
if (valeur_port.p18 !=valeur_port.p14)
{
valeur_port.p1a=0x378;
valeur_port.p14=lecturemot(0x3f);
if (valeur_port.p18 !=valeur_port.p14)
{
valeur_port.p1a=0x278;
valeur_port.p14=lecturemot(0x3f);
if (valeur_port.p18 != valeur_port.p14)
valeur_port.p16=1;

}
}
}

alea()
{
unsigned char temp1,temp2,temp3,temp4;
temp2=rnd();
temp3=rnd() & 3;
temp3++;
do {
sort(~valeur_port.pa & temp2);
tempo();
tempo();
tempo();
tempo();
tempo();
temp2=~temp2;
sort(~valeur_port.pa & temp2);
tempo();
tempo();
tempo();
tempo();
tempo();
temp2=~temp2;
}
while (--temp3 );
sort(valeur_port.p=0);
tempo();
tempo();
tempo();
}

calcul_masque()
{
valeur_port.pa= 1<<(xnet.p14-2);
valeur_port.pb= 1<<(xnet.p17-2);
valeur_port.pc= 1<<(xnet.p16-2);
valeur_port.pd= 1<<(xnet.p15-2);
valeur_port.p18=xnet.p14;
valeur_port.p18*=10;
valeur_port.p18+=xnet.p15;
valeur_port.p18*=10;
valeur_port.p18+=xnet.p16;
valeur_port.p18*=10;
valeur_port.p18+=xnet.p17;
}
lecture()
{
valeur_port.p14=lecturemot(valeur_port.p10);
}
ecrit(oper)
unsigned char *oper;
{
unsigned short temp1;
unsigned char temp2,temp3;
unsigned short temp4;
temp1=valeur_port.p12;
temp2=valeur_port.p10;
valeur_port.p10=valeur_port.p10>>2;
valeur_port.p10 +=0x20;
temp4=ecritcode(valeur_port.p10,temp1);
valeur_port.p14=lecturemot(valeur_port.p10);
if (temp1==valeur_port.p14)
if (temp4==0)
valeur_port.p16=1;
if (valeur_port.p16==1)
{
*oper=1;
return;
}
valeur_port.p12=temp1;
valeur_port.p10=temp2;
temp4=ecritcode(valeur_port.p10,temp1);
if (valeur_port.p16!=1)
if (temp4==0)
{
*oper=0x1;
return;
}
*oper=0x0;
}
driver()
{
haltscheduler();
choixnumero();
if (valeur_port.p16==1)
{
valeur_port.p8=0;
goto more3;
}
valeur_port.p14=lecturemot(0x1f);
valeur_port.p8=valeur_port.p14;
more3:
freescheduler();
}

haltscheduler()
{
int temp2;
for (temp2=0;temp2<0x1ffff;++temp2);
}
freescheduler()
{
unsigned int temp1;
}

rnd(number)
unsigned short number;
{
int temp;
temp=valeur_port.p24 & 0x2000;
if (temp!=0)
valeur_port.p24^=0x1;
valeur_port.p24=valeur_port.p24<<1;
if (temp!=0)
valeur_port.p24|=1;
return(valeur_port.p24);
}
dec_adjust()
{
/* not used */
}

Whats going on here. A bunch of mucking around to hide
the real and trivial protection stuff. Also various
ands, ors, and inverts to further hide the data, plus
moving around more than the one data line and clock
line to the eeprom to throw off my Tektronix 7d01/df2
logic analizer. (NOT)


Basically you write to the device driver a "P#####*" to
the device driver and get back an ascii answer.
where the ##### is an ascii number between 0 and 65535



So lets write a simple program which when compiled and
run with the output re-directed to the file "datafile"
generates all 64K possible output answers without having
to really figure out the entire thing.

Thus




/* read data from real microphar dongle */
/* using the real driver and a real key */

#include <stdio.h>
#include <signal.h>
#include <errno.h>

FILE *fps,*fopen();

main()
{
unsigned lvalue,address;

fps=fopen("/dev/mp","r+");

printf("unsigned int data[] = {");

for (address=0; address<=65535; address ++)
        {
        fprintf(fps,"P%d*",address);
        fseek(fps,0L,0);
        fscanf(fps,"%d",&lvalue);
        printf("%d , ",lvalue);
        }
printf("}");
fclose(fps);
}


which generates int data [] = {#,#,#,#.....}


Pretty simple huh. OH, and yes this takes a while
to run.


Now lets hack the origional device driver removing
lots and lots of useless garbage.

Thus



#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/dir.h>
#include <sys/seg.h>
#include <sys/page.h>
#include <sys/user.h>
#include <sys/cmn_err.h>

#include "datafile"

struct valeur_port {
	unsigned char p;
	unsigned short p8;
	unsigned char pa,pb,pc,pd;
	unsigned short p10,p12,p14,p16,p18,p1a,p1c;
	short p1e,p20;
	unsigned short p22;
	short p24;
}valeur_port;

mpopen(dev,flag)
short int dev,flag;
{
	if (valeur_port.p1c & 0x1)
		u.u_error=EBUSY;
	else
	{
		valeur_port.p1c=1;
		valeur_port.p1e=0;
	}
}
mpclose(dev,flag)
short int dev,flag;
{
	valeur_port.p1c=0;
}
mpwrite(dev,flag)
short int dev,flag;
{
	while ((valeur_port.p20 = cpass()) >= 0)
	{
		switch (valeur_port.p1e) {
		case 0 :
			if(valeur_port.p20 == 'P')
			{
				valeur_port.p1e=1;
				valeur_port.p22=0;
			}
			break;
		case 1 :
			w1();

		default:
			break;
		}
	}
}

w1()
{
	unsigned int temp;
	if (valeur_port.p20 != '*')
	{
		if (valeur_port.p20 >= '0')
		{
			if (valeur_port.p20 <= '9')
			{
				valeur_port.p22=valeur_port.p22*10;
				valeur_port.p22=valeur_port.p22+(valeur_port.p20-'0');
				return;
			}
		}
		return;
	}
	else
	{
		/*cmn_err (CE_NOTE,"!%x,",valeur_port.p22);*/
		valeur_port.p1e = 0;
		valeur_port.p8=data[valeur_port.p22];
	}
}
mpread(dev,flag)
short int dev,flag;
{
	unsigned int temp;
	temp=0x2710;   /* 10000 */
	passc(0x30);
	while (temp > 0)
	{
		passc((valeur_port.p8/temp)+'0');
		valeur_port.p8= valeur_port.p8 % temp;
		temp=temp/10;
	}
	passc('\n');
}


Now we compile this thing, call it Driver.o, put it in
the right place in place of their driver, relink and install
the unix kernel, and bye bye dongle.


In addition, all the utilites necessary to break this come as
part of the operating system (use the crippled C compiler or get
GCC)



What we have seen here is that parallel port dongles used on
unix boxes give virtually worthless protection, far easier
than their dos/windows counterparts. They all fail the
freddy the freeloader part of the fiat-shamir zero knowledge test!

And while we are on the subject of fiat-shamir, the biggest failure
on this so far is the DSS-02 smart cards for the DSS receivers in
the USA market. The pirates are making way too much money selling
solutions that do not work for long. Look people it does not make
sense. Why give a pirate $500 for a T or L or Battery card, which
1.5 years later croaks permanantly, just to buy the latest twiddled
H card for another $500, and then buy the shim card for another 500.
Its way cheaper to legally subscribe (Canada is a different story).
This is something that needs to be brought to the grass-roots level.
The latest card is way to easy to twiddle. Lets put the people that
are strictly in this for the money out of buisness. We know who you
are. I personally do not have a DSS, and because i have a REAL dish,
I am not likely to ever get one. (4dtv is another story). And since
I live in the USA, I will NOT get involved in DSS activities...



Soon will come an essay on the Rainbow Technologies Software
sentinel superpro. Which can also be busted numerous ways.
And just slightly harder. I now have a virtually 100 percent working
programable hardware solution. And yes you can read the algorithm
and time keys out of the device.

Also an essay on breaking protection on the EESOF (now hp)
touchstone, montecarlo, linecalc and other high priced spreads.
hint( one cmos 8 bit eprom size >128 bytes, 8 diodes, one cap,
one cmos buss transiever). The hardware solution here was just
too simple to ignore.


Other fun targets. The protection DEC uses on their unix that runs
on the alpha motherboards. This one could be fairly tough.

Other fun targets: serial dongles (like netsentinel). Almost as
easy, you have to create a shim between the program and the real
tty driver...
