[ 3DMAX v3.0 Una inofensiva mochila ]
Por Black Fenix


En este tutorial tratamos de nuevo las mochilas, el objetivo es el 3D Studio Max V3.0. He de decir que me ha defraudado bastante el sistema de protección, parece más una broma que no otra cosa. En menos de media hora puedes crackear el programa, es ridiculo, incluso he tardado mucho más en escribir este tutorial. Hasta se puede prescindir del SoftIce para crackear el programa, con sólo usar el IDA y un poco de inteligencia habriamos llegado al crack final. Me parece patético.He encontrado muchas aplicaciones shareware mejor protegidas y además sin que estas utilizen ningún tipo de mochila. Esto me lleva a pensar el dinero que gastán en mochilas, para luego proteger de esta manera, es extraño que tan preocupados que son ellos para el dinero, no se den cuenta de como lo están tirando. Me sentiría muy avergonzado si malgastara mi dinero en un software que está protegido de tal manera y además resulta tan caro.
De nuevo se demuestra que no importa lo caro que sea un programa, la vagancia de los programadores (en este caso de Autodesk) se pone de nuevo de manifiesto. A veces intento dar una explicación al porqué de estas inofensivas protecciones,quizas son las prisas por sacar cuanto antes una nueva versión, o quizas no protegen mejor para que su software sea crackeado rápidamente y se convierta así en el más utilizado (curiosa técnica de marketing), no se, no entiendo el porqué... sólo se que alguien dijo: es una mala protección, entonces es un mal programa.


Necesitaremos:

marcador.gif (1024 bytes)IDA Pro
marcador.gif (1024 bytes)SoftIce 4.0

Puedes buscar estas tools en http://astalavista.box.sk

marcador.gif (1024 bytes) Bueno primero vamos a utilizar el metodo del trazado a lo retro, si no sabes de que va, mirate mi tutorial génerico sobre mochilas. Antes de comenzar sería bueno desensamblar el ejecutable , yo le he intentado desensamblar con el W32Dasm pero no funciona (el programa se ejecuta sólo antes de desensamblarse, como es posible? ), de todas maneras no importa ya que siempre disponemos del IDA, que en estos casos nunca falla :). Pues nada desensamblamos el archivo y guardamos el resultado para su posterior uso.
Luego ejecutaremos el 3DMax, nos aparece la pantalla de inicio y posteriormente un mensaje de error que dice algo así:

' El bloqueo de hardware no está conectado o no es válido bla bla... '

Pulsa aceptar, bueno parece que no tenemos la mochila reglamentaria, pero esto tiene fácil solución, activamos el SoftIce y pones un breakpoint sobre la funcion MessageBoxA con:

> BPX MessageBoxA

marcador.gif (1024 bytes) Ahora ejecutamos el 3DMax y veremos que el SoftIce se activa antes de mostrar el cuadro de error, pulsamos F12 para ir a la linea de código donde se llamó a MessageBoxA y veremos al así :

0046977C loc_46977C: ; CODE XREF: sub_469610+155j
0046977C call sub_5C3910
00469781 and eax, 0FFFFh
00469786 test eax, eax
00469788 jz short loc_4697FA
0046978A mov ecx, [esp+10h]
0046978E dec ecx
0046978F test ecx, ecx
00469791 mov [esp+10h], ecx
00469795 jg short loc_469742
00469797 test eax, eax
00469799 jz short loc_4697FA
0046979B mov eax, dword_5F3740
004697A0 test eax, eax
004697A2 jz short loc_4697B5
004697A4 push 1025h
004697A9 call sub_414DA0
004697AE push eax
004697AF call ds:KillTimer
004697B5
004697B5 loc_4697B5: ; CODE XREF: sub_4697A2
004697B5 xor ebx, ebx
004697B7 call edi
004697B9 mov eax, [eax]
004697BB push 0FFh
004697C0 lea ecx, [esp+18h]
004697C4 push ecx
004697C5 push 2
004697C7 push eax
004697C8 call esi
004697CA call edi
004697CC mov eax, [eax]
004697CE push 0FFh
004697D3 lea edx, [esp+118h]
004697DA push edx
004697DB push 0EA81h
004697E0 push eax
004697E1 call esi
004697E3 push 2010h
004697E8 lea eax, [esp+18h]
004697EC push eax
004697ED lea ecx, [esp+11Ch]
004697F4 push ecx
004697F5 push ebx
004697F6 call ebp                        -> Llama al cuadro de mensaje
004697F8 jmp short loc_4697FE   
-> y salta más abajo !! ESTAMOS AQUÍ !!
004697FA loc_4697FA:                  ; CODE XREF: sub_469788 y sub_469799
004697FA test ebx, ebx                
-> comprueba si EBX es 0
004697FC jnz short loc_46980B    -> no, salta
004697FE
004697FE loc_4697FE:                  ; CODE XREF: sub_4697F8
004697FE call sub_403A00         
-> llamada a una función
00469803 push 0
00469805 call ds:ExitProcess         -> Sale del programa
0046980B                                     -> el siguiente código sólo se ejecuta si se salta desde 4697FC
0046980B loc_46980B:                 
; CODE XREF: sub_4697FC
0046980B pop esi
0046980C pop ebp
0046980D pop edi
0046980E mov eax, ebx
00469810 pop ebx
00469811 add esp, 608h                
-> restaura la pila
00469817 retn                               
-> y continua el programa
00469817 sub_469610 endp

Bueno, examinando este código podemos comprobar como después de mostrar el mensaje de error, se nos desvia a mediante un salto incondicional a 4697FE, donde se termina la aplicación mediante la función del Kernel ExitProcess. Si miramos el código que nos ofrece el desensamblado del ejecutable, veremos que sólo hay dos maneras de esquivar la llamada a ExitProcess. La primera es pasar por el código en 4697FA, este código es referenciado por las líneas de código 469788 y 469799 tal y como nos indica el IDA. La segunda la encontramos por debajo del salto incondicional en la línea de código 46980B la cual es referenciada por la línea 4697FC.Esta última línea está por debajo de 4697FA, de lo cual se deduce que la única manera de esquivar el códigoque sale del programa es pasar por aquí. Como he dicho antes, la única manera de llegar aquí es mediante un salto en las lineas 469788 y 469799. Examinemos primero el salto en 469788 ya que es la primera referencia.

0046977C call sub_5C3910
00469781 and eax, 0FFFFh
00469786 test eax, eax
00469788 jz short loc_4697FA

Parece sospechoso, se llama a una función y posteriormente se borra el word alto EAX mediante el AND, y se comprueba si EAX es igual a 0, si esto es así, se salta a la línea 4697FA, donde se encuentra nuestro test ebx,ebx. Probemos a invertir el salto y ver lo que sucede. Activemos el breakpoint a MessageBoxA, pulsamos g para seguir, el breakpoint surte efecto, pulsamos F12 y aceptamos, de nuevo estamos en SoftIce. Ponemos ahora un breakpoint sobre la linea 469788 con el siguiente comando del SoftIce:

> bpx 469788

Desactivamos el bpx del MessageBoxA y pulsamos g para continuar el programa, este finalizará. Volvemos a ejecutarlo y estaremos de nuevo en SoftIce, delante de la linea 469788. Ahora la modificaremos con el siguiente comando:

> a 469788
> jmp 4697FA
> (pulsa enter y luego esc)

Puedes ver com ha cambiado el código, ahora pulsa g para ver lo que sucede... Magia el programa funciona !!

marcador.gif (1024 bytes) Bueno, no te lo creas del todo, efectivamente el programa se inicia correctamente, puedes cargar y salvar archivos, pero...¿ Averigua lo que sucede después de que el programa este abierto durante cierto tiempo ? Pues lo que era de esperar, se comprueba de nuevo la mochila, y se nos informa con otro mensaje de error. Podriamos actuar de la misma manera que lo hemos hecho antes, pero se trata de variar un poco y aprender nuevas técnicas, o no?
Si te fijas, veras que el programa siempre comprueba la mochila en el mismo intervalo de tiempo, vamos a medir este tiempo aproximadamente. Coge un cronometro y conometra hasta que aparezca el mensaje de error, no ejecutes ninguna orden, solo espera.
Después de aproximadamente 5 minutos aparecerá el mensaje de error. Si lo compruebas varias veces verás que el intervalo de tiempo siempre es el mismo, aproximadamente 5 minutos. Por lo tanto
es posible que el programa utilize un temporizador que se active cada cinco minutos y compruebe la mochila. !! Lo cual quiere decir que si pudieramos eliminar el temporizador, podriamos eliminar también la comprobación de la mochila e incluso hariamos que la aplicación se ejecutará más deprisa !! .Vamos a comprobarlo, primero necesitamos saber que funciones hay en windows para crear temporizadores. La única que yo conozco se llama SetTimer y tiene el siguiente formato:

UINT SetTimer(

    HWND hWnd,                      
// handle de la ventana donde se enviaran los mensajes del temporizador
    UINT nIDEvent,                     
// Identificador del temporizador
    UINT uElapse,                     
// tiempo de espera en milisegundos
    TIMERPROC lpTimerFunc    
// dirección del proceso que el temporizador ejecutará al alcanzar el tiempo de espera
);

Bueno lo más importante aquí es el tiempo de espera (uElapse), este valor se pasa como seguno parámetro, por lo tanto lo encontraremos en la pila a partir de (esp->0xC), ya que los 2 argumentos + la dirección de retorno ocupan 4 bytes cada uno (4*3=0xC).
Teniendo en cuenta esto, en vez de poner un simple bpx SetTimer, utilizaremos un breakpoint condicional que sólo se ejecutará cuando se intente crear un temporizador con el tiempo de espera que nosotros le especifiquemos. Como vamos a buscar un temporizador que se active a los cinco minutos, deberemos convertir los minutos a milisegundos (5 minutos son 60*5=300 segundos * 1000 = 300000) y el valor resultante a hexadecimal 3000000 en hexadecimal es 493E0. Por lo tanto el breakpoint quedará:

> bpx SetTimer if (esp->C)==493E0

Pues nada, entra en el SoftIce y teclealo. Sal del SoftIce y ejecuta el 3DMax, modifica el salto como hicimos previamente y continua con pulsando g. Antes de que desaparezca la pantalla de inicio, veras que el breakpoint del SetTimer a surtido efecto, pulsa F12 para ver el lugar desde donde se llamó y verás lo siguiente:

004023A1 6A00 push 0                                 -> pasa NULL como función a ejecutar.
004023A3 68E0930400 push 493E0h            
-> Aquí estan los cinco minutos de espera !!!
004023A8 6825100000 push 1025h               
-> Identificador
004023AD 55 push ebp                                
-> Handle de la ventana donde enviar mensajes
004023AE 896804 mov [eax+4], ebp             
-> guarda handle de la ventana
004023B1 FF1594755C00 call ds:SetTimer   
-> establece el temporizador
004023B7 8B0D40375F00 mov ecx, dword_5F3740
004023BD 8B4108 mov eax, [ecx+8]
004023C0 6800040000 push 400h
004023C5 6870795E00 push 5E7970h
004023CA 50 push eax
004023CB 8944241C mov [esp+1Ch], eax
004023CF E8EC280100 call sub_414CC0
004023D4 8B54241C mov edx, [esp+1Ch]

Como puedes comprobar se trata sin duda alguna de nuestra función. Ahora solo debemos pensar como hacer para eliminar el temporizador. Podemos sustituir por NOPs la llamada a SetTimer, pero tambien deberiamos de eliminar los 4 PUSH previos para no alterar el estado de la pila y provocar un posible cuelgue. Bien entonces vamos a hacer un resumen de los cambios necesarios, primero el salto en 469788, para averiguar el offset usaremos el IDA, nos colocaremos en la dirección 469788 con el menu Navigate/jump to/Ask for Address e introduciremos 469788 luego iremos al menu Edit/Patch Program/Change Byte y allí se nos mostrará el offset del archivo que es 69788h (no cambies nada y pulsa cancel).

Entonces en el offset 69788h encontraremos los bytes 7470 (que corresponden al salto jz short loc_4697FA) que cambiaremos por un salto incondicional (el opcode para un jmp es EB) por lo tanto quedará EB70.

Y para el temporizador actuaremos de igual forma para los pushes y el call, quedando:

Desde el offset 23A1h hasta el 23ADh sustituiremos todos los bytes por 90h (código del NOP) y
desde el offset 23B1h hata el 23B6H sustituiremos todos los bytes por 90 (código del NOP).

marcador.gif (1024 bytes) Resumen:

Offset: / Bytes originales             / Sustituir por:

23A1h / 6A 00 68 E0 93 04 00 68 25 10 00 00 55 / 90 90 90 90 90 90 90 90 90 90 90 90 90
23B1h / FF 15 94 75 5C 00    / 90 90 90 90 90 90
69788H / 74 70                     / EB 70

Gracias a Alfredo10 por corregir un fallo tipográfico en el offset (parece ser que hay alguien que si lee esto, aunque sea por las malas :) ).

marcador.gif (1024 bytes) Bueno esto es todo para los que busquen un crack rápido, pero nuestro propósito es aprender un poquito más.Lo siguente es sólo necesario para los que esten interesados en conocer algunos trucos más. Sabemos que el 3DMax utiliza una mochila tipo sentinel (mira en el CD y veras un archivo VXD con este nombre), vamos a utilizar otro de los metodos descritos en mi tutorial genérico sobre mochilas, el metodo 4 (anticiparse a la carga del VXD).

Utilizaremos el EXE sin modificar, por lo que si ya has modifcado el EXE, deberás recuperarlo.

Borramos todos los breakpoints que tengamos y establecemos un BPX condicional para capturar la carga del controlador sentinel.vxd con el SoftIce:

> bpx CreateFileA if *(esp->4+4)=='SENT'

el breakpoint surte efecto, estamos dentro de la función CreateFileA, comprobamos que efectivamente se va a cargar el driver sentinel.vxd con:

> d *(esp+14)

veremos SENTINEL.VXD en la ventana de datos del SoftIce.Pulsamos F12 para ejecutar la función y volver al código que llama a CreateFileA, veremos algo así:

005C3000 sub_5C3000 proc near ; C
005C3000 push 0                        
// prepara parametros para CreateFileA
005C3002 push 0
005C3004 push 3
005C3006 push 0
005C3008 push 0
005C300A push 0
005C300C push 5C2FE0h           
// dirección de la cadena "\\. \SENTINEL.VXD"
005C3011 call j_CreateFileA         // llama a CreateFileA
005C3016 mov ecx, [esp+4]        
005C301A cmp eax, 0FFFFFFFFh    // comprueba que se devuelve un handle correcto, eax debe de ser <> a -1
005C301D mov [ecx], eax           
// guarda el handler en la posicon apuntada por ECX
005C301F jnz short loc_5C3028  
// salta si el handle es válido
005C3021 mov ax, 1C0Bh           
// retorna un misterioso código de error
005C3025 retn 4                         
// y retorna
005C3028
005C3028 loc_5C3028:                ; CODE XREF: sub_5C3000+1Fj
005C3028 mov ax, 1C00h          
// retorna otro misterioso código de error
005C302C retn 4                        
// y retorna
005C302C sub_5C3000 endp

El código anterior intenta cargar el dispositivo virtual que controla la mochila. Según el funcionamiento de la función CreateFileA, si se pudo abrir el dispositivo, se retorna un valor diferente a INVALID_HANDLE_VALUE que es una constante del API de Windows que equivale a -1 (FFFFFFFFH). La rutina comprueba esta condicición en la línea 5C301A mediante la instrucción cmp eax,0FFFFFFFFh. Se guarda el handle, aúnque este no sea válido y se retorna el valor 1C0Bh si no se pudo abrir el dispositivo y 1C00h si se obtuvo un handle válido.
Por lo tanto sabemos que esta función siempre devolverá uno de estos dos valores. Nuestro código de error que indica que todo va bien es el 1C00h.
Si continuamos trazando hasta ejecutar el ret veremos algo así:

005C30B0 sub_5C30B0 proc near ; CODE XREF: sub_5C2D80+5p
005C30B0 sub esp, 4
005C30B3 push esi
005C30B4 mov esi, [esp+0Ch]
005C30B8 lea eax, [esp+4]
005C30BC push eax
005C30BD call sub_5C3000          -> Aquí se llama a la rutina anterior
005C30C2 mov [esi+6], ax            
-> guarda el código de error retornado
005C30C6 or al, al                        
-> comprueba si AL = 0
005C30C8 jz short loc_5C30D9    
-> salta si es 0
005C30CA or ah, ah                     
-> comprueba si AH = 0
005C30CC jnz short loc_5C30FA   -> salta si AH = 0
005C30CE or ax, 1C00h               
-> solo llegaráremos aquí en caso de que la llamada a 5C3000 retorne 1C0B, esto hace que AX valga 1C0B siempre
005C30D2 pop esi                       
-> restaura la pila
005C30D3 add esp, 4
005C30D6 retn 4                         
-> y regresa con código de error 1C0Bh

Si se pudo cargar el driver, se tomará el salto en 5C30C8 que nos llevará hasta aquí:

005C30D9 loc_5C30D9: ; CODE XREF: sub_5C30B0+18j

005C30D9 mov eax, [esp+4]        -> carga Handle al dispositivo SENTINEL.VXD
005C30DD push esi                       
-> empuja un parámetro que no sabemos aún que es
005C30DE push eax                      
-> pasa handle de SENTINEL.VXD
005C30DF call sub_5C3030             -> llama a una función (ver más abajo)
005C30E4 mov eax, [esp+4]       
005C30E8 push eax
005C30E9 call sub_5C3090
005C30EE mov ax, [esi+6]
005C30F2 or ah, ah
005C30F4 jnz short loc_5C30FA
005C30F6 or ax, 1C00h                   
-> Otra vez el misterioso código de error

005C30FA loc_5C30FA:               -> aquí sólo llegamos desde 5C30CC

005C30FA pop esi
005C30FB add esp, 4
005C30FE retn 4

Previamente se abrió el driver SENTINEL.VXD, por lo que es lógico pensar que este código enviará datos al driver y este a la mochila. La aplicación solo tiene una manera de comunicarse con el VXD y es mediante la función DeviceIOControl. La función DeviceIOControl tiene la siguiente forma:

BOOL DeviceIoControl(

    HANDLE hDevice,                        
// handle to device of interest
    DWORD dwIoControlCode,            
// control code of operation to perform
    LPVOID lpInBuffer,                        
// pointer to buffer to supply input data
    DWORD nInBufferSize,                
// size of input buffer
    LPVOID lpOutBuffer,                     
// pointer to buffer to receive output data
    DWORD nOutBufferSize,               
// size of output buffer
    LPDWORD lpBytesReturned,       
// pointer to variable to receive output byte count
    LPOVERLAPPED lpOverlapped    
// pointer to overlapped structure for asynchronous operation
);

Si continuamos trazando dentro de las funciones veremos que nuestras predicciones se cumplen. Este es el código que verás al entrar en la función llamada en la linea 5C30DF:

005C3030 sub_5C3030 proc near ; CODE XREF: 5C30DF
005C3030 sub esp, 4
005C3033 push esi                            
-> empuja ESI
005C3034 mov esi, [esp+10h]             
-> carga ESI de nuevo (esto no es necesario)
005C3038 push esi                            
-> y lo vuelve a empujar a la pila
005C3039 mov word ptr [esi+6], 0FFFFh
-> establece posición de memoria a FFFFh
005C303F call sub_5C2990
005C3044 lea eax, [esp+4]
005C3048 push 0                               
-> pasa NULL como puntero a una estructura para las operaciones asincronas
005C304A push eax                           
-> pasa puntero a una variable que recibira el numero de bytes de salida en mi caso 9CF694
005C304B push 0                             
-> Tamaño del buffer de salida 0
005C304D xor eax, eax                      
-> borra EAX
005C304F push 0                             
-> pasa NULL como buffer donde recibir datos
005C3051 mov ax, [esi+2]                  
-> carga en AX el tamaño del buffer de entrada
005C3055 push eax                           
-> tamaño del buffer de datos de entrada
005C3056 push esi                           
-> Pasa el buffer con información
005C3057 mov eax, [esp+24h]            
-> Carga handler del VXD en EAX
005C305B push 1                              
-> código de operación 1
005C305D push eax                           
-> pasa handle como primer argumento
005C305E call j_DeviceIoControl         -> Envia comandos al dispositivo VXD, y retorna 1 si todo OK
005C3063 push esi                             -> pasa de nuevo el buffer de entrada
005C3064 call sub_5C2950                 -> realiza muchos calculos sobre la estructura
005C3069 cmp word ptr [esi+6], 0FFFFh
-> comprueba los datos del buffer de entrada
005C306F jnz short loc_5C3077          -> sino es igual salta
005C3071 mov word ptr [esi+6], 1C3Bh
-> guarda 1C3B en el buffer de entrada
005C3077 loc_5C3077:                       ; CODE XREF: 5C306F
005C3077 mov ax, [esi+6]                  
-> guarda en AX datos del buffer
005C307B pop esi                            
-> restaura la pila
005C307C add esp, 4
005C307F retn 8                               
-> y retorna con código en AX = 400h
005C307F sub_5C3030 endp

Como era de esperar este código prepara los argumentos para la función DeviceIOControl y posteriormente en la línea 5C3064 se llama a una función (5C2950) la cual realiza una serie de cálculos sobre la estructura pasada en ESI. No he incluido estos cálculos por ser muy extensos.

Si continuamos trazando después de la llamada veremos que el programa toma el salto en 5C306F y retorna en AX el valor que habia en el buffer de entrada apuntado por [ESI+6]. Continuamos hasta ejecutar el ret y estaremos al principio:

005C30DF call sub_5C3030          -> venimos de aquí
005C30E4 mov eax, [esp+4]       
-> estamos aquí, carga en EAX en Handle del VXD
005C30E8 push eax                   
-> Pasa handle
005C30E9 call sub_5C3090       
-> llama a función que cierra el VXD con CloseHandle y retorna 1C00 en EAX
005C30EE mov ax, [esi+6]         
-> recuperamos el valor de retorno
005C30F2 or ah, ah                     
-> comprueba si AH = 0
005C30F4 jnz short loc_5C30FA  
-> no, salta
005C30F6 or ax, 1C00h               
-> todo bien

005C30FA loc_5C30FA:             
-> aquí sólo llegamos desde5C30CC
005C30FA ; y desde 5C30FA
005C30FA pop esi
005C30FB add esp, 4
005C30FE retn 4

Bueno por todo lo que tenemos hasta ahora parece que el programa utiliza códigos de error para comprobar el exíto de sus operaciones, por lo que podemos deducir, el código que indica que todo va bien es 1C00h y cualquier otra cosada un error. Aún así hemos comprobado que la comuncación con el VXD se cierra en cualquier caso, por lo que podemos sospechar que el programa volverá a abrir el VXD con CreateFileA y volverá a establecer comunicación con este.
Reactivaremos nuestros breakpoints (si los habias borrado, vuelvelos a crear) y pulsaremos g en SoftIce para continuar. Veremos que de nuevo el SoftIce se activará, pulsamos F12 para ver desde donde se llamó a CreateFileA y veremos que ya habiamos pasado por aquí, estamos de nuevo en 5C3000. Esto nos da otra pista, parece que la función en 5C3000 se utiliza en más de un sitio, por lo que vamos a pulsar de nuevo F12 para ver desde donde se llamó. Para nuestra sorpresa veremos que a sido llamada desde 5C30BD y aquí ya estuvimos antes, por lo que pulsaremos de nuevo F12. Ahora veremos que estamos en una nueva dirección:

005C2D80 sub_5C2D80 proc near ; CODE XREF: sub_5C23B0+43p
005C2D80 mov eax, [esp+4]
005C2D84 push eax
005C2D85 call sub_5C30B0

005C2D8A retn 4        -> ESTAMOS AQUÍ
005C2D8A sub_5C2D80 endp

Bueno parece que no hay muchas opciones por lo que pulsaremos F12 de nuevo y veremos:

005C24ED call sub_5C23B0 
005C24F2 mov [esi+6], ax    -> ESTAMOS AQUÍ, guarda resultado en ESI+6
005C24F6 pop esi       
-> reajusta la pila
005C24F7 add esp, 4
005C24FA retn 4       
-> y retorna

notese como se almacena en [esi+6] el resultado de la función previa, pulsamos F12 de nuevo y vemos:

005C396C mov word ptr [esi+6], 403h
005C3972
005C3972 loc_5C3972:                         ; CODE XREF: sub_5C3910+78
005C3972 cmp word ptr [esi+6], 403h
005C3978 jnz short loc_5C398A
005C397A mov [esi+0Ah], bl
005C397D push esi
005C397E call sub_5C2460                  -> venimos de aquí
005C3983 inc bl                                  
-> incrementa contador
005C3985 cmp bl, 3                             -> llevamos 3 intentos ?
005C3988 jbe short loc_5C3972          
-> No, continua en 5C3972
005C398A
005C398A loc_5C398A:                      ; CODE XREF: sub_5C3910+68j
005C398A mov ax, [esi+6]                   
-> carga en AX resultado de la función
005C398E mov cx, ax                         
-> lo copia a CX
005C3991 and cl, 9                             
-> realiza un AND sobre el byte bajo del resultado
005C3994 cmp cl, 9                            
-> es 9?
005C3997 jnz short loc_5C39A2         
-> no, salta a 5C39A2
005C3999 mov ax, 3                       -> si, devolvemos 3
005C399D pop esi                             
-> reajusta pila
005C399E pop ebx
005C399F retn 8                                
-> y retorna
005C39A2 loc_5C39A2:                      ; CODE XREF: sub_5C3997
005C39A2 push eax                          
-> pasa resultado como parametro a la función siguiente
005C39A3 call sub_5C3110                
-> llama a una función,valor de retorno en EAX
005C39A8 pop esi                            
-> restaura pila
005C39A9 pop ebx
005C39AA retn 8                              
-> y retorna.
005C39AA sub_5C3910 endp

El programa realiza tres intentos (línea 5C3985) para comprobar la mochila y después de eso lee el valor de retorno de esi+6 realiza un AND con nueve y comprueba el resultado con 9, ni no es igual la función retorna 3 en AX (línea 5C3999) y sale. Si continuas trazando hasta ejecutar el ret, veras que después te aperecerá el código al cual hemos llegado utilizando el metodo del MessageBox(pincha aquí para ir a este código), a partir de aquí el modus operandi es el mismo :). Con esto queda demostrado que siempre existe más de una puerta de entrada.


You are inside Reversed Minds pages.

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