Karpoff Spanish Tutor 2001

Programa:

5 StarZIP-2001 1.0


 

PROTECCION: Un serial distinto para cada día..
Objetivo:

Registrarnos el día que nos apetezca :-)

Descripcion:

Otro compresor/descompresor, y van...

Dificultad: Aficionado
DOWNLOAD:

www.pepsoft.com

Herramientas:

Softice,MAsm32.

CRACKER: CaoS ReptantE   FECHA: 18/11/2001

 

 INTRODUCCION

 Seguramente habréis oído pronunciar la frase "vivir al día", pues el infausto programador de hoy también. Lo malo es que a él se le ocurrió la brillante idea de hacer que el serial para registrar este programa variara cada día. Yo al principio, creía que el serial cambiaba al efectuar una nueva instalación, pero si se busca el serial del programa instalado días antes, se desinstala, se instala de nuevo y se vuelve a buscar el serial, se puede ver que es el mismo. Sin embargo si se vuelve a buscar al día siguiente, el serial ha variado. Vivir para ver...

 

 AL ATAKE

 
Encontrar el serial es sencillo, mediante el sistema que ya he descrito en otras ocasiones para pillar al programa cuando lee el serial chungo que hemos introducido (instrucción s 0 l ffffffff 'serial_chungo' y a continuación bpm o bpr de la dirección o direcciones obtenidas). Vamos a parar aquí:

:004039C9 8B0E                    mov ecx, dword ptr [esi]
:004039CB 8B1F                    mov ebx, dword ptr [edi]
:004039CD 39D9                    cmp ecx, ebx
:004039CF 7558                    jne 00403A29
:004039D1 4A                      dec edx
:004039D2 7415                    je 004039E9
:004039D4 8B4E04                  mov ecx, dword ptr [esi+04]
:004039D7 8B5F04                  mov ebx, dword ptr [edi+04]
:004039DA 39D9                    cmp ecx, ebx
:004039DC 754B                    jne 00403A29
:004039DE 83C608                  add esi, 00000008
:004039E1 83C708                  add edi, 00000008
:004039E4 4A                      dec edx
:004039E5 75E2                    jne 004039C9
:004039E7 EB06                    jmp 004039EF

El problema está en que el programa es teóricamente imparcheable - De eso ya hablaremos otro día ;-) - porque pasa un montón de veces por la comprobación comparando cadenas de texto, algunas de las cuales no tienen nada que ver con el serial y que unas veces son iguales y otras no. Así que de eso de invertir el salto... nada de nada :-(

Podemos encontrar el serial hoy y registrar el programa, pero si dejamos el registro para mañana, deberemos repetir el proceso. Y por supuesto, nada de pasárselo a un colega para que pueda evaluar el programa sin prisas. ¿Qué podemos hacer? Se impone mirar el registro. Es una buena práctica la de utilizar el TechFacts en el momento de registrarse, así se puede ver al registrar el programa, los cambios que se han efectuado en el registro o en algún archivo extraño. En nuestro caso, veremos que se han añadido estas dos claves:

HKEY_USERS\.DEFAULT\Software\Pepsoft\5StarZIP\Reg\UserKey="RFLCLZMEZVZKUFF"
HKEY_USERS\.DEFAULT\Software\Pepsoft\5StarZIP\Reg\UserName="CAOS REPTANTE"

Por cierto, si ponéis en el SoftIce un bpx RegCreateKeyExA, podréis ver como las claves que crea el programa son:

HKEY_CURRENT_USER\Software\Pepsoft\5StarZIP\Reg\UserKey="RFLCLZMEZVZKUFF"
HKEY_CURRENT_USER\Software\Pepsoft\5StarZIP\Reg\UserName="CAOS REPTANTE"

Por lo visto, unas claves generan las otras, lo que no sé es por qué el TechFacts sólo detecta las dos que he mencionado en primer lugar.
Por cierto, para ver esas claves hay que mirar los valores almacenados en la pila, mediante
d esp, y a partir de esa dirección se van comprobando los valores guardados de cuatro en cuatro bytes (algunos de estos valores corresponden a números y otros, a las direcciones donde se hallan los datos que nos interesan).
Si hacéis la prueba, veréis que el serial que aparece, siempre es el mismo independientemente del día en que estemos. Ahora ya tenemos donde agarrarnos :-) Así pues se trata de introducir esos datos en el registro y tendremos registrado el programa a nuestro nombre. En primer lugar hay que localizar el punto en el que el programa escribe este serial mediante la instrucción
s 0 l ffffffff 'RFLCLZMEZVZKUFF' y a continuación bpm o bpr de las direcciones obtenidas. Así llegaremos haciendo marcha atrás, a este código:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:

|:00496995(C)
|
:0049694E 33C0                    xor eax, eax
:00496950 8A03                    mov al, byte ptr [ebx]

EBX apunta a una cadena que está formada por 0Fh que es la longitud del serial (siempre comprendida entre un mínimo de 15 y la longitud del nombre que como máximo será de 25 caracteres), y "CAOS REPTANTECA" (nuestro nombre más las letras que falten hasta 15, a partir de la primera).

:00496952 8B55F4                  mov edx, dword ptr [ebp-0C]
:00496955 0FB6543AFF              movzx edx, byte ptr [edx+edi-01]

EBX apunta a una cadena que empieza por 00 y sigue con "5STARZIPv1.0358093027". Pero lo que yo no supe ver, es que las nueve últimas cifras no eran constantes. Afortunadamente ByTESCRK ha evitado que metiera la pata hasta lo más hondo. Se dió cuenta de que el programa que le había mandado para registrar el programa no funcionaba y detectó que se debía a que el programa obtenía los datos del disco duro para completar la generación del serial de registro. Justo aquí:

:00496DF3 E808E5F6FF              call 00405300

:00405300 FF25E4524A00            jmp dword ptr [004A52E4]

Esto se hace mediante GetVolumeInformationA que es una API que da información de un disco especificado. Los parámetros de esta función son ocho:

        1º- "C:\" (el disco donde está instalado el programa)
        2º- Una dirección en la que la función escribe el nombre del disco.
        3º- 0104h (la longitud del nombre del disco)
        4º- Una dirección en la que la función escribe el número del disco.
        5º- Una dirección en la que hay la longitud máxima del nombre de archivo.
        6º- Una dirección en la que la función escribe el valor de los flags del
        sistema de archivos.
        7º- Una dirección en la que la función escribe el nombre del sistema de
        archivos.
        8º- 0104h (la longitud del nombre del sistema de archivos)

Esto aunque en inglés, está mejor explicado en la Win32 Programmer's Reference.

Ahora que ya sabemos el verdadero origen de estos números, continuemos con el código.

:0049695A 6603C2                  add ax, dx
:0049695D 668945FA                mov word ptr [ebp-06], ax
:00496961 0FBF45FA                movsx eax, word ptr [ebp-06]
:00496965 B917000000              mov ecx, 00000017
:0049696A 99                      cdq
:0049696B F7F9                    idiv ecx

Va sumando los códigos ASCII de los caracteres de las dos cadenas y los divide por 23 (17h)

:0049696D B8F4694900              mov eax, 004969F4

EAX apunta a la cadena "ABCDEFGHJKLMNPQRTUVWXYZ".

:00496972 8A1410                  mov dl, byte ptr [eax+edx]

Coloca en DL la letra que está en la posición determinada por el resto de la división. Ahora vemos por qué divide por 23: el resto será un número comprendido entre el 0 (A) y el 22 (Z).

:00496975 8D85E8FCFFFF            lea eax, dword ptr [ebp+FFFFFCE8]
:0049697B 885001                  mov byte ptr [eax+01], dl

Guarda la letra en la memoria...

:0049697E C60001                  mov byte ptr [eax], 01
:00496981 8D95E8FCFFFF            lea edx, dword ptr [ebp+FFFFFCE8]
:00496987 8D85F4FDFFFF            lea eax, dword ptr [ebp+FFFFFDF4]
:0049698D E88EC1F6FF              call 00402B20

En este call, va colocando las letras en su lugar.

:00496992 47                      inc edi
:00496993 43                      inc ebx
:00496994 4E                      dec esi
:00496995 75B7                    jne 0049694E

Vuelve al inicio del bucle.

Ya sabemos lo necesario para hacer un programa que nos registre la aplicación :-)

Echaremos mano del Masm y crearemos dos archivos: el de recursos rsrc.rc en el que definiremos la ventana y los botones, y el 5Star.asm en el que escribiremos el código del programa.
El fichero de recursos podría ser este:

#include "\masm32\include\resource.h"

#define IDD_DIALOG 1000

#define IDC_NOMBRE 1001

#define IDC_REGIS 1002

#define IDC_INFO 1003

#define IDC_STATIC -1

app ICON reg.ico

IDD_DIALOG DIALOG 100,100,129,104

STYLE WS_CAPTION | DS_CENTER | WS_SYSMENU |WS_MINIMIZEBOX

CAPTION "CaoS ReptantE"

FONT 10, "MS Sans Serif"

BEGIN

    GROUPBOX        "Programa",IDC_STATIC,8,6,113,20

    LTEXT           "5 StarZIP-2001 1.0",IDC_STATIC,12,14,97,8,ES_CENTER

    GROUPBOX        "Website",IDC_STATIC,8,28,113,20

    LTEXT           "www.pepsoft.com",IDC_STATIC,12,36,97,8,ES_CENTER

    GROUPBOX        "Nombre",IDC_STATIC,8,50,113,27

    EDITTEXT        IDC_NOMBRE,22,60,85,11,ES_AUTOHSCROLL

    PUSHBUTTON      "Registrar",IDC_REGIS,8,84,48,14

    PUSHBUTTON      "Información",IDC_INFO,73,84,48,14

END

No parecen necesarias muchas explicaciones. Hemos definido la ventana y los controles que están situados en ella, también hemos incluido un archivo de recursos y el icono correspondiente.

Ahora vamos a por el archivo de código :-)

Me limito a explicar alguna de las características del código, ya que hay cosas que he comentado en algún otro tuto. Si queréis alguna aclaración, podéis darme un toque.

.386

.model flat,stdcall

option casemap:none

include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\advapi32.inc
include c:\masm32\include\kernel32.inc
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\advapi32.lib
includelib c:\masm32\lib\kernel32.lib

.const

IDD_DIALOG           EQU 1000
IDC_NOMBRE           EQU 1001
IDC_REGIS            EQU 1002
IDC_INFO             EQU 1003

DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD
Operacion PROTO

.data

Icono     db "app",0
Iconimage db "app",0
error1    db "¡Error!",0
error2    db "¡Mínimo siete caracteres!",0
error3    db "¡Fallo en registro!",0
exito1    db "¡Exito!",0
exito2    db "¡5 StarZIP ha sido registrado!",0
txt1      db "Una vez instalado el 5 StarZIP, es suficiente con pulsar el ",\
  "botón ""Registrar"" para que el programa quede registrado con el nombre ",\
  "que se haya introducido previamente.",13,10,13,10,\
  "Es indiferente el directorio desde el que se ejecute este                                                       programa.",13,10,13,10,\
  "Saludos de CaoS ReptantE :o)",0
txt2      db "Información",0
cadena    db 0,"5STARZIPv1.0",12 dup(0),0
abc       db "ABCDEFGHJKLMNPQRTUVWXYZ",0
hInstance dd 0
hIcon     dd 0
hWnd      dd 0
clave     db "Software\Pepsoft\5StarZIP\Reg",0
subclave1 db "UserName",0
subclave2 db "UserKey",0
valor1    db 25 dup(0),0
valor2    db 25 dup(0),0
disco     db 4 dup(0),0
archivo   db "5starzip.exe",0
dff       db 0,0
varios    db 255 dup(0),0
numdisc   db 4 dup(0),0
handle    dd ?
dev       dd ?

De las variables que acabamos de definir, sólo comentaré las últimas: archivo es el nombre del ejecutable que emplearemos para saber en que disco está instalado el programa; varios es un campo en el que se pondrán sucesivamente algunos de los valores que devuelve las funciones GetFullPath... y GetVolume... , he puesto un solo campo y ahí se irán machacando; numdisc es donde se colocará el número del HD; handle es el valor del handle de la clave que hemos creado en la instrucción RegCreateKeyExA y que se utiliza a continuación en RegSetValueExA; dev es un valor que nos indica si la clave ha sido creada o si ya existía y sólo se ha abierto. En nuestro caso no lo utilizamos.

.code

start:

invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,IDD_DIALOG,NULL,ADDR DlgFunc,NULL
invoke ExitProcess,NULL

DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

   .if uMsg==WM_INITDIALOG
        mov eax,hDlg
        mov hWnd,eax
        invoke LoadIcon,hInstance,ADDR Icono
        mov hIcon,eax
        invoke SendMessage, hDlg, WM_SETICON,1,hIcon

Muestra la ventana con el icono que hemos definido.

    .elseif uMsg==WM_CLOSE
        invoke EndDialog,hDlg,NULL

Si cerramos la ventana desde la esquina superior derecha, adiós.

    .elseif uMsg==WM_COMMAND
        mov eax,wParam
        mov edx,eax
        shr edx,16

Si hemos pulsado un botón, el programa busca cual ha sido.

        .if dx==BN_CLICKED
            .if ax==IDC_INFO
                invoke MessageBox,hDlg,ADDR txt1,ADDR txt2,MB_ICONINFORMATION

Si hemos pulsado el de "Información", nos muestra la ventana con el mensaje que hemos definido y el icono correspondiente.

            .elseif ax==IDC_REGIS
                invoke GetDlgItemText,hDlg,IDC_NOMBRE,ADDR valor1,24
                invoke Operacion
                .if eax==777
                invoke MessageBox,hDlg,ADDR error2,ADDR error1,MB_ICONSTOP

                .elseif eax==ERROR_SUCCESS
                invoke MessageBox,hDlg,ADDR exito1,ADDR exito2,MB_ICONEXCLAMATION
                invoke EndDialog,hDlg,NULL
                .else
                    invoke MessageBox,hDlg,ADDR error1,ADDR error2,MB_ICONSTOP
                    invoke EndDialog,hDlg,NULL
                .endif
                ret

Y si hemos pulsado el de "Registrar", vamos a la subrutina Operacion y al regreso, nos muestra un mensaje de éxito o de fracaso. Si el fracaso es debido a que el nombre que hemos introducido tiene menos de siete letras, nos deja seguir intentándolo, en los otros casos, cierra el programa.

            .endif
        .endif
    .endif
ret

DlgFunc endp

Operacion proc
           pushad

Para saber el número de serie del disco en que está instalado el programa, primero hemos de averiguar que disco es. Eso lo hacemos con esta función:

           invoke GetFullPathNameA,Offset archivo,255,Offset varios,Offset varios+4

Le damos el nombre del programa "víctima" y el número de caracteres que creemos que puede ocupar, nos devuelve el path completo en el campo varios y en varios +4, la dirección del nombre del ejecutable. Se machacarán parte de los datos pero sólo nos interesan los tres primeros caracteres)

           mov ebp, Offset varios
           mov ecx, Offset disco
           mov eax, dword ptr [ebp]
           and eax, 00ffffffh
           mov [ecx], eax

Ya tenemos puesta la letra de nuestro disco en el campo disco. La instrucción anterior sirve para poner el último carácter de los cuatro que hemos tomado, a cero.

           xor esi, esi
           mov ebx, Offset cadena
           add ebx, 13
           invoke GetVolumeInformationA,Offset disco,Offset varios,0104h,Offset numdisc,Offset dff,Offset varios,Offset varios,0104h
           mov eax, Offset numdisc
           mov eax, dword ptr [eax]

Tenemos el número del disco duro en EAX, ahora lo pasaremos a decimal.

           mov ecx, 10
otro:
           cmp eax, ecx
           jb yaesta
           xor edx, edx
           idiv ecx
           add dl, 48
           mov byte ptr [ebp], dl
           inc ebp
           inc esi
           jmp otro
yaesta:
           add al, 48
           inc esi
           mov byte ptr [ebp], al

Tenemos el número pasado a decimal y colocado en el campo varios al que como veis, le sacamos mucho partido :-) Lo hemos puesto tal como se iba generando, es decir: en orden inverso. A continuación lo iremos pasando a su lugar definitivo, detrás de la parte conocida del campo cadena, también en orden inverso para que quede ordenado correctamente.

mas:
           mov al, byte ptr [ebp]
           mov byte ptr [ebx], al
           inc ebx
           dec ebp
           dec esi
           .if esi > 0
              jmp mas
           .endif

Ya esta hecho. Ahora después de esta fase previa vamos al lio.

           xor eax, eax
           xor ecx, ecx
           xor ebp, ebp
           mov edx, Offset valor2
           mov edi, Offset cadena
           mov esi, Offset abc
           mov ebx, Offset valor1
           invoke lstrlen, offset valor1
           .if eax < 7
              popad
              mov eax, 777
              ret
           .endif
           xor eax, eax

Obtenemos la longitud del nombre introducido ya que he observado que, si tiene menos de siete letras, el programa no se registra. En este caso, le damos un valor arbitrario a EAX y volvemos a la rama principal del programa. En algún sitio debe haber un control de la longitud del nombre, aunque no lo he visto y tampoco he buscado demasiado.

bucle1:
           mov cl, byte ptr [ebx+eax]
           .if ecx==0
              jmp seguir
           .endif
           .if ecx>96
              .if ecx<123
                  sub ecx, 32
              .endif
           .endif
           mov [ebx+eax], cl
           inc eax
           jmp bucle1

Este bucle toma las letras una a una, si son minúsculas las convierte en mayúsculas y las vuelve a poner donde estaban. Si el carácter no es una letra, lo deja como estaba.

seguir:
           .if eax<15
              mov eax, 15
           .endif
           push eax
           mov [edx], al
           pop eax
           dec eax
           push ebx
           inc edx

La primera letra del serial viene dada por la longitud del nombre, si esta es menor de 15, se iguala a esta cifra y se coloca al inicio del campo valor2 donde se formará el serial. Esta cifra se debería dividir por 23, pero he tenido problemas con los seriales de 24 o 25 letras (como podéis ver, el programa no deja entrar más) y más arriba, en invoke GetDlgItemText... he limitado la entrada a 23 letras.

bucle2:
           mov cl, byte ptr [ebx]
           .if ecx==0
              mov ebx, Offset valor1
              jmp bucle2
           .endif
           mov [edx+ebp], cl
           dec eax
           inc ebx
           inc ebp
           .if eax==0
              xor ebp, ebp
           dec edx
              pop ebx
              jmp bucle3
           .endif
           jmp bucle2

Se pasan las letras del nombre al campo valor2 donde se formará el serial. Si el nombre tiene menos de 15 letras se continúan poniendo letras a partir de la primera hasta llegar a las quince.

bucle3:
           mov al, byte ptr [edx+ebp]
           and eax, 000000FFh
           .if eax==0
              jmp reg
           .endif
           mov cl, byte ptr [edi+ebp]
           add eax, ecx
           push edx
           xor edx, edx
           mov ecx, 23
           idiv ecx
           mov cl, byte ptr [esi+edx]
           pop edx
           mov [edx+ebp], cl
           inc ebp
           inc ebx
           jmp bucle3
 

Ponemos en AL el código ASCII de cada letra, ponemos a cero el resto del registro EAX y le sumamos el código del carácter correspondiente del campo cadena, dividimos la suma por 23 y tomamos la letra del campo abc indicada por el resto (desde la "A" que corresponde al 0, a la "Z" que corresponde al 22). ¿Os habéis preguntado por qué 23 letras? ¿Y por qué faltan precisamente la "I", la "O" y la "S"? Pues hay una razón para ello. Pensad a ver si se os ocurre :-) Bien, sigamos. Se repite el proceso hasta que se acaban las letras. Insisto en que si algo no está claro, me deis un toque. Ya tenemos el serial que hemos de colocar en el registro de Windows. Vamos a ello...

 reg:
     popad
     invoke RegCreateKeyExA,80000001h,ADDR clave,NULL,NULL,NULL,0F003Fh,NULL,ADDR
handle,ADDR dev
     .if eax!=ERROR_SUCCESS
         push eax
         jmp fin
     .endif

El valor 8000001 corresponde a HKEY_CURRENT_USER, y el F003F, al nivel de seguridad de acceso (hemos puesto el mismo que pone el 5Star Zip). Los demás valores ya están vistos, y la instrucción push eax es porque en caso de error, como la última instrucción de la subrutina es RegCloseKey, que no nos dará error, esto cambiaría el valor de EAX y enmascararía el resultado.

     invoke RegSetValueExA,handle,ADDR subclave1,NULL,REG_SZ,ADDR valor1,25
     .if eax!=ERROR_SUCCESS
         push eax
         jmp fin
     .endif
     invoke RegSetValueExA,handle,ADDR subclave2,NULL,REG_SZ,ADDR valor2,25
     .if eax!=ERROR_SUCCESS
         push eax
         jmp fin
     .endif
     push eax

Colocamos los valores correspondientes y si todo ha ido bien, la instrucción push eax que hay al final guarda el valor de EAX para recuperarlo a continuación (Si no la pusiéramos, la instrucción pop eax que hemos puesto para recuperar el valor de EAX en caso de error, machacaría EAX y nos daría un resultado imprevisible).

fin: invoke RegCloseKey,HKEY_USERS
     pop eax
     ret

Cerramos el registro abierto y volvemos a la rama principal del programa.

Operacion endp

end start

Supongo que alguien que entienda de programación en assembler podrá opinar que este código es una chapuza, pero tiene algo de bueno: funciona ;-) Ahora sólo queda compilar los dos archivos y ya podemos registrar el programa.

Yo sólo quería hacer un tutorial sencillo que mostrara como hacer un pequeño programa que se encargara de introducir datos en el registro sin tener que utilizar un archivo .reg, pero las cosas se han complicado de mala manera. Hubiera sido preferible hacer un keygen, el algoritmo es mucho más sencillo. Quizá lo haga de otro programa de la misma empresa :-)

Como siempre, quiero recordaros que debéis pagar por los programas que utilicéis regularmente, ya que de lo contrario, podéis acabar en la lista de los más buscados, junto con los tíos de los turbantes y las barbas :-)

Para terminar, quiero saludar a mis amigos DeK_Oin - esta vez te he puesto el primero ;-) -, Act MagO, y Pr@fEsOr X. También a Karpoff, KuaTo_ThoR, vLugo y especialmente a Silver Storm, ya que un comentario suyo me dio la idea de hacer este tuto y por supuesto a ByTESCRK agradeciéndole su inestimable colaboración.

Si queréis alguna aclaración sobre mis tutos, mi dirección de e-mail es: caos_reptante@hotmail.com

 

Karpoff Spanish Tutor: Pagina dedicada a la divulgacion de informacion en Castellano, sobre Ingenieria Inversa y Programacion. Email "Colabora con tus Proyectos"
www.000webhost.com