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...
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! |