+ORC Tutorials


LESSON C (2) - How to crack, Cracking as an art

How to crack, an approach LESSON 1
How to crack, tools and tricks of the trade LESSON 2
How to crack, hands on, paper protections LESSON 3.1  3.2
How to crack, hands on, time limits LESSON 4.1  4.2
How to crack, hands on, disk-CDrom access LESSON 5
How to crack, funny tricks LESSON 6
How to crack, intuition and luck LESSON 7
How to crack windows, an approach LESSON 8.1  8.2
How to crack windows, tools of the trade LESSON 9.1  9.2  9.3  9.4
How to crack, advanced cracking LESSON A
How to crack, zen-cracking LESSON B
How to crack, cracking as an art LESSON C.1  C.2  C.3
How to crack INDEX


cracking Instant Access (2) - strainer for the +HCU

[SEE LESSON C.1 for the first part of this cracking session]
Here follow the relevant protection routines for the first
(The "Registration") number_code of Instant Access, with my
comments: you have to investigate a little the following code.
Later, when you'll crack on your own, try to recognize the
many routines that fiddle with input BEFORE the relevant (real
protection) one. In this case, for instance, a routine checks the
correctness of the numbers of your input:

1B0F:2B00 C45E06 LES BX,[BP+06] ; set/reset pointer
1B0F:2B03 03DF ADD BX,DI
1B0F:2B05 268A07 MOV AL,ES:[BX] ; get number
1B0F:2B08 8846FD MOV [BP-03],AL ; store
1B0F:2B0B 807EFD30 CMP BYTE PTR [BP-03],30
1B0F:2B0F 7C06 JL 2B17 ; less than zero?
1B0F:2B11 807EFD39 CMP BYTE PTR [BP-03],39
1B0F:2B15 7E05 JLE 2B1C ; between 0 & 9?
1B0F:2B17 B80100 MOV AX,0001 ; no, set flag=1
1B0F:2B1A EB02 JMP 2B1E ; keep flag
1B0F:2B1C 33C0 XOR AX,AX ; flag=0
1B0F:2B1E 0BC0 OR AX,AX ; is it zero?
1B0F:2B20 7507 JNZ 2B29 ; flag NO jumps away
1B0F:2B22 8A46FD MOV AL,[BP-03] ; Ok, get number
1B0F:2B25 8842CC MOV [BP+SI-34],AL ; Ok, store number
1B0F:2B28 46 INC SI ; inc storespace
1B0F:2B29 47 INC DI ; inc counter
1B0F:2B2A C45E06 LES BX,[BP+06] ; reset pointer
1B0F:2B2D 03DF ADD BX,DI ; point next number
1B0F:2B2F 26803F00 CMP BYTE PTR ES:[BX],00 ; input end?
1B0F:2B33 75CB JNZ 2B00 ; no:loop next num

You now obviously understand that the "real" string is
stored inside memory location [BP+SI-34]... set a memory
breakpoint on this area to get the next block of code that
fiddles with the transformed input. Notice how this routine
"normalizes" the input, strips the "-" off and puts the 10
numbers together:
user input: 1 2 1 2 1 2 1 2 1 2 End
1E7F:92E2 31 32 31 32 31 32 31 32 31 32 00 45 AF 1F 70 9B
Stack ptr: 0 1 2 3 4 5 6 7 8 9 A B C D E F

Let's now look at the "real" protection routine: the one
that checks these numbers and throw you out if they are not
"sound". Please pay attention to the following block of code:

:4B79 8CD0 MOV AX,SS ; we'll work inside the stack...
:4B7B 90 NOP
:4B7C 45 INC BP
:4B7D 55 PUSH BP ; save real BP
:4B7E 8BEC MOV BP,SP ; BP = stackpointer
:4B80 1E PUSH DS ; save real Datasegment
:4B81 8ED8 MOV DS,AX ; Datasegment = stacksegment
:4B83 83EC04 SUB SP,+04
:4B86 C45E06 LES BX,[BP+06] ; BX points input_start
:4B89 268A07 MOV AL,ES:[BX] ; load first number
:4B8C 98 CBW ; care only for low
:4B8D C45E06 LES BX,[BP+06] ; reset pointer
:4B90 50 PUSH AX ; save 1st number
:4B91 268A4701 MOV AL,ES:[BX+01] ; load 2nd number
:4B95 98 CBW ; only low
:4B96 8BD0 MOV DX,AX ; 2nd number in DX
:4B98 58 POP AX ; get 1st number
:4B99 03C2 ADD AX,DX ; sum with second
:4B9B C45E06 LES BX,[BP+06] ; reset pointer
:4B9E 50 PUSH AX ; save sum
:4B9F 268A4707 MOV AL,ES:[BX+07] ; load 8th number
:4BA3 98 CBW ; only low
:4BA4 8BD0 MOV DX,AX ; 8th number in DX
:4BA6 58 POP AX ; old sum is back
:4BA7 03C2 ADD AX,DX ; sum 1+2+8
:4BA9 C45E06 LES BX,[BP+06] ; reset pointer
:4BAC 50 PUSH AX ; save sum
:4BAD 268A4703 MOV AL,ES:[BX+03] ; load 4rd number
:4BB1 98 CBW ; only low
:4BB2 8BD0 MOV DX,AX ; #4 in DX
:4BB4 58 POP AX ; sum is back
:4BB5 03C2 ADD AX,DX ; sum 1+2+8+4
:4BB7 C45E06 LES BX,[BP+06] ; reset pointer
:4BBA 50 PUSH AX ; save sum
:4BBB 268A4704 MOV AL,ES:[BX+04] ; load 5th number
:4BBF 98 CBW ; only low
:4BC0 8BD0 MOV DX,AX ; #5 in DX
:4BC2 58 POP AX ; sum is back
:4BC3 03C2 ADD AX,DX ; 1+2+8+4+5
:4BC5 C45E06 LES BX,[BP+06] ; reset pointer
:4BC8 50 PUSH AX ; save sum
:4BC9 268A4705 MOV AL,ES:[BX+05] ; load 6th number
:4BCD 98 CBW ; only low

:4BCE 8BD0 MOV DX,AX ; #6 in DX
:4BD0 58 POP AX ; sum is back
:4BD1 03C2 ADD AX,DX ; 1+2+8+4+5+6
:4BD3 C45E06 LES BX,[BP+06] ; reset pointer
:4BD6 50 PUSH AX ; save sum
:4BD7 268A4706 MOV AL,ES:[BX+06] ; load 7th number
:4BDB 98 CBW ; only low
:4BDC 8BD0 MOV DX,AX ; #7 in DX
:4BDE 58 POP AX ; sum is back
:4BDF 03C2 ADD AX,DX ; 1+2+8+4+5+6+7
:4BE1 C45E06 LES BX,[BP+06] ; reset pointer
:4BE4 50 PUSH AX ; save sum
:4BE5 268A4708 MOV AL,ES:[BX+08] ; load 9th number
:4BE9 98 CBW ; only low
:4BEA 8BD0 MOV DX,AX ; #9 in DX
:4BEC 58 POP AX ; sum is back
:4BED 03C2 ADD AX,DX ; 1+2+8+4+5+6+7+9
:4BEF C45E06 LES BX,[BP+06] ; reset pointer
:4BF2 50 PUSH AX ; save sum
:4BF3 268A4709 MOV AL,ES:[BX+09] ; load 10th #
:4BF7 98 CBW ; only low
:4BF8 8BD0 MOV DX,AX ; #10 in DX
:4BFA 58 POP AX ; sum is back
:4BFB 03C2 ADD AX,DX ; 1+2+8+4+5+6+7+9+10
:4BFD 0550FE ADD AX,FE50 ; clean sum to 0-51
:4C00 BB0A00 MOV BX,000A ; BX holds 10
:4C03 99 CWD ; only AL
:4C04 F7FB IDIV BX ; remainder in DX
:4C06 C45E06 LES BX,[BP+06] ; reset pointer
:4C09 268A4702 MOV AL,ES:[BX+02] ; load now # 3
:4C0D 98 CBW ; only low
:4C0E 05D0FF ADD AX,FFD0 ; clean # 3 to 0-9
:4C11 3BD0 CMP DX,AX ; remainder = pampered #3?

:4C13 7407 JZ 4C1C ; yes, go on good guy
:4C15 33D2 XOR DX,DX ; no! beggar off! Zero DX
:4C17 33C0 XOR AX,AX ; and FLAG_AX = FALSE
:4C19 E91701 JMP 4D33 ; go to EXIT
:4C1C C45E06 LES BX,[BP+06] ; reset pointer
:4C1F 268A4701 MOV AL,ES:[BX+01] ; now load #2 anew
:4C23 98 CBW ; only low
:4C24 05D7FF ADD AX,FFD7 ; pamper adding +3
:4C27 A38D5E MOV [5E8D],AX ; save SEC_+3
:4C2A 3D0900 CMP AX,0009 ; was it < 9? (no A-F)
:4C2D 7E05 JLE 4C34 ; ok, no 0xletter
:4C2F 832E8D5E0A SUB WORD PTR [5E8D],+0A ; 0-5 if A-F
:4C34 C45E06 LES BX,[BP+06] ; reset pointer
:4C37 268A07 MOV AL,ES:[BX] ; load 1st input number
:4C3A 98 CBW ; only low
:4C3B 05C9FF ADD AX,FFC9 ; pamper adding +7
:4C3E A38F5E MOV [5E8F],AX ; save it in FIR_+7
:4C41 0BC0 OR AX,AX ; if #1 > 7
:4C43 7D05 JGE 4C4A ; no need to add 0xA
:4C45 83068F5E0A ADD WORD PTR [5E8F],+0A ; FIR_+7 + 0xA

:4C4A C45E0E LES BX,[BP+0E] ; Set pointer to E
:4C4D 26C747020000 MOV WORD PTR ES:[BX+02],0000 ; 0 flag
:4C53 26C7070000 MOV WORD PTR ES:[BX],0000 ; 0 flag
:4C58 C706975E0900 MOV WORD PTR [5E97],0009 ; counter=9
:4C5E E99500 JMP 4CF6 ; Jmp check_counter
:4C61 C45E06 LES BX,[BP+06] ; reset pointer
:4C64 031E975E ADD BX,[5E97] ; add running counter
:4C68 268A07 MOV AL,ES:[BX] ; load # counter+1
:4C6B 98 CBW ; only low
:4C6C 50 PUSH AX ; save 10th number
:4C6D A18D5E MOV AX,[5E8D] ; ld SEC_+3 down_slider
:4C70 BA0A00 MOV DX,000A ; BX holds 0xA
:4C73 F7EA IMUL DX ; SEC_+3 * 0xA
:4C75 03068F5E ADD AX,[5E8F] ; plus FIR_+7 up_slider
:4C79 BAA71E MOV DX,1EA7 ; fixed segment
:4C7C 8BD8 MOV BX,AX ; BX = Lkup_val=(SEC_+3*10+FIR_+7)
:4C7E 8EC2 MOV ES,DX ; ES = 1EA7
:4C80 268A870000 MOV AL,ES:[BX+0000] ; ld 1EA7:[Lkup_val]
:4C85 98 CBW ; only low: KEY_PAR
:4C86 8BD0 MOV DX,AX ; save KEY_PAR in DX
:4C88 58 POP AX ; repops 10th number
:4C89 03C2 ADD AX,DX ; RE_SULT=KEY_PAR+#10
:4C8B 05D0FF ADD AX,FFD0 ; polish RE_SULT
:4C8E 99 CWD ; only low: RE_SULT
:4C8F 8956FC MOV [BP-04],DX ; save here KEY_PAR [9548]
:4C92 8946FA MOV [BP-06],AX ; save here RE_SULT [9546]
:4C95 0BD2 OR DX,DX ; KEY_PAR < 0?
:4C97 7C0F JL 4CA8 ; yes: KEY_PAR < 0
:4C99 7F05 JG 4CA0 ; no: KEY_PAR > 0
:4C9B 3D0900 CMP AX,0009 ; KEY_PAR = 0
:4C9E 7608 JBE 4CA8 ; no pampering if RE_SULT < 9
:4CA0 836EFA0A SUB WORD PTR [BP-06],+0A ; else pamper
:4CA4 835EFC00 SBB WORD PTR [BP-04],+00 ; and SBB [9548]
:4CA8 C45E0E LES BX,[BP+0E] ; reset pointer to E
:4CAB 268B4F02 MOV CX,ES:[BX+02] ; charge CX [958C]
:4CAF 268B1F MOV BX,ES:[BX] ; charge BX slider [958A]
:4CB2 33D2 XOR DX,DX ; clear DX to zero
:4CB4 B80A00 MOV AX,000A ; 10 in AX
:4CB7 9A930D2720 CALL 2027:0D93 ; call following RO_routine

This is the only routine called from our protection, inside the
loop (therefore 8 times), disassembly from WCB. Examining this
code please remember that we entered here with following
configuration: DX=0, AX=0xA, CX=[958C] and BX=[958A]...
1.0D93 56 push si ; save si
1.0D94 96 xchg ax, si ; ax=si, si=0xA
1.0D95 92 xchg ax, dx ; dx=0xA ax=dx
1.0D96 85C0 test ax, ax ; TEST this zero
1.0D98 7402 je 0D9C ; zero only 1st time
1.0D9A F7E3 mul bx ; BX slider! 0/9/5E/3B2...
1.0D9C >E305 jcxz 0DA3 ; cx=0? don't multiply!
1.0D9E 91 xchg ax, cx ; cx !=0? cx = ax & ax = cx
1.0D9F F7E6 mul si ; ax*0xA in ax
1.0DA1 03C1 add ax, cx ; ax= ax*0xA+cx = M_ULT
1.0DA3 >96 xchg ax, si ; ax=0xA; si evtl. holds M_ULT
1.0DA4 F7E3 mul bx ; ax= bx*0xA
1.0DA6 03D6 add dx, si ; dx= dx_add
1.0DA8 5E pop si ; restore si
1.0DA9 CB retf ; back to caller with two parameters: DX and AX

:4CBC C45E0E LES BX,[BP+0E] ; reset pointer
:4CBF 26895702 MOV ES:[BX+02],DX ; save R_DX par [958C]
:4CC3 268907 MOV ES:[BX],AX ; save R_AX par [958A]
:4CC6 0346FA ADD AX,[BP-06] ; add to AX RE_SULT [9546]
:4CC9 1356FC ADC DX,[BP-04] ; add to DX KEY_PAR [9548]
:4CCC C45E0E LES BX,[BP+0E] ; reset pointer
:4CCF 26895702 MOV ES:[BX+02],DX ; save R_DX+KEY_PAR [958C]

:4CD3 268907 MOV ES:[BX],AX ; save R_AX+RE_SULT [958A]
:4CD6 FF0E8D5E DEC WORD PTR [5E8D] ; down_slide SEC_+3
:4CDA 7D05 JGE 4CE1 ; no need to add
:4CDC 83068D5E0A ADD WORD PTR [5E8D],+0A ; pamper adding 10
:4CE1 FF068F5E INC WORD PTR [5E8F] ; up_slide FIR_+7
:4CE5 A18F5E MOV AX,[5E8F] ; save upslided FIR_+7 in AX
:4CE8 3D0900 CMP AX,0009 ; is it over 9?
:4CEB 7E05 JLE 4CF2 ; no, go on
:4CED 832E8F5E0A SUB WORD PTR [5E8F],+0A ; yes, pamper -10
:4CF2 FF0E975E DEC WORD PTR [5E97] ; decrease loop counter
:4CF6 833E975E03 CMP WORD PTR [5E97],+03 ; counter = 3?
:4CFB 7C03 JL 4D00 ; finish if counter under 3
:4CFD E961FF JMP 4C61 ; not yet, loop_next_count
:4D00 C45E06 LES BX,[BP+06] ; reset pointer to input
:4D03 268A4701 MOV AL,ES:[BX+01] ; load 2nd number (2)
:4D07 98 CBW ; only low
:4D08 05D0FF ADD AX,FFD0 ; clean it
:4D0B BA0A00 MOV DX,000A ; DX = 10
:4D0E F7EA IMUL DX ; AX = SEC_*10 = 14
:4D10 C45E06 LES BX,[BP+06] ; reset pointer
:4D13 50 PUSH AX ; save SEC_*10
:4D14 268A07 MOV AL,ES:[BX] ; load 1st number (1)
:4D17 98 CBW ; only low
:4D18 8BD0 MOV DX,AX ; save in DX
:4D1A 58 POP AX ; get SEC_*10
:4D1B 03C2 ADD AX,DX ; sum SEC_*10+1st number
:4D1D 05D0FF ADD AX,FFD0 ; clean it
:4D20 99 CWD ; only low
:4D21 C45E0A LES BX,[BP+0A] ; get pointer to [9582]
:4D24 26895702 MOV ES:[BX+02],DX ; save 1st (1) in [9584]
:4D28 268907 MOV ES:[BX],AX ; save FINAL_SUM (15) [9582]
:4D2B 33D2 XOR DX,DX ; DX = 0
:4D2D B80100 MOV AX,0001 ; FLAG TRUE !
:4D30 E9E6FE JMP 4C19 ; OK, you_are_a_nice_guy
:4D33 59 POP CX ; pop everything and
:4D34 59 POP CX ; return with flag
:4D35 1F POP DS ; AX=TRUE if RegNum OK
:4D36 5D POP BP ; with 1st # in [9584]
:4D37 4D DEC BP ; with FINAL_SUM in [9582]

Let's translate the preceding code: first of all the pointers:
At line :4B86 we have the first of a long list of stack ptrs:
LES BX,[BP+06]
This stack pointer points to the beginning of the input string,
which, once polished from the "-", has now a length of 10 bytes,
concluded by a 00 fence. At the beginning, before the main loop,
9 out of our 10 numbers are added, all but the third one.

Notice that protection has jumped # 3 (and added # 8 out of the
line). The rest is straightforward. Now, at line :4BFD we have
our first "cleaning" instruction. You see: the numbers are
hexadecimal represented by the codes 0x30 to 0x39. If you add
FE50 to the minimum sum you can get adding 9 numbers (0x30*9 =
0x160) You get 0. The maximum you could have adding 9 numbers,
on the contrary is (0x39*9=0x201), which, added to FE50 gives
0x51. So we'll have a "magic" number between 0x0 and 0x51 instead
of a number between 0x160 and 0x201. Protection pampers this
result, and retains only the last ciffer: 0-9. Then protection
divides this number through 0xA, and what happens? DX get's the

If we sum the hexcodes of our (1212-1212-12) we get 0x1BE (we
sum only 9 out of then numbers: the third "1" -i.e. "31"- does
not comes into our count); 0x1BE, cleaned and pampered gives E.
Therefore (0xE/0xA = 1) We get 1 with a remainder of 4.

You may observe that of all possible answers, only sums
finishing with A, B, C, D, E or F give 1 (and rem=0,1,2,3,4 or
5). Sums finishing 0 1 2 3 4 5 6 7 8 or 9 give 0 as result and
themselves as reminder. The chance of getting a 0,1,2,3 or 4 are
therefore bigger as the chance of getting a 5, 6, 7, 8 or 9. We
are just observing... we do not know yet if this should play a
role or not.

Now this remainder is compared at :4C11 with the third number
polished from 0x30-0x39 to 0-9. This is the only protection check
for the registration number input: If your third number does not
match with the remainder of the sum of all the 9 others numbers
of your input you are immediately thrown out with FLAG AX=FALSE
(i.e. zero).

To crack the protection you now have to MODIFY your input string
accordingly. Our new input string will from now on be "1242-1212-
12": we have changed our third number (originally a "2") to a "4"
to get through this first strainer in the correct way. Only now
protection starts its mathematical part (We do not know yet why
it does it... in order to seed the random product number? To
provide a check for the registration number you'll input at the
end? We'll see).
- Protection saves the second number of your input (cleaned
with FFD7) in SEC_+3 [5E8D], pampering it if it is bigger
than 9 (i.e. if it is 0xA-0xF). Here you'll have therefore
following correspondence: 0=7 1=8 2=9 3=0 4=1 5=2 6=3 7=4
8=5 9=6. The second number of your input has got added +3.
This is value SEC_+3. In (lengthy) C it would look like
If (RegString(2)is lower than 7) RegString(2) = RegString(2)+3
Else Regstring(2) = ((RegString(2)-10)+3)
- Protection saves your first number in FIR_+7 [5E8F] with a
different cleaning parameter (FFC9). The next pampering
adds 0xA if it was not 7/8/9 therefore you have here
following correspondence 7=0 8=1 9=2 0=3 1=4 2=5 3=6 4=7
5=8 6=9). This is value FIR_+7. In (lengthy) C it would
look like this:
If (RegString(1) is lower than 3) RegString(1) = RegString(1)+7
Else Regstring(1) = ((RegString(1)-10)+7)

So protection has "transformed" and stored in [5E8D] and [5E8F]
the two numbers 1 and 2. In our RegString: 1242-1212-12 the first
two numbers "12" are now stored as "94". These will be used as
"slider" parameters inside the main loop, as you will see.

Only now does protection begin its main loop, starting from the
LAST number, because the counter has been set to 9 (i.e. the
tenth number of RegString). The loop, as you'll see, handles only
the numbers from 10 to 3: it's an 8-times loop that ends without
handling the first and second number. What happens in this
loop?... Well, quite a lot: Protection begins the loop loading
the number (counter+1) from the RegString. Protection then loads
the SEC_+3 down_slider parameter (which began its life as second
number "transformed"), multiplies it with 0xA and then adds the
up_slider parameter FIR_+7 (at the beginning it was the first
number transformed).

This sum is used as "lookup pointer" to find a parameter
inside a table of parameters in memory, which are all numbers
between 0 and 9. Let's call this value Lkup_val.
Protection looks for data in 1EA7:[Lkup_val]. In our case (we
entered 1242-1212-12, therefore the first SEC_+3 value is 9 and
the first FIR_+7 value is 4): [Lkup_val] = 9*0xA+4; 0x5A+4 =
0x5E. At line :4C80 therefore AL would load the byte at 1EA7:005E
(let's call it KEY_PAR), which now would be ADDED to the #
counter+1 of this loop. In our case KEY_PAR at 1EA7:005E it's a
"7" and is added to the pampered 0x32=2, giving 9.

Let's establish first of all which KEY_PAR can possibly get
fetched: the maximum is 0x63 and the minimum is 0x0. The possible
KEY_PARs do therefore dwell in memory between 1EA7: and
1EA7:0063. Let's have a look at the relative table in memory,
where these KEY_PARs are stored ("our" first 0x5Eth byte is
1EA7:0000 01 03 03 01 09 02 03 00-09 00 04 03 08 07 04 04
1EA7:0010 05 02 09 00 02 04 01 05-06 06 03 02 00 08 05 06
1EA7:0020 08 09 05 00 04 06 07 07-02 00 08 00 06 02 04 07
1EA7:0030 04 04 09 05 09 06 00 06-08 07 00 03 05 09 00 08
1EA7:0040 03 07 07 06 08 09 01 05-07 04 06 01 04 02 07 01
1EA7:0050 03 01 08 01 05 03 03 01-02 08 02 01 06 05 07 02
1EA7:0060 05 09 09 08 02 09 03 00-00 04 05 01 01 03 08 06
1EA7:0070 01 01 09 00 02 05 05 05-01 07 01 05 08 07 01 09
1EA7:0080 08 07 07 04 04 08 03 00-06 01 09 08 08 04 09 09
1EA7:0090 00 07 05 02 03 01 03 08-06 05 07 06 03 07 06 07
1EA7:00A0 04 02 02 05 02 04 06 02-06 09 09 01 05 02 03 04
1EA7:00B0 04 00 03 05 00 03 08 07-06 04 08 08 02 00 03 06
1EA7:00C0 09 00 00 06 09 04 07 02-00 01 01 01 01 00 01 FF
1EA7:00D0 00 FF FF FF FF 00 FF 01-00 00 00 00 00 00 00 00

An interesting table, where all the correspondences are
between 0 and 9... are we getting some "secret" number here? But,
hey, look there... funny, isn't it? Instead of only 0-0x63 bytes
we have roughly the DOUBLE here: 0-0xC8 bytes (the 01 sequence
starting at CA "feels" like a fence). We'll see later how
important this is. At the moment you should only "perceive" that
something must be going on with a table that's two time what she
should be.

As I said the result of KEY_PAR + input number is polished
(with a FFDO) and pampered (subtracting, if necessary, 0xA).
Therefore the result will be the (counter+1) input number +
KEY_PAR (let's call it RE_SULT], in our case, (at the beginning
of the loop) a 9. Now (DX=0 because of the CWD instruction) DX
will be saved in [9548] and RE_SULT in [9546].

Now Protection prepares for the RO_routine: resets its pointer
and charges CX and BX from [958C] and from [958A] respectively,
charges AX with 0xA and sets DX to zero.

The routine performs various operations on AX and DX and saves
the results in the above mentioned locations [958A] and [958C].
Now KEY_PAR and RE_SULT are added respectively to the DX and AX
value we got back from the RO_routine call, and saved once more
in the last two locations: AX+RE_SULT in [958A] and DX+KEY_PAR
in [958C]

Now the value in SEC_+3 is diminished by 1 (if it was 9 it's now
8, if it was zero it will be pampered to 9). It's a "slider"
parameter (in this case a down_slider), typically used in
relatively complicated protections to give a "random" impression
to the casual observer. The value in FIR_+7, on the contrary, is
augmented by one, from 4 to 5... up_sliding also.

Protection now handles the next number of your input for the
loop. In our case this loop uses following protection
configuration with our "sliding" parameters:
Input # pamp_2nd pamp_1st Lookup value KEY_PAR # RE_SULT
# 10 = 2, SEC_+3= 9, FIR_+7= 4, Lkup_val = 0x5E, KEY=7 +2 = 9
# 9 = 1, SEC_+3= 8, FIR_+7= 5, Lkup_val = 0x55, KEY=3 +1 = 4
# 8 = 2, SEC_+3= 7, FIR_+7= 6, Lkup_val = 0x4C, KEY=4 +2 = 6
# 7 = 1, SEC_+3= 6, FIR_+7= 7, Lkup_val = 0x43, KEY=7 +1 = 7
# 6 = 2, SEC_+3= 5, FIR_+7= 8, Lkup_val = 0x3A, KEY=0 +2 = 2
# 5 = 1, SEC_+3= 4, FIR_+7= 9, Lkup_val = 0x31, KEY=4 +1 = 5
# 4 = 2, SEC_+3= 3, FIR_+7= 0, Lkup_val = 0x1E, KEY=5 +2 = 7
# 3 = 4, SEC_+3= 2, FIR_+7= 1, Lkup_val = 0x15, KEY=2 +4 = 5
Notice how our "regular" input 21212124 has given an "irregular"

You may legitimately ask yourself what should all this mean:
what are these RE_SULTs used for? Well they are used to slide
another parameter: this one inside the called routine... this is
what happens to AX and DX inside the routine, and the lines after
the called routine:
:4CBF 26895702 MOV ES:[BX+02],DX ; save R_DX par [958C]
:4CC3 268907 MOV ES:[BX],AX ; save R_AX par [958A]
:4CC6 0346FA ADD AX,[BP-06] ; add to AX RE_SULT [9546]
:4CC9 1356FC ADC DX,[BP-04] ; add to DX KEY_PAR [9548]
:4CCC C45E0E LES BX,[BP+0E] ; reset pointer to E
:4CCF 26895702 MOV ES:[BX+02],DX ; save R_DX+KEY_PAR [958C]
:4CD3 268907 MOV ES:[BX],AX ; save R_AX+RE_SULT [958A]

:4CC6 :4CC9 :4CCF Odd_DX :4CD3 slider_sum
RE_SULT [958A] [958C] [958C] [958A]

0 0 0 0 0
9 5A 0 0 9
4 3AC 0 0 5E
6 24F4 0 0 3B2
7 71CE 1 1 24FB
2 7220 4 E 71D0
5 7572 4 90 7225

Now the loops ends, having handled the input numbers from tenth
to third. Protection loads the second number and multiplies it
by 10 (let's call this result SEC_*10), in our case 2*0xA=14.
Protection loads the first number and adds it to the
multiplication, in our case 1+0x14=0x15 (FINAL_SUM].
Now everything will be added to FFDO to "clean" it.
Pointer will now be set to the end of the input number.
DX, zeroed by CDW, will be saved as parameter in [9584] and the
cleaned and pampered sum will be saved in [9582].
FLAG is set to true and this routine is finished! No parameter
are passed and the only interesting thing is what actually
happens in the locations [9582], [9584], [958A] and [958C], i.e.:
FINAL_SUM, 0, slider_sum, odd_dx.

In the next lesson we'll crack everything, but I'll give you
already some hints here, in case you would like to go ahead on
your own: we'll see how the scheme used for the third (the
registration) number show analogies and differences with the
scheme we have studied (and cracked) here for the first number.
Our 3434-3434-3434-3434-34 input string for the registration
number will be transformed in the magic string
141593384841547431, but this will not work because the "magic"
12th number: "1" will not correspond to the remainder calculated
inside this check through the previous locations of the other

Here the things are more complicated because every little
change in your input string transforms COMPLETELY the "magic"
string... therefore in order to pass the strainer you'll have to
change 3434-3434-3434-3434-34 in (for instance) 7434-3434-3434-
3434-96. The "magic" string 219702960974498056 that this
registration input gives will go through the protection strainer.
Only then we'll be able to step over and finally crack the whole
protection... it's a pretty complicated one as I said. Now crack
it pupils... you have three months time. From this crack depends
your admission to the Uni, there will be no other admission text
till summer 1997 (it's a hell of work to prepare this crap)...
work well.

Well, that's it for this lesson, reader. Not all lessons of my
tutorial are on the Web.

You 'll obtain the missing lessons IF AND ONLY IF you mail
me back (via anon.penet.fi) some tricks of the trade I may not
know but YOU've discovered. I'll probably know most of them
already, but if they are really new you'll be given full credit,
and even if they are not, should I judge that you "rediscovered"
them with your work, or that you actually did good work on them,
I'll send you the remaining lessons nevertheless. Your
suggestions and critics on the whole crap I wrote are also
+ORC an526164@anon.penet.fi

+ORC contents