; Serial Number generator by Lord Soth. Version 1.0
; for ID crackme number 3.
; Any comments are welcome, but will be ignored :)
;-----------------------------------------------------------------
model compact
.data


querystr db 'Enter your soon to be registered name: $'
headline db 'Immortal Descendants CrackMe 3 serial number generator!$'
sernumstr db 'Your serial number is: $'
creditstr db 'Cracked and brought to you by Lord Soth!!$'
zerostr db 'ERROR: Zero length string.$'
username db 256 DUP (0)		; max length of string that can be brought by a dialog box

str_out db 12 DUP (0)		; length of final serial
ser_len dw 0000

.code
.startup
.386C

MOV AH,0
MOV AL,3
INT 10h					; set normal text video mode.
MOV AX,0625h			; set 25 lines to scroll up
MOV BH,1Fh				; set color attribute for blank chars
MOV CX,0000				; set upper-left corner of window
MOV DX,184Fh			; set lower-right corner of window
INT 10h					; fill screen with blue death :)

push ds
pop es
mov ah,13h
mov al,1
mov bh,0
mov bl,0eh
mov cx,37h
mov dx,0
mov ebp,offset es:headline		; this will print the headline string..
int 10h

MOV Ah,3
MOV BH,0
INT 10h							; get cursor position

ADD DH,2						; go down 2 lines
MOV AH,2
MOV DL,0
PUSH DX
MOV BH,0
INT 10h							; set cursor on new position

POP DX
MOV AH,13h
MOV AL,1
MOV BX,1Fh
MOV CX,27h						; 39 chars to print
MOV EBP,offset ES:querystr		; load offset of string
INT 10h							; print the damn string :)

; get the user name from the user, of course.. who did u think? :)

MOV CX,0FFh						; counter from 255 to 0 (bytes read)
mov bx,0						; pointer for memory
LEA DI,username

KBread:	MOV AH,0
	INT 16h						; get char from keyboard
	MOV BX,CX
	OR BX,0FF00h
	NOT BX						; BX will be used to point to the memory where char is stored
	CMP AH,1Ch					; check for Enter pressed
	JZ  CreateUserName 			; Bug outa here
	CMP AH,0Eh					; check for backspace
	JNZ store
	CALL delchar
	JMP Kbread

store:	MOV [DI+BX],AL 			; store ASCII byte in username buffer
		MOV AH,0Ah
		MOV BX,0
		PUSH CX					; store counter on stack
		MOV CX,1
		INT 10h					; print the char on screen
		MOV AH,3
		INT 10h					; get cursor position
		INC DL		 			; increase X pos by 1
		MOV AH,2
		MOV BH,0
		INT 10h					; set on new pos
		POP CX	
		LOOP KBread				; get another char (CX decreases)

; Create the REAL username the program has to calculate the serial from

CreateUserName : 

NOT CL							; reversing CX makes it the string length
CMP CL,0						; if no chars, get outa here :)
JNZ contuser					; 
JMP out_of_prog					; this is a fixed JMP, if your calculation routine is long
								; this might be needed

contuser:

MOV AX,0						; initailize to 0
LEA SI,username					; pointer to the username we entered
MOV CX,5						; 5 chars to process
MOV BX,0						; set BX to 0
LEA DI,str_out          		; load target string offset into EDI :)

;Calculation of the serial number , those 4 little lines :)
calc:

MOV AL,[SI+BX]			; get char
CALL num2str			; turn it into an ASCII digit
INC BX					; next char
LOOP calc				; all over again..

;Get string length and check against 10, which is max
LEA SI,str_out
MOV DI,SI
not_end:
INC SI
CMP BYTE PTR [SI],0				; check for end of string
JNZ not_end
SUB SI,DI						; substract will give the length
CMP SI,0Ah						; compare to 10
JLE good_num					; if 10 or less, continue, this is a good number
MOV SI,0Ah						; if more than 10, set to 10, coz 10 is the max the prog uses..
good_num:
MOV WORD PTR [ser_len],SI			; store length of serial number

		
	MOV AH,3
	MOV BX,0
	INT 10h				; get cursor position
	ADD DH,2
	MOV DL,0
	MOV AX,1301h
	MOV BX,1Fh
	MOV CX,17h			; 23 chars to print - "Your serial number...."
	MOV EBP,offset sernumstr
	INT 10h
	MOV AH,3
	MOV BX,0
	INT 10h				; get cursor position
	MOV CX,WORD PTR [ser_len] ; get serial length
	MOV AX,1301h
	MOV BX,1Fh
	MOV EBP,offset str_out
	INC EBP
	INT 10h				; print serial number
	MOV AH,3
	MOV BX,0
	INT 10h				; get cursor position
	ADD DH,3			; increase Y pos by 3
	MOV DL,0			; X pos = 0, start of line
	MOV AX,1301h
	MOV BX,1Fh
	MOV CX,29h
	MOV EBP,offset creditstr
	INT 10h				; print my name
	POP BP
	MOV AX,4C00h
	INT 21h				; get da fuck outa here :)

out_of_prog : 
	MOV AH,3
	MOV BX,0
	INT 10h				; get cursor position
	MOV AX,1301h
	MOV DL,0
	ADD DH,3
	MOV BX,1Fh
	MOV CX,1Ah			; 26 chars to print, "ERROR: Zero length..."
	MOV EBP,offset ES:zerostr
	INT 10h
	POP BP
	MOV AX,4C00h
	INT 21h				; get outa here, no chars in username

delchar PROC near
	PUSH CX
	PUSH AX
	CMP BX,0
	JZ nochars
	MOV BYTE PTR [DI+BX],0		; store null char on the buffer
	DEC BX						; reduce memory pointer by 1
	PUSH BX
	MOV BH,0
	MOV AH,3
	INT 10h				; get cursor position
	DEC DL
	MOV AH,2
	MOV BH,0
	INT 10h				; set it on X-1 pos
	MOV AX,0A20h		; store "space" on screen pos
	MOV BX,0
	MOV CX,1
	INT 10H				; write space on screen
	POP BX			
	POP AX
	POP CX
	INC CX				; increase CX, coz it normally decreases
						; and we want to go back 1 char
	RET					; return to caller

nochars:MOV BX,0		; zero out BX, no chars to del
		POP AX
		POP CX			; free stack
		MOV CX,0FFh		; zero out counter, intial value is FFh
		RET
delchar	ENDP

num2str PROC near
		; AX assumed to hold the 3 digit number
		; BX used in indirection to put the ASCII in memory
		; CX used to divide by 100, 10 etc.....

		PUSH CX
		MOV CX,64h			; to divide by 100, find hundreds-digit
		CWD
		IDIV CX
		CMP AX,0
		JZ  zero_dig
		ADD AX,30h			; add 30h to make it ASCII
		MOV [EDI],AL
		SUB AX,30h			; return me to normal numbers
		INC EDI
zero_dig:MOV CX,0Ah
		MOV AX,DX
		CWD
		IDIV CX
 	    ADD AX,30h
		ADD DX,30h
		MOV [EDI],AL
		INC EDI
		MOV [EDI],DL
		INC EDI		
		POP CX
		RET
num2str	ENDP

.exit
END







		
