Programa:
CDumper v1.0


 

PROTECCION: Simular estar registrados mediante un patch.
Descripcion: Reproductor de CDs con ecualizador de sonido.
Dificultad: Novato, Facililla, Media, Avanzado, Difícil, Elite, ETC.
DOWNLOAD: http://www.gmixon.com
Herramientas:
Para el patch: W32dasm y MASM32. 
Para el KeyGen: File Inspector, DeDe 3.0, SoftICE, las anteriores.
CRACKER:
SACCOPHARYNX
  FECHA: 15/02/2002

 

 INTRODUCCION

Saludos a todos:

Aquí tienen mi cuarto tutorial, el primero de este 2002. Antes que nada, a todos los colegas que me mandaron e-mails acerca de mis tutoriales anteriores, les quiero decir gracias por invertir su tiempo en aprender con ellos. Me alegro que les hayan servido, y que les haya gustado la explicación en los mismos. Bueno amigos, manos a la obra, jejeje...

 

 AL ATAKE

Intro

Creo que a esta víctima (CDumper v1.0) le tenía ganas desde mucho antes de introducirme en este hermoso mundo del cracking. Obviamente mi falta de conocimientos en aquel entonces solo produjo resultados sin éxito, por eso estoy aquí nuevamente, en busca de la revancha para que los programadores de CDumper no se la lleven de arriba.

Presentando a la víctima

Como con los reproductores comunes de CDs (Windows Media Player, Winamp, CDplayer, etc), solo podemos reproducir el sonido de un CD, y no ecualizarlo a nuestro gusto, es que he seleccionado a esta víctima, con la cual podemos lograr esto.

Pero, cómo con CDumper v1.0 sí y con los comunes no?

Bueno, es sencillo. Los reproductores comunes de CD sólo ponen en PLAY la unidad de CD-ROM y no procesan ninguna señal. El audio sale a través del cable que va conectado desde nuestra unidad de CD-ROM a la placa de sonido, y de esta va directamente a los parlantes. O sea, la CPU no procesa nada. Esta maravilla de programa trabaja de forma distinta. Si eliminamos el cable que va desde nuestra unidad de CD-ROM a la placa de sonido, oiremos música igual. Por qué? Porque este programa lo que hace en realidad es una EXTRACCION DIGITAL DE AUDIO (lo mismo que los programas que graban CDs). El sonido en lugar de salir por el cable que va a la placa de sonido, sale por el bus de datos, luego es procesado según nuestras preferencias seteadas en el ecualizador, y luego se manda la señal ya procesada a la placa de sonido. Es por eso que podremos disfrutar de nuestros CDs a pleno.

Fanáticos de la música no se pierdan este tutorial.

Manos a la obra

Bueno, el objetivo de este tutorial es aprender un poco acerca de las APIs de Window$ que controlan la registry, para usar esos conocimientos en la realización de un patch en assembler, para simular que estamos registrados. Este patch no solo escribe una entrada en la resgistry, sino que también parchea unos bytes en CDumper.exe. El patch lo haremos en MASM32, por lo tanto recomiendo bastantes conocimientos de assembler para que este tutorial no resulte muy difícil. Igualmente tartaré de explicar de la mejor manera posible. Luego, al final, les mostraré que existe otro punto de ataque, pero la idea de este tutorial es hacer algo distinto a lo que venimos haciendo y programar un poco más.

Pero antes de hacer un patch debemos conocer como trabaja nuestra víctima. Entonces vamos a inspeccionarla.

Una vez que instalamos el CDumper v1.0 ponemos un CD de audio en nuestra lectora de CDs y ejecutamos el programa. Ni bien hacemos esto ya aparece la primera nag molestándonos:

la cual nos dice que es una versión demo y que sólamente podremos reproducir tres CDs, las veces que queramos, pero solo tres. Con mi extensa colección de CDs, solo reproducir tres? jejejejeje, qué cargada es esa?

Si analizamos un poco la situación, es obvio que en algún lugar se guardan la cantidad de CDs reproducidos y algún identificadr de los mismos para saber cuales fueron reproducidos y cuales no. Si buscamos en el directorio dónde instalamos CDumper no hay rastros, por lo tanto vamos a la REGISTRY. Para los que no tengan idea de lo que es la registry, ojo con lo que hacen de ahora en más, no arruinen su Window$, jejeje.

Vamos a Inicio->Ejecutar, escribimos regedit seguido de ENTER, y accedemos al editor de la registry de Window$. Una vez aquí vamos a HKEY_LOCAL_MACHINE\SOFTWARE y veremos que nuestra víctima generó una entrada llamada GMixon y otras más que dependen de esta:

Si observan bien, la entrada con los datos más interesantes es HKEY_LOCAL_MACHINE\SOFTWARE\GMixon\CDumper\Demo (la que se observa en la imagen de arriba):

 Data   ->   El primer dato de este valor es 01, el cual nos indica la catidad de
             CDs que hemos reproducido ya. El resto de los datos se usan para
             identificar a los CDs, para saber si es uno nuevo o no, e 
             incrementar o no, el dato 01. Es obvio que cuando se llega a 03
             fuimos historia.

 DataCount y Test   ->   Aparentemente se usan en conjunto para hacer alguna
                         especie de chequeo que muy bien no les funcionó a los
                         programadores, jejejeje.

 DemoMsg   ->  Como se puede observar contiene un 1. Si cierran CDumper y lo
               ejecutan otra vez notarán que la molesta nag no aparece. Que quiere
               decir eso?, Que si DemoMsg está en 1 no debe mostrarnos nuevamente
               la nag, jejeje.

Ahora que ya tenemos bastante información, pensemos un poco. Nuestra víctima de alguna forma escribe valores en la registry de Window$. Bueno, si buscamos en nuestra Referencia de APIs (si no la tienen recomiendo que la bajen de internet porque es muy útil, http://spiff.tripnet.se/~iczelion/files/win32api.zip), la API que escribe la Registry es RegSetValue o RegSetValueExa. Lo que debemos hacer entonces es buscar en CDumper.exe donde se efectua la llamada a esta API, para lo cual lo desensamblamos con W32Dasm. Una vez desensamblada la víctima vamos a Search->Find Text y buscamos RegSetValue.

La primer aparición no nos interesa, solo dice que es un módulo importado desde advapi32.dll. Buscamos otra vez dando F3 y llegamos a la segunda aparición que es solo un salto a la tercera y última de todas, la cual si nos interesa y mucho:

Bien, como dijimos anteriormente la idea no es parchear la víctima con un editor Hexa, sino hacer un patch en Win32Asm con MASM32, para lo cual debemos saber que es lo que debemos parchear. Esta vez no vamos a andar parcheando simples saltitos (jmp, je, etc). Como se habrán dado cuenta la idea es parchear la escritura de datos en la registry, por lo tanto antes debemos saber como funciona la API RegSetValueExa. Aquí les describo los parámetros que se le pasan a la misma:

LONG RegSetValueEx(
    HKEY hKey,           // Es el handle de la entrada donde escribiremos el valor.  
    LPCTSTR lpValueName, // Es la dirección del nombre del valor a escribir. 
    DWORD Reserved,      // Reservado para uso futuro. Le pasaremos un NULL. 
    DWORD dwType,        // Flag que nos indica el tipo de valor a escribir.
    CONST BYTE *lpData,  // Es la dirección de los datos del valor a escribir.
    DWORD cbData         // Es el tamaño de los datos del valor a escribir. 
   );	

Ahora que ya sabemos esto, si observan la llamada de la posición :004A85FC, muchos se preguntarán, dónde se pasan todos estos parámetros?. Bueno, si no se usa la sentencia invoke de MASM32, la forma de pasar los parámetros es ponerlos en la pila (stack) mediante sentecias push antes del call, en orden inverso debido a que en una pila, siempre, el último en poner es el primero en sacar. Entonces:

:004A85E8:   ->   Pone en la pila DWORD cbData.
:004A85EC:   ->   Pone en la pila CONST BYTE *lpData.
:004A85ED:   ->   Pone en la pila DWORD dwType.
:004A85EE:   ->   Pone en la pila DWORD Reserved.
:004A85F7:   ->   Pone en la pila LPCTSTR lpValueName.
:004A85FB:   ->   Pone en la pila HKEY hKey.

Luego viene el call.

Bien, una cosa a tener en cuenta es que nuestra víctima siempre usa este código para escribir en la registry, porque no hay más llamadas a esta API, por lo tanto hay que tener cuidado como parcheamos este código, porque es obvio que afectaremos a todas las escrituras en la registry. Analizando la situación, creo que la mejor opción es pasarle un NULL en el parámetro correspondiente a CONST BYTE *lpData, porque de esta forma tendremos un puntero nulo que no apunta a ningún dato, entonces no se escribirá absolutamente nada en la registry (ningún dato para ninguno de los valores de las entradas generadas en HKEY_LOCAL_MACHINE\SOFTWARE\GMixon\). Para lograr esto debemos hacer los siguientes cambios:

Las instrucciones:
dirección  codificación  instrucción
:004A85E9  8B45FC        mov eax, dword ptr [ebp-04]
:004A85EC  50            push eax

debemos cambiarlas por:
dirección  codificación  instrucción
:004A85E9  6A00          push 00000000
:004A85EB  90            nop
:004A85EC  90            nop

o sea, cuatro bytes se cambian por otros cuatro bytes, para no alterar el tamaño 
del archivo (CDumper.exe):
8B -> 6A
45 -> 00
FC -> 90
50 -> 90

Listo, ya sabemos que hay que cambiar para lograr que no se escriban datos en la registry. Solo nos falta saber el desplazamiento (offset) que hay hasta los bytes que vamos a cambiar. Como estos bytes comienzan en la dirección :004A85E9, solo tenemos que posicionarnos en esta línea dentro de W32Dasm. Una vez hecho esto veremos el desplazamiento abajo de todo, el cual es:

a79e9  (En hexadecimal).
686569 (En decimal).              

Antes de hacer el patch en assembler hice los cambios con un editor Hexa y probé los resultados. Funcionó de maravillas, asique luego puse manos a la obra en el patch. Existe un pequeño costo extra al impedir que se escriban datos en los valores de las entradas de la registry:

Este costo es que no se escribirá el dato del valor DemoMsg. Entonces siempre aparecerá esa molesta nag? Noooooooooooooooooo. Claro que no. Aprovecharemos el patch del archivo CDumper.exe y crearemos esa entrada en la registry y le pondremos un 1 como dato en el valor DemoMsg, jejeje. Todo en el mismo patch, y de esa forma eliminaremos la molesta nag.

Bueno, ahora que vimos como funciona nuestra víctima, vamos a desinstalarla (Inicio->Panel De Control->Agregar o Quitar programas) y a borrar la entrada GMixon que generó en la registry. Esto lo hacemos porque en la primer ejecución, al no estar parcheado CDumper.exe, escribió datos en los valores de la registry, lo cual no queremos que suceda, jejeje. Ahora sí, se viene el patch en assembler para Win32 hecho con MASM32.

Código fuente del patch y su explicación

Antes que nada les quiero decir que los siguientes códigos fuente (rsrc.rc y CDumperPatch.asm) se puden bajar enteros desde aquí: CDumperPatchSrc.zip

El primero de los códigos fuente corresponde a rsrc.rc y el otro a CDumperPatch.asm. El código será explicado por partes y evitaré explicar lo que está explícito en el mismo código fuente.

Código fuente de rsrc.rc
--------------------------------------------------------------------
#include "c:\masm32\include\resource.h"
#define IDC_STATIC      -1
#define IDD_DIALOG      1000
#define IDC_SERIAL      1002
#define IDC_EXIT        1003
#define IDC_ABOUT       1004
#define IDC_CRACKEAR    1005

app ICON gulper2.ico                 ;ícono del patch

IDD_DIALOG DIALOG  100,100,229,98    ;tamaño del dialog box

;Estilo del Dialog Box
STYLE WS_CAPTION | DS_CENTER | WS_SYSMENU |WS_MINIMIZEBOX

CAPTION "CDumper v1.0 Patch"

;Tipografía y tamaño
FONT 8, "MS Sans Serif"

BEGIN
    GROUPBOX        "Cracker",IDC_STATIC,8,6,214,20
    LTEXT           "SACCOPHARYNX",IDC_STATIC,55,14,115,8,ES_CENTER 
    GROUPBOX        "Website",IDC_STATIC,8,28,214,20
    LTEXT           "http://www.gmixon.com",IDC_STATIC,55,36,115,8,ES_CENTER 

    GROUPBOX        "Registro",IDC_STATIC,8,50,214,40
    PUSHBUTTON      "&Crackear",IDC_CRACKEAR,35,65,50,14
    PUSHBUTTON      "&Acerca de",IDC_ABOUT,90,65,50,14
    PUSHBUTTON      "&Salir",IDC_EXIT,144,65,50,14
END

Como puede observarse claramente en rsrc.rc se definen los controles y la apariencia de nuestro patch (botones, ícono, tipografía, estilos, etc.). El archivo c:\masm32\include\resource.h lo incluimos porque en el están definidas muchas características (WS_CAPTION, DS_CENTER, ES_CENTER, etc.).

Código fuente de CDumperPatch.asm
--------------------------------------------------------------------
;
; -----------------------------------------------------------------------------
.386
.model flat,stdcall
option casemap:none

; -----------------------------------------------------------------------------
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\advapi32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\advapi32.lib 

; -----------------------------------------------------------------------------
DlgFunc           PROTO :DWORD,:DWORD,:DWORD,:DWORD
PatchFile         PROTO :BYTE,:DWORD
WriteRegistry     PROTO :DWORD
ControlDeArchivo  PROTO :DWORD
CerrarArchivo     PROTO :DWORD
De este código lo que más importa son los 5 procedimientos definidos:
DlgFunc -> Controla el DialogBox que usamos para el patch.
PatchFile -> Realiza el parcheo de CDumper.exe.
WriteRegistry -> Escribe 1 como dato del valor DemoMsg en la Registry.
ControlDeArchivo -> Chequea que CDumper.exe sea v1.0 y que no esté parcheado ya.
CerrarArchivo -> Cierra CDumper.exe si fue abierto y libera la memoria.

; -----------------------------------------------------------------------------
.const
IDD_DIALOG     equ 1000   ; Este es el identificador del Dialog Box
IDC_SERIAL     equ 1002   ; 
IDC_EXIT       equ 1003   ; Son los mismos identificadores de controles
IDC_ABOUT      equ 1004   ; definidos en rscr.rc
IDC_CRACKEAR   equ 1005

MAXSIZE        equ 260    ; Tamaño máximo del nombre del archivo (incluido el path).
MEMSIZE        equ 10     ; Pongo 10 porque se leen 9 valores (MEMSIZE-1).
D_OFFSET       equ 686569 ; Desplazamiento que hay hasta los bytes a parchear

En esta sección se definen las constantes que tendrá el patch. MAXSIZE, MEMSIZE y D_OFFSET son tamaños medidos en bytes y en notación decimal. MEMSIZE es el tamaño de la memoria donde se copiarán los cuatro bytes a parchear, más los otros cinco que usaré para ControlDeArchivo.

; -----------------------------------------------------------------------------
.data
bytescorrectos db 8Bh,45h,0FCh,50h,56h,6Ah,00h,8Bh,0C7h
ofn            OPENFILENAME <>               
FilterString   db "CDumper.exe",0,"CDumper.exe",0,0
buffer         db MAXSIZE dup(0)
Icono          db "app",0
Iconimage      db "app",0
hIcon          dd 0
hWnd           dd 0

;MANEJO DE MENSAJES
MsgCaption     db "CDumper v1.0 Patch",0
MsgBoxText4    db "CDumper v1.0 patched OK.",0
ErrorMessage1  db "Error abriendo archivo!!!",0
ErrorMessage2  db "Error inesperado de memoria!!!",0
ErrorMessage3  db "Error inesperado de archivo!!!",0
ErrorMessage4  db "Error de lectura de archivo!!!",0
ErrorMessage5  db "Error de escritura de archivo!!!",0
ErrorMessage6  db "No se seleccionó ningún archivo!!!",0
ErrorMessage7  db "CDumper.exe versión distinta o ya parcheada",0
ErrorMessage8  db "Error escribiendo CLAVE en Registry",0
ErrorMessage9  db "Error escribiendo VALOR en Registry",0
ErrorMessage10 db "Error cerrando Registry",0
txt1           db "Desde las profundidades del abismo, SACCOPHARYNX / [13/02/2002]",0

;MANEJO DE REGISTRY
sSubKey        db "SOFTWARE\GMixon\CDumper\Demo",0
sName1         db "DemoMsg",0
Dato1          dd 1

Aquí definimos los datos inicializados: bytescorrectos son los bytes originales de CDumper.exe y los uso para control; ofn es una estructura necesaria para el manejo de archivos; buffer contendrá el path y el nombre del archivo a parchear (que como se ve en FileString este será Cdumper.exe); sSubKey contiene el nombre de la entrada que crearemos en la registry; sName1 contiene el nombre del único valor que escribiremos en esta entrada, y Dato1 es el dato que escribiremos en el valor DemoMsg. El resto es para el manejo del ícono del patch, los handles necesarios y los mensajes.

; -----------------------------------------------------------------------------
.data?
hInstance      HINSTANCE ?
hFile          HANDLE ?
hMemory        HANDLE ?
pMemory        DWORD ?
SizeReadWrite  DWORD ?
phKey          dd ?
phDisposition  dd ?

En esta sección se definen los datos no inicializados para el manejo de instancias, archivos y memoria.

; -----------------------------------------------------------------------------
.code
start:
   invoke GetModuleHandle,NULL
   mov hInstance,eax
   invoke DialogBoxParam,hInstance,IDD_DIALOG,NULL,addr DlgFunc,NULL
   invoke ExitProcess,NULL

Aquí comienza la sección de código. Se obtiene el handle del módulo, se invoca a DialogBoxParam, diciendo que DlgFunc es el procedimiento que controlará los eventos de este Dialog Box (IDD_DIALOG), y que cuando salgamos del Dialog Box se invoque a ExitProcess para terminar la ejecución de nuestro patch.

; -----------------------------------------------------------------------------
DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
.if uMsg==WM_INITDIALOG
   ;Inicializa el DialogBox
   mov eax,hDlg
   mov hWnd,eax
   invoke LoadIcon,hInstance,ADDR Icono
   mov hIcon,eax
   invoke SendMessage, hDlg, WM_SETICON,1,hIcon
   
   ;Inicializa los miembros de la estructura OPENFILENAME
   mov ofn.lStructSize,SIZEOF ofn
   push hWnd
   pop  ofn.hWndOwner
   push hInstance
   pop  ofn.hInstance
   mov  ofn.lpstrFilter, OFFSET FilterString
   mov  ofn.lpstrFile, OFFSET buffer
   mov  ofn.nMaxFile,MAXSIZE

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

Comienza el procedimiento DlgFunc el cual procesa los mensajes enviados a nuestro DIALOG BOX. Como se ve aquí, si el DIALOG BOX recibe el mensaje WM_INITDIALOG inicializo el handle, el ícono y la estructura necesaria para el manejo de archivos. Cuando recibe el mensaje WM_CLOSE se debe cerrar el DIALOG BOX para lo cual invocamos a EndDialog.

.elseif uMsg==WM_COMMAND
   mov eax,wParam
   mov edx,eax
   shr edx,16
   .if dx==BN_CLICKED
      .if ax==IDC_EXIT
         ; --------------------------------------------------------------------
         ; EXIT (SALIR)
         ; --------------------------------------------------------------------
         invoke SendMessage,hDlg,WM_CLOSE,0,0
            
      .elseif ax==IDC_CRACKEAR
         ; --------------------------------------------------------------------
         ; COMIENZO DE LA OPERACION DE CRACKING (CRACKEAR)
         ; --------------------------------------------------------------------
         invoke PatchFile,1,hDlg
         .if eax == 6666         ;Se escribió correctamente el archivo.    
            invoke WriteRegistry,hDlg
            .if eax == 6666      ;Se escribió correctamente la registry.    
               invoke MessageBox, \
               hDlg, ADDR MsgBoxText4, \
               ADDR MsgCaption, \
               MB_OK
            .elseif              ;Si la registry se escribió mal restauro el archivo.
               invoke PatchFile,0,hDlg
            .endif
         .endif

      .elseif ax==IDC_ABOUT
         ; --------------------------------------------------------------------
         ; ABOUT (ACERCA DE)
         ; --------------------------------------------------------------------
         invoke MessageBox,hDlg,ADDR txt1,ADDR MsgCaption,MB_OK
      .endif
   .endif
.endif
ret
DlgFunc endp

Cuando el DIALOG BOX recibe el mensaje WM_COMMAND quiere decir que uno de los botones definidos en rsrc.rc pudo haber sido clickeado, eso es lo que controlaremos aquí. El que más nos importa es IDC_CRACKEAR, ya que aquí se harán las operaciones necesarias para el patch. Si al retorno de PatchFile eax vale 6666 quiere decir que CDumper.exe se parcheó correctamente, entonces procedemos a escribir la entrada en la registry invocando WriteRegistry. Si la entrada se escribió correctamente eax volverá a valer 6666 y el patch funcionó a la perfección. Si no se pudo escribir con éxito la entrada en la registry, restauro los bytes originales de CDumper.exe y todo queda como estaba antes (el patch no cambia nada). Tanto para escribir los bytes originales, como los del patch uso el mismo procedimiento (PatchFile), por eso el primer parámetro que le paso es un flag, que está en 1 (para parchear) o en 0 (para escribir los bytes originales si hubo un error).

Bueno, esa es la estructura principal del patch. Ahora entraremos en más detalles.

; -----------------------------------------------------------------------------
PatchFile PROC dbFlag:BYTE,hDlg:DWORD
   .if dbFlag == 1
      mov ofn.Flags, OFN_FILEMUSTEXIST or \
          OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
          OFN_EXPLORER or OFN_HIDEREADONLY
      invoke GetOpenFileName, ADDR ofn
   .else
      mov eax,1
   .endif   

   ;Si no hubo error y se seleccionó un archivo sigue.       
   .if eax==TRUE

Comenzamos con el procedimiento PatchFile. La API GetOpenFileName es la que nos deja seleccionar un archivo. En este caso el que queremos parchear. Pero antes de invocarla debemos saber si estamos parcheando el archivo, o si estamos restaurando los bytes originales. Porque si estamos restaurando los bytes originales debido a un error en la escritura de la registry, ya habíamos seleccionado el archivo y no hace falta hacerlo nuevamente. Si eax es TRUE (distinto de cero) no hubo error al seleccionar el archivo y podemos empezar con el patch. El registro eax solo valdrá 6666 en PatchFile cuando se llegue al final del procedimiento (antes del último ret).

   ; Abro el archivo seleccionado; CreateFile también abre archivos 
   ; ------------------------------------------------------------------------
   invoke CreateFile, ADDR buffer,\
         GENERIC_READ or GENERIC_WRITE ,\
         FILE_SHARE_READ or FILE_SHARE_WRITE,\
         NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
         NULL
   ; Control de error de apertura de archivo
   ; ------------------------------------------------------------------------
   .if eax == INVALID_HANDLE_VALUE
      invoke MessageBox, hDlg,ADDR ErrorMessage1, ADDR MsgCaption, MB_OK
      ret
   .endif
   mov hFile,eax


   ; Obtengo el handle de la memoria a utilizar para alojar los bytes a leer
   ; ------------------------------------------------------------------------
   invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
   ; Control de error de Memoria
   ; ------------------------------------------------------------------------
   .if eax == NULL
      invoke MessageBox, hDlg,ADDR ErrorMessage2, ADDR MsgCaption, MB_OK
      invoke CerrarArchivo, eax
      ret
   .endif
   mov hMemory,eax


   ; Obtengo el puntero a la zona de memoria donde se alojarán los bytes a leer
   ; ------------------------------------------------------------------------
   invoke GlobalLock,hMemory
   ; Control de error de Memoria
   ; ------------------------------------------------------------------------
   .if eax == NULL
      invoke MessageBox, hDlg,ADDR ErrorMessage2, ADDR MsgCaption, MB_OK
      invoke CerrarArchivo, eax
      invoke GlobalFree,hMemory  
      ret
   .endif
   mov pMemory,eax


   ; Pongo el puntero de archivo apuntando a los bytes a parchear.
   ; ------------------------------------------------------------------------
   invoke SetFilePointer,hFile,D_OFFSET,NULL,FILE_BEGIN
   ; Control de error de búsqueda en archivo
   ; ------------------------------------------------------------------------
   .if eax == 0FFFFFFFFh
      invoke MessageBox, hDlg,ADDR ErrorMessage3, ADDR MsgCaption, MB_OK
      invoke CerrarArchivo, eax
      ret
   .endif


   ; Leo del archivo los bytes para control y modificación del mismo.
   ; ------------------------------------------------------------------------
   invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
   ; Control de error de lectura en archivo
   ; ------------------------------------------------------------------------
   .if eax == 0
      invoke MessageBox, hDlg,ADDR ErrorMessage4, ADDR MsgCaption, MB_OK
      invoke CerrarArchivo, eax
      ret
   .endif

Esta parte no presenta complicaciones si observan bien. Está dividida claramente en cinco pasos cada uno de los cuales dice que es lo que hace, y además todos presentan la misma estructura: Se invoca una API y luego se controla si hubo un error o no mediante el valor que tenga el registro eax. Pueden ver que si hubo un error eax no vale 6666, se despliega el mensaje correspondiente, se invoca al procedimiento CerrarArchivo y retornamos a DlgFunc. Los valores retornados por las APIs en eax y los parámetros que se les pasan a las mismas, los tomé de La Referencia de APIs, por eso anteriormente les dije que la bajen de internet ya que es de mucha utilidad. No vayan a pensar que yo sabía todo eso por arte de magia.

   ; Controlo que sea el CDumper v1.0 antes de parchear.
   ; ------------------------------------------------------------------------
   .if dbFlag == 1
      invoke ControlDeArchivo, pMemory
      .if eax == 666
         invoke MessageBox, hDlg,ADDR ErrorMessage7, ADDR MsgCaption, MB_OK
         invoke CerrarArchivo, eax
         ret
      .endif
   .endif

Si dbFlag vale 1 quiere decir que estoy intentando parchear Cdumper.exe y no restaurando los bytes originales. Eso lo hago porque si hay un error escribiendo la registry, no me dejaría restaurar los bytes originales de CDumper.exe, porque me diría que el archivo ya fue parcheado. Luego invoco a ControlDeArchivo y si este procedimiento retorna un 666 en eax quiere decir que CDumper.exe no es v1.0 o que ya fue parcheado.

   ; Escribo el patch en memoria dependiendo del Flag
   ; ------------------------------------------------------------------------
   push edi
   mov edi, pMemory
   .if dbFlag == 1
      mov byte ptr [edi], 6ah
      mov byte ptr [edi+1], 00h
      mov byte ptr [edi+2], 90h
      mov byte ptr [edi+3], 90h
   .elseif
      mov byte ptr [edi], 8Bh
      mov byte ptr [edi+1], 45h
      mov byte ptr [edi+2], 0FCh
      mov byte ptr [edi+3], 50h
   .endif   
   pop edi

Antes de escribir el patch o la restauración en el archivo CDumper.exe debemos escribir los cambios en la memoria. Para lo cual movemos pMemory a edi, para que edi contenga un puntero a la zona de memoria donde se leyeron los bytes del archivo. Una vez hecho esto vamos incrementando en uno este puntero y copiamos los bytes según corresponda, dependiendo del valor del flag.

   ; Retorno el puntero al lugar correcto porque avanzó los bytes que se leyeron
   ; ------------------------------------------------------------------------
   invoke SetFilePointer,hFile,D_OFFSET,NULL,FILE_BEGIN
   ; Control de error de búsqueda en archivo
   ; ------------------------------------------------------------------------
   .if eax == 0FFFFFFFFh
      invoke MessageBox, hDlg,ADDR ErrorMessage3, ADDR MsgCaption, MB_OK
      invoke CerrarArchivo, eax
      ret
   .endif


   ; Escribo los cambios (el patch) en el archivo
   ; ------------------------------------------------------------------------
   invoke WriteFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
   ; Control de error de escritura en archivo
   ; ------------------------------------------------------------------------
   .if eax == 0
      invoke MessageBox, hDlg,ADDR ErrorMessage5, ADDR MsgCaption, MB_OK
      invoke CerrarArchivo, eax
      ret
   .endif

Las dos partes de código de arriba funcionan igual que las cinco anteriores. Pero cabe una explicación extra: cuando leemos bytes de un archivo a partir de una posición, el puntero del archivo avanza la cantidad de bytes que leímos, por lo tanto antes de hacer la escritura del archivo, debemos poner el puntero nuevamente en el mismo lugar en el que estaba justo antes de la lectura, porque lo que nosotros queremos es sobreescribir los bytes leídos.

   ; Si llegamos hasta aquí y no hubo errores, entonces delvuelvo 6666 en eax
   ; ------------------------------------------------------------------------
   invoke CerrarArchivo, eax
   mov eax, 6666
   ret
   .elseif
      invoke MessageBox, hDlg,ADDR ErrorMessage6, ADDR MsgCaption, MB_OK
      ret
   .endif   ; Este .endif cierra .if eax==TRUE
PatchFile ENDP

No hay más que explicar con respecto a ese código. Basta con la explicación en él incluída. Aquí se terminó el procedimiento PatchFile.

; -------------------------------------------------------------------------
WriteRegistry PROC hDlg:DWORD
   ; CREAR ENTRADA EN LA REGISTRY
   ; ----------------------------------------------------------------------
   invoke RegCreateKeyEx, HKEY_LOCAL_MACHINE, addr sSubKey, NULL, NULL,
          REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, addr phKey, 
          addr phDisposition
   ; chequea si no hubo error en crear la entrada en registry
   ; ----------------------------------------------------------------------
   .if eax != ERROR_SUCCESS
      invoke MessageBox, hDlg,ADDR ErrorMessage8, ADDR MsgCaption, MB_OK
      invoke CerrarArchivo, eax
      ret
   .endif
            

   ; ESCRITURA DEL VALOR DE DemoMsg EN REGISTRY
   ; ----------------------------------------------------------------------
   invoke RegSetValueEx, phKey, addr sName1, NULL, REG_DWORD, addr Dato1, 
          SIZEOF Dato1
   ; chequea si no hubo error en la escritura
   ; ----------------------------------------------------------------------
   .if eax != ERROR_SUCCESS
      invoke MessageBox, hDlg,ADDR ErrorMessage9, ADDR MsgCaption, MB_OK
      .if phDisposition == REG_CREATED_NEW_KEY	
	     invoke RegDeleteKey, phKey, addrs SubKey
      .elseif
         invoke RegCloseKey, phKey
      .endif
      invoke CerrarArchivo, eax
      ret
   .endif

   ; CERRANDO ENTRADA EN LA REGISTRY
   ; ----------------------------------------------------------------------
   invoke RegCloseKey, phKey
   ; chequea si no hubo error en el cierre
   ; ----------------------------------------------------------------------
   .if eax != ERROR_SUCCESS
       invoke MessageBox, hDlg,ADDR ErrorMessage10, ADDR MsgCaption, MB_OK
       invoke CerrarArchivo, eax
       ret
   .endif

   ; Si llegamos hasta aquí y no hubo errores, delvuelvo 6666 en eax
   ; ----------------------------------------------------------------------
   mov eax, 6666
   ret
WriteRegistry ENDP

Este es el procedimiento que escribe la entrada en la registry. Presenta la misma estructura que el procedimiento anterior y no merece mucha explicación. Antes de escribir un valor y su dato en la registry, necesitamos abrir o crear una entrada en la cual lo escribiremos, y luego de la escritura se debe cerrar la entrada. Si todo funcionó bien retornamos 6666 en eax. Si hubo un error al intentar escribir la registry borramos la entrada (Key), solo si esta fue creada por nuestro patch. Si ya existía no se borra, solo cerramos dicha entrada.

; -------------------------------------------------------------------------
; Este procedimiento chequea que el archivo CDumper.exe no esté parcheado
; o que no sea de otra versión.
; ReadFile lee nueve bytes a partir de la posición de donde se parchean
; los cuatro bytes necesarios. Leo cinco bytes más para hacer la 
; comparación entre nueve bytes:
; bytescorrectos -> contiene los bytes correctos de CDumper.exe v1.0 
; mientras que pDataMemory -> apunta a los bytes leidos
; Yo asumo que CDumper.exe es la versión 1.0 sin parchear, si la 
; comparación de los nueve bytes es exitosa. Se pude hilar más fino. 
ControlDeArchivo PROC pDataMemory:DWORD
   push esi
   push edi          ; Salvo en la pila el valor de los registros que uso
   push ecx          ; para evitar errores 
   push edx 
   mov ecx, -1
   mov edi, pDataMemory                ; edi apunta a los bytes leidos
   mov esi, offset bytescorrectos      ; esi apunta a los bytes correctos
ciclo:
   inc ecx
   cmp ecx, 9           ; cuando ecx valga 9 se han comparado los 9 bytes 
   je ok
   mov dl, byte ptr [edi+ecx]          ; comparo un byte leido con uno de
   cmp dl, byte ptr [esi+ecx]          ; los originales
   je ciclo
   jmp error
error:
   mov eax, 666              ; Si la comparación falló retorno 666 en eax  
   jmp salir
ok:
   mov eax, 0            ; Si la comparación fue exitosa retorno 0 en eax
salir:
   pop edx
   pop ecx
   pop edi                             ; Recupero los registros y retorno
   pop esi
   ret
ControlDeArchivo ENDP

Este es el procedimiento para saber si debemos parchear o no CDumper.exe. En el mismo códido se incluye la explicación del procedimiento.

; -------------------------------------------------------------------------
;Cierro el archivo y libero la memoria según corresponda
CerrarArchivo PROC ddValor:DWORD
    invoke CloseHandle,hFile
   .if ddValor != NULL       ;Solo si ddValor no es nulo se libera memoria
      invoke GlobalUnlock,pMemory
      invoke GlobalFree,hMemory
   .endif
   ret      
CerrarArchivo ENDP

; -------------------------------------------------------------------------
end start

Bueno, hemos llegado al último de los procedimientos. Solo sirve para cerrar CDumper.exe y liberar la memoria según ddValor, en caso de que se haya pedido memoria al sistema.

Aca se termina la explicación del código fuente. Este es todo el patch. Para compilar el código fuente solo deben descomprimir CDumperPatchSrc.zip en un directorio, cargar MASM32, abrir el archico CDumperPatch.asm, ir a la opción Project del menu, y seleccionar Build All. Esto generará CDumperPatch.exe, que al ejecutarlo se verá algo así:

Recuerden que antes de parchear a Cdumper.exe, por las dudas, es recomendable desinstalar este programa y borrar las entradas en la registry, volver a instalarlo pero sin ejecutarlo aún, aplicar el patch y luego ejecutarlo, y ahora sí, a disfrutar de la música. Ahora les voy a mostrar el otro punto de ataque.

 

Otro punto de ataque

Cuando se me ocurrió pasar el CDumper.exe por File InsPEctor vi que fue desarrollado en Borland Delphi 3.0, por lo tanto procedí a desensamblar con DEDE 3.0 y me llevé una pequeña sorpresa que es muy común últimamente. Los programadores ocultan los Forms de registración. Si desensamblan CDumper.exe y van a Forms, verán esto:

Así es, existe un formulario para el ingreso del password y seguramente registración del software. Entoces vamos a buscar este formulario oculto. Si van a Help->About verán lo siguiente:

Si hacen dos clicks (no doble-click) sobre la imagen de este Form verán esto:

Hemos habilitado el botón de registración (Register). Click sobre el mismo y:

No se preocupen que el tutorial termina aquí, jejeje, pero quería mostrarles esto, para tener en cuenta que a veces puede haber más de un camino para alcanzar el éxito.

Yo estuve traceando un buen rato, luego de poner el breakpoint más famoso en este mundo (bpx hmemcpy), pero debido a un imprevisto en la computadora no pude continuar con el traceo. Si alguien tiene la amabilidad de hacerlo por mí y mandarme un KeyGen, o al menos explicarme el algoritmo que comprueba que el password sea correcto, se lo agradecería mucho. Un dato: El password se guarda encriptado en la registry en la entrada GMixon. Abajo de todo está mi dirección de e-mail por si alguien hace algo o tiene dudas.

Saludos

Bueno colegas de todo el mundo, aquí se terminó este tutorial, de verdad, jejeje. Se que se hizo extenso, pero quería que todo este bien explícito. Aprovecho para agradecer a Karpoff por enviarme unos archivos que necesitaba, y al resto de los crackers que aportan material al sitio. Sigamos así gente que la unión hace la fuerza. Saludos a todos.

Desde las profundidades del abismo

saccopharynx@yahoo.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