Automotive Forums .com - the leading automotive community online! Automotive Forums .com - the leading automotive community online!
Automotive Forums .com - the leading automotive community online! 
-
Latest | 0 Rplys
Go Back   Automotive Forums .com Car Chat > Chevrolet > General Discussion
Reply Show Printable Version Show Printable Version | Email this Page Email this Page | Subscription Subscribe to this Thread
 
Thread Tools
Old 11-10-2004, 02:11 PM   #31
fordtrukman
AF Newbie
 
Join Date: Nov 2004
Location: savannah, Georgia
Posts: 1
Thanks: 0
Thanked 0 Times in 0 Posts
ez link scanner seed codes

Hey guys,
New to the forum. I've had my EZ link now for about 4 years, upgraded it once for gm. Cost me 50 bucks for the cable (the code was free) yeah right.....
Any way I've been trying to find someway to hack this seed code thing ever since and the one guy was right....you'll wear out the damn buttons! I've since lost the the code they gave me for the upgrade but I do remember a little. Heres what I know:

Went to upgrade GM
serial- 002E87CD
SEED CODE-87C0
authorization CODE GIVEN- CD-- cannot remember last two although they were numbers not letters.

There is an pattern to the numbers.
seed code for following
chrysler-87D0
OBD-II-87D1
GM ENHANCED-87D7
FORD ENHANCED-87D8
CHRYSLER ENHANCED-87D9

From what I know of this kind of encryption , this is called HASP coded protection. Check this OUT----

Service: Call: Return:
ISHASP BH <- 1, BL <- LPT no. AX = 0 (no HASP) or 1 (HASP found)

HASPCODE BH <- 2, BL <- LPT no. AX = return code 1
AX <- seed code BX = return code 2
CX <- password1 CX = return code 3
DX <- password2 DX = return code 4

HASPSTATUS BH <- 5, BL <- LPT no. AX = memory size
CX <- password1 BX = HASP type
DX <- password2 CX = actual LPT no.
With the exception of HASPCODE, the principles of operation of the above services are easy to understand and hence emulate in a fairly obvious and straightforward way. This is also true of the extended services. It is therefore no surprise that the HASPCODE service is the foundation upon which almost all of HASP's security is based, and it is the operation of just this service that will be discussed in detail here.
3. HASP Security Measures.
Before dealing with the low-level details of HASPCODE, a little needs to be said about some of the measures taken by AKS to enhance software security and protection. The three most important of these are virus/tampering detection, the Pattern Code Security (PCS) facility and the HASP envelope.
Virus/tampering detection is a relatively standard cyclical redundancy check (CRC) run on (a portion of) the executable's disc file image. The CRC signature(s) must match a set of hard-coded ones. The routine that performs this check can be found and disabled in fairly short order by comparing program flow between successful and failed CRCs.
Pattern Code Security is a facility which updates up to 25 instances of a predefined HASP data structure with a single call to the HASP routine. These structures are static and can live anywhere in the executable's memory space (provided the memory is writable). The exact layout of a PCS structure is defined in the HASP developer kit, but it includes an ANSI string signature ('$HASP$PCS'), the service, the service parameters and space for the return values. All of the PCS structures can be found by finding the PCS masterlist. This is a structure with the ANSI string signature '@SCP@PSAH', followed by the number of PCS entries (word) and a list of 32 bit offsets to these structures. Note that this masterlist will not be found in the disc image of the executable if the HASP envelope was used to protect the program since it is encrypted. Once again, given the address of the PCS masterlist, PCS emulation is not especially difficult.
Finally, and probably most importantly, the HASP envelope provides software security that is "virtually impossible to crack," according to AKS's blurb. The basic idea is to encrypt most of the executable using the HASPCODE service to obtain an encryption key and to decrypt the program once it is loaded into memory, provided the proper HASP key is attached to the computer. The idea itself seems to make sense and appears to be logically sound until one subjects it to closer scrutiny. As will be described later, the HASP envelope divulges two vital pieces of information about the HASP key, one of which will be instrumental in emulating the key in software. The only really important aspects here are that (a) the HASP routine usually occurs twice in the executable - once for the envelope and again for the executable itself, and (b) the breakpoints for intercepting HASP calls can only be set after the decryption routine(s) are complete.
It should be noted that the envelope can be applied to an executable more than once. In this case a separate HASP routine exists for each envelope, but this does not make the program significantly more secure because the en/decryption scheme is the same for each envelope with only the crypto keys being different. As we shall see, getting these keys is a cinch once the outermost envelope is penetrated.
All of the cryptography routines used by AKS in the envelope that I have seen are 64 bit block ciphers - i.e. a 64 bit mask is generated and the target bytes are XORed to en/decrypt them. Nothing new there. The tricky bit is finding the first 64 bit block, which is got from the HASP key via the HASPCODE service. The envelope, like all Win32 applications, is bound by the conventions of good behaviour, and that presents perhaps the biggest chink in AKS's armour - the HASP envelope mustn't raise any exceptions or faults.
The decryption routine usually proceeds as follows:
1. A preliminary decryption routine, which does not depend on any HASP key data, is run on a block of memory to decrypt the envelope's HASP and main decryption routines.
2. The decrypted HASP routine is called with the ISHASP service to check whether any HASP key is attached to the computer.
3. If a HASP key is attached, the routine is then called with the HASPCODE service and a fixed seed code.
4. The HASPCODE return values are then compared directly with a set of expected values hard-coded into the envelope. No attempt is made to hide, encrypt or spread this comparison.
5. If the return values agree with the expected ones, the envelope calls the HASP routine again, requesting the HASPCODE service with a different seed code. The return values are used to prepare a 64 bit decryption key. The envelope then performs the main decryption and thereafter passes control to the executable.
It is at steps (3) and (4) that a great deal of valuable information is gleaned. By checking that the correct key is attached, the envelope divulges the two 16 bit passwords for the dongle and, more importantly, a complete set of return codes for a specific seed code. Later I will show how the latter fact reduces HASP's cipher strength from 64 bits to between 20 and 25 bits, putting it well within the limits of a brute-force attack. To be completely accurate, the cipher strength is actually only 16 bits because the seed code is only 16 bits - i.e. there are at most 65536 (= 2^16) unique 64 bit return code sequences. Thus, the envelope "protection," as implemented by AKS, does quite the opposite of what it was intended to do.
Before analysing the cryptography aspect further we need to know how the HASPCODE routine works.
4. The HASPCODE Algorithm.
The algorithm by which the HASP key generates return codes is surprisingly simple - a linear-congruential pseudo-random number generator is used to obtain a 6 bit (0 to 63) offset into a static 64 bit lookup table, and the return code is generated one bit at a time. The following assembly listing shows the complete ISHASP and HASPCODE emulation routine for a 32 bit environment.

80 FF 01 cmp bh,1 ; ISHASP requested?
75 05 jne @CheckTwo ; jump if not
31 C0 xor eax,eax ; else...
40 inc eax ; ...return eax = 1...
EB 67 jmp @EndHProc ; ...and exit
@CheckTwo:
80 FF 02 cmp bh,2 ; HASPCODE requested?
74 0A je @MakeCode ; jump if so
EB 60 jmp @EndHProc ; else do nothing
@HASPData:
db x0 x1 x2 x3 x4 x5 x6 x7 ; HASP static data
@MakeCode:
31 C9 xor ecx,ecx ; ecx <- 0 bit offs
31 DB xor ebx,ebx ; ebx <- 0 byte offs
E8 00 00 00 00 call @NextInst ; push next addr...
@NextInst:
5E pop esi ; ...and save to esi
@NextHBit:
53 push ebx ; save byte counter
66 BB 89 19 mov bx,1989h
F7 E3 mul eax,ebx
83 C0 05 add eax,5 ; seed <- seed*1989h+5
0F B7 C0 movzx eax,ax ; seed <- seed and FFFFh
50 push eax ; save seed
C1 E8 09 shr eax,9
80 E0 3F and al,3Fh ; al <- bit offset
89 C3 mov ebx,eax
C1 EB 03 shr ebx,3 ; ebx <- byte no.
80 E0 07 and al,7 ; al <- bit no.
51 push ecx ; save current code
88 C1 mov cl,al
B0 01 mov al,1
D2 E0 shl al,cl ; al <- bit mask
84 44 33 EF test byte ptr [ebx+esi-11h],al ; is bit set?
B0 00 mov al,0 ; assume not so
74 01 je @BitClear ; jump if clear
40 inc eax ; else set bit
@BitClear:
59 pop ecx ; ecx <- code string
D0 E5 shl ch,1 ; move to next bit
08 C5 or ch,al ; add latest bit
58 pop eax ; eax <- seed
5B pop ebx ; ebx <- byte count
41 inc ecx ; inc bit count
80 F9 08 cmp cl,8 ; 8 bits done?
75 C7 jne @NextHBit ; jump if not
F6 C3 01 test bl,1 ; byte count odd?
74 03 je @EvenByte ; jump if so
5A pop edx ; get ret code
88 F1 mov cl,dh ; add upper 8 bits
@EvenByte:
51 push ecx ; save ret code
31 C9 xor ecx,ecx ; clear code string
43 inc ebx ; inc byte count
80 FB 08 cmp bl,8 ; 8 bytes done?
75 B6 jne @NextHBit ; jump if not
5A pop edx ; edx <- ret code 4
59 pop ecx ; ecx <- ret code 3
5B pop ebx ; ebx <- ret code 2
58 pop eax ; eax <- ret code 1
@EndHProc:
C3 ret ; return to caller
The above code is tailored to be space efficient and to work regardless of where in memory it is loaded so that there is no need for any further address checking. It follows the machine code calling protocol for the ISHASP and HASPCODE services, as outlined earlier. Also, the code bytes are shown to reflect the actual size (114 bytes) and the signature of the emulation routine on an 80386 or later.
The only difficulty in applying the above to a given HASP key lies in finding the 64 bits of static lookup data (the 8 bytes appearing as x0 to x7 in the above listing). This is where the information previously collected from the envelope is very useful, and leads us into the next section.
5. Reconstructing HASP's Static Table.
The basic idea behind reconstructing the 64 bits of static HASP data is to recreate the sequence of pseudo-random bit offsets for the known seed code and extracting the corresponding bits in the known return codes, so building, at least in part, the lookup table for the dongle.
The procedure is as follows - given Seed and RetCode_1 to RetCode_4:
(1) write the four 16 bit return codes in order as a sequential bit string of 64 0s and 1s, not forgetting about byte reversals on 8086 processors, i.e. as LSB(RetCode_1) MSB(RetCode_1) LSB(RetCode_2) ... LSB(RetCode_4) MSB(RetCode_4)
(2) Construct a 64 character string of '?' - this will become the HASP static table bit string
(3) Let CurrentBitNo <- 0
(4) Let Seed <- (Seed*0x1989h+0x5h) and 0xFFFFh and BitOffs <- (Seed shr 0x9h) and 0x3Fh
(5) Let BitOffs <- 7+((BitOffs shl 1) and 0x70h)-BitOffs
(6) Get bit no. CurrentBitNo (from left to right, zero-based) from the bit string of the return codes in (1)
(7) Set the character in the string under (2) at position BitOffs (left to right, zero-based) to '0' or '1', in accordance with the state of the bit referred to in (6)
(8) CurrentBitNo <- CurrentBitNo+1
(9) if (CurrentBitNo < 64) then go to step (4) else stop
Once finished, the above routine yields a string with 0s, 1s and ?s. The ?s are those bits which were not referenced by the seed code, while the other bits are as they appear in the HASP key's static table in the correct order, viz. x0 x1 ... x7, when subdivided into groups of eight bits.
Generally, a single set of HASPCODE data composed of a seed code plus four return codes will reveal between 39 and 44 bits, leaving 20 to 25 unknown. A different seed code for which the return codes are unknown will reference between 14 and 19 of the unknown bits. At worst, these bits can be found by a brute-force search. However, reasonable guesses can be made as to the state of these bits because the static data exhibits some patterns and cycles that only become evident when it is represented as an eight-by-eight bit matrix. As an example, the bit matrix for the Memo/Time/NetHASP DEMOMA demo key has the following bit matrix:
x0 = 0 0 1 1 1 0 0 1 = 39h
x1 = 0 1 1 0 0 0 0 1 = 61h
x2 = 0 0 1 1 1 0 1 1 = 3Bh
x3 = 0 1 1 0 0 0 1 1 = 63h
x4 = 1 0 0 1 1 1 0 1 = 9Dh
x5 = 1 1 0 0 0 1 0 1 = C5h
x6 = 1 0 0 1 1 1 1 1 = 9Fh
x7 = 1 1 0 0 0 1 1 1 = C7h
Now suppose that we do not know this HASP key's static table, but that we have learned that for the seed code 11073 (decimal) this HASP key returns the code set
RetCode_1 = 14990 = 3A8Eh
RetCode_2 = 23754 = 5CCAh
RetCode_3 = 43929 = AB99h
RetCode_4 = 45507 = B1C3h
Upon applying the procedure given above, we obtain
[ 8E ] [ 3A ] [ CA ] [ 5C ] [ 99 ] [ AB ] [ C3 ] [ B1 ]
BitString(1) = "10001110 00111010 11001010 01011100 10011001 10101011 11000011 10110001
"OutString(9) = "00111001 01?000?? ?0?????1 0?1?0??1 1??1110? 1?0?0?01 100?11?? 1?000?11"
whence
x0 = 0 0 1 1 1 0 0 1 = 39h
x1 = 0 1 ? 0 0 0 ? ? = 40h, 41h, 42h, 43h, 60h, 61h, 62h or 63h
x2 = ? 0 ? ? ? ? ? 1 = (one of 64 possibilities)
x3 = 0 ? 1 ? 0 ? ? 1 = (one of 16 possibilities)
x4 = 1 ? ? 1 1 1 0 ? = 9Ch, 9Dh, BCh, BDh, DCh, DDh, FCh or FDh
x5 = 1 ? 0 ? 0 ? 0 1 = 81h, 85h, 91h, 95h, C1h, C5h, D1h or D5h
x6 = 1 0 0 ? 1 1 ? ? = 8Ch, 8Dh, 8Eh, 8Fh, 9Ch, 9Dh, 9Eh or 9Fh
x7 = 1 ? 0 0 0 ? 1 1 = 83h, 87h, C3h or C7h
In this case 24 bits remain indeterminate, but inferring bit values based on assumed patterns reduces this number. For example, the first column has one unknown bit in the third row and the bits appear to be composed of two groups of four. It is therefore reasonable to suppose that the unknown bit is a 0. Similarly, the unknown bit in column 5, row 3 is probably a 1, and the three unknown bits in column 8 are all likely to be 1s. Continuing in this way, other patterns can be sought and built upon to yield a few of the most likely bit matrices, each of which can then be tested either against the HASP envelope or against other HASPCODE requests until the correct candidate is found.
Having picked out the bit patterns for seven different HASP keys (the bit patterns and serial/batch numbers of which I will not divulge here for obvious reasons), it is safe, and indeed profitable, to infer the unknown bits of other keys, using the above example as a rough guide. One should look mainly for horizontal and vertical cycles and patterns, but diagonals can also prove useful, especially if the entire bit matrix is copied immediately to the right as well as below itself. Similar groups of two and four bits recur in all sorts of fairly self-evident ways.
With some trial-and-error, the remaining bits can normally be found in fewer than about 30 attempts.
Does AKS's claim of unsurpassed security still hold water in light of the above? The answer is, I think, quite obvious.
6. Completing the HASP Emulation.
With the knowledge that it is possible to construct a short piece of code that performs exactly the same function as the ISHASP and HASPCODE calls to the HASP routine, emulating the remaining services is almost trivial. Routines that do what the READMEMO, WRITEMEMO, HASPSTATUS, HASPID, READBLOCK and WRITEBLOCK services do can be coded in a straightforward way, making use of the main program's code space to read/write HASP "memory," if necessary. Of course, this is only required if the main program actually makes any of these calls to the HASP routine. Some difficulty may be experienced in the case where a TimeHASP is used and its on-board clock is accessed, but even this can be overcome by using the GetSystemTime() Win32 API function, if required.
Once a complete list exists of the HASP services a given program makes use of, a complete HASP emulation routine can be cobbled together without too much difficulty from the various bits and pieces. If the program makes use of PCS (see Section 3.), the emulation routine can be called from a loop which loads, executes and saves the PCS specifications sequentially from the data in the PCS masterlist and the PCS structures this list identifies.
By reversing the relevant decryption routines and adjusting any CRC or checksum routines, an executable can be patched so as to not require an actual HASP key. This can generally be achieved with a total patch size not exceeding 1kB. This may sound like a lot, but pales in comparison to the 32 bit HASP routine which is around 70kB in size - another victim of code bloat?
In some versions the block cipher mask depends on the en/decrypted bytes of the preceding en/decrypted block. This is a further safeguard against patching but is not particularly effective since the blocks immediately before and/or after the patch can be padded with adjusting bytes to ensure that other code and data are decrypted correctly.
7. Conclusions.
This article has described the low-level operation of Aladdin Knowledge System's HASP dongles in some detail. Particular emphasis was placed on the HASPCODE service due to its pivotal role in the HASP security architecture. A technique and some tips for expediting the determination of the static 64 bit lookup table which fully defines the HASPCODE response for any HASP key were presented.
The information contained in this article is accurate to the best of the author's knowledge, and appertains to the HASP-3, MemoHASP, TimeHASP and NetHASP keys. At the time of writing, it was not yet clear whether it remains applicable to AKS's latest generation of dongles, the HASP4. Investigations into this will be initiated in the near future and reported on as soon as some light is shed on the matter. In this context, it is worth noting that AKS have once again applied that annoyingly presumptuous phrase, "impossible to crack." We shall see.
On the assumption that you, the reader, have at least an intermediate-level grasp of (32 bit) assembly language, you now have the basic knowledge and tools to understand fully the operation of a HASP dongle. How you use this information is, of course, entirely up to you.



Hope this helps....

Last edited by fordtrukman; 11-10-2004 at 09:34 PM.
fordtrukman is offline   Reply With Quote
Old 11-12-2004, 02:20 PM   #32
bcs
AF Newbie
 
Join Date: Aug 2004
Location: A City, Alabama
Posts: 7
Thanks: 0
Thanked 0 Times in 0 Posts
wow! If I knew assembly there would be a chance. This is way over my head. CoRE group where are you?
bcs is offline   Reply With Quote
Old 01-29-2005, 09:07 PM   #33
grapony
AF Newbie
 
Join Date: Jul 2003
Location: Russellville, Alabama
Posts: 11
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via AIM to grapony
Re: AutoXray Enhanced function upgrade?

Quote:
Originally Posted by bcs
wow! If I knew assembly there would be a chance. This is way over my head. CoRE group where are you?
So has anybody had any luck cracking the code?
grapony is offline   Reply With Quote
Old 03-08-2005, 02:51 PM   #34
yankee0304
AF Newbie
 
Join Date: Aug 2004
Location: Downers Grove, Illinois
Posts: 4
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via AIM to yankee0304
Nobody has cracked the code that I know of...The guy I sent my info to has not gotten back to me with the info regarding if he has been able to crack the code.

What I do know is that the 4 digits for the upgrade remain the same, but the screen with the unit ID (not the serial number) and the other info in the menu for user upgrades changes every time an upgrade is performed, thus making it a giant PITA to figure things out. I only know this much since I have done two or three upgrades to my scanner and that's how things worked out.
yankee0304 is offline   Reply With Quote
Old 03-09-2005, 02:18 PM   #35
greasyhog
AF Premium User
 
Join Date: Sep 2004
Location: ohio, Ohio
Posts: 146
Thanks: 0
Thanked 2 Times in 2 Posts
i found a guy willing to help crack but he needs the serial number, seed, 4 digit code and whatever other numbers you can provide.
greasyhog is offline   Reply With Quote
Old 03-09-2005, 03:49 PM   #36
greasyhog
AF Premium User
 
Join Date: Sep 2004
Location: ohio, Ohio
Posts: 146
Thanks: 0
Thanked 2 Times in 2 Posts
Re: AutoXray Enhanced function upgrade?

is it possible to use lc4 on the scan tools if so how
greasyhog is offline   Reply With Quote
Old 04-14-2005, 02:05 AM   #37
Sissle
AF Newbie
 
Join Date: Apr 2005
Location: Tīrana
Posts: 1
Thanks: 0
Thanked 0 Times in 0 Posts
Post

Has anybody made progress yet ...?

Please mail me combinations of serial, seed, and code so I can try to figure out ...
Sissle is offline   Reply With Quote
Old 01-24-2006, 09:13 PM   #38
jeffintexas
AF Newbie
 
Join Date: Jan 2006
Location: New Braunfels, Texas
Posts: 1
Thanks: 0
Thanked 0 Times in 0 Posts
Re: AutoXray Enhanced function upgrade?

Ok I realy dont care if they do read this post. I have an autoxray ezread 2000. My unit serial# is01CA86C2. I upgraded OBD1 scan and the seed number was86D2 and the code they gave me was EC31. I hope this helps anyone in cracking this considering the money it cost me. I can not afford anymore upgrade costs.
jeffintexas is offline   Reply With Quote
Old 11-07-2006, 09:21 PM   #39
greasyhog
AF Premium User
 
Join Date: Sep 2004
Location: ohio, Ohio
Posts: 146
Thanks: 0
Thanked 2 Times in 2 Posts
Re: AutoXray Enhanced function upgrade?

anyone making any progress on this or is this pretty much dead?
greasyhog is offline   Reply With Quote
Old 12-12-2007, 08:34 PM   #40
sledhead26
AF Newbie
 
Join Date: Dec 2007
Location: duluth, Minnesota
Posts: 1
Thanks: 0
Thanked 0 Times in 0 Posts
Re: AutoXray Enhanced function upgrade?

Does anyone have codes to unlock a auto xray chrysler ezlink
sledhead26 is offline   Reply With Quote
Old 02-06-2008, 10:21 AM   #41
kevdogg91
AF Newbie
 
Join Date: Feb 2008
Location: columbus, Ohio
Posts: 1
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via Yahoo to kevdogg91
Re: AutoXray Enhanced function upgrade?

dose anyone have EZ-PC AX500
that thay can copy for me ?

my email is kevdogg91@gmail.com

thank you
kevin
kevdogg91 is offline   Reply With Quote
Old 08-19-2008, 09:28 AM   #42
Route6t6
AF Newbie
 
Join Date: Feb 2004
Location: Indianapolis, Indiana
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Re: AutoXray Enhanced function upgrade?

Wow! I can't believe this thread is still alive - at least as of Feb. this year. I'm still interested in cracking this thing if it can happen. Are there any other more recent threads on the subject n these forums presuming anyone is still reading this.
Route6t6 is offline   Reply With Quote
Old 11-25-2010, 08:07 AM   #43
sc6lou
AF Newbie
 
Join Date: Nov 2010
Location: shirley, New York
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
Re: AutoXray Enhanced function upgrade?

Were you able to find the pinouts? I have tried and as of yet have not. I need the serial cable pinout. If so you can email me at itsafree1@netzero.net. thanks in advance and if I find it I will send to you.
sc6lou is offline   Reply With Quote
Old 11-25-2010, 08:09 AM   #44
sc6lou
AF Newbie
 
Join Date: Nov 2010
Location: shirley, New York
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
Re: AutoXray Enhanced function upgrade?

Hi did you get the software? I am trying to get the software and the cable pinout. Could I ask if you have the cable pinout and software? thanks Leo itsafree1@netzero.net
sc6lou is offline   Reply With Quote
Old 01-20-2013, 07:46 PM   #45
bcs
AF Newbie
 
Join Date: Aug 2004
Location: A City, Alabama
Posts: 7
Thanks: 0
Thanked 0 Times in 0 Posts
Re: AutoXray Enhanced function upgrade?

I solved this ON MY UNIT a while ago using a computerized brute force attack. Takes maximum a week per code.

I went out and bought a $25 ELM327 OBDII adapter and it is FAR more capable than the autoxray ever was. I would never recommend anybody buy an autoxray again.
bcs is offline   Reply With Quote
 
Reply

POST REPLY TO THIS THREAD

Go Back   Automotive Forums .com Car Chat > Chevrolet > General Discussion

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



All times are GMT -5. The time now is 04:01 AM.

Community Participation Guidelines | How to use your User Control Panel

Powered by: vBulletin | Copyright Jelsoft Enterprises Ltd.
 
 
no new posts