Karpoff Spanish Tutor 1999-2002

Juego: America - no peace beyond the lines


 

PROTECCIÓN: VOB 3.32-3.42
Descripción: Protección comercial anticopia de CD's
Dificultad: Alta (ahora menos ;)
Herramientas: SoftIce v4.2.1, IceDump 6.023, PEditor 1.7, ProcDump 1.62, ADump, Hex WorkShop
CRACKER: Gádix/MrOcean[WKT!2001]   FECHA: ../../....

 

 

 INTRODUCCIÓN

Lo primero deciros que aquí en España sólo me he encontrado un juego que lleve esta protección. Pero eso es lo de menos no? El juego en cuestión es "America - no peace beyond the lines", y primeramente no le hice demasiado caso ya que sólo te pide el CD la primera vez que lo ejecutas. Esta primera vez te crea un pequeño fichero con el cual ya no te pide el CD. No te lo pide en tu ordenador, ya que para mi sorpresa llevé el juego a casa de un amigo, puse ese fichero y me pidió el CD. En ese fichero se guarda pues, información del ordenador-usuario.  Finalmente me puede hacer con una ISO de un juego alemán con esta protección(con una versión más nueva, la 3.42) y comprobar que el método funciona (sólo que este juego pedía siempre el CD) 

La protección no es fácil, requiere, entre otras cosas, un conocimiento total del formato PE y de las IAT's.  Lleva también JUNKCODE y OBFUSCATION CODE (aunque yo os he pasado las rutinas importantes ya "limpitas"). Junk Code, como su propio nombre indica es "código basura". Por ejemplo "mov eax,eax", "push edi - mov edi,68234 - pop edi". Obfuscation Code son saltos en medio de funciones, consiguiendo así ocultar el código al desensamblado. Un ejemplo:

:007E7A69    JMP 007E7A6D ;  Salta a 7E7A6D, que como podemos ver "no está visible"
:007E7A6B   SBB BYTE PTR [EBP+7A41B58D],44 
:007E7A72    ADD BL,CH 

:007E7A6D  LEA ESI,[EBP+00447A41]
; Y esta es la instrucción obfuscada(escondida)

Es importantísimo pues, que distingáis entre el código "real" y el código "basura". Sino os tirais horas trazando inutilmente.

 

 

 

 AL ATAKE


 PASO 1- OCULTAR EL DEBUGGER

VOB utiliza dos métodos para detectar si está corriendo junto a un debugger. El primero es un simple 'MeltICE' que se basa en comprobar con CreateFileA si están cargados los drivers del Sice (\\.\SICE, \\.\SIWVID para Win9x y \\.\NTICE para WinNT). Si el SoftIce no está cargado el valor esperado es -1 (FFFFFFFFh). El nombre del fichero es el último parámetro en ser empujado a la pila, por lo tanto (una vez dentro de la API) lo encontraremos en *(esp+4) (en *esp estará la dirección de retorno). Gracias a los breakpoints condicionales y las macros del Sice vamos a poder cambiar lo que apunta a esp+4: 

BPX CreateFileA if *(esp->4+4)=='SICE' || *(esp->4+4)=='SIWV' do "e *(esp+4) 0;x"

El segundo lo que hace es comprobar que no hallamos puesto ningún BPX en las APIS que llama el juego. Para ello comprueba que el primer byte de la API no sea 'CCh' (recordad que en realidad un brekpoint (BPX) es una INT 3 cuyo opcode es 'CCh'). ¿Cómo rompemos entonces en la API que queremos? En este caso, muy fácil: con un BPM. 

PASO 2 - ENCONTRAR EL OEP (Original Entry Point)

El primer método con el cual lo hallé, fue muy "al tuntún": rompiendo en las APIs en que muchos programas llaman al iniciarse y trazando a la "retro". Pero bueno.....conseguí mi objetivo, hallar el OEP: 0047606F 

Y ahora viene lo interesante. Fue de esta forma que viene a continuación, con la cual hallé la forma de hallar el "verdadero" Entry Point de cualquier juego protegido con VOB 3. Poned un BPM en esa dirección (recordad poner siempre BPM y no BPX en zonas de memoria que vayan a ser descomprimidas/desencriptadas), cerrad el programa y lo volverlo a ejecutar. El SoftIce rompe en el OEP. Es un buen momento para volcar el fichero :)

:a eip

:0047606F  JMP EIP

Le damos al F5 para volver a Windows, abrimos el ProcDump...coño!!! Dónde está nuestro proceso??? 

Umm, vamos a investigar dónde puede estar la causa....me huele a que nuestro querido VOB debe crear un thread que compruebe si hemos parado en el OEP. Ejecutamos de nuevo nuestro juego, con lo cual volvemos a romper en el OEP. Borramos este breakpoint y lo cambiamos por uno de lectura y escritura (RW). F5 again y salta el Sice. VOB está rellenando de ceros toda la sección .code!!! Será maricón. Si trazamos esta rutina no llegamos a ninguna conclusión con la cual evitar esto....Vamos a poner un BPM en CreateThread. "F12" tras romper en CreateThread y trazando un poco veremos que EAX va cogiendo forma de nuestro OEP.

(en ese thread vemos un sospechoso WaitForSingleObject....me lo imaginaba es un pequeño truco de sincronización. Nuestro juego debe activar algún "flag" que le diga a VOB que hemos pasado por el OEP. El truco no tiene mayor importancia, si trazamos 3 o 4 instrucciones podremos usar tranquilamente el truquillo del "jmp eip". Pero todavía es pronto para volcar, las APIS están rederigidas)

PASO 3 - CORREGIR LAS LLAMADAS A LAS APIS

Vamos a ver como corregirlas. Nos situamos en el OEP y vemos lo siguiente:

018F:0047606F    6AFF                     PUSH FF 
018F:00476071    6840314800           PUSH 00483140 
018F:00476076    68D0614700          PUSH 004761D0 
018F:0047607B    64A100000000      MOV EAX,FS:[00000000] 
018F:00476081    50                           PUSH EAX 
018F:00476082    64892500000000   MOV FS:[00000000],ESP 
018F:00476089    83EC68                  SUB ESP,68 
018F:0047608C    53                          PUSH EBX 
018F:0047608D    56                          PUSH ESI 
018F:0047608E    57                           PUSH EDI 
018F:0047608F    8965E8                   MOV [EBP-18],ESP 
018F:00476092    33DB                      XOR EBX,EBX 
018F:00476094    895DFC                 MOV [EBP-04],EBX 
018F:00476097    6A02                      PUSH 02 
018F:00476099    E8CEE53A00         CALL 0082466C
   <-- PRIMERA LLAMADA A LA API
018F:0047609E    90                           NOP     <-- ¿¿??
018F:0047609F    59                           POP ECX 

¿Qué pinta ese NOP hay? Muy fácil. Las APIS, lo habitual y más común, es llamarlas mediante un CALL DWORD PTR a la IAT, que son 2 bytes(FF15) + 4 de offset-dirección a la que llama. Pero aquí lo que hace VOB es una CALL "NORMAL" a una zona de memoria alojada por el mismo. Un CALL ocupa 1 byte(E8)+4 bytes que es la longitud del salto. De ahí el "NOP", de ese byte de menos. Y esa CALL va a parar a:

PUSH F32B009F   ; "valor mágico" usado para desencriptar la API correspondiente
PUSH 007E9ECE 
  ; mete en la pila el offset de la rutina que desencripta la API
RET  
                      ; salta a esa dirección 

Y la rutina que la desencripta es:

 PUSHAD                                            ; guarda todos los registros
 MOV EDX,003D9F4C 
 MOV ESI,[EDX+00449847] 
             ; ESI=IMAGE BASE 
 MOV ECX,[ESI+3C]  
                       ; ECX=110h (ESI+ECX=comienzo de la cabecera PE)
 MOV EBX,[ECX+ESI+00000080]  
  ; EBX=RVA de la IAT
 MOV EAX,[ESI+ECX+28] 
               ; EAX=EntryPoint (no OEP)
 ADD EBX,[ECX+ESI+00000084]  
   ; le añade a EBX el tamaño de la IAT
 SUB EAX,01FE6EFE
 LEA ESI,[ESP+20]   
 MOV EDI,[ESI] 
                                 ; obtiene el "valor mágico"
 SUB EBX,66EFCEFE
 SUB EDI,EBX 
                                        ; marea EDI 
 SUB EDI,EAX      
                                   ; hasta conseguir 
 ROR EDI,03                                       
     ; la dirección 
 ADD EDI,550BFACF                           
  ; de retorno
 MOV [ESI],EDI   
                              ; mete en la pila la dirección de retorno
 POPAD           
                                   ; restaura todos los registros
 RET
; Atentos a este RET porque también haré mención a él mas adelante

Y el RET nos lleva a:

0177:007F946B    E9EAA98077    JMP MSVCRT!__set_app_type

Si le damos al F8 saltamos a:

MSVCRT!__set_app_type 
0177:78003E5A    8B442404         MOV EAX,[ESP+04] 

Y la pregunta es: ¿cómo pillamos a dónde salta la rutina? Muy fácil calculando la longitud del salto.....lo primero que vamos a hacer es cambiar ese RET por un salto a nuestra memoria ADUMP. Y en la memoria ADUMP ensamblamos esto:

MOV EBX,[ESP] ; Pillamos la dirección a la que saltaría el RET (es decir el 007F946B de arriba)
MOV EDX,[EBX+01]
; En EBX+1 tenemos la longitud del salto, la metemos en EDX
ADD EBX,05
   ; La longitud del salto es respecto a la siguiente instrucción al salto (SALTO LEJANO=5 bytes)
ADD EBX,EDX
; Le añadimos a la instrucción siguiente al salto la longitud del mismo

Bien, si hemos hecho todo correctamente, EBX debe valer 78003E5A. Ya tenemos lo que buscabamos. Ahora tenemos que buscar ese valor en la IAT(en la del fichero original) Arrrgggghhhh!!! Si buscamos ese valor no lo encontramos..... pueden pasar  tres cosas:

- Que la IAT esté dañada o encriptada
- Que no esté inicializada
- Simplemente que no exista

Pues está dañada y no inicializada. Vamos a ver cómo deducir ésto. Ejecutamos de nuevo el juego y rompemos en el OEP. Usaremos el comando "/DUMP" del IceDump, ya que como veremos en el fichero volcado VOB daña la cabecera PE.......pero un momento, vamos a ver primero algo de información del exe:

SECCIÓN Virtual Size Virtual Offset Raw Size Raw Offset
.code 3D7000 1000 3D7000 1000
.rsrc AFC0 3D8000 AFC0 3D8000
.idata 1000 3E3000 200 C000
.data 41000 3E4000 40778 C200

También miramos el EntryPoint y vemos que es 003E4000. Qué casualidad no? Justo el inicio de la última sección.....sacamos la conclusión de que en esa sección está alojado el código del packer. Por tanto ese código no nos hará falta en el "dumpeado". La sección .idata tampoco se va a salvar. En esta sección está la IAT pero la de VOB, no la del fichero original. Ahora sí:

                  INICIO         LONGITUD
/DUMP    400000        3E3000       DUMPED.EXE

"400000" es la IMAGEBASE y 3E3000 el final de la sección ".rsrc"

Nos llama la atención dos cosas: 

- El icono del fichero no se ve
- El fichero a pasado de 533kb a 3980kb

El primero es un "problema" muy común en ficheros dumpeados. Es debido a que los VirtualOffsets y Raw Offsets no coinciden. Afortunadamente esto es fácil de arreglar. Abrimos el fichero dumpeado con PEditor, vamos a Section y le damos a "DumpFixer". Pero NO! Como os decía más arriba, VOB daña la cabecera PE en tiempo de ejecución, con lo cual en el volcado la cabecera está dañada. "No problem again", cogeremos la cabecera del fichero original. Abrimos el fichero original(america.exe) y el dumpeado(dumped.exe) con HexWorkShop y pegamos la cabecera del original en la del dumpeado. Ahora sí le damos a "DumpFixer". Bien, en la cabecera PE todavía están ".idata" y ".data". Las borramos con PEditor. Nos debe quedar una cosa así:

SECCIÓN Virtual Size Virtual Offset Raw Size Raw Offset
.code 3D7000 1000 3D7000 1000
.rsrc AFC0 3D8000 AFC0 3D8000

Bueno y ahora a lo que ibamos, a por nuestra IAT. Usaremos la técnica de buscar la cadena "kernel32.dll". ÑASCAS! No la encuentra :(  Pensemos. VOB debe por fuerza inicializar los imports y cargar las dll's que el juego va a usar. Y para ello usará LoadLibraryA/GetModuleHandleA. Vamos a poner un BPM en LoadLibraryA. Ejecutar el proceso. En *(esp+4) tendréis el nombre de la librería. Las seis primeras llamadas a esta función no tienen mayor importancia. Pero atentos a la 7ª. F12, trazad un poco hasta llegar a un POP EDI. Le damos a F8 y vemos como EDI pasa a valer 487836. Si miramos en esa dirección de memoria:

017F:00487836 00 00  00  00  00 00 00  00-00  00  00  00 00 00  5B 02 ..............[.
017F:00487846 C8 FE EF CC F2 F5 FF F4-EC CB F4 E8 9B 00 5C 01 ..............\.

En principio nada significativo, pero si subimos unas páginas hacia arriba:

017F:00487418  FF FF FF FF 90 A3 47 00 00 00 00 00 98 A3 47 00 ......G.......G. 
017F:00487428  48 75 08 00   00 00 00 00  00 00 00 00 36 78 08 00  Hu..........6x..
017F:00487438  30 B0 07 00  44 76 08 00  00 00 00 00 00 00 00 00  ...Dv..........   
017F:00487448  AC 79 08 00 2C B1 07 00 38 75 08 00 00 00 00 00 .y..,...8u......   
017F:00487458  00 00 00 00   E8 79 08 00 20 B0 07 00 18 75 08 00   .....y.. ....u..  
017F:00487468  00 00 00 00   00 00 00 00 02 7A 08 00 00 B0 07 00  .........z......    
017F:00487478  2C 77 08 00  00 00 00 00 00 00 00 00 34 7A 08 00  ,w..........4z..  
017F:00487488  14 B2 07 00  20 75 08 00 00 00 00 00 00 00 00 00   .... u..........     
017F:00487498  52 7A 08 00  08 B0 07 00 28 75 08 00 00 00 00 00  Rz......(u......   
017F:004874A8  00 00 00 00  72 7A 08 00 10 B0 07 00 30 75 08 00  ....rz......0u..   
017F:004874B8  00 00 00 00  00 00 00 00  7E 7A 08 00 18 B0 07 00  ........~z......   
017F:004874C8  C4 76 08 00  00 00 00 00 00 00 00 00 F4 7C 08 00  .v...........|..     
017F:004874D8  AC B1 07 00 9C 76 08 00 00 00 00 00 00 00 00 00 .....v..........      
017F:004874E8  AC 7D 08 00 84 B1 07 00 90 75 08 00 00 00 00 00  .}.......u......    
017F:004874F8  00 00 00 00   E4 7E 08 00 78 B0 07 00 00 00 00 00   .....~..x.......  
017F:00487508  00 00 00 00   00 00 00 00  00 00 00 00 00 00 00 00   ................    

Bien, este "churro de colores" es nuestra IAT con sus IMAGE_IMPORT_DESCRIPTOR's(Original First Thunk, TimeDateStamp y ForwardChain (habitualmente 0's), Name, First Thunk) y finalmente el DESCRIPTOR FINAL NULO.

Los más avispados sacaréis de aquí mismo el tamaño de la IAT. Con el SoftIce: ? 487518-487428=F0. (trabajamos siempre en hexadecimal)

Bueno, vamos a ver esa dirección de memoria en nuestro fichero dumpeado. Abrimos HexWorkShop y vamos al offset 87428h (= 487428-ImageBase, recordad que esto es así porque hemos equiparado los Virtual Offsets con los Raw Offsets). En ese offset:

00087428  48 75 08 00  00 00 00 00 00 00 00 00 36 78 08 00 30 B0 07 00

Vamos al offset 00087836 (que es el "NAME" del primer IMAGE_IMPORT_DESCRIPTOR)

00 00 00 00 00 00 00 00 00  00 00 00 00 00 5B 02 53 65  . . . . . . . . . . . . . .[.Se
74 57 69 6E 64 6F 77 50 6F 73 00 00 5C 01 47 65 74 57  tWindowPos..\.GetW

Coño! El muy cabrón nos ha borrado el nombre de las dll's!!! Bueno, no pasa nada ;) recordáis los LoadLibraryA--POP EDI?  Pues se corresponden. Me explico. Tras el POP EDI en EDI tendréis la dirección donde va el nombre de la librería. Pos ya está. Cada LoadLibraryA nos apuntamos el nombre de la librería en un papel (jeje OldSchool), trazamos hasta el POP EDI y nos apuntamos al lado la dirección que nos da EDI. Nos vamos al fichero dumpeado y las corregimos. Otra posibilidad es pillarla todavía intacta. Para ello ponemos un BPM de escritura en la IAT y cuando salte el Sice nos fijamos donde acaba la rutina y ponemos un breakpoint ahí. Volcamos la IAT y la pegamos en el fichero que habíamos volcado antes. Pues ya tenemos nuestra IAT corregida, ya sólo nos queda inicializarla. Cómo la inicializamos? Muy fácil, dejaremos que sea el propio Windows quien nos haga la "faenita". Cada vez que ejecutamos un proceso el windoze(CreateProcessA) inicializa la IAT. Pos ya está. Cargamos el fichero con el Loader del Sice (que es como si lo cargasemos suspendido). Si nos fijamos en los "First Thunk" de la tabla de arriba  vemos que los pointers a las importaciones empiezan en 7B000 (47B000 en memoria)

017F:00487468  00 00 00 00   00 00 00 00   02 7A 08 00   00 B0 07 00  .........z......   

Bien ahora lo que tenemos que hacer es copiarlos a nuestra memoria ADump. Para ello usaremos el comando "M" del Sice. Cogemos una longitud considerable, mejor de más que de menos. Con 1000h tendremos de sobra. Y como destino final la memoria ADump+200h (en esos primeros 200h bytes meteremos la rutina de corrección de API's).

: m 47B000 L 1000 ADumpAddress+200

Ahora ya podemos saber donde está el pointer correcta a la API correspondiente. Arrancamos el fichero original y una vez ya sabemos la dirección de la API (calculando el salto recordad), codificamos una rutinilla tal como:

    mov eax,0047b000 ; dirección donde empiezan los thunks en nuestro fichero volcado
    xor edx,edx 
    mov edi,ADumpAddress+200  
loop_busca:
    cmp dword ptr[edi+edx],ebx 
; buscamos la API en nuestros thunks
    jz encontrado
    inc edx 
; incrementa el contador
    cmp edx,00001000 
    jnz loop_busca
    int 3 
; int3/bpint3 si algo falla pararemos aquí
encontrado:
    add eax,edx   
; 47b000+CONTADOR (EDX)
    mov word ptr [ecx],15ff  
; ECX=dirección de la llamada a la API a corregir  (FF15=call dword ptr)
    mov dword ptr[ecx+02],eax 
; EAX=dirección correcta a nuestro thunk

  Si trazamos unas cuantas llamadas a las APIS vemos que el "byte de menos" puede ser tanto un NOP como un CLD y que puede estar antes o después de la CALL:

0177:00476099   E8CEE53A00   CALL 0082466C  ; CALL a VOB
0177:0047609E   90                     NOP 
<---

0177:004760BC  E874E53A00    CALL 00824635  ; CALL a VOB
0177:004760C1   FC                    CLD 
<---

0177:004760E8    FC                  CLD   <---
0177:004760E9    E85DE53A00 CALL 0082464B  
; CALL a VOB

0177:00476194    90                   NOP   <---
0177:00476195    E82EE03A00 CALL 008241C8   
; CALL a VOB

Tendriamos que codificar también una rutinilla que nos buscase todas las llamadas a las APIS:

    mov eax,00401000 ; inicio de la sección de código 
    mov ecx,0047b000 ; VOB mete a excepción de .rsrc todas las secciones, por tanto 
      
                             ; no podemos saber con exactitud donde acaba esta sección. Pero
                                   ; como sabemos que los thunks empiezan en 47b000 como mucho
                                   ; llegará hasta ahí ;) 

loop_busca:
   cmp byte ptr [eax],e8
; E8=OPCODE DE CALL 
   jz encontrado 
   incrementa:
   inc eax 
   cmp ecx,eax
; Hemos llegado al final de la sección de código? 
   jnz loop_busca ;
No? Pos sigue buscando... :b
   int 3
; BPINT3 y cuando no halla más llamadas romperemos aquí
encontrado:

---- AHORA MIRAMOS SI EL "BYTE DE MENOS" ESTÁ ANTES O DESPUÉS ----

   cmp byte ptr [ecx+5],90 ; 90 = OPCODE de NOP
   jz ya_bien 
   cmp byte ptr [ecx+5],fc 
; FC = OPCODE de CLD
   jz ya_bien
   dec ecx   
; El "byte de menos" está antes del "CALL", decrementamos ECX en 1
ya_bien: 


----- AQUÍ CALCULARIAMOS LA LONGITUD DEL SALTO, EVITANDO "TOCAR" ECX QUE ES -----
                     ----- DONDE TENEMOS LA DIRECCION DE LA LLAMADA A VOB -----


(Momento de recordar este fragmento de código a la que saltan todas "las calls de VOB":
0177:0082466C  689F002BF3 PUSH F32B009F 
0177:00824671   68CE9E7E00 PUSH 007E9ECE 
0177:00824676   C3 RET )

---- SUPONIENDO QUE EAX ES LA DIRECCION A LA QUE NOS LLEVA EL SALTO: ---- 

  cmp byte ptr[eax+6],68 ; Chequeamos si está el  PUSH 007E9ECE
   jnz incrementa               
; si no incrementa y sigue buscando
  cmp dword ptr [eax+7]
,CE9E7E00  
   jnz incrementa 

--- AQUÍ YA SABEMOS QUE EL CALL PERTENECE A VOB ---

  jmp ecx   ; Saltamos a esa dirección para que la rutina VOB nos devuelva
                 ; la API correcta

Como VOB deja "intactos" los registros, se nos queda en ECX la dirección para corregir a "FF15" ;)

Más de uno seguro que esto ya está llegando a su fin, pero estáis equivocados, VOB no tiene reservada alguna sorpresita. Si trazamos unas cuantas llamadas más a las API's vemos que el RET nos lleva a:

0177:007F945D  EB01                JMP 007F9460 
0177:007F9460   55                     PUSH EBP
<--------
0177:007F9461   EB01                JMP 007F9464     
·   Ojito a esto 
0177:007F9464   8BEC               MOV EBP,ESP
<---
0177:007F9466   E908AA8077   JMP 78003E73  ; 

MSVCRT!__getmainargs 
0177:78003E70   55            PUSH EBP 
0177:78003E71   8BEC       MOV EBP,ESP 
0177:78003E73   8B4518    MOV EAX,[EBP+18] 
<--- Salta directamente aquí

Ostras.......ejecuta parte del código de la API en su propio código. De nuevo nuestro ingenio puede más que el de los programadores de VOB ;)

    xor edx,edx
    cmp byte ptr [ebx],e9 
    jz salta_directamente
; si es salto directo a la API (E9) nos vamos

--- CALCULARIAMOS A QUE API NOS LLEVA EL SALTO EN EBX ---

    mov al,byte ptr[ebx] ;  pillamos del código de VOB el primer byte de la API 
incrementa:                  
; (que en este caso sería el "55" de arriba)
    inc ebx
    cmp byte [ebx],al     
; comprobamos que no se repita ese valor
    jnz es_salto_a_API?
    inc edx                     
; si encontramos otra coincidencia aumentamos nuestro contador
es_salto_a_API?: 
    cmp byte ptr [ebx],e9 
; hemos encontrado ya el salto a la API???
    jnz incrementa


--- AQUI CALCULARIAMOS A QUE API NOS LLEVA EL SALTO Y... ---

reduce:
   dec ebx                      
; buscamos 
   cmp byte ptr[ebx],al   
; en la API el primer byte
   jnz reduce
   cmp edx,0                
   ; se repetía ese valor???
   jz salta_directamente 
   dec edx                      
; reduce el contador y busca de nuevo ese valor
   jmp reduce
salta_directamente:

Casi, casi pero no......aún nos queda una cosilla.....VOB también redirige los SALTOS A LAS APIS. Aquí tenéis un ejemplillo:

0177:004761FE   FC                    CLD 
0177:004761FF   E9D9E33A00   JMP 008245DD
 

De nuevo el "CLD" ese por el byte de menos, en este caso por un "JMP DWORD PTR" cuyos opcodes son FF25. La rutina para corregir los saltos a las APIS será igual que la las CALL, salvo que:

- Logicamente en vez de corregir el NOP/CLD-E8 por FF15 lo sustituiremos por FF25
- En vez de buscar los E8 para buscar las llamadas(CALLS) a VOB, buscaremos E9 que es el OPCODE de salto lejano 

Bueno pues una vez hallamos corregido todas las llamadas y saltos a las APIS, volcamos la sección code y la pegamos en el fichero en el que habíamos reconstruido la IAT. Nos aseguramos de haber corregido el OEP, el RVA de la IAT y su tamaño, ejecutamos el fichero y........EYYY  FUNCIONA! :)

Pero nuestro fichero reconstruido todavía tiene "un pero". El fichero ocupa casi 4 megas, mucho si tenemos en cuenta que el fichero original ocupaba 533 Kb. A qué es debido? Pues a que hemos volcado con el tamaño del fichero en memoria(SizeOfImage), que nada tiene que ver con el tamaño del fichero en disco. Abrimos nuestro fichero con HexWorkShop. Ponemos un poco de vista y vemos que desde el offset 8B452h hasta el 3D8000h(que es el principio de la sección .rsrc) son todo 0's. Ahí están esos 3,460,014 bytes "de más" ;) Seleccionamos los primeros 8C000h bytes de nuestro fichero recontruido y los insertamos en un fichero nuevo. Después nos vamos al offset 3D8000h, seleccionamos hasta el final y lo pegamos a continuación en el fichero nuevo que habiamos creado. Salvamos el fichero y salimos. Umm ahora no se nos ve el iconito del fichero...esto es debido a que el Raw Offset de la sección .rsrc es incorrecto y debemos corregirlo con PEditor. También actualizamos el Raw Size de la sección .CODE que será "Raw Offset de .rsrc - Raw Offset de .CODE" (8C000h-1000h=8B000h). Nos debe quedar así:

 SECCIÓN Virtual Size Virtual Offset Raw Size Raw Offset
.code 3D7000 1000 8B000 1000
.rsrc AFC0 3D8000 AFC0 8C000

Ahora sí........VOB is DEAD! :)

Gádix-MrOcean[WKT!2001]

 

 

Karpoff Spanish Tutor: Página dedicada a la divulgación de información en Castellano, sobre Ingeniería Inversa y Programación. Email "Colabora con tus Proyectos"
www.000webhost.com