Como hacer un Key Generator para el Winzip 7.0 por Black Fenix |
Otra vez estoy aquí, esta vez vamos a subir un poco el listón y vamos a hacer un generador de numeros de serie para el WinZip 7.0. que es lo que un verdadero cracker debe saber hacer.
SoftIce para windows
W32Dasm
Turbo Pascal, para hacer el Key Generator (Opcional)
Como siempre pondremos en marcha el SoftIce antes de ejecutar el programa luego ejecutamos el programa. Cada vez que lo ejecutamos aparece una ventana que nos comunica que el programa es una versión totalmente funcional pero no registrada, dandonos la opción de continuar, salir o introducir el numero de registro.
Esta última opción es la que seleccionaremos haciendo click sobre el boton 'Enter Registration Code'.Ahora nos aparece una ventana donde podemos introducirle los siguientes datos:
Name:
Registration #:
Esto es el nombre y el código de registros, actuaremos como es habitual en este tipo de casos.
Activamos el SoftIce (Ctrl+D) y ponemos un bpx getdlgitemtexta, salimos del SoftIce (Ctrl+D) e introducimos los datos por ejemplo
Name : Black Fenix
Registration #: 12345
pulsamos Ok y
Boom! estamos de nuevo en el SoftIce el bpx a surtido efecto, pulsamos F12 y aterrizamos en el código fuente del ejecutable del programa WinZip32.exe en estos momentos el programa está leyendo el nombre que le hubiesemos introducido, en mi caso 'Black Fenix' la dirección donde lo almacena está en EBX puedes
comprobarlo haciendo D EBX (dentro del SoftIce).
Pulsamos g para seguir con la ejecución del programa y de nuevo apareceremos en el SoftIce, esta vez el programa esta leyendo el numero que le hemos introducido, F12 y estaremos en el código del ejecutable. Ahora vamos a ir trazando paso a paso y vamos a ir viendo que es lo que hace el programa con el nombre y nuestro número no válido.
Esto es lo que podemos ver despues de pulsar F12.
|
:00408036 | FF150C844600 | Call dword ptr [0046840C] | |
:0040803C | 56 | push esi | // En esta dirección está nuestro numero (malo) |
:0040803D | E857160200 | call 00429699 | // llama a una función (descartada) |
:00408042 | 59 | pop ecx | |
:00408043 | 56 | push esi | // En esta dirección está nuestro numero (malo) |
:00408044 | E879160200 | call 004296C2 | // llama a una función (descartada) |
:00408049 | 803D28D9470000 | cmp byte ptr [0047D928], 00 | // mira si el primer caracter del nombre es NULL |
:00408050 | 59 | pop ecx | |
:00408051 | 745F | je 004080B2 | // salta si es NULL |
:00408053 | 803D58D9470000 | cmp byte ptr [0047D958], 00 | // mira si el primer caracter del número es NULL |
:0040805A | 7456 | je 004080B2 | // salta si es NULL |
:0040805C | E8EAFAFFFF | call 00407B4B | // llama a una función que retorna un valor en EAX |
:00408061 | 85C0 | test eax, eax | // esto es sospechoso |
:00408063 | 744D | je 004080B2 | // salta si es 0 hmmm... |
:00408065 | 53 | push ebx |
Un vistazo a este código nos permite afirmar con toda seguridad que si el numero o la cadena no se introdujeron (esto se comprueba con los cmp byte ptr [XXXXXXXXX], 0) el programa salta a una parte donde se nos comunica que los datos introducidos no son correctos. También sucede lo mismo si al regresar de la llamada a 407B4B EAX es igual a 0, esto es muuyyyy sospechoso, por lo que al llegar a esta función pulsaremos F8 para trazarla paso a paso.
Trazando poco a poco dentro de la función anterior iremos viendo como el programa realiza una serie de operaciones sobre el nombre, entre ellas supresión de espacios etc. Pero no será hasta llegar a la posición de código 407C0E donde el programa empuja a la pila el nombre y posteriormente nos aparecerá un misterioso numero.
:00407C0E | 8D85C0FEFFFF | lea eax, dword ptr [ebp+FFFFFEC0] | // lee dirección vacia |
:00407C14 | 50 | push eax | // la pasa como parametro |
:00407C15 | 57 | push edi | // EDI apunta a nuestro numero |
:00407C16 | E8AB000000 | call 00407CC6 | |
:00407C1B | 59 | pop ecx | |
:00407C1C | BE58D94700 | mov esi, 0047D958 | // ESI apunta a nuestro numero |
:00407C21 | 59 | pop ecx | |
:00407C22 | 8D85C0FEFFFF | lea eax, dword ptr [ebp+FFFFFEC0] | // lee dirección del otro número misterioso |
:00407C28 | 56 | push esi | // empuja nuestro numero a la pila |
:00407C29 | 50 | push eax | // empuja el otro numero a la pila |
:00407C2A | E8D1FC0400 | call 00457900 | // llama a una misteriosa función |
:00407C2F | F7D8 | neg eax | // invierte EAX |
Despues de llamar a la funcion 407cc6, el codigo siguiente obtiene un numero misterioso de la pila y lo pasa como argumento junto con nuestro otro numero a la función 457900 esta función retorna con un valor en EAX que resulta ser un 0 o un 1 segun la 'semejanza' de los dos numeros. En mi caso el numero misterioso resulta ser '252714DB' y da la casualidad que si lo introducimos como numero de serie junto con el nombre 'Black Fenix' el programa se registrará. ( Si introduces tu numero junto con tu nombre luego podras des-registrar el programa buscando el nombre y borrandolo del editor de registros de Windows, esta en la Carpeta WinZip y la clave es SN).
Ya has deducido que función es la que calcula el numero válido, claro es muy fácil la función es la 407CC6. Bueno pues ahora borramos todos los bpx con BC * y ponemos un bpx en 00407CC6.
Pulsaremos G y continuaremos con la ejecución del programa reciendo el mensaje de error correspondiente. Volvemos a pulsar en OK y esta vez estaremos en la función 407CC6. Pulsamos F8 para trazarla paso a paso.
:00407CC6 | 55 | push ebp | ||
:00407CC7 | 8BEC | mov ebp, esp | ||
:00407CC9 | 51 | push ecx | ||
:00407CCA | 8B4D08 | mov ecx, dword ptr [ebp+08] | // Coge puntero al nombre | |
:00407CCD | 8365FC00 | and dword ptr [ebp-04], 00000000 | ||
:00407CD1 | 53 | push ebx | ||
:00407CD2 | 56 | push esi | ||
:00407CD3 | 8A11 | mov dl, byte ptr [ecx] | // lee primer caracter | |
:00407CD5 | 57 | push edi | // prepara registros | |
:00407CD6 | 33C0 | xor eax, eax | ||
:00407CD8 | 8BF1 | mov esi, ecx | // copia puntero al nombre | |
:00407CDA | 33FF | xor edi, edi | // EDI es el contador de caracteres | |
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407CF1(U) | ||||
// Esta parte calcula la segunda parte // del código válido y lo deja en [ebp-4] | ||||
:00407CDC | 84D2 | test dl, dl | // comprueba que no sea un caracter nulo | |
:00407CDE | 7413 | je 00407CF3 | // si es nulo, fin de cadena y de calculo | |
:00407CE0 | 660FB6D2 | movzx dx, dl | // extiende el caracter a DX | |
:00407CE4 | 8BDF | mov ebx, edi | // copia contador de caracteres | |
:00407CE6 | 0FAFDA | imul ebx, edx | // multiplica contador x caracter actual | |
:00407CE9 | 015DFC | add dword ptr [ebp-04], ebx | // lo suma a la segunda parte del codigo finbal | |
:00407CEC | 8A5601 | mov dl, byte ptr [esi+01] | // coge siguiente caracter | |
:00407CEF | 47 | inc edi | // incrementa contador | |
:00407CF0 | 46 | inc esi | // y puntero | |
:00407CF1 | EBE9 | jmp 00407CDC | // pasa al siguiente caracter // Aquí se inicia el cálculo de la primera parte // del código válido | |
:00407CF3 | C705ECD3470001000000 | mov dword ptr [0047D3EC], 00000001 | ||
:00407CFD | 8BF1 | mov esi, ecx | // copia puntero al nombre | |
:00407CFF | 8A09 | mov cl, byte ptr [ecx] | // coge el primer caracter | |
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407D1C(U) | ||||
:00407D01 | 84C9 | test cl, cl | // comprueba si es 0 | |
:00407D03 | 7419 | je 00407D1E | // salta a 407D1E si lo es | |
:00407D05 | 660FB6C9 | movzx cx, cl | // extiende cl sin signo a CX (CX=caracter actual) | |
:00407D09 | 6821100000 | push 00001021 | // empuja 1021h = 4219 | |
:00407D0E | 51 | push ecx | // y el caracter actual | |
:00407D0F | 50 | push eax | // y eax (la primera vez EAX = 0 ) | |
:00407D10 | E82A000000 | call 00407D3F | // llama a una función (ver más abajo) // que realiza un cálculo con el caracter actual | |
:00407D15 | 8A4E01 | mov cl, byte ptr [esi+01] | // coge el siguiente caracter de la cadena | |
:00407D18 | 83C40C | add esp, 0000000C | // ajusta puntero de pila | |
:00407D1B | 46 | inc esi | // incrementa el puntero de la cadena | |
:00407D1C | EBE3 | jmp 00407D01 | // salta hacia atras 407D01 | |
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407D03(C) | ||||
:00407D1E | 0FB74DFC | movzx ecx, word ptr [ebp-04] | // coge 4 ultimos digitos del código | |
:00407D22 | 83C063 | add eax, 00000063 | // añade 63 a los 4 primeros digitos del código | |
:00407D25 | 51 | push ecx | // empuja a la pila | |
:00407D26 | 0FB7C0 | movzx eax, ax | // extiende los 4 primeors digitos del codigo a EAX | |
:00407D29 | 50 | push eax | // empuja EAX | |
* Possible StringData Ref from Data Obj //"%04X%04X" | ||||
:00407D2A | 6884F44600 | push 0046F484 | // esta función se encarga de unirlos | |
:00407D2F | FF750C | push [ebp+0C] | // y salvarlos como una cadena de caracteres | |
:00407D32 | E869E20400 | call 00455FA0 | // de longitud máxima 8 caracteres y en formato hexadecimal | |
:00407D37 | 83C410 | add esp, 00000010 | // si la primera parte del código tiene más de cuatro | |
:00407D3A | 5F | pop edi | // caracteres, estos prevaleceran sobre la segunda | |
:00407D3B | 5E | pop esi | // parte del código de la cual se suprimiran los | |
:00407D3C | 5B | pop ebx | // caracteres necesarios por la izquierda para dar | |
:00407D3D | C9 | leave | // cabida a los de la primera parte del código | |
:00407D3E | C3 | ret | ||
* Referenced by a CALL at Addresses: |:00407D10 , :00407DFB | ||||
:00407D3F | 55 | push ebp | // guarda estado de la pila | |
:00407D40 | 8BEC | mov ebp, esp | ||
:00407D42 | 8B4508 | mov eax, dword ptr [ebp+08] | // eax = coge código actual | |
:00407D45 | 56 | push esi | ||
:00407D46 | 33C9 | xor ecx, ecx | ||
:00407D48 | 6A08 | push 00000008 | // empuja 8 a la pila | |
:00407D4A | 8A6D0C | mov ch, byte ptr [ebp+0C] | // lee caracter pasado en CH | |
:00407D4D | 5A | pop edx | // inicia un contador con 8 pasos | |
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407D65(C) | ||||
:00407D4E | 8BF1 | mov esi, ecx | // copia caracter a esi | |
:00407D50 | 33F0 | xor esi, eax | // XOR con código actual | |
:00407D52 | 66F7C60080 | test si, 8000 | // comprueba si esá por debajo de 8000h | |
:00407D57 | 7407 | je 00407D60 | // si salta | |
:00407D59 | 03C0 | add eax, eax | // no, duplica el codigo actual | |
:00407D5B | 334510 | xor eax, dword ptr [ebp+10] | // hace un xor con 1021h (ver argumentos antes del call) | |
:00407D5E | EB02 | jmp 00407D62 | // salta hacia abajo | |
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407D57(C) | ||||
:00407D60 | D1E0 | shl eax, 1 | // desplaza un bit a la izquierda el codigo actual | |
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407D5E(U) | ||||
:00407D62 | D1E1 | shl ecx, 1 | // desplaza un bit a la izquierda el caracter actual | |
:00407D64 | 4A | dec edx | // decrementa contador | |
:00407D65 | 75E7 | jne 00407D4E | // sigue si no es 0 | |
:00407D67 | 5E | pop esi | ||
:00407D68 | 5D | pop ebp | ||
:00407D69 | C3 | ret |
Bueno, es una rutina bastante larga pero con los comentarios que he puesto creo que podras entenderla sin problemas. Pero todo no queda aquí ya que si seguimos la ejecución del programa podremos comprobrar que se vuelve a generar otro numero sospechoso, a partir de una cadena que equivale a nuestro nombre pero
esta vez el nombre entero está en minusculas. Esto quiere decir que el programa tiene dos numeros de registro válidos por nombre, uno para el nombre tal y como se introdujo y otro para el nombre en minusculas. Para nuestra decepción el programa no usa la misma rutina de cálculo pero si que es muy similar a la que usa para calcular el primer código, aquí tienes un volcado de la rutina. Sólo he marcado lo que la hace diferente, el resto se comporta igual que la anterior.
:00407D6A | 55 | push ebp | ||
:00407D6B | 8BEC | mov ebp, esp | ||
:00407D6D | 81ECCC000000 | sub esp, 000000CC | ||
:00407D73 | 53 | push ebx | ||
:00407D74 | 56 | push esi | ||
:00407D75 | 57 | push edi | ||
:00407D76 | 8D8534FFFFFF | lea eax, dword ptr [ebp+FFFFFF34] | // Parte de este código es para | |
:00407D7C | FF7508 | push [ebp+08] | // comprobar que | |
:00407D7F | 33DB | xor ebx, ebx | // el primer código | |
:00407D81 | 895DFC | mov dword ptr [ebp-04], ebx | // se generó correctamente | |
:00407D84 | 33FF | xor edi, edi | // | |
:00407D86 | 50 | push eax | // | |
:00407D87 | E894E00400 | call 00455E20 | // | |
:00407D8C | 59 | pop ecx | // | |
:00407D8D | 8D8534FFFFFF | lea eax, dword ptr [ebp+FFFFFF34] | // | |
:00407D93 | 59 | pop ecx | // | |
:00407D94 | 50 | push eax | // | |
:00407D95 | E836D60500 | call 004653D0 | // | |
:00407D9A | 80BD34FFFFFF00 | cmp byte ptr [ebp+FFFFFF34], 00 | // | |
:00407DA1 | 59 | pop ecx | // | |
:00407DA2 | 8DB534FFFFFF | lea esi, dword ptr [ebp+FFFFFF34] | // | |
:00407DA8 | 741F | je 00407DC9 | // | |
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407DC7(C) | ||||
:00407DAA | 0FB606 | movzx eax, byte ptr [esi] | ||
:00407DAD | 50 | push eax | // + | |
:00407DAE | E85DF80400 | call 00457610 | // + Esta es una de la diferencia respecto a la otra // + Esta función comprueba la validez del caracter actual // + retornando un valor <> de 0 si es válido // + rutina | |
:00407DB3 | 85C0 | test eax, eax | // + si retorna 0 | |
:00407DB5 | 59 | pop ecx | // + el caracter no es válido y no se tiene | |
:00407DB6 | 740B | je 00407DC3 | // + en cuenta para el cálculo | |
:00407DB8 | 660FB606 | movzx ax, byte ptr [esi] | ||
:00407DBC | 0FAFC3 | imul eax, ebx | ||
:00407DBF | 0145FC | add dword ptr [ebp-04], eax | ||
:00407DC2 | 43 | inc ebx | ||
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407DB6(C) | ||||
:00407DC3 | 46 | inc esi | ||
:00407DC4 | 803E00 | cmp byte ptr [esi], 00 | ||
:00407DC7 | 75E1 | jne 00407DAA | ||
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407DA8(C) | ||||
:00407DC9 | 80BD34FFFFFF00 | cmp byte ptr [ebp+FFFFFF34], 00 | ||
:00407DD0 | C705ECD3470001000000 | mov dword ptr [0047D3EC], 00000001 | ||
:00407DDA | 8DB534FFFFFF | lea esi, dword ptr [ebp+FFFFFF34] | ||
:00407DE0 | 7429 | je 00407E0B | ||
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407E09(C) | ||||
:00407DE2 | 0FB606 | movzx eax, byte ptr [esi] | ||
:00407DE5 | 50 | push eax | // + | |
:00407DE6 | E825F80400 | call 00457610 | // + Esta es otra de las diferencias respecto a | |
:00407DEB | 85C0 | test eax, eax | // + a la otra rutina se comporta igual que | |
:00407DED | 59 | pop ecx | // + el call anterior (si caracter no es valido | |
:00407DEE | 7415 | je 00407E05 | // + no lo procesa) | |
:00407DF0 | 660FB606 | movzx ax, byte ptr [esi] | // + | |
:00407DF4 | 6821100000 | push 00001021 | ||
:00407DF9 | 50 | push eax | ||
:00407DFA | 57 | push edi | ||
:00407DFB | E83FFFFFFF | call 00407D3F | ||
:00407E00 | 83C40C | add esp, 0000000C | ||
:00407E03 | 8BF8 | mov edi, eax | ||
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407DEE(C) | ||||
:00407E05 | 46 | inc esi | ||
:00407E06 | 803E00 | cmp byte ptr [esi], 00 | ||
:00407E09 | 75D7 | jne 00407DE2 | ||
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407DE0(C) | ||||
:00407E0B | 0FB745FC | movzx eax, word ptr [ebp-04] | ||
:00407E0F | 8B750C | mov esi, dword ptr [ebp+0C] | ||
:00407E12 | 83C763 | add edi, 00000063 | ||
:00407E15 | 50 | push eax | ||
:00407E16 | 0FB7C7 | movzx eax, di | ||
:00407E19 | 50 | push eax | ||
* Possible StringData Ref from Data Obj //"%04u%04u" | ||||
:00407E1A | 6890F44600 | push 0046F490 | ||
:00407E1F | 56 | push esi | ||
:00407E20 | E87BE10400 | call 00455FA0 | ||
:00407E25 | 83C410 | add esp, 00000010 | ||
:00407E28 | 80660800 | and byte ptr [esi+08], 00 | ||
:00407E2C | 5F | pop edi | ||
:00407E2D | 5E | pop esi | ||
:00407E2E | 5B | pop ebx | ||
:00407E2F | C9 | leave | ||
:00407E30 | C3 | ret | ||
// Este es el volcado de la rutina llamada en los puntos marcados con + de la rutina anterior: | ||||
:00457610 | 833DBC63470001 | cmp dword ptr [004763BC], 00000001 | // comprueba algo ?? | |
:00457617 | 7E13 | jle 0045762C | // este salto no se realiza si se esta // introduciendo un numero de registro | |
:00457619 | 8B442404 | mov eax, dword ptr [esp+04] | ||
:0045761D | 6803010000 | push 00000103 | // Pasa 103h a como primer argumento | |
:00457622 | 50 | push eax | // Este argumento es el código del caracter // actual | |
:00457623 | E8F82D0000 | call 0045A420 | // Llama a la función | |
:00457628 | 83C408 | add esp, 00000008 | ||
:0045762B | C3 | ret |
Este es el volcado de la rutina llamada en la rutina anterior. Esta rutina retorna un valor segun el caracter pasado por la anterior, se usa para comprobar si los caracteres del nombre introducido van a ser procesados. Si retorna 0, el caracter no será tenido en cuenta cuando se realiza el cálculo del segundo código.
:0045A420 | 51 | push ecx | ||
:0045A421 | 8B4C2408 | mov ecx, dword ptr [esp+08] | ||
:0045A425 | 56 | push esi | ||
:0045A426 | 8D4101 | lea eax, dword ptr [ecx+01] | ||
:0045A429 | 3D00010000 | cmp eax, 00000100 | ||
:0045A42E | 7715 | ja 0045A445 | // este salto nunca se realiza cuando se esta introduciendo un código de registros | |
:0045A430 | 8B15B0614700 | mov edx, dword ptr [004761B0] | // Carga puntero a un tabla | |
:0045A436 | 33C0 | xor eax, eax | // borra EAX | |
:0045A438 | 668B044A | mov ax, word ptr [edx+2*ecx] | // usa el código del caracter pasado como índice a una tabla | |
:0045A43C | 8B4C2410 | mov ecx, dword ptr [esp+10] | // lee 103h en ecx | |
:0045A440 | 23C1 | and eax, ecx | // Realiza un AND con 103h sobre | |
:0045A442 | 5E | pop esi | // el valor de la tabla | |
:0045A443 | 59 | pop ecx | ||
:0045A444 | C3 | ret | // resultado en eax, este resultado es comprobado por las rutina marcadas con + |
El problema reside en la tabla, tenemos que copiarla para poder reproducir el algoritmo. Su contenido lo podemos encontrar a partir de la dirección 4761B0, son exactamente 256 valores uno por cada código de caracter.
Bueno, ahora examina las dos rutinas y crea el programilla en Pascal o C que genere los dos numeros. Yo lo he hecho en Pascal, aquí tienes el código fuente. Pero recuerda copiarlo no te aportará saber y tampoco pretenderas que te lo den todo hecho eh??.
Vuelve a examinar este documento e intenta hacerlo por ti mismo si aún tienes dudas. Si necesitas desensamblar el WinZip puedes usar el W32Dasm para examinar mejor las rutinas aquí expuestas.
Tambien encontrarás el programa compilado en el mismo lugar que este documento (wzipkg.exe).
Program winzip7_KeyGenerator;
Uses Crt,strings;
{ Esta es la tabla que el WinZip usa para calcular el segundo código }
Const Table:array [0..255] of byte = (
$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
$20,$00,$28,$00,$28,$00,$28,$00,$28,$00,$28,$00,$20,$00,$20,$00,
$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
$48,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,
$84,$00,$84,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
$10,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$01,$00,
$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,
$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,
$01,$00,$01,$00,$01,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
$10,$00,$82,$00,$82,$00,$82,$00,$82,$00,$82,$00,$82,$00,$02,$00,
$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,
$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,
$02,$00,$02,$00,$02,$00,$10,$00,$10,$00,$10,$00,$10,$00,$20,$00);
Var Name,Name2:string[80];
charb:byte;
Function ComputeCode2(Cadena:string):longint;
Var j:byte;
code,chard:longint;
Begin
code:=0;
for j:=1 to length(Cadena) do
Begin
chard:=ord(cadena[j]);
code:=code+(chard*(j-1));
End;
ComputeCode2:=(code shl 16) shr 16;
End;
Function ComputeCode2b(Cadena:string):longint;
Var j,k:byte;
code,chard:longint;
Begin
code:=0;
k:=0;
for j:=1 to length(Cadena) do
Begin
chard:=ord(cadena[j]);
if boolean(Table[chard*2] and $103) then
Begin
code:=code+(chard*k);
inc(k);
End;
End;
ComputeCode2b:=(code shl 16) shr 16;
End;
Function ComputeCode1(keygen: word; charb: byte; code:longint):longint;
Var j:word;
charw1,charw2:longint;
Begin
charw1:=charb shl 8;
For j:=1 to 8 do
Begin
charw2:=charw1;
charw2:=((charw2 XOR code) shl 16) shr 16;
if charw2<=$8000 then
Begin
code:=code shl 1;
End
Else
Begin
code:=code+code;
code:=(code XOR keygen);
End;
charw1:=charw1 shl 1;
End;
ComputeCode1:=code;
End;
{ Las 3 rutinas siguientes son de Trevor Carlsen }
Function Byte2Hex(numb : Byte): String; { Byte a hex String }
Const HexChars : Array[0..15] of Char = '0123456789ABCDEF';
begin
Byte2Hex[0] := #2; Byte2Hex[1] := HexChars[numb shr 4];
Byte2Hex[2] := HexChars[numb and 15];
end; { Byte2Hex }
Function Numb2Hex(numb: Word): String; { Word a hex String.}
begin
Numb2Hex := Byte2Hex(hi(numb))+Byte2Hex(lo(numb));
end; { Numb2Hex }
Function Long2Hex(L: LongInt): String; { LongInt a hex String }
begin
Long2Hex := Numb2Hex(L shr 16) + Numb2Hex(L);
end; { Long2Hex }
FUNCTION LowerCase( s: STRING ): STRING; ASSEMBLER;
ASM
PUSH DS
CLD
LDS SI, s
XOR AX, AX
LODSB
XCHG AX, CX
LES DI, @Result
MOV BYTE PTR ES:[DI], CL
INC DI
JCXZ @@3
@@1: LODSB
CMP AL, 'A'
JB @@2
CMP AL, 'Z'
JA @@2
OR AL, $20
@@2: STOSB
LOOP @@1
@@3: POP DS
END;
Var i:byte;
codigo1a,codigo2a,codigo1b,codigo2b:longint;
c1:string;
c2:string[4];
codigo2:string[8];
Begin
textcolor(7);
Writeln('WinZip 7.0 Key Generator by Black Fenix (c) 1999:');
Writeln('-------------------------------------------------');
Write('Enter your name: ');
Readln(Name);
Name2:=Name;
Name2:=LowerCase(Name2);
codigo1a:=0;
codigo2a:=0;
codigo1b:=0;
codigo2b:=0;
for i:=1 to length(Name) do
Begin
charb:=ord(Name[i]);
if charb<>0 then
Begin
codigo1a:=ComputeCode1($1021,charb,codigo1a);
End Else break;
charb:=ord(Name2[i]);
if boolean(Table[word(charb*2)] and $103) then
Begin
codigo1b:=ComputeCode1($1021,charb,codigo1b);
End
End;
codigo1a:=((codigo1a+$63) shl 16) shr 16;
codigo1b:=((codigo1b+$63) shl 16) shr 16;
codigo2a:=ComputeCode2(Name);
codigo2b:=ComputeCode2b(Name2);
{ convierte a cadena la primera parte del segundo codigo }
str(codigo1b,c1);
{ convierte a cadena la segunda parte del segundo código }
{ con un longitud máxima de 4 caracteres }
str(codigo2b:4,c2);
{ concatena los dos código, Nota: nunca sobrepasaran los 8 caracteres ya que -> codigo2[8]:string }
codigo2:=c1+c2;
write('Valid registration numbers are: ');
textcolor(15);
writeln(Numb2Hex(codigo1a),Numb2Hex(codigo2a),' or ',codigo2);
textcolor(7);
End.
![]() You are inside Reversed Minds pages. por |