Net Pal v1.2 Cómo hacer un Keygenerator por Black Fenix


En este tutorial vamos a tratar un marcador de conexión a Internet (Internet Dialer), el objetivo es hacer un keygenerator, he de decir que al principio me pareció que usaba un algoritmo bastante complejo pero como siempre si se piensa primero en lo fácil uno se da cuenta de que no resulta tan difícil. El programa dispone de tres tipos de números de série: licencia sencilla (un solo ordenador), multi licencia (varios ordenadores) y de evaluación (estos incrementan el número de días de evaluación). Sólo trataré los dos primeros, por ser los más utiles y por razones de tamaño de este archivo que ya casí me es imposible de editar debido al extensivo uso de tablas, aunque el tercero resulta práctico como ejemplo para aprender.Por el momento este es el tutorial con más código documentado que he escrito, por lo que su tamaño es bastante elevado. El funcionamiento del algoritmo pues se deduce de los comentarios del código.

SoftIce para Windows
W32DAsm
Compilador de C (para el keygenerator)

El programa viene en un ejecutable autoinstalable llamado np1setup.exe de unos 314Kb de tamaño. Lo ejecutamos y nos aparecerán el contrato de licencia y todas las típicas chorradas que uno se puede encontrar cuando instala este tipo de programas. Llegamos a la pantalla de registro, que es así:

Vamos a hacer el keygenerator sin instalar el software, aprovechando que se nos pide el número de série en la instalación, pero para ello debemos buscar los archivos de instalación que se han descomprimido, para ver como se llama el que contiene la rutina de registro vamos a intentar capturar la rutina que lee el nombre y el número de las casillas de edición. Como estamos ante un cuadro de dialogo, lo más lógico es que se use alguna función del API especifica para diálogos, tal como GetDlgItemTextA. Pues insertamos algún nombre y un número y activamos el SoftIce (Ctrl+D), dentro del SoftIce tecleamos:

> BPX GetDlgItemTextA

y continuamos con la ejecución del programa pulsando G. El breakpoint surtirá efecto, pero ahora estamos dentro de la función del API GetDlgItemTextA, por lo que pulsaremos F12 para continuar la ejecución hasta que se retorne al punto donde fue llamada, una vez pulsada la tecla F12 veremos el siguiente código ante nosotros (la línea roja marca nuestra posición):

:0040309D FF1568C24000 Call dword ptr [0040C268] // Lee número de serie
:004030A3 6880000000 push 00000080 // Máximo número de caracteres a leer del nombre
:004030A8 8D85FCFEFFFF lea eax, dword ptr [ebp+FFFFFEFC] // carga dirección del buffer donde copiar el nombre
:004030AE 50 push eax // empuja para pasar a la siguiente llamada a GetDlgItemText
:004030AF 68EB030000 push 000003EB // Identificador del cuadro de diálogo
:004030B4 8B85F0FEFFFF mov eax, dword ptr [ebp+FFFFFEF0] // carga dirección
:004030BA FF7024 push [eax+24] // y pasa handle del cuadro de dialogo
* Reference To: USER32.GetDlgItemTextA, Ord:0104h
:004030BD FF1568C24000 Call dword ptr [0040C268] // lee el nombre introducido
:004030C3 8D857CFFFFFF lea eax, dword ptr [ebp+FFFFFF7C] // y carga su dirección en EAX
:004030C9 50 push eax // pasa dirección a función
:004030CA E8D00B0000 call 00403C9F // esta función comprueba que el nombre tenga un formato válido devolviendo 0 en EAX en caso afirmativo
:004030CF 85C0 test eax, eax // es válida ?
:004030D1 7419 je 004030EC // si, salta
:004030D3 8D85FCFEFFFF lea eax, dword ptr [ebp+FFFFFEFC] // no, carga puntero al número
:004030D9 50 push eax // pasa como parámetro a la misma función de antes
:004030DA E8C00B0000 call 00403C9F // llama a la función
:004030DF 85C0 test eax, eax // es válida ?
:004030E1 7409 je 004030EC // si, salta
:004030E3 C685F4FEFFFF01 mov byte ptr [ebp+FFFFFEF4], 01 // no, guarda flag y salta a la quinta puñeta,
:004030EA EB7E jmp 0040316A // esto hará que se instale como versión de evaluación ya que no le metimos numero ni nombre válidos
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004030D1(C), :004030E1(C)
:004030EC 8D45FC lea eax, dword ptr [ebp-04] // hay nombre y número, carga una dirección
:004030EF 50 push eax // pasa como último argumento
:004030F0 8D85F8FEFFFF lea eax, dword ptr [ebp+FFFFFEF8] // carga dirección
:004030F6 50 push eax // pasa como 5º argumento
:004030F7 8D85FCFEFFFF lea eax, dword ptr [ebp+FFFFFEFC] // carga puntero al número introducido
:004030FD 50 push eax // pasa como 4º argumento
:004030FE 8D857CFFFFFF lea eax, dword ptr [ebp+FFFFFF7C] // carga puntero al nombre
:00403104 50 push eax // pasa como tercer parámetro
:00403105 8B85F0FEFFFF mov eax, dword ptr [ebp+FFFFFEF0] // carga dirección
:0040310B 0FBF802C010000 movsx eax, word ptr [eax+0000012C] // y lee desde desplazamiento 12c ( valor Ah )
:00403112 50 push eax // pasa Ah
:00403113 8B85F0FEFFFF mov eax, dword ptr [ebp+FFFFFEF0] // carga de nuevo dirección
:00403119 668B802A010000 mov ax, word ptr [eax+0000012A] // lee valor de AX (valor=0, la parte alta de EAX aún existe)
:00403120 50 push eax // y pasa como primer argumento
:00403121 E833380000 call 00406959 // llama rutina
:00403126 0FBFC0 movsx eax, ax
:00403129 F7D8 neg eax
:0040312B 1BC0 sbb eax, eax
:0040312D F7D8 neg eax
:0040312F 8885F4FEFFFF mov byte ptr [ebp+FFFFFEF4], al
:00403135 0FB685F4FEFFFF movzx eax, byte ptr [ebp+FFFFFEF4]
:0040313C 85C0 test eax, eax
:0040313E 7409 je 00403149
:00403140 83BDF8FEFFFF00 cmp dword ptr [ebp+FFFFFEF8], 00000000
:00403147 7517 jne 00403160

Como podemos observar, se obtiene el nombre introducido y posteriormente el número de série introducido. Si miramos en la ventana de código veremos el nombre del ejecutable que llamó a a GetDlgItemTexta, en mi caso NPALINST!etxt, NPALINST es el nombre del archivo ejecutable que debemos buscar por nuestro disco duro y copiarlo a un directorio aparte para desensamblarlo y poderlo analizar más detenidamente. Salimos de SoftIce no sin antes apuntar la dirección 40309D, y ahora buscamos el archivo (recuerda usar Buscar Archivo del explorador de Windows), yo lo encontré en c:\windows\temp. Ahora lo copiamos y lo desensamblamos con el W32DAsm, y nos vamos a la dirección de código que habiamos apuntado antes (Menu Goto/Code Location). Ahora podemos examinar el código más tranquilamente. Veamos que sea comprueba en la primera rutina despues de leer el nombre y el número:

:00403C9F 55 push ebp // reajusta pila
:00403CA0 8BEC mov ebp, esp //
:00403CA2 83EC0C sub esp, 0000000C //
:00403CA5 C745F801000000 mov [ebp-08], 00000001 // pone a 1 una dirección de memoria
:00403CAC 837D0800 cmp dword ptr [ebp+08], 00000000 // mira si es 0, cosa que no es posible ¿?
:00403CB0 7505 jne 00403CB7 // salta siempre
:00403CB2 6A01 push 00000001
:00403CB4 58 pop eax
:00403CB5 EB3A jmp 00403CF1
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00403CB0(C)
:00403CB7 8B4508 mov eax, dword ptr [ebp+08] // lee puntero al nombre
:00403CBA 8945FC mov dword ptr [ebp-04], eax // lo copia
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00403CEC(U)
:00403CBD 8B45FC mov eax, dword ptr [ebp-04] // copia el puntero al nombre en eax
:00403CC0 8A00 mov al, byte ptr [eax] // Lee caracter
:00403CC2 8845F4 mov byte ptr [ebp-0C], al // copia en otra dirección
:00403CC5 0FBE45F4 movsx eax, byte ptr [ebp-0C] // lo lee de allí conservando el signo
:00403CC9 8B4DFC mov ecx, dword ptr [ebp-04] // copia el puntero al nombre en ecx
:00403CCC 41 inc ecx // incrementa puntero al nombre
:00403CCD 894DFC mov dword ptr [ebp-04], ecx // y lo guarda
:00403CD0 85C0 test eax, eax // mira el valor del caracter
:00403CD2 741A je 00403CEE // salta si es nulo (cadena nula)
:00403CD4 0FBE45F4 movsx eax, byte ptr [ebp-0C] // lee de nuevo el caracter
:00403CD8 83F820 cmp eax, 00000020 // mira si es un espacio
:00403CDB 740F je 00403CEC // salta si es un espacio
:00403CDD 0FBE45F4 movsx eax, byte ptr [ebp-0C] // lee de nuevo el caracter
:00403CE1 83F839 cmp eax, 00000039 // es igual a '9' ?
:00403CE4 7406 je 00403CEC // si, salta
:00403CE6 8365F800 and dword ptr [ebp-08], 00000000 // borra el flag (antes 1) ( 1 and 0 = 0)
:00403CEA EB02 jmp 00403CEE // salta para copiar flag y salir
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00403CDB(C), :00403CE4(C)
:00403CEC EBCF jmp 00403CBD
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00403CD2(C), :00403CEA(U)
:00403CEE 8B45F8 mov eax, dword ptr [ebp-08] // copia el flag en EAX
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00403CB5(U)
:00403CF1 C9 leave // restaura pila y sale
:00403CF2 C20400 ret 0004

La rutina anterior comprueba que el primer carácter de la cadena pasada sea diferente a nulo, un espacio o '9'. Si es diferente devuelve 0 en EAX, en caso contrario devuelve 1. Con esto se comprueba parte de la validez del nombre, notese que de momento no se ha tenido en cuenta una longitud mínima (excepto cadena nula). Volvemos a 4030CF donde se actua en consecuencia con el resultado de la rutina previa, se continua la ejecución suponiendo que sea un nombre válido hasta 403121, donde se llama a la siguiente rutina 406959.

:00406959 55 push ebp // Ajusta pila
:0040695A 8BEC mov ebp, esp // para dar espacio a las variables
:0040695C 83EC64 sub esp, 00000064 // locales
:0040695F 668365A800 and word ptr [ebp-58], 0000
:00406964 8D4DB0 lea ecx, dword ptr [ebp-50]
:00406967 E805C9FFFF call 00403271
:0040696C 8D4DC4 lea ecx, dword ptr [ebp-3C]
:0040696F E8FDC8FFFF call 00403271
:00406974 8B4518 mov eax, dword ptr [ebp+18] // carga puntero al sexto argumento
:00406977 832000 and dword ptr [eax], 00000000
:0040697A 8B4D1C mov ecx, dword ptr [ebp+1C] // carga puntero al septimo argumento
:0040697D E803C9FFFF call 00403285
:00406982 8B4514 mov eax, dword ptr [ebp+14] // lee puntero al número introducido (quinto argumento)
:00406985 0FBE00 movsx eax, byte ptr [eax] // lee primer caracter del número en EAX extendiendo el signo
:00406988 83F845 cmp eax, 00000045 // es una letra 'E' ?
:0040698B 7529 jne 004069B6 // salta si no es igual,
:0040698D 8B4514 mov eax, dword ptr [ebp+14] // lee puntero al número introducido (quinto argumento)
:00406990 40 inc eax // incrementa puntero
:00406991 50 push eax // pasa dirección del segundo caracter a función lstrcpy
:00406992 8D45C8 lea eax, dword ptr [ebp-38] // y lee en EAX buffer donde se copiará
:00406995 50 push eax // pasa buffer a lstrcpy
* Reference To: KERNEL32.lstrcpyA, Ord:0302h
:00406996 FF155CC14000 Call dword ptr [0040C15C] // copia cadena con el numero al buffer en ebp-38 (se copia a partir del segundo caracter)
:0040699C 66C745E80100 mov [ebp-18], 0001 // pone a 1 variable local de la rutina, esto indica que se introdujo un número de evaluación, tal y como se comprueba en 406A5D
:004069A2 6A1E push 0000001E // pasa 1eh
:004069A4 FF7510 push [ebp+10] // y puntero al nombre introducido
:004069A7 6A07 push 00000007 // y 7h
:004069A9 FF7508 push [ebp+08] // y primer argumento pasado
:004069AC E8E1F8FFFF call 00406292 // a esta función
:004069B1 8945BC mov dword ptr [ebp-44], eax // copia resultado en variable local
:004069B4 EB25 jmp 004069DB // y salta incondicionalmente
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040698B(C)
:004069B6 FF7514 push [ebp+14] // pasa puntero al numero para lstrcpy
:004069B9 8D45C8 lea eax, dword ptr [ebp-38] // y puntero al buffer donde copiar
:004069BC 50 push eax // pasa como argumento para lstrcpy
* Reference To: KERNEL32.lstrcpyA, Ord:0302h
:004069BD FF155CC14000 Call dword ptr [0040C15C] // copia el número entero
:004069C3 668365E800 and word ptr [ebp-18], 0000 // pone a 0 variable local de la rutina, (Nota: cuando el primer caracter es 'E' este flag vale 1 tal y como se establece en 40699C)

Asta aquí lo que se ha hecho es comprobar si la primera letra del número introducido empieza por 'E', en caso afirmativo se copia el resto del número a un buffer, si el número no empieza por 'E' se copia todo el número al buffer posteriormente se llama a la función en 406292. Veamos esta función detenidamente

Nota:el código esta comentado y linkado a las rutinas de interés, por lo que se hace fácil su comprensión:

:00406292 55 push ebp // ajusta pila
:00406293 8BEC mov ebp, esp // para dar espacio a las variables
:00406295 81EC20010000 sub esp, 00000120 // locales
:0040629B 56 push esi
:0040629C 57 push edi
:0040629D 0FBF4508 movsx eax, word ptr [ebp+08] // carga puntero al primer argumento
:004062A1 8B0485CCEE4000 mov eax, dword ptr [4*eax+0040EECC] // obtiene la dirección de una cadena númerica = 613533840
:004062A8 8985F4FEFFFF mov dword ptr [ebp+FFFFFEF4], eax // copia dirección
:004062AE FFB5F4FEFFFF push dword ptr [ebp+FFFFFEF4] // y pasa dirección de la cadena para medir su longitud (9)
* Reference To: KERNEL32.lstrlenA, Ord:0308h
:004062B4 FF1528C14000 Call dword ptr [0040C128] // obtiene la longitud de la cadena numérica (9 dígitos)
:004062BA 8985ECFEFFFF mov dword ptr [ebp+FFFFFEEC], eax // copia longitud
:004062C0 8D85FCFEFFFF lea eax, dword ptr [ebp+FFFFFEFC] // lee una dirección
:004062C6 50 push eax // y pasa como segundo argumento
:004062C7 FF7510 push [ebp+10] // pasa puntero al nombre introducido como primer argumento para la función siguiente
:004062CA E8EC000000 call 004063BB // llama a función que verifica los caracteres válidos, los convierte y devuelve un cadena conteniendo el nombre con caracteres intercalados del principio y del final y su inversa concatenada (ver función)
:004062CF 668985E0FEFFFF mov word ptr [ebp+FFFFFEE0], ax // lee longitud retornada
:004062D6 6683A5F0FEFFFF00 and word ptr [ebp+FFFFFEF0], 0000 // borra word bajo dirección
:004062DE 8B450C mov eax, dword ptr [ebp+0C] // lee argumento pasado (Ah)
:004062E1 8985F8FEFFFF mov dword ptr [ebp+FFFFFEF8], eax // copia Ah a variable local
:004062E7 6683A5E4FEFFFF00 and word ptr [ebp+FFFFFEE4], 0000 // borra word bajo dirección
:004062EF EB12 jmp 00406303 // salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004063AA(U)
:004062F1 668B85E4FEFFFF mov ax, word ptr [ebp+FFFFFEE4] // lee contador de ejecución
:004062F8 66050100 add ax, 0001 // suma 1
:004062FC 668985E4FEFFFF mov word ptr [ebp+FFFFFEE4], ax // y guarda
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004062EF(U)
:00406303 0FBF85E4FEFFFF movsx eax, word ptr [ebp+FFFFFEE4] // lee contador de ejecuciones
:0040630A 0FBF4D14 movsx ecx, word ptr [ebp+14] // lee último argumento pasado = 1Eh
:0040630E 3BC1 cmp eax, ecx // contador < 1Eh ?
:00406310 0F8D99000000 jnl 004063AF // no, salta
:00406316 0FBF85F0FEFFFF movsx eax, word ptr [ebp+FFFFFEF0] // si, lee contador de carácteres
:0040631D 8A8405FCFEFFFF mov al, byte ptr [ebp+eax-00000104] // lee caracter nombre cadena resultante de 4063BB
:00406324 8885E8FEFFFF mov byte ptr [ebp+FFFFFEE8], al // guarda en un buffer
:0040632A 0FBF85E4FEFFFF movsx eax, word ptr [ebp+FFFFFEE4] // lee contador de ejecución
:00406331 83E001 and eax, 00000001 // mira si el bit bajo es 0
:00406334 85C0 test eax, eax // (con esto se mira si el contador es par o impar si el bit es 1 es impar, si es 0 es par)
:00406336 7409 je 00406341 // es par (bit = 0), salta
:00406338 C745FC02000000 mov [ebp-04], 00000002 // es impar, copia 2 a variable local
:0040633F EB07 jmp 00406348 // y salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406336(C)
:00406341 C745FC01000000 mov [ebp-04], 00000001 // es par, copia 1 a variable local
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040633F(U)
:00406348 8B8DF8FEFFFF mov ecx, dword ptr [ebp+FFFFFEF8] // la primera vez lee Ah en ECX, luego lee el resultado del cálculo
:0040634E 0FAF4DFC imul ecx, dword ptr [ebp-04] // y multiplica Ah por 1 o 2 segun fuera par o impar
:00406352 0FB6B5E8FEFFFF movzx esi, byte ptr [ebp+FFFFFEE8] // lee caracter sin signo en ESI
:00406359 0FB685E8FEFFFF movzx eax, byte ptr [ebp+FFFFFEE8] // lee caracter sin signo en EAX
:00406360 99 cdq // convierte a qword EAX -> EDX=0 y EAX = caracter
:00406361 F7BDECFEFFFF idiv dword ptr [ebp+FFFFFEEC] // divide caracter entre longitud de la cadena de caracteres calculada en 4062B4 = 9h
:00406367 8B85F4FEFFFF mov eax, dword ptr [ebp+FFFFFEF4] // lee dirección de esa cadena
:0040636D 0FB63C10 movzx edi, byte ptr [eax+edx] // copia caracter de la cadena usando el resto de la division como índice
:00406371 8BC6 mov eax, esi // copia caracter del nombre sin signo a EAX para volver a dividir
:00406373 99 cdq // convierte a qword EAX -> EDX=0 y EAX = caracter
:00406374 F7FF idiv edi // divide entre el carácter que se saco de la cadena
:00406376 03CA add ecx, edx // y suma al relsultado de la multiplicación
:00406378 898DF8FEFFFF mov dword ptr [ebp+FFFFFEF8], ecx // guarda resultado donde la primera vez habia Ah
:0040637E 668B85F0FEFFFF mov ax, word ptr [ebp+FFFFFEF0] // carga contador de carácteres
:00406385 66050100 add ax, 0001 // lo incrementa
:00406389 668985F0FEFFFF mov word ptr [ebp+FFFFFEF0], ax // y lo guarda
:00406390 0FBF85F0FEFFFF movsx eax, word ptr [ebp+FFFFFEF0] // carga contador de caracteres
:00406397 0FBF8DE0FEFFFF movsx ecx, word ptr [ebp+FFFFFEE0] // carga longitud cálculada en 4062B4
:0040639E 3BC1 cmp eax, ecx // compara contador de caracteres con longitud
:004063A0 7C08 jl 004063AA // si contador < salta adelante
:004063A2 6683A5F0FEFFFF00 and word ptr [ebp+FFFFFEF0], 0000 // pone contador a 0, esto hace que se continue realizando el cálculo 1Eh veces aunque no haya más carácteres
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004063A0(C)
:004063AA E942FFFFFF jmp 004062F1 // salta atras para seguir procesando más carácteres
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406310(C)
:004063AF 8B85F8FEFFFF mov eax, dword ptr [ebp+FFFFFEF8] // y retorna resultado del cálculo
:004063B5 5F pop edi // restaura la pila y sale
:004063B6 5E pop esi
:004063B7 C9 leave
:004063B8 C21000 ret 0010

Esta es la rutina que verifica los caracteres, los convierte a minusculas o mayúsculas según convenga y mezcla los carácteres del principio y del final, si la cadena de entrada fuese:

Black Fenix

el resultado sería:

XbiLNaeCFkkFCeaNLibX

Para realizar estas operaciones utiliza varios contadores y un buffer auxiliar donde se va copiando la cadena resultante, de nuevo el código esta bastante comentado, como para entender el funcionamiento de la rutina.

:004063BB 55 push ebp // Ajusta pila
:004063BC 8BEC mov ebp, esp // para dar espacio a las variables locales
:004063BE 81EC10020000 sub esp, 00000210
:004063C4 8B4508 mov eax, dword ptr [ebp+08] // lee dirección del nombre introducido
:004063C7 8985FCFEFFFF mov dword ptr [ebp+FFFFFEFC], eax // copia dirección
:004063CD FFB5FCFEFFFF push dword ptr [ebp+FFFFFEFC] // pasa a lstrslen para obtener longitud
* Reference To: KERNEL32.lstrlenA, Ord:0308h
:004063D3 FF1528C14000 Call dword ptr [0040C128] // calcula longitud del nombre
:004063D9 668985F0FDFFFF mov word ptr [ebp+FFFFFDF0], ax // copia longitud a variable local
:004063E0 6683A5F8FDFFFF00 and word ptr [ebp+FFFFFDF8], 0000 // estable un par de contadores
:004063E8 6683A5F4FDFFFF00 and word ptr [ebp+FFFFFDF4], 0000
:004063F0 EB12 jmp 00406404 // salta incondicionalmente
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406462(U)
:004063F2 668B85F8FDFFFF mov ax, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:004063F9 66050100 add ax, 0001 // incrementa (podría ser inc AX que es más corto)
:004063FD 668985F8FDFFFF mov word ptr [ebp+FFFFFDF8], ax // y guarda
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004063F0(U)
:00406404 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // carga contador de caracteres procesados
:0040640B 0FBF8DF0FDFFFF movsx ecx, word ptr [ebp+FFFFFDF0] // carga longitud del nombre
:00406412 3BC1 cmp eax, ecx // compara contador con longitud
:00406414 7D4E jge 00406464 // si es >= salta (se procesaron todos los caracteres)
:00406416 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // recarga contador
:0040641D 8B8DFCFEFFFF mov ecx, dword ptr [ebp+FFFFFEFC] // lee puntero al nombre
:00406423 660FB60401 movzx ax, byte ptr [ecx+eax] // obtiene caracter del nombre
:00406428 50 push eax // pasa caracter a función
:00406429 E8A4010000 call 004065D2 // llama a una función que comprueba si el caracter es válido
:0040642E 85C0 test eax, eax // es válido
:00406430 7430 je 00406462 // no, salta
:00406432 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:00406439 0FBF8DF4FDFFFF movsx ecx, word ptr [ebp+FFFFFDF4] // lee contador de caracteres copiados
:00406440 8B95FCFEFFFF mov edx, dword ptr [ebp+FFFFFEFC] // y dirección del nombre
:00406446 8A0402 mov al, byte ptr [edx+eax] // lee caracter
:00406449 88840DFCFDFFFF mov byte ptr [ebp+ecx-00000204], al // copia a un buffer
:00406450 668B85F4FDFFFF mov ax, word ptr [ebp+FFFFFDF4] // lee contador de caracteres copiados
:00406457 66050100 add ax, 0001 // incremente contador
:0040645B 668985F4FDFFFF mov word ptr [ebp+FFFFFDF4], ax // y lo guarda
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406430(C)
:00406462 EB8E jmp 004063F2 // salta para procesar siguiente carácter (este bucle copia los carácteres del nombre introducido que sean válidos a otro buffer)
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406414(C)
:00406464 668B85F4FDFFFF mov ax, word ptr [ebp+FFFFFDF4] // lee contador de caracteres copiados
:0040646B 668985F0FDFFFF mov word ptr [ebp+FFFFFDF0], ax // y copia donde habia la longitud total del nombre original
:00406472 6683A5F8FDFFFF00 and word ptr [ebp+FFFFFDF8], 0000 // y borra contador de caracteres procesados
:0040647A EB12 jmp 0040648E // salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004064F8(U)
:0040647C 668B85F8FDFFFF mov ax, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:00406483 66050100 add ax, 0001 // incrementa (podría ser inc AX que es más corto)
:00406487 668985F8FDFFFF mov word ptr [ebp+FFFFFDF8], ax // y guarda
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040647A(U)
:0040648E 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // carga contador de caracteres procesados
:00406495 0FBF8DF0FDFFFF movsx ecx, word ptr [ebp+FFFFFDF0] // carga longitud del nombre (una vez se eliminaron todos los caracteres no válidos)
:0040649C 3BC1 cmp eax, ecx // compara contador con longitud
:0040649E 7D5A jge 004064FA // si >= salta (se procesaron todos)
:004064A0 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:004064A7 83E001 and eax, 00000001 // mira si el último bit del contador está a 1, con esto se consigue
:004064AA 85C0 test eax, eax // comprobar si el contador es impar
:004064AC 7426 je 004064D4 // no es impar, salta y no procesa el carácter
:004064AE 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // recarga contador
:004064B5 660FB68405FCFDFFFF movzx ax, byte ptr [ebp+eax-00000204] // lee caracter del nombre (el nombre con caracteres validos)
:004064BE 50 push eax // pasa a función que pasa el caracter en posición impar a mayúsculas
:004064BF E8F1010000 call 004066B5 // siempre que se den ciertas condiciones (ver función)
:004064C4 0FBF8DF8FDFFFF movsx ecx, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:004064CB 88840DFCFDFFFF mov byte ptr [ebp+ecx-00000204], al // guarda caracter convertido (usando ecx como indice)
:004064D2 EB24 jmp 004064F8 // salta para procesar siguiente carácter (este bucle convierte los caracteres en posiciones impares del nombre, de minusculas a mayúsculas siempre que se den ciertas condiciones)
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004064AC(C)
:004064D4 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:004064DB 660FB68405FCFDFFFF movzx ax, byte ptr [ebp+eax-00000204] // y carácter a procesar
:004064E4 50 push eax // pasa carácter como argumento
:004064E5 E87C020000 call 00406766 // a función que pasa el caracter en posicion par a minúsculas siempre que se den ciertas condiciones (ver función)
:004064EA 0FBF8DF8FDFFFF movsx ecx, word ptr [ebp+FFFFFDF8] // lee contador de carácteres procesados
:004064F1 88840DFCFDFFFF mov byte ptr [ebp+ecx-00000204], al // y guarda carácter convertido
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004064D2(U)
:004064F8 EB82 jmp 0040647C // salta para procesar siguiente carácter
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040649E(C)
:004064FA 6683A5F8FDFFFF00 and word ptr [ebp+FFFFFDF8], 0000 // borra contador si no es 0, si es 0 será 1
:00406502 6683A5F4FDFFFF00 and word ptr [ebp+FFFFFDF4], 0000 // borra contador si no es 0, si es 0 será 1
:0040650A EB12 jmp 0040651E // salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406595(U)
:0040650C 668B85F8FDFFFF mov ax, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:00406513 66050100 add ax, 0001 // suma 1
:00406517 668985F8FDFFFF mov word ptr [ebp+FFFFFDF8], ax // guarda contador
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040650A(U)
:0040651E 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:00406525 0FBF8DF0FDFFFF movsx ecx, word ptr [ebp+FFFFFDF0] // lee longitud del nombre
:0040652C 3BC1 cmp eax, ecx // compara contador con longiutd
:0040652E 7D6A jge 0040659A // si >= salta
:00406530 0FBF85F0FDFFFF movsx eax, word ptr [ebp+FFFFFDF0] // lee longitud
:00406537 0FBF8DF8FDFFFF movsx ecx, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:0040653E 2BC1 sub eax, ecx // resta longitud a contador
:00406540 0FBF8DF4FDFFFF movsx ecx, word ptr [ebp+FFFFFDF4] // lee contador de caracteres copiados
:00406547 8A8405FBFDFFFF mov al, byte ptr [ebp+eax-00000205] // lee último carácter del nombre
:0040654E 88840D00FFFFFF mov byte ptr [ebp+ecx-00000100], al // copia a un buffer
:00406555 668B85F4FDFFFF mov ax, word ptr [ebp+FFFFFDF4] // lee contador de caracteres copiados
:0040655C 66050100 add ax, 0001 // suma 1
:00406560 668985F4FDFFFF mov word ptr [ebp+FFFFFDF4], ax // guarda contador
:00406567 0FBF85F8FDFFFF movsx eax, word ptr [ebp+FFFFFDF8] // lee contador de caracteres procesados
:0040656E 0FBF8DF4FDFFFF movsx ecx, word ptr [ebp+FFFFFDF4] // lee contador de caracteres copiados
:00406575 8A8405FCFDFFFF mov al, byte ptr [ebp+eax-00000204] // lee caracter
:0040657C 88840D00FFFFFF mov byte ptr [ebp+ecx-00000100], al // copia a un buffer
:00406583 668B85F4FDFFFF mov ax, word ptr [ebp+FFFFFDF4] // lee contador de caracteres copiados
:0040658A 66050100 add ax, 0001 // incrementa contador
:0040658E 668985F4FDFFFF mov word ptr [ebp+FFFFFDF4], ax // guarda contador
:00406595 E972FFFFFF jmp 0040650C // salta para procesar resto de carácteres (este bucle copia el nombre usando un carácter del final de la cadena y otro del principio, )
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040652E(C)
:0040659A 668B85F4FDFFFF mov ax, word ptr [ebp+FFFFFDF4] // copia contador de caracteres copiados
:004065A1 668985F0FDFFFF mov word ptr [ebp+FFFFFDF0], ax // y lo copia donde habia la longitud del nombre
:004065A8 0FBF85F0FDFFFF movsx eax, word ptr [ebp+FFFFFDF0] // lee longitud
:004065AF 80A40500FFFFFF00 and byte ptr [ebp+eax-00000100], 00 // y pone NULL al final de la cadena con el nombre invertido
:004065B7 8D8500FFFFFF lea eax, dword ptr [ebp+FFFFFF00] // lee buffer con nombre al reves
:004065BD 50 push eax // pasa para copiar
:004065BE FF750C push [ebp+0C] // pasa dirección donde copiar el nombre invertido
* Reference To: KERNEL32.lstrcpyA, Ord:0302h
:004065C1 FF155CC14000 Call dword ptr [0040C15C] // copia el nombre final
:004065C7 668B85F0FDFFFF mov ax, word ptr [ebp+FFFFFDF0] // y retorna su longitud en AX
:004065CE C9 leave // sale volviendo a 004062CF
:004065CF C20800 ret 0008

Lo siguiente comprueba que el carácter pasado se encuentre dentro de unos rangos válidos, retornando 1 en EAX si el caracter es válido.Se llama desde 406429. Notese que la codificación de esta rutina es bastante pesima debido a su constante acceso al carácter, se podría haber guardado en un registro y así eliminado código innecesario.

ebp+8 -> caracter

:004065D2 55 push ebp
:004065D3 8BEC mov ebp, esp
:004065D5 0FB74508 movzx eax, word ptr [ebp+08] // lee caracter
:004065D9 83F830 cmp eax, 00000030 // es inferior al caracter '0'
:004065DC 7C0D jl 004065EB // si salta
:004065DE 0FB74508 movzx eax, word ptr [ebp+08]
:004065E2 83F839 cmp eax, 00000039 // es <= que el caracter '9'
:004065E5 0F8EBF000000 jle 004066AA // si salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004065DC(C)
:004065EB 0FB74508 movzx eax, word ptr [ebp+08]
:004065EF 83F841 cmp eax, 00000041 // es < que el caracter 'A'
:004065F2 7C0D jl 00406601 // si, salta
:004065F4 0FB74508 movzx eax, word ptr [ebp+08]
:004065F8 83F85A cmp eax, 0000005A // es <= que el caracter 'Z'
:004065FB 0F8EA9000000 jle 004066AA // si, salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004065F2(C)
:00406601 0FB74508 movzx eax, word ptr [ebp+08]
:00406605 83F861 cmp eax, 00000061 // es < que el caracter 'a'
:00406608 7C0D jl 00406617 // si, salta
:0040660A 0FB74508 movzx eax, word ptr [ebp+08]
:0040660E 83F87A cmp eax, 0000007A // es <= que el caracter 'z'
:00406611 0F8E93000000 jle 004066AA // si salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406608(C)
:00406617 0FB74508 movzx eax, word ptr [ebp+08]
:0040661B 3D83000000 cmp eax, 00000083 // es = a 83h
:00406620 0F8484000000 je 004066AA // si, salta
:00406626 0FB74508 movzx eax, word ptr [ebp+08]
:0040662A 3D8A000000 cmp eax, 0000008A // es = a 8Ah
:0040662F 7479 je 004066AA // si, salta
:00406631 0FB74508 movzx eax, word ptr [ebp+08]
:00406635 3D8C000000 cmp eax, 0000008C // es = a 8Ch
:0040663A 746E je 004066AA // si salta
:0040663C 0FB74508 movzx eax, word ptr [ebp+08]
* Possible Reference to Dialog: DialogID_009A
:00406640 3D9A000000 cmp eax, 0000009A // es = a 9Ah
:00406645 7463 je 004066AA // si, salta
:00406647 0FB74508 movzx eax, word ptr [ebp+08]
:0040664B 3D9C000000 cmp eax, 0000009C // es = a 9Ch
:00406650 7458 je 004066AA
:00406652 0FB74508 movzx eax, word ptr [ebp+08]
:00406656 3D9F000000 cmp eax, 0000009F // es = a 9Fh
:0040665B 744D je 004066AA // si, salta
:0040665D 0FB74508 movzx eax, word ptr [ebp+08]
:00406661 3DB2000000 cmp eax, 000000B2 // es = a B2h
:00406666 7442 je 004066AA // si, salta
:00406668 0FB74508 movzx eax, word ptr [ebp+08]
:0040666C 3DB3000000 cmp eax, 000000B3 // es = a B3h
:00406671 7437 je 004066AA // si, salta
:00406673 0FB74508 movzx eax, word ptr [ebp+08]
:00406677 3DB9000000 cmp eax, 000000B9 // es = B9h
:0040667C 742C je 004066AA // si, salta
:0040667E 0FB74508 movzx eax, word ptr [ebp+08]
:00406682 3DC0000000 cmp eax, 000000C0 // es < C0h
:00406687 7C26 jl 004066AF // si, salta
:00406689 0FB74508 movzx eax, word ptr [ebp+08]
:0040668D 3DFF000000 cmp eax, 000000FF // es > FFh
:00406692 7F1B jg 004066AF // si, salta
:00406694 0FB74508 movzx eax, word ptr [ebp+08]
:00406698 3DD7000000 cmp eax, 000000D7 // es = a D7h
:0040669D 7410 je 004066AF // si, salta
:0040669F 0FB74508 movzx eax, word ptr [ebp+08]
:004066A3 3DF7000000 cmp eax, 000000F7 // es = a F7h
:004066A8 7405 je 004066AF // si, salta
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004065E5(C), :004065FB(C), :00406611(C), :00406620(C), :0040662F(C) |:0040663A(C), :00406645(C), :00406650(C), :0040665B(C), :00406666(C) |:00406671(C), :0040667C(C)
:004066AA 6A01 push 00000001 // empuja 1
:004066AC 58 pop eax // lo saca y EAX = 1
:004066AD EB02 jmp 004066B1 // salta y retornará con EAX=1
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00406687(C), :00406692(C), :0040669D(C), :004066A8(C)
:004066AF 33C0 xor eax, eax // borrar EAX
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004066AD(U)
:004066B1 5D pop ebp // restaura pila
:004066B2 C20400 ret 0004 // y sale

La siguiente rutina convierte el carácter pasado en mayúsculas, siempre que este entre 'a' y 'z', si el carácter ya está en mayúsculas no lo convierte. Algunos caracteres que no son letras tiene un tratamiento especial y son sustituidos por otros (esto es muy raro que suceda ya que en el nombre no apareceran estos carácteres). Los carácteres en minúsculas que esten acentuados son convertidos a mayúsculas conservando su acento. Notese que la codificación de esta rutina es bastante pesima debido a su constante acceso al carácter, se podría haber guardado en un registro y así eliminado código innecesario.

ebp+8 -> caracter

:004066B5 55 push ebp // reajusta pila
:004066B6 8BEC mov ebp, esp
:004066B8 0FB74508 movzx eax, word ptr [ebp+08] // copia caracter pasado a EAX
:004066BC 83F861 cmp eax, 00000061 // es < a 61h 'a'
:004066BF 7C1A jl 004066DB // si, salta
:004066C1 0FB74508 movzx eax, word ptr [ebp+08]
:004066C5 83F87A cmp eax, 0000007A // es > a 7Ah 'z'
:004066C8 7F11 jg 004066DB // si, salta
:004066CA 668B4508 mov ax, word ptr [ebp+08] // lee caracter de nuevo
:004066CE 662D2000 sub ax, 0020 // resta 20h (esto lo convierte en mayusculas)
:004066D2 66894508 mov word ptr [ebp+08], ax // y lo guarda
:004066D6 E984000000 jmp 0040675F // y salta
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004066BF(C), :004066C8(C)
:004066DB 0FB74508 movzx eax, word ptr [ebp+08] // lee caracter
:004066DF 3DE0000000 cmp eax, 000000E0 // es < a E0h 'à'
:004066E4 7C2F jl 00406715 // si, salta
:004066E6 0FB74508 movzx eax, word ptr [ebp+08]
:004066EA 3DFE000000 cmp eax, 000000FE // es > a FEh
:004066EF 7F24 jg 00406715 // si, salta
:004066F1 0FB74508 movzx eax, word ptr [ebp+08]
:004066F5 3DF0000000 cmp eax, 000000F0 // es = a F0H
:004066FA 7419 je 00406715 // si, salta
:004066FC 0FB74508 movzx eax, word ptr [ebp+08]
:00406700 3DF7000000 cmp eax, 000000F7 // es = a F7h
:00406705 740E je 00406715 // si, salta
:00406707 668B4508 mov ax, word ptr [ebp+08] // lee caracter
:0040670B 662D2000 sub ax, 0020 // y resta 20h
:0040670F 66894508 mov word ptr [ebp+08], ax // y lo copia donde estaba
:00406713 EB4A jmp 0040675F // salta para salir retornando el nuevo caracter
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004066E4(C), :004066EF(C), :004066FA(C), :00406705(C)
:00406715 0FB74508 movzx eax, word ptr [ebp+08] // lee carácter
* Possible Reference to Dialog: DialogID_009A
:00406719 3D9A000000 cmp eax, 0000009A // es <> a 9Ah
:0040671E 7508 jne 00406728 // si, salta
:00406720 66C745088A00 mov [ebp+08], 008A // sustituye por 8Ah
:00406726 EB37 jmp 0040675F // salta y sale retornando el nuevo caracter
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040671E(C)
:00406728 0FB74508 movzx eax, word ptr [ebp+08] // lee caracter
:0040672C 3D9C000000 cmp eax, 0000009C // es <> a 9Ch
:00406731 7508 jne 0040673B // si, salta
:00406733 66C745088C00 mov [ebp+08], 008C // sustituye por 8Ah
:00406739 EB24 jmp 0040675F // salta y sale retornando el nuevo caracter
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406731(C)
:0040673B 0FB74508 movzx eax, word ptr [ebp+08] // lee caracter
:0040673F 3DF0000000 cmp eax, 000000F0 // es <> a F0h
:00406744 7508 jne 0040674E // si, salta
:00406746 66C74508D000 mov [ebp+08], 00D0 // sustituye por D0h
:0040674C EB11 jmp 0040675F // salta y sale retornando el nuevo caracter
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406744(C)
:0040674E 0FB74508 movzx eax, word ptr [ebp+08] // lee caracter
:00406752 3DFF000000 cmp eax, 000000FF // es <> a FFh
:00406757 7506 jne 0040675F // si, salta y el caracter no se tocará (esto sucederá cuando sea un caracter con código inferior a 9A)
:00406759 66C745089F00 mov [ebp+08], 009F // sustituye por 9Fh
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004066D6(U), :00406713(U), :00406726(U), :00406739(U), :0040674C(U) |:00406757(C)
:0040675F 8A4508 mov al, byte ptr [ebp+08] // copia carácter convertido y lo retorna en AL
:00406762 5D pop ebp // restaura pila y
:00406763 C20400 ret 0004 // sale

La siguiente rutina convierte el carácter pasado en minúsculas, siempre que este entre 'A' y 'Z', si el carácter ya está en mayúsculas no lo convierte. Algunos caracteres que no son letras tiene un tratamiento especial y son sustituidos por otros. Los caracteres en minúsculas que estén acentuados son pasados a mayúsculas conservando su acento. Notese que la codificación de esta rutina es bastante pesima debido a su constante acceso al carácter, se podría haber guardado en un registro y así eliminado código innecesario.

ebp+8 -> caracter

:00406766 55 push ebp // ajusta pila
:00406767 8BEC mov ebp, esp
:00406769 0FB74508 movzx eax, word ptr [ebp+08] // lee carácter
:0040676D 83F841 cmp eax, 00000041 // es < a 41h 'A'
:00406770 7C1A jl 0040678C // si, salta
:00406772 0FB74508 movzx eax, word ptr [ebp+08]
:00406776 83F85A cmp eax, 0000005A // es > a 5A 'Z'
:00406779 7F11 jg 0040678C // si, salta
:0040677B 668B4508 mov ax, word ptr [ebp+08] // el caracter está entre 'A' y 'Z'
:0040677F 66052000 add ax, 0020 // le suma 20h convirtiendolos en minúsculas
:00406783 66894508 mov word ptr [ebp+08], ax // copia caracter convertido
:00406787 E984000000 jmp 00406810 // salta al final de la rutina
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00406770(C), :00406779(C)
:0040678C 0FB74508 movzx eax, word ptr [ebp+08] // lee carácter
:00406790 3DC0000000 cmp eax, 000000C0 // es inferior a C0h 'À'
:00406795 7C2F jl 004067C6 // si, salta
:00406797 0FB74508 movzx eax, word ptr [ebp+08]
:0040679B 3DDE000000 cmp eax, 000000DE // es > a DEh
:004067A0 7F24 jg 004067C6 // si, salta
:004067A2 0FB74508 movzx eax, word ptr [ebp+08]
:004067A6 3DD0000000 cmp eax, 000000D0 // es = a D0
:004067AB 7419 je 004067C6 // si, salta
:004067AD 0FB74508 movzx eax, word ptr [ebp+08]
:004067B1 3DD7000000 cmp eax, 000000D7 // es = a D7h
:004067B6 740E je 004067C6 // si, salta
:004067B8 668B4508 mov ax, word ptr [ebp+08] // no, el carácter está entre C0 y DEh y no es D0 ni D7
:004067BC 66052000 add ax, 0020 // le suma 20h
:004067C0 66894508 mov word ptr [ebp+08], ax // y lo guarda
:004067C4 EB4A jmp 00406810 // salta al final de la rutina
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00406795(C), :004067A0(C), :004067AB(C), :004067B6(C)
:004067C6 0FB74508 movzx eax, word ptr [ebp+08] // lee carácter
:004067CA 3D8A000000 cmp eax, 0000008A // es <> a 8A
:004067CF 7508 jne 004067D9 // si, salta
:004067D1 66C745089A00 mov [ebp+08], 009A // no, reemplaza por 9Ah
:004067D7 EB37 jmp 00406810 // y salta al final
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004067CF(C)
:004067D9 0FB74508 movzx eax, word ptr [ebp+08] // lee carácter
:004067DD 3D8C000000 cmp eax, 0000008C // es <> a 8Ch
:004067E2 7508 jne 004067EC // si, salta
:004067E4 66C745089C00 mov [ebp+08], 009C // no, reemplaza por 9Ch
:004067EA EB24 jmp 00406810
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004067E2(C)
:004067EC 0FB74508 movzx eax, word ptr [ebp+08] // lee carácter
:004067F0 3DD0000000 cmp eax, 000000D0 // es <> a D0h
:004067F5 7508 jne 004067FF // si, salta
:004067F7 66C74508F000 mov [ebp+08], 00F0 // no, reemplaza por F0h
:004067FD EB11 jmp 00406810 // salta al final de la rutina
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004067F5(C)
:004067FF 0FB74508 movzx eax, word ptr [ebp+08] // lee caracter
:00406803 3D03010000 cmp eax, 00000103 // es <> a 103h
:00406808 7506 jne 00406810 // si, salta
:0040680A 66C74508FF00 mov [ebp+08], 00FF // no, reemplaza por FFh
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00406787(U), :004067C4(U), :004067D7(U), :004067EA(U), :004067FD(U) |:00406808(C)
:00406810 8A4508 mov al, byte ptr [ebp+08] // copia carácter
:00406813 5D pop ebp // y sale
:00406814 C20400 ret 0004

El algoritmo es bastante extenso, pero no quiere decir que sea complejo, el nombre se manipula de tal manera que primero se anulan los caracteres no válidos (ver 4063BB), luego se procesa la cadena resultante dentro de un bucle que va comprobando si la posición de cada carácter es par o impar, y según esto convierte dicho caracter a mayúsculas o minúsculas (ver 4064A7 algunos carácteres tiene un tratamiento especial ver rutinas 4066B5 y 406766 ), con el resultado crea una nueva cadena que contiene los caracteres de la anterior, pero intercalando uno del final y otro del principio y a su vez se le añade la inversa de lo que se va copiando (ver 40651E) al final de la cadena, resultando una cadena capicua. Luego se retorna a 4062CF, donde se guarda la longitud resultante y se inicia el cálculo del número de serie real utilizando la cadena resultante y otra que el programa guarda en memória. El número se va guardando en ECX (ver 406378) y se retorna este valor en EAX una vez se finaliza el cálculo. A partir de aquí el funcionamiento del resto del algoritmo es bastante irrelevante (ver más abajo 4069C8, continuación 4069C3) ya que simplemente se convierte la cadena numérica que nosotros introdujimos como número de série a su valor en hexadecimal y se compara con el resultado del cálculo que realizamos sobre el nombre, si no son iguales el número no es correcto. También se buscan guiones en el número introducido (4069DB), esto indica si es un número de licencia múltiple o sencilla. El primer grupo de dígitos siempre será igual para los números de licencia sencilla y los de licencia multiple, en cambio para los de evaluación no. Por lo tanto calcular os numeros de licencia sencilla solo debemos imitar el algoritmo de cálculo que se realiza sobre el nombre y cálcular la representación decimal del resultado (facilmente implementable usando wsprintf o printf).Para los de licencia multiple se puede ver más abajo como se calcula el resto del número (406A83).

* Reference To: KERNEL32.lstrcpyA, Ord:0302h
:004069BD FF155CC14000 Call dword ptr [0040C15C] // copia el número entero
:004069C3 668365E800 and word ptr [ebp-18], 0000 // pone a 0 variable local de la rutina, (Nota: cuando el primer caracter es 'E' este flag vale 1 tal y como se establece en 40699C)
:004069C8 6A1E push 0000001E // pasa 1eh
:004069CA FF7510 push [ebp+10] // y puntero al nombre
:004069CD FF750C push [ebp+0C] // y Ah (este es el valor al que se inicializa la hash por defecto)
:004069D0 FF7508 push [ebp+08] // y primer argumento
:004069D3 E8BAF8FFFF call 00406292 // para función que cálcula valor hash del nombre
:004069D8 8945BC mov dword ptr [ebp-44], eax // copia resultado en variable local y continua, este es el número de série válido expresado en hex, notese que cuando el número es de evaluación (comienza por 'E') el cálculo de este número se realiza con otros parámetros
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004069B4(U)
:004069DB 6A2D push 0000002D // continuamos igual, tanto si empezaba por E o por otra cosa. pasa 2Dh como segundo argumento '-' extraño mmmm.... (esto puede indicar que tengamos que el número de serie se componga de varios grupos de dígitos separados por guiones)
:004069DD 8D45C8 lea eax, dword ptr [ebp-38] // y puntero al número introducido
:004069E0 50 push eax // como primer argumento
:004069E1 E85A3B0000 call 0040A540 // Esta rutina busca el primer caracter 2Dh '-' en el número, retornando la dirección en el buffer donde se encontro, si no hay guiones devuelve , vaya parece que hay más de un tipo de números mmmm...
:004069E6 59 pop ecx //
:004069E7 59 pop ecx //
:004069E8 8945EC mov dword ptr [ebp-14], eax // guarda dirección donde se encuentra el primer guión
:004069EB 8365B800 and dword ptr [ebp-48], 00000000 //
:004069EF 837DEC00 cmp dword ptr [ebp-14], 00000000 // lee puntero al número introducido
:004069F3 742F je 00406A24 // si no había ningún guión salta (este salto siempre lo toma a no ser que el numero contenga algún caracter de guión'-' )
:004069F5 8B45EC mov eax, dword ptr [ebp-14] // hay guión, copia dirección donde este se encuentra
:004069F8 802000 and byte ptr [eax], 00 // y lo borra poniendolo a 0
:004069FB 8B45EC mov eax, dword ptr [ebp-14] // se vuelve a cargar el puntero
:004069FE 40 inc eax // y se incrementa, pasando al siguiente carácter
:004069FF 8945EC mov dword ptr [ebp-14], eax // y lo vuelve a guardar, joder se repite más que el ajo ;)
:00406A02 6A2D push 0000002D // y comprueba si hay otro guión en el resto del número
:00406A04 FF75EC push [ebp-14]
:00406A07 E8343B0000 call 0040A540
:00406A0C 59 pop ecx
:00406A0D 59 pop ecx
:00406A0E 8945B8 mov dword ptr [ebp-48], eax // guarda dirección del siguiente guión
:00406A11 837DB800 cmp dword ptr [ebp-48], 00000000 // hay otro guión '-' ?
:00406A15 740D je 00406A24 // no, salta
:00406A17 8B45B8 mov eax, dword ptr [ebp-48] // pues igual que antes, lo borra
:00406A1A 802000 and byte ptr [eax], 00
:00406A1D 8B45B8 mov eax, dword ptr [ebp-48]
:00406A20 40 inc eax // incrementa el contador
:00406A21 8945B8 mov dword ptr [ebp-48], eax // y lo vuelve a guardar , parece que solo busca dos guiones, por lo que el otro tipo de números tendrá un formato algo asi: xxx - xxx - xxx
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004069F3(C), :00406A15(C)
:00406A24 6A0A push 0000000A // se computa el valor de la cadena pasada en base hexadecimal
:00406A26 6A00 push 00000000 // es decir pasamos el puntero al numero introducido y esta función nos
:00406A28 8D45C8 lea eax, dword ptr [ebp-38] // devuelve en EAX
:00406A2B 50 push eax // el valor hexadecimal del número introducido
:00406A2C E8573E0000 call 0040A888 // que lo guarda y lo
:00406A31 83C40C add esp, 0000000C // compara con el hash del nombre
:00406A34 8945F4 mov dword ptr [ebp-0C], eax // entonces quiere decir que con solo calcular el hash del nombre
:00406A37 8B45F4 mov eax, dword ptr [ebp-0C] // y luego pasarlo a una cadena decimal obtendremos el
:00406A3A 3B45BC cmp eax, dword ptr [ebp-44] // numero de serie válido (para licencias sencillas)
:00406A3D 0F85A0010000 jne 00406BE3 // salta si no es igual al hash del nombre
:00406A43 837DEC00 cmp dword ptr [ebp-14], 00000000 // tenemos primer guión?
:00406A47 7414 je 00406A5D // no, solo hay dígitos saltamos
:00406A49 837DEC00 cmp dword ptr [ebp-14], 00000000 // miramos de nuevo si tenemos (otra vez?)
:00406A4D 0F8490010000 je 00406BE3 // no, salta
:00406A53 837DB800 cmp dword ptr [ebp-48], 00000000 // hay un segundo guión ?
:00406A57 0F8486010000 je 00406BE3 // no, salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406A47(C)
:00406A5D 0FBF45E8 movsx eax, word ptr [ebp-18] // Ok el número es correcto
:00406A61 85C0 test eax, eax // mira el flag que nos marca si el número empieza por 'E'
:00406A63 0F85AD000000 jne 00406B16 // si hay 'E' como 1er carácter saltamos adelante
:00406A69 837DEC00 cmp dword ptr [ebp-14], 00000000 // no hay 'E', miramos si tenemos dirección para un segundo guión
:00406A6D 7514 jne 00406A83 // si hay, saltamos
:00406A6F 8B4518 mov eax, dword ptr [ebp+18] // es un número simple, licencia sencilla sin 'E' carga puntero al sexto arguento
:00406A72 C70001000000 mov dword ptr [eax], 00000001 // copia 1 en esa dirección (esto indica número válido)
:00406A78 66C745A80100 mov [ebp-58], 0001 // y en variable local
:00406A7E E98E000000 jmp 00406B11 // salta incondicionalmente y el numero es válido
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406A6D(C)
:00406A83 6A0A push 0000000A // números sin 'E' con varios grupos de dígitos (varios guiones), estos numeros corresponden a números para multiples licencias
:00406A85 6A00 push 00000000 // calculamos valor hex
:00406A87 FF75EC push [ebp-14] // pasa puntero al segundo grupo de dígitos introducido
:00406A8A E8F93D0000 call 0040A888 // calculamos valor hex del segundo grupo de dígitos, este grupo indica el número de ordenadores para el que está licenciado el programa multiplicado por 10 (p. ej licencia para 35 maquinas sería un 350 en hex 15E)
:00406A8F 83C40C add esp, 0000000C
:00406A92 8945AC mov dword ptr [ebp-54], eax // guarda resultado
:00406A95 6A0A push 0000000A // empuja AH (10) el iterador
:00406A97 FF7510 push [ebp+10] // empuja puntero al nombre
:00406A9A 8B45AC mov eax, dword ptr [ebp-54] // copia valor hex a EAX (inutil)
:00406A9D 33D2 xor edx, edx // borra EDX para dividir
:00406A9F 6A0A push 0000000A // mete y saca
:00406AA1 59 pop ecx
:00406AA2 F7F1 div ecx // divide numero de licencias entre Ah (10) dando el valor real ;)
:00406AA4 6BC00B imul eax, 0000000B // y multiplica por Bh (11)
:00406AA7 50 push eax // pasa resultado como valor de inicio de la hash
:00406AA8 FF7508 push [ebp+08] // y el primer argumento
:00406AAB E8E2F7FFFF call 00406292 // Calcula otra hash del nombre pero con argumentos diferentes (utiliza un calculo con el primer grupo de dígitos como se ve en 406A9A)
:00406AB0 8945F8 mov dword ptr [ebp-08], eax // guarda hash (corresponde al valor hex tercer grupo de dígitos)
:00406AB3 6A0A push 0000000A // calcula valor hex del trecer grupo de dígitos
:00406AB5 6A00 push 00000000
:00406AB7 FF75B8 push [ebp-48] // pasa puntero al tercer grupo de caracteres
:00406ABA E8C93D0000 call 0040A888 // calcula valor hex del tercer grupo de dígitos
:00406ABF 83C40C add esp, 0000000C
:00406AC2 8945A0 mov dword ptr [ebp-60], eax // guarda valor hex de los dígitos
:00406AC5 6A0A push 0000000A // pasa Ah (contador)
:00406AC7 FF7510 push [ebp+10] // y puntero al nombre normal (sin validar)
:00406ACA FF75A0 push [ebp-60] // y inicializa la hash al valor hex del tercer grupo de dígitos
:00406ACD FF7508 push [ebp+08] // y primer argumento
:00406AD0 E842FDFFFF call 00406817 // esta función calcula otra hash del nombre el resultado se devuelve en EAX, se usa más adelante para verificar los dígitos (ver función)
:00406AD5 8945F0 mov dword ptr [ebp-10], eax // y la guarda
:00406AD8 8B45F8 mov eax, dword ptr [ebp-08] // carga valor hex del tercer grupo de digitos (ver 406AAB)
:00406ADB 3B45A0 cmp eax, dword ptr [ebp-60] // compara con valor hex del tercer grupo de dígitos
:00406ADE 7531 jne 00406B11 // si no es igual salta (número no válido)
:00406AE0 8B45F0 mov eax, dword ptr [ebp-10] // hasta aquí se han calculado todos los digitos, ahora los verifica
:00406AE3 33D2 xor edx, edx // copia hash calculada en 406AD0 y borra EDX
:00406AE5 6A0B push 0000000B //
:00406AE7 59 pop ecx //
:00406AE8 F7F1 div ecx // divide hash entre Bh
:00406AEA 85D2 test edx, edx // comprueba si el resto 0 (esto indica que la hash es divisible entre Bh = 11)
:00406AEC 7523 jne 00406B11 // no es divisible, salta y número no válido
:00406AEE 8B45AC mov eax, dword ptr [ebp-54] // carga número de licencias
:00406AF1 33D2 xor edx, edx // borra EDX
:00406AF3 6A0A push 0000000A // y
:00406AF5 59 pop ecx //
:00406AF6 F7F1 div ecx // divide entre Ah (10)
:00406AF8 85D2 test edx, edx // comprueba si el resto 0 (esto indica que es divisible entre Ah = 10)
:00406AFA 7515 jne 00406B11 // no es divisible, salta y número no válido
:00406AFC 8B45F0 mov eax, dword ptr [ebp-10] // carga hash calculada en 406AD0
:00406AFF 33D2 xor edx, edx // y divide
:00406B01 6A0B push 0000000B
:00406B03 59 pop ecx
:00406B04 F7F1 div ecx // entre Bh (11)
:00406B06 8B4D18 mov ecx, dword ptr [ebp+18] // lee dirección del flag
:00406B09 8901 mov dword ptr [ecx], eax // guarda resultado de la division
:00406B0B 66C745A80100 mov [ebp-58], 0001 // número válido, saltaremos y todo irá OK ;), pues de aquí deducimos que el segundo grupo de dígitos son el número de licencias que queremos pero multiplicado por 10 (Ah), Y que el tercer grupo de dígitos se calcula a partir del numero de licencias y del nombre validado, tal y como vemos en 406AA2, el numero tendra este formato: hash1 nombre validado - numero licencias * 10 - hash2 nombre calculada con numero de licencias *
Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00406A7E(U), :00406ADE(C), :00406AEC(C), :00406AFA(C)
:00406B11 E9CD000000 jmp 00406BE3 // salta incondicionalmente
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406A63(C)
:00406B16 6A0A push 0000000A // caracteres con tres grupos de dígitos y que empiezan por 'E' , numeros de evaluación
:00406B18 6A00 push 00000000 // vamos allá ;)
:00406B1A FF75EC push [ebp-14]
:00406B1D E8663D0000 call 0040A888 // Calcula valor hex del segundo grupo de digitos introducido
:00406B22 83C40C add esp, 0000000C
:00406B25 8945FC mov dword ptr [ebp-04], eax // lo guarda
:00406B28 6A0A push 0000000A
:00406B2A 6A00 push 00000000
:00406B2C FF75B8 push [ebp-48]
:00406B2F E8543D0000 call 0040A888 // y calcula valor hex del tercer grupo de dígitos introducidos
:00406B34 83C40C add esp, 0000000C
:00406B37 8945C0 mov dword ptr [ebp-40], eax // lo guarda
:00406B3A 6A0A push 0000000A
:00406B3C FF7510 push [ebp+10] // pasa dirección del nombre normal sin validar
:00406B3F FF75C0 push [ebp-40] // y valor hex del tercer grupo de dígitos
:00406B42 FF7508 push [ebp+08] // y primer argumento
:00406B45 E8CDFCFFFF call 00406817 // calcula hash del nombre validado usando como hash inicial el valor hex del tercer grupo de caracteres
:00406B4A 8945B4 mov dword ptr [ebp-4C], eax // guarda hash
:00406B4D 68CD070000 push 000007CD // pasa 7CD = 2000, mmmm... año 2000 ?
:00406B52 FF75B4 push [ebp-4C] // y hash anterior
:00406B55 8B4D1C mov ecx, dword ptr [ebp+1C] // copia en ECX sexto argumento
:00406B58 E8C12A0000 call 0040961E // llama rutina, parece que se calcula el año de expiración
:00406B5D 8B45FC mov eax, dword ptr [ebp-04] // lee valor hex del segundo argumento
:00406B60 33D2 xor edx, edx // prepara para dividir
:00406B62 6A32 push 00000032
:00406B64 59 pop ecx
:00406B65 F7F1 div ecx // divide valor hex entre 32h
:00406B67 52 push edx // guarda resto (dias)
:00406B68 8B45FC mov eax, dword ptr [ebp-04] // lee valor hex del segundo argumento
:00406B6B 33D2 xor edx, edx // prepara para dividir
:00406B6D 6A32 push 00000032
:00406B6F 59 pop ecx
:00406B70 F7F1 div ecx // divide valor hex entre 32h otra vez (dando el mes)
:00406B72 50 push eax // guarda resultado, estas dos últimas multiplicaciones calculan lo que parece ser el día y més hasta el cual vale el número
:00406B73 8B4D1C mov ecx, dword ptr [ebp+1C] // lee sexto argumento
:00406B76 E897000000 call 00406C12 // obtine parte alta del valor calculado en 40961E
:00406B7B 50 push eax // lo pasa como parametro
:00406B7C 8D4DC4 lea ecx, dword ptr [ebp-3C] // para combinarlo con el resultado de las divisiones anteriores
:00406B7F E867000000 call 00406BEB // combina valores, dando como resultado un dword con el dia-mes-año y año de caducidad
:00406B84 6A0A push 0000000A // pasa 0xA
:00406B86 FF7510 push [ebp+10] // y puntero al nombre
:00406B89 68CD070000 push 000007CD // y 0x7CD -> de nuevo año 2000 mmm...
:00406B8E 8D4DC4 lea ecx, dword ptr [ebp-3C] // lee valor combinado en ECX
:00406B91 E8F1290000 call 00409587
:00406B96 50 push eax
:00406B97 FF7508 push [ebp+08]
:00406B9A E8F3F6FFFF call 00406292
:00406B9F 8945A4 mov dword ptr [ebp-5C], eax
:00406BA2 8D4DB0 lea ecx, dword ptr [ebp-50]
:00406BA5 E8272B0000 call 004096D1
:00406BAA 8B45A4 mov eax, dword ptr [ebp-5C]
:00406BAD 3B45C0 cmp eax, dword ptr [ebp-40]
:00406BB0 7531 jne 00406BE3
:00406BB2 FF75B0 push [ebp-50]
:00406BB5 8B4D1C mov ecx, dword ptr [ebp+1C]
:00406BB8 E884000000 call 00406C41
:00406BBD 85C0 test eax, eax
:00406BBF 7422 je 00406BE3
:00406BC1 6A1E push 0000001E
:00406BC3 8D459C lea eax, dword ptr [ebp-64]
:00406BC6 50 push eax
:00406BC7 8D4DB0 lea ecx, dword ptr [ebp-50]
:00406BCA E891000000 call 00406C60
:00406BCF FF30 push dword ptr [eax]
:00406BD1 8B4D1C mov ecx, dword ptr [ebp+1C]
:00406BD4 E849000000 call 00406C22
:00406BD9 85C0 test eax, eax
:00406BDB 7406 je 00406BE3
:00406BDD 66C745A80100 mov [ebp-58], 0001
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00406A3D(C), :00406A4D(C), :00406A57(C), :00406B11(U), :00406BB0(C) |:00406BBF(C), :00406BDB(C)
:00406BE3 668B45A8 mov ax, word ptr [ebp-58] // copia flag, esto marca 1 si el número era válido, 0 si no lo era
:00406BE7 C9 leave // y se va...
:00406BE8 C21800 ret

Esta es otra de las rutinas de hash...

:00406817 55 push ebp
:00406818 8BEC mov ebp, esp
:0040681A 81EC20010000 sub esp, 00000120
:00406820 56 push esi
:00406821 0FBF4508 movsx eax, word ptr [ebp+08] // carga puntero al primer argumento
:00406825 8B0485CCEE4000 mov eax, dword ptr [4*eax+0040EECC] // obtiene la dirección de una cadena númerica = 613533840
:0040682C 8985F4FEFFFF mov dword ptr [ebp+FFFFFEF4], eax // y guarda dirección
:00406832 FFB5F4FEFFFF push dword ptr [ebp+FFFFFEF4] // para medir longitud de la cadena (9h)
* Reference To: KERNEL32.lstrlenA, Ord:0308h
:00406838 FF1528C14000 Call dword ptr [0040C128] // calcula longitud
:0040683E 8985ECFEFFFF mov dword ptr [ebp+FFFFFEEC], eax // guarda longitud
:00406844 8D85FCFEFFFF lea eax, dword ptr [ebp+FFFFFEFC] //
:0040684A 50 push eax // pasa dirección del nombre validado
:0040684B FF7510 push [ebp+10] // pasa dirección del nombre normal
:0040684E E868FBFFFF call 004063BB // y lo vuelve a validar borrando el anterior
:00406853 668985E0FEFFFF mov word ptr [ebp+FFFFFEE0], ax // guarda su longitud
:0040685A 0FBF4514 movsx eax, word ptr [ebp+14] // lee último argumento = Ah
:0040685E 48 dec eax // decrementa
:0040685F 0FBF8DE0FEFFFF movsx ecx, word ptr [ebp+FFFFFEE0] // lee contador de longitud del nombre
:00406866 99 cdq // borra EDX
:00406867 F7F9 idiv ecx // divide entre longitud
:00406869 668995F0FEFFFF mov word ptr [ebp+FFFFFEF0], dx // guarda el resto ((Ah-1) % longitud)
:00406870 8B450C mov eax, dword ptr [ebp+0C] // lee valor inicial de la suma en EAX
:00406873 8985F8FEFFFF mov dword ptr [ebp+FFFFFEF8], eax // y copia este valor
:00406879 0FBF4514 movsx eax, word ptr [ebp+14] // lee de nuevo el último argumento (Ah)
:0040687D 48 dec eax // decrementa (9h)
:0040687E 668985E4FEFFFF mov word ptr [ebp+FFFFFEE4], ax // y copia como contador de iteracciones
:00406885 EB12 jmp 00406899
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406949(U)
:00406887 668B85E4FEFFFF mov ax, word ptr [ebp+FFFFFEE4] // carga contador de iteracciones
:0040688E 662D0100 sub ax, 0001 // le resta 1
:00406892 668985E4FEFFFF mov word ptr [ebp+FFFFFEE4], ax // y lo guarda
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406885(U)
:00406899 0FBF85E4FEFFFF movsx eax, word ptr [ebp+FFFFFEE4] // lee contador de iteracciones
:004068A0 85C0 test eax, eax // es < 0 ?
:004068A2 0F8CA6000000 jl 0040694E // si, salta (se termino el calculo)
:004068A8 0FBF85F0FEFFFF movsx eax, word ptr [ebp+FFFFFEF0] // lee resto
:004068AF 8A8405FCFEFFFF mov al, byte ptr [ebp+eax-00000104] // lo usa como indice al nombre
:004068B6 8885E8FEFFFF mov byte ptr [ebp+FFFFFEE8], al // coge caracter y lo guarda
:004068BC 0FBF85E4FEFFFF movsx eax, word ptr [ebp+FFFFFEE4] // carga contador de iteracciones
:004068C3 83E001 and eax, 00000001 // comprueba si es par o impar
:004068C6 85C0 test eax, eax // es par ?
:004068C8 7409 je 004068D3 // si, salta
:004068CA C745FC02000000 mov [ebp-04], 00000002 // no es impar, el divisor será 2
:004068D1 EB07 jmp 004068DA // salta para multiplicar
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004068C8(C)
:004068D3 C745FC01000000 mov [ebp-04], 00000001 // es impar, el divisor será 1
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004068D1(U)
:004068DA 0FB68DE8FEFFFF movzx ecx, byte ptr [ebp+FFFFFEE8] // lee caracter sacado del nombre
:004068E1 0FB685E8FEFFFF movzx eax, byte ptr [ebp+FFFFFEE8] // en EAX y ECX
:004068E8 99 cdq // borra EDX para dividir
:004068E9 F7BDECFEFFFF idiv dword ptr [ebp+FFFFFEEC] // divide caracter entre longitud de la tabla (9h)
:004068EF 8B85F4FEFFFF mov eax, dword ptr [ebp+FFFFFEF4] // carga dirección de la tabla
:004068F5 0FB63410 movzx esi, byte ptr [eax+edx] // y usa resto com índice para coger caracter de la tabla en ESI
:004068F9 8BC1 mov eax, ecx // copia caracter que se saco por primera vez
:004068FB 99 cdq // y lo divide
:004068FC F7FE idiv esi // entre el que se saco usando el contador como indice
:004068FE 8B85F8FEFFFF mov eax, dword ptr [ebp+FFFFFEF8] // lee suma (la primera vez vale Ah)
:00406904 2BC2 sub eax, edx // y le resta el resto de la division
:00406906 8985F8FEFFFF mov dword ptr [ebp+FFFFFEF8], eax // y la guarda
:0040690C 8B85F8FEFFFF mov eax, dword ptr [ebp+FFFFFEF8] // y la vuelve a coger
:00406912 33D2 xor edx, edx // borra EDX
:00406914 F775FC div [ebp-04] // divide suma entre el divisor
:00406917 8985F8FEFFFF mov dword ptr [ebp+FFFFFEF8], eax // guarda resultado donde estaba la suma
:0040691D 668B85F0FEFFFF mov ax, word ptr [ebp+FFFFFEF0] // lee resto calculado en 406867
:00406924 662D0100 sub ax, 0001 // le quita 1
:00406928 668985F0FEFFFF mov word ptr [ebp+FFFFFEF0], ax // y lo guarda
:0040692F 0FBF85F0FEFFFF movsx eax, word ptr [ebp+FFFFFEF0]
:00406936 85C0 test eax, eax // es >=0 ?
:00406938 7D0F jge 00406949 // si, salta
:0040693A 0FBF85E0FEFFFF movsx eax, word ptr [ebp+FFFFFEE0] // lo vuelve a leer conservando el signo
:00406941 48 dec eax // y le resta 1
:00406942 668985F0FEFFFF mov word ptr [ebp+FFFFFEF0], ax // y lo guarda
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406938(C)
:00406949 E939FFFFFF jmp 00406887 // continuamos procesando
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004068A2(C)
:0040694E 8B85F8FEFFFF mov eax, dword ptr [ebp+FFFFFEF8] // coge suma final
:00406954 5E pop esi
:00406955 C9 leave
:00406956 C21000 ret 0010 // y retorna

Esta es la rutina que calcula el día y mes según el valor hex del tercer grupo de dígitos...

:0040961E 55 push ebp
:0040961F 8BEC mov ebp, esp
:00409621 51 push ecx
:00409622 51 push ecx
:00409623 894DF8 mov dword ptr [ebp-08], ecx // copia dirección a pila
:00409626 8B45F8 mov eax, dword ptr [ebp-08] // lee dirección
:00409629 C60001 mov byte ptr [eax], 01 // guarda un 1 en el primer byte
:0040962C 8B45F8 mov eax, dword ptr [ebp-08]
:0040962F C6400101 mov [eax+01], 01 // y otro 1 en el segundo
:00409633 837D0C00 cmp dword ptr [ebp+0C], 00000000 // comprueba si segundo argumento = 0 (7CDh)
:00409637 750B jne 00409644 // no, salta
:00409639 8B45F8 mov eax, dword ptr [ebp-08] // lee dirección en EAX
:0040963C 66C740026C07 mov [eax+02], 076C // y copia 76C (1900) al segundo word queda (01016c07)
:00409642 EB0B jmp 0040964F // y salta
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00409637(C)
:00409644 8B45F8 mov eax, dword ptr [ebp-08] // lee dirección con el año
:00409647 668B4D0C mov cx, word ptr [ebp+0C] // y segundo valor pasado (7CDh = 2000 )
:0040964B 66894802 mov word ptr [eax+02], cx // copia al 2 word de la dirección cargada
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00409642(U), :00409682(U)
:0040964F 8B45F8 mov eax, dword ptr [ebp-08] // lee dirección
:00409652 0FBF4002 movsx eax, word ptr [eax+02] // lee año
:00409656 50 push eax // lo pasa como parametro a función
:00409657 E892FEFFFF call 004094EE // que calcula el número de dias, devolviendo 365 para los años normales y 366 ? para los bisiestos, que extraño sería un dia menos no uno más ¿?
:0040965C 8945FC mov dword ptr [ebp-04], eax // guarda los dias
:0040965F 8B4508 mov eax, dword ptr [ebp+08] // lee hash pasada
:00409662 3B45FC cmp eax, dword ptr [ebp-04] // es <= que dias calculado ?
:00409665 7E1D jle 00409684 // si, salta adelante
:00409667 8B4508 mov eax, dword ptr [ebp+08] // lee hash
:0040966A 2B45FC sub eax, dword ptr [ebp-04] // le resta los dias
:0040966D 894508 mov dword ptr [ebp+08], eax // los guarda donde ya estaba la vieja hash
:00409670 8B45F8 mov eax, dword ptr [ebp-08] // lee dirección
:00409673 668B4002 mov ax, word ptr [eax+02] // y lee el año de nuevo
:00409677 66050100 add ax, 0001 // le suma 1 año
:0040967B 8B4DF8 mov ecx, dword ptr [ebp-08] // y lo guarda
:0040967E 66894102 mov word ptr [ecx+02], ax
:00409682 EBCB jmp 0040964F // salta y sigue procesando
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00409665(C), :004096BB(U)
:00409684 8B45F8 mov eax, dword ptr [ebp-08] // lee dirección
:00409687 0FBF4002 movsx eax, word ptr [eax+02] // y lee año
:0040968B 50 push eax // lo pasa como parametro
:0040968C 8B45F8 mov eax, dword ptr [ebp-08] // y lee segundo byte (mes)
:0040968F 0FBE4001 movsx eax, byte ptr [eax+01] // de la misma dirección
:00409693 50 push eax // lo pasa como parametro
:00409694 E806FEFFFF call 0040949F // llama rutina que devuelve los dias del més (esta se utiliza tambien en 4094EE)
:00409699 8945FC mov dword ptr [ebp-04], eax // guarda dias obtenidos
:0040969C 8B4508 mov eax, dword ptr [ebp+08] // lee hash pasada
:0040969F 3B45FC cmp eax, dword ptr [ebp-04] // compara hash con dias
:004096A2 7E19 jle 004096BD // si <= salta
:004096A4 8B4508 mov eax, dword ptr [ebp+08] // lee hash
:004096A7 2B45FC sub eax, dword ptr [ebp-04] // le resta los dias
:004096AA 894508 mov dword ptr [ebp+08], eax // y la guarda
:004096AD 8B45F8 mov eax, dword ptr [ebp-08] // lee dirección del mes
:004096B0 8A4001 mov al, byte ptr [eax+01] // lee mes
:004096B3 0401 add al, 01 // pasa al mes siguiente
:004096B5 8B4DF8 mov ecx, dword ptr [ebp-08] // y lo
:004096B8 884101 mov byte ptr [ecx+01], al // guarda
:004096BB EBC7 jmp 00409684 // salta y continua el proceso de calcula y resta de días
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004096A2(C)
:004096BD 8B45F8 mov eax, dword ptr [ebp-08] // lee dirección
:004096C0 8A4D08 mov cl, byte ptr [ebp+08] // y primer byte de la hash (dias)
:004096C3 8808 mov byte ptr [eax], cl // lo guarda en la dirección
:004096C5 8B4DF8 mov ecx, dword ptr [ebp-08] // lee dirección en ECX
:004096C8 E810FDFFFF call 004093DD
:004096CD C9 leave
:004096CE C20800 ret 0008

 


Habiendo examinado el código y sus distintas rutinas, nos encontramos en disposición de hacer un keygen. He utilizado el lccWin32 para crear una aplicación Windows basada en un cuadro de diálogo donde se introduce el nombre y mediante la pulsación de un botón se devuelve el nombre en una casilla de texto (habilitada soló para su visualización, también dispone de un botón que permite copiar directamente el número calculado al portapepeles, esto facilita nuestra tarea de tener que copiar el número manualmente y luego pegarlo en el cuadro de dialogo de registro ;). Los archivos de recursos así como los fuentes puedes cogerlos de aquí, estos archivos pueden ser utilizados como patrón para los keygens que hagamos bajo Windows en modo ventana.

#include <windows.h>
#include "keygen.h"
#define MIN_NAME_LENGTH 1 // longitud mínima del nombre
#define MAX_NAME_LENGTH 80 // longitud máxima del nombre
int licenses = 1;
char name[MAX_NAME_LENGTH];
unsigned char TabNum[]={'6','1','3','5','3','3','8','4','0',0x0};
unsigned char UCaseChar1(register unsigned char ch)
{
   register unsigned char c=ch;
   if ((c>=0x61) && (c<=0x7A)) // si es una letra minúscula
   {
   return (c-0x20); // la pasa a mayúsculas
   }
   if ((c>=0xE0) && (c<=0xFE) && (c!=0xF0) && (c!=0xf7))
   {
   return (c-0x20);
   }
 if ((c==0x8A) || (c==0x8C) || (c==0xD0) || ((unsigned short)c==0x103)) return    (c+0x10);
 return c;
}
unsigned char UCaseChar2(register unsigned char ch)
{
   register unsigned char c=ch;
 if ((c>=0x41) && (c<=0x5A)) // si es una letra mayúscula
   {
   return (c+0x20); // la pasa a minúscula
   }
   if ((c>=0xC0) && (c<=0xDE) && (c!=0xD0) && (c!=0xD7))
   {
   return (c+0x20);
   }
 if ((c==0x9A) || (c==0x9C) || (c==0xf0) || (c==0xff)) return (c-0x10);
 return c;
}
BOOL ValidateChar(register unsigned char caracter)
{
   register unsigned char c=caracter;
// si es un caracter del '0' al '9' vale
if (((c>=0x30) && (c<=0x39)) || ((c>=0x41) && (c<=0x5A)) || ((c>=0x61) && (c<=0x7A))) return TRUE;
 if ((c==0x83) || (c==0x8A) || (c==0x8c) || (c==0x9A) || (c==0x9c) || (c==0x9f) || (c==0xb2) || (c==0xb3) || (c==0xb9)) return TRUE;
 if (((c<0xC0) || (c>0xFF)) || ((c==0xD7) || (c>0xF7)) ) return FALSE;
 return TRUE;
}
// valida los caracteres de la cadena, y retorna la longitud final, source se cambia por la cadena validada
int ValidateName( char * source)
{
   int longitud,i;
   int ccopiados=0;
   unsigned char caracter;
   char aux[255];
   char aux2[255];
 longitud=lstrlen(source);
 for (i=0;i<longitud;i++)
   {
   caracter=source[i];
   if (ValidateChar(caracter))
   {
   aux[ccopiados]=caracter;
   ccopiados++;
   }
   }
   // pone NULL al final de la cadena
   aux[ccopiados]=0x0;
 longitud=ccopiados-1;
 for (i=0;i<=longitud;i++)
 {
   // es par o impar ?
   if ((i & 1)!=0)
   {
   // impar
   aux[i]=UCaseChar1(aux[i]);
   }
   else
   {
   // par
   aux[i]=UCaseChar2(aux[i]);
   }
 }
 i=i & 0;
 ccopiados=ccopiados & 0;
 for (;i<=longitud;i++)
   {
   aux2[ccopiados]=aux[longitud-i];
   ccopiados++;
   aux2[ccopiados]=aux[i];
   ccopiados++;
   }
 aux2[ccopiados]=0x0;
 lstrcpy(source,aux2);
 return lstrlen(source);
}
// calcula el hash de la cadena pasada, se le pasa: numero de iteracciones,
// puntero a la cadena, valor de inicio de la hash, y longitud de la cadena
unsigned long ComputeHash(int iterations, char * cadena, unsigned long hash_start, int lcadena) { unsigned char caracter; unsigned long suma,mult,tablong; int cont;
 // calcula longitud de la tabla
   tablong=lstrlen(TabNum);
   cont=0;
// inicializa la suma tal y come lo hace el original
   suma=hash_start;
   for (int i=0;i<iterations;i++)
   {
	   // es par o impar ?
	   if ((i & 1)!=0)
   {
	   // impar
	   mult=2;
   }
   else
   {
	   // par
	   mult=1;
   }
   caracter=cadena[cont];
   suma = (suma * mult)+ (caracter % TabNum[(caracter % tablong)]);
   cont++;
   if (cont>=lcadena) cont=0;
   }
 return suma;
}
void ComputeSerial(HWND winHwnd)
{
   char MsgOut[255];
   int lname; // longitud del nombre , longitud auxiliar
   int whatserial;
   unsigned long digits1;
   // lee nombre de la casilla de edición
   GetDlgItemText(winHwnd,ID_NAME,name,sizeof(name));
   // calcula longitud
   lname=lstrlen(name);
 if ((lname<MIN_NAME_LENGTH) || (lname>MAX_NAME_LENGTH))
   {
   wsprintf(MsgOut,"Name must have a length between %d char/s and %d chars    .",MIN_NAME_LENGTH,MAX_NAME_LENGTH);
   // el nombre es más corto de lo admitido
   MessageBox(winHwnd,MsgOut,"ERROR",MB_ICONEXCLAMATION);
   return;
   }
   if ((whatserial=SendDlgItemMessage(winHwnd,ID_LICENSE1,BM_GETCHECK,0,0))==BST_CHECKED)
   {
	 // validamos el nombre y obtenemos su nueva longitud
	   lname=ValidateName(name);
	 // calcula el hash del nombre, pasa la suma hash a decimal, dando el número de série
	   digits1=ComputeHash(0x1e,name,0xa,lname);
	   wsprintf(MsgOut,"%d",digits1);
	   SetWindowText(GetDlgItem(winHwnd,ID_SERIAL),MsgOut);
   }
   else
   {
   	 // licencia para X ordenadores grupo de tres numeros separados por guiones
	 unsigned long digits2=licenses*10;
	 // validamos el nombre y obtenemos su nueva longitud
	 lname=ValidateName(name);
	 digits1=ComputeHash(0x1e,name,0xa,lname);
	 // calculamos número resultante, el tercer digito sale del numero de licencias y del nombre validado
	 wsprintf(MsgOut,"%d-%d-%d",digits1,digits2,ComputeHash(0xA,name,(digits2/0xa)*0xB,lname));
	 SetWindowText(GetDlgItem(winHwnd,ID_SERIAL),MsgOut);
	}
}
void CopySerial(HWND winHwnd)
   {
   int longitud;
   char *p;
   HGLOBAL hand;
 // abre portapapeles, si no puede sale sin copiar
   if (!OpenClipboard(winHwnd)) return;
 // obtiene longitud del serial en caracteres
   longitud=GetWindowTextLength(GetDlgItem(winHwnd,ID_SERIAL));
 // si no hay nada que copiar retorna
   if (longitud==0) return;
 // y le suma 1 para guardar el final de cadena
   longitud++;
 hand=GlobalAlloc(GMEM_DDESHARE|GMEM_MOVEABLE,longitud);
 // bloquea la memoria obteniendo puntero al primer byte
   p=GlobalLock(hand);
 // y copia serial al buffer
   GetDlgItemText(winHwnd,ID_SERIAL,p,longitud);
 // desbloquea memoria
   GlobalUnlock(hand);
 // borra contenido anterior
   EmptyClipboard();
 // y copia serial al portapapeles
   SetClipboardData(CF_TEXT,hand);
 // cierra portapapeles
   CloseClipboard();
}
BOOL CALLBACK KeyGenDlg(HWND hwndDlg,UINT uMsg,WPARAM wParam, LPARAM lParam)
{
 int c;
   int s=FALSE;
 switch (uMsg) {
   case WM_INITDIALOG:
	   // Por defecto activamos la generación de un número de licencia    sencilla
	   SendDlgItemMessage(hwndDlg,ID_LICENSE1,BM_SETCHECK,(WPARAM)TRUE,0);
	   EnableWindow(GetDlgItem(hwndDlg,ID_NUMLICENSES),FALSE);
   break;
   case WM_COMMAND:
	   switch (LOWORD(wParam))
   		{
		   case ID_LICENSE1:
			   EnableWindow(GetDlgItem(hwndDlg,ID_NUMLICENSES),FALSE);
			   EnableWindow(GetDlgItem(hwndDlg,ID_COMPUTE),TRUE);
		   break;
		   case ID_LICENSE2:
			   EnableWindow(GetDlgItem(hwndDlg,ID_NUMLICENSES),TRUE);
			   EnableWindow(GetDlgItem(hwndDlg,ID_COMPUTE),FALSE);
			   case ID_NUMLICENSES:
			   licenses=GetDlgItemInt(hwndDlg,ID_NUMLICENSES,&c,s);
			   // esto hara que se active o no el botón segun el número sea válido y mayor que 0
			   EnableWindow(GetDlgItem(hwndDlg,ID_COMPUTE),((c) && (licenses >0)));
		   break;
		   case ID_COMPUTE:
			   // llamamos a la función que cálcula el serial, pasandole el handle del control donde esta el nombre
			   ComputeSerial(hwndDlg);
			   break;
		   case ID_COPY:
			   // llamamos a la función que copia el serial al portapapeles, pasandole el handle del control donde está el serial
			   CopySerial(hwndDlg);
		   break;
		   case IDCANCEL:
		 	  // finalizamos el cuadro de diálogo y retornamos 0
		 	  EndDialog(hwndDlg, 0);
	   }
   break;
   default:
   return FALSE;
   }
   return TRUE;
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow )
{
	int dlghwnd;

dlghwnd=DialogBox(hInstance,MAKEINTRESOURCE(DLG_0100),NULL,KeyGenDlg);
	if (dlghwnd==-1)
	{
	   MessageBox("ERROR","Could not create dialog box",NULL,MB_ICONEXCLAMATION);
	}
	return 0;
}

You are inside Reversed Minds pages.

por Mr. Silver / WKT!
La información aquí vertida es exclusivamente para uso educacional, no puedo hacerme responsable del uso que se haga de esta, por lo que atiendo a la honradez de cada uno :), recuerda que debes comprar el software que utilices :)