;----------------------------------------------------------------------------- ; 100% keygenerator for CDR-Win 3.1e/DAO 16/32 3.1e ; coded 06/26/97 ; full credits go to the guy who localised the hidden check. ; do_what_you_want_with_it_source ; compile : tasm dao31e-k ; tlink /t /3 dao31e-k ;----------------------------------------------------------------------------- ; some notes on the registration check by the guy mentioned above ;) ; ; the registration algorithm works this way: ; 1.1 compute 32 bit hash value from user name (must be at least 6 chars) ; 1.2 swap upper and lower 16 bits of this hash value ; 1.3 compute 32 bit hash value from company name (must be at least 6 chars) ; 1.4 XOR 32 bit numbers computed in step.1.2 and step.1.3 with each other ; 1.5 convert registration number (which is an ascii string) to a 32 bit number ; by calling atol() (this is a C library function) ; 1.6 compare 24 bits of the two numbers we got in step.1.4 and step.1.5 ; 1.7 if they match (in those 24 bit positions) then accept registration ; ; wow, now we know how to compute the registration number! ; ; well, at least this is what all crackers thought when they were tracing ; through the registration code. it seems that nobody had a second thought ; about the fact that only 24 bits were checked/compared, 8 bits were simply ; ignored, thus making key generators where those 8 bits were chosen randomly ; (usually taken from the number computed in step.1.4) ; ; however, later people noticed that certain types of CDs were fucked up ; 'cos apparently some random data was written into some sectors (which ; made the CD unusable). rumour went around stating that there was a second ; or hidden check in the program. ; ; well, as you probably found out by now this hidden check does exist indeed! ; to find it i used IDA, probably one of the best disassemblers ever made. ; (i didn't use WINICE at all) ; ; this is how i approached the problem: first, i had to find out what those ; 24 bits were that were checked during registration. this was easy, however ; i'd like to point out that the checking algorithm is extremly lame (well, ; maybe it was intentional to confuse the crackers) 'cos instead of using ; a bitmask to mask out the uninteresting 8 bits and then doing a simple cmp, ; the author wrote a for loop in which he compared 24 of 32 bits, one by one. ; ; the bits to be compared were determined by a table of 32 entries, where each ; entry (a 0 or 1) corresponded to a bit position, namely a 1 caused a bit ; position to be compared (i.e. there are 24 1s and 8 0s in that table). ; btw, the effective bitmask in the comparison is 0xFB39DEBF. ; ; after learning this, i assumed that in the hidden check only the remaining ; 8 bits were checked, either by using the table of 1s and 0s mentioned above ; (which was not the case, as IDA didn't find any more references to it) or ; by using the more effective masking method (which turned out to be the case). ; ; now, the masking method basically could have been implemented in two ways: ; either using 0xFB39DEBF or its binary complement 0x04C62140. a quick search ; revealed that the first value was used, as it was stored in the data area ; of the program (you can find it in the executable if you search for it). ; now, all i had to do was to find the code that referenced this value (which ; IDA did for me automatically) and see how it was used. ; ; so, the hidden check works this way: ; ; 2.1 read 0xFB39DEBF from the data area and then NOT it ; 2.2 AND the registration number and the mask computed in step.2.1 ; 2.3 substract 0x822040 from the number computed in step.2.2 ; 2.4 if the result is not zero, then set a variable to 0x930 in the data area ; 2.5 later, during burning, check whether that variable mentioned in the ; previous step is equal to 0x930 or not. if it is then write some random ; data into the current sector being written ; ; so, all we have to make sure is that the substraction done in step.2.3 will ; result in 0 (in this case that memory variable will contain a 0 by default). ; i guess, from this point on it is very trivial what those 8 bits have to be ; set to. if not then have a look at the code below ;). ;----------------------------------------------------------------------------- .model tiny .code .386 org 100h main: push cs pop ds mov dx,offset(presentation) mov ah,09h int 21h mov ah,0ah mov dx,offset(keyb_buffer) int 21h cmp byte ptr [string_length],06h jb not_enough_char_4_name mov esi,offset(DAO_REG_TABLE) call magic_num_calc push eax mov esi,offset(CDRWIN_REG_TABLE) call magic_num_calc rol eax,10h mov dword ptr [offset(magic1)],eax mov dx,offset(ask_company) mov ah,09h int 21h mov ah,0ah mov dx,offset(keyb_buffer) int 21h cmp byte ptr [string_length],06h jb not_enough_char_4_company mov esi,offset(DAO_REG_TABLE) call magic_num_calc push eax mov esi,offset(CDRWIN_REG_TABLE) call magic_num_calc xor eax,dword ptr [offset(magic1)] ;------------Fixing Calculated key in order to make it real----------- and eax,0FB39DEBFh or eax,00822040h ;--------------------------------------------------------------------- mov bx,offset(CDRWIN31eRegCode)+9 call hex2dec mov ah,09h mov dx,offset(HereisCDRWIN31e) int 21h pop ebx pop eax rol eax,10h xor eax,ebx ;------------Fixing Calculated key in order to make it real----------- and eax,0FB39DEBFh or eax,00822040h ;--------------------------------------------------------------------- mov bx,offset(DAO31eRegCode)+9 call hex2dec mov ah,09h mov dx,offset(HereisDAO31e) int 21h prog_ends_ok: mov ax,4C00h int 21h hex2dec proc near mov ecx,000ah xor edx,edx div ecx add dl,30h mov [BX],dl dec bx or eax,eax jnz hex2dec ret endp not_enough_char_4_name: mov dx,offset(toofew4name) jmp print_error not_enough_char_4_company: mov dx,offset(toofew4cie) print_error: mov ah,09h int 21h mov ax,4Cffh int 21h MAGIC_NUM_CALC PROC NEAR xor eax,eax mov ebx,offset(string) push word ptr string_length keep_calc: xor edx,edx mov DL,[EBX] INC EBX mov ECX,EDX shr edx,02h xor ecx,eax shr eax,04h and ecx,0000000fh mov ecx,[ecx*4+esi] xor ecx,eax lea eax,[ecx*4+0] shr ecx,04h xor eax,edx and eax,3ch mov eax,[esi+eax] xor eax,ecx DEC byte ptr [offset(string_length)] jnz keep_calc pop word ptr string_length ret endp CDRWIN_REG_TABLE: DB 000h,000h,000h,000h,07Eh,088h,03Eh,01Ch,0FCh,010h,07Dh,038h,082h,098h,043h,024h DB 0F8h,021h,0FAh,070h,086h,0A9h,0C4h,06Ch,004h,031h,087h,048h,07Ah,0B9h,0B9h,054h DB 0F0h,043h,0F4h,0E1h,08Eh,0CBh,0CAh,0FDh,00Ch,053h,089h,0D9h,072h,0DBh,0B7h,0C5h DB 008h,062h,00Eh,091h,076h,0EAh,030h,08Dh,0F4h,072h,073h,0A9h,08Ah,0FAh,04Dh,0B5h DAO_REG_TABLE: DB 000h,000h,000h,000h,0C0h,0ADh,055h,019h,080h,05Bh,0ABh,032h,040h,0F6h,0FEh,02Bh DB 000h,0B7h,056h,065h,0C0h,01Ah,003h,07Ch,080h,0ECh,0FDh,057h,040h,041h,0A8h,04Eh DB 000h,06Eh,0ADh,0CAh,0C0h,0C3h,0F8h,0D3h,080h,035h,006h,0F8h,040h,098h,053h,0E1h DB 000h,0D9h,0FBh,0AFh,0C0h,074h,0AEh,0B6h,080h,082h,050h,09Dh,040h,02Fh,005h,084h magic1: DB 00h,00h,00h,00h presentation: DB 0ah,0dh DB ' 100% Key-Generator for DAO 3.1e/CDR-Win 3.1e, hidden check supported 26-06-97',0ah,0dh DB ' ---[Brought to you by #cracking]---------------------------------------------',0ah,0dh,0ah,0dh DB ' Produces original keys, you won''t fuck CDs with it! CDRWIN/DAO both tested.',0ah,0dh DB ' Thanks to Midi-Man for testing with his rewritable.',0ah,0dh,0ah,0dh ask_name: DB 'Enter your name (min 6 char): $' ask_company: DB 0ah,0dh DB 'Enter your company (min 6 char): $' toofew4name: DB 0dh,0ah,0dh,0ah DB 'At least 6 characters for name needed$' toofew4cie: DB 0dh,0ah,0dh,0ah DB 'At least 6 characters for company needed$' HereisCDRWIN31e: DB 0dh,0ah,0dh,0ah DB 'CDRWIN 3.1e : ' CDRWIN31eRegCode: db ' $' HereisDAO31e: DB 0dh,0ah DB 'DAO 3.1e : ' DAO31eRegCode: db ' $' keyb_buffer: DB 80h string_length: DB 00h string: end main