494 lines
26 KiB
Plaintext
494 lines
26 KiB
Plaintext
![]() |
HOW TO CRACK, by +ORC, A TUTORIAL
|
|||
|
LESSON C (2) - How to crack, Cracking as an art
|
|||
|
[INSTANT ACCESS]
|
|||
|
|
|||
|
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:
|
|||
|
|
|||
|
This_loop_checks_that_numbers_are_numbers:
|
|||
|
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:
|
|||
|
|
|||
|
check_if_sum_other_9_numbers_=_remainder_of_the_third_number:
|
|||
|
: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
|
|||
|
let's_go_on_if_first_check_passed:
|
|||
|
: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
|
|||
|
now_we_have_the_sliders_let's_prepare_for_loop:
|
|||
|
|
|||
|
: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
|
|||
|
loop_8_times:
|
|||
|
: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
|
|||
|
Back_to_main_protection_loop_from_RO_routine:
|
|||
|
: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
|
|||
|
check_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
|
|||
|
loop_is_ended:
|
|||
|
: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
|
|||
|
EXIT:
|
|||
|
: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]
|
|||
|
:4D38 CB RETF
|
|||
|
|
|||
|
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
|
|||
|
REMAINDER of it.
|
|||
|
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
|
|||
|
this:
|
|||
|
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
|
|||
|
underlined):
|
|||
|
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"
|
|||
|
94672575.
|
|||
|
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
|
|||
|
7579
|
|||
|
|
|||
|
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
|
|||
|
checks.
|
|||
|
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
|
|||
|
welcomed.
|
|||
|
|
|||
|
|
|||
|
+ORC an526164@anon.penet.fi
|