| ESTUDIO COLECTIVO DE DESPROTECCIONES Última Actualizacion: 25/10/2001 |
Programa | Messiah | W95 / W98 / NT
Descripción | Juego en 3D
Tipo | Protección anticopia de CD's
Tipo de Tutorial | [X]Original, []Adaptación, []Aplicación, []Traducción
Url | NO DISPONIBLE
Protección | LaserLok 5
Dificultad | 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas | SoftIce 4.01, IceDump 6.020, Hiew 6.55, ADump, Hex WorkShop
Objetivo | Eliminar LaserLok
Cracker | Mr.Ocean & Mr.Snow [WkT!]
Grupo | http://www.whiskeykontekila.org
Fecha | 24 de Mayo de 2001
INTRODUCCION
Hola amigos! Tras unos meses de farras, estudios y demás proyectos, vamos a eliminar una vieja cuenta pendiente: el LaserLock Primero de todo vamos a hacer un breve resumen de las características de esta protección: - No encripta ninguna sección del ejecutable protegido - No lleva antidebug - No aumenta el tamaño del ejecutable, la protección se basa en una DLL la cual no tiene nombre fijo (en este juego se llama 'Mes3ddll.dll'). El nombre de ésta lo podéis averiguar con un editor PE y es el primer IMAGE_IMPORT_DESCRIPTOR. Esta librería siempre tiene una función que es 'CallDLL'. - REDIRECCIONA las llamadas a las API'S Así pues si corregimos las llamadas a las API'S tendremos nuestro juego libre de LaserLock ;) Vamos a ver 2 métodos de hacerlo. La elección final, queda en vuestras manos Pero primero vamos a estudiar un poco por encima la protección. Si cargamos MESSIAHD3D.EXE con el Loader del Sice vemos lo siguiente: :004DD7C0 PUSH EBP <--- Entry Point :004DD7C1 MOV EBP,ESP :004DD7C3 PUSH FF :004DD7C5 PUSH 004E7430 :004DD7CA PUSH 004E12E8 :004DD7CF MOV EAX,FS:[00000000] :004DD7D5 PUSH EAX :004DD7D6 MOV FS:[00000000],ESP :004DD7DD SUB ESP,58 :004DD7E0 PUSH EBX :004DD7E1 PUSH ESI :004DD7E2 PUSH EDI :004DD7E3 MOV [EBP-18],ESP :004DD7E6 CALL [004E8868] <--- Llamada a LaserLock :004DD7EC XOR EDX,EDX <--- Dirección de retorno Si trazamos un poco en la llamada a LaserLock encontramos esto: :10001D4B PUSH EAX :10001D4C PUSH EBP :10001D4D MOV EBP,ESP :10001D4F PUSH EAX :10001D50 PUSH EBX :10001D51 PUSH ECX :10001D52 PUSH EDX :10001D53 PUSH ESI :10001D54 PUSH EDI :10001D55 MOV EAX,SS:[EBP+08] <------------- (0) :10001D59 PUSH EAX :10001D5A CALL 100018E1 <----------------------- (1) :10001D5F ADD SP,04 <------------------------------ (2) :10001D63 MOV EAX,DS:[EAX] <----------------- (3) :10001D66 CMP BYTE PTR [100168EC],01 <--- (4) :10001D6D JZ 10001D7F :10001D73 MOV SS:[EBP+04],EAX :10001D77 POP EDI :10001D78 POP ESI :10001D79 POP EDX :10001D7A POP ECX :10001D7B POP EBX :10001D7C POP EAX :10001D7D POP EBP :10001D7E RET :10001D7F MOV SS:[EBP+08],EAX :10001D83 POP EDI :10001D84 POP ESI :10001D85 POP EDX :10001D86 POP ECX :10001D87 POP EBX :10001D88 POP EAX :10001D89 POP EBP :10001D8A ADD ESP,04 :10001D8D RET (0) Aquí EAX=004DD7EC (dirección desde donde hemos sido llamados+6) (1) Esta es la gran llamada a LaserLock. Esta rutina se encarga de todo: de mostrar el LOGO, de pedir el CD, pero sobretodo de devolver la API correcta. La primera vez que se pasa por la rutina se muestra el LOGO del juego (no en todos los juegos) y llama a la primera API (normalmente GetVersion). La segunda vez que pasa comprueba que esté insertado el CD original (atentos a este dato que como veremos más adelante es importantísimo) y llama a la siguiente API. Las sucesivas veces solo devuelve la API correcta (2) EAX=004E7130 :what eax The value 4E7130 is (a) MESSIAH!.rdata+0130 :d eax 0177:004E7130 FD 33 F8 BF F6 E1 F6 BF-85 77 F6 BF 5E CA F7 BF .3.......w..^... :what *eax The value BFF833FD is (a) KERNEL32!GetVersion (3) EAX pasa a valer BFF833FD (GetVersion). Aquí o en el punto 2 PODRÍAMOS llamar a nuestra rutina correctora de API'S, ya que en EAX tenemos la dirección de .rdata que queremos y en [EBP+04] desde donde hemos sido llamados. Pero todo se nos jode por el punto (4) (4) si BYTE PTR [100168EC]=1 salta a 10001D7F. Esto es debido a LaserLock también redirige "los saltos" a las API'S. Os hago un ejemplo con GetVersion: Una llamada "normal" sería: 004DD7E6 FF1530714E00 call KERNEL32!GetVersion pero si BYTE PTR [100168EC]=1 sería: 004DD7E6 FF2530714E00 jmp KERNEL32!GetVersion Fíjaos bien en el FF25, porque nuestra rutina tendrá que cambiar FF15 por FF25 si se da este caso
AL ATAQUE
MÉTODO Mr.Ocean
Bien con toda esta información ya podemos escribir nuestra rutina correctora. Primero de todo hemos de parchear el código del LaserLock para que salte a nuestra rutina: :10001D59 PUSH EAX :10001D5A CALL 100018E1 :10001D5F ADD SP,04 :10001D63 MOV EAX,DS:[EAX] <--- Lo cambiamos a JMP ADump_Adress Y la rutina es la siguiente: ADUMP_ADRESS: mov ecx,00401000 ; Inicio de .text mov edx,004E7000 ; Fin de .text LOOP_BUSCA: cmp word ptr [ecx],15ffh ; FF15 son los opcodes de "call [xxxxxxxx]" jnz SIGUE_BUSCANDO push ecx ; Metemos en la pila la siguiente llamada a LaserLock jmp ecx ; Saltamos a la llamada SIGUE_BUSCANDO: inc ecx cmp ecx,edx ; Hemos llegado al final de .text ??? jnz LOOP_BUSCA ; Si no es así, sigue buscando int 3 ; Cuando rompamos aquí ---> todo OK CORRIGE: pop ecx mov ebx,[esp+8] ; EBX = dirección de "Call [LaserLock]" mov dword ptr[ebx+2],eax cmp byte ptr[100168ec],1 ; Miramos si era salto o call jnz ADUMP_ADRESS ; Si es call no hay que corregir nada mov dword ptr[ebx+1],25h ; Lo convertimos a JMP API jmp ADUMP_ADRESS ; Vamos a por la siguiente llamada Ahora: "r eip ADump_Adress" "BPINT 3" Le damos al F5 y en cuestión de segundos aparecemos en el INT 3. Volcamos la sección .text y la sobreescribimos en el fichero. Ejecutamos y ZASH! Obtenemos un bonito fallo de protección general. Si investigamos el tema, nos damos cuenta de que LaserLock comprueba el CRC de su código: BPM 10001D5F R Y aparecemos en la rutina de chequeo del CRC. Para los interesados, aquí podeis ver la rutina. Tras dos F12, vemos lo siguiente: :10008CC0 CALL 10008AB8 :10008CC5 ADD ESP,04 :10008CC8 MOVZX EAX,AX :10008CCB MOV ECX,[EBP-08] ; [EBP-08] contiene el valor del CRC :10008CCE AND ECX,0000FFFF :10008CD4 XOR EAX,ECX ; se XORea el valor de EAX con el valor del CRC (ECX) Como podéis comprobar el valor del CRC en ningún momento se compara, lo xorea directamente con el valor de EAX. Además el chequeo se va haciendo por partes, con lo cual las posibilidades de "trapichear" con el CRC son escasas. Pero entonces......cómo redirigimos el código a nuestra rutina? Fácil, con un BPM y una pequeña "macro": BPM 10001D63 X do "r eip CORRIGE;g;" "CORRIGE" es pues eso, la dirección de memoria de "CORRIGE:" de nuestra rutina correctora. Bueno, pues otra vez ponemos el EIP en ADump_Adress, BPINT 3 y F5. Tras unos 3-5 minutillos llegamos otra vez al INT 3. (NOTA: es normal que parezca que el Sice se vuelva loco). Volcamos, pegamos, ejecutamos y....ZASH! otra vez!!! Bueno......antes que meterle dos patadas a nuestro querido ordena, decidimos irnos a la cama, mañana será otro día... Sip. Con un poco más de calma, nos ponemos a investigar el tema.....a ver....problema de CRC no es porque no escribimos en su código (los BPM's al contrario que los BPX's no parchean en el código). Hacemos un rutina que deje la pila "intacta" y tampoco. Tras otro ratito más calentándonos la cabeza, damos con la solución: el muy cabrón comprueba si hemos corregido la anterior llamada!!! De nuevo para los interesados, aquí tenéis el código que se encarga de ello. Tenemos dos opciones. La primera: LaserLock sólo comprueba si hemos parcheado la anterior, por tanto si hacemos que nuestra rutinilla vaya parcheando posteriormente asunto solucionado. La segunda se aprovecha de una gran "puerta abierta". Quiero pensar que lo han hecho para ganar velocidad porque sino....Os explico. LaserLock usa un contador para saber el número de veces que ha sido :10001C8B CMP DWORD PTR [100168F8],32 ; ¿ Hemos pasado 32 veces ? :10001C92 JLE 10001CD0 ; Si hemos pasado 32 o menos se va sin corregir :10001C98 MOV EAX,[1001690C] :10001C9D ADD [EBP+08],EAX :10001CA0 MOV EAX,[EBP+08] :10001CA3 SUB EAX,05 :10001CA6 MOV [EBP-1C],EAX :10001CA9 XOR EAX,EAX :10001CAB MOV AL,[100168EC] :10001CB0 CMP EAX,01 <--- 0=CALL; 1=SALTO :10001CB3 JNZ 10001CBF :10001CB9 MOV EAX,[EBP-1C] :10001CBC MOV BYTE PTR [EAX],25 <--- Corrige el FF25 (si es salto) :10001CBF MOV EAX,[EBP+08] :10001CC2 SUB EAX,04 :10001CC5 MOV [EBP-18],EAX :10001CC8 MOV EAX,[EBP-04] :10001CCB MOV ECX,[EBP-18] :10001CCE MOV [ECX],EAX <--- Corrige la llamada a la API :10001CD0 MOV EAX,[EBP-04] :10001CD3 JMP 10001CD8 :10001CD8 POP EDI :10001CD9 POP ESI :10001CDA POP EBX :10001CDB LEAVE :10001CDC RET Alguien puede pensar que si NOPeamos el salto en 10001C92 asunto resuelto, pero NO. Recordad que no podemos parchear...pero tranquilos que esta vez la solución es fácil. Haremos que nuestra rutina ponga a 33 el contador del LaserLock (mov dword ptr[100168F8], 33h) o con el propio Sice ("e 100168F8 33"). Y aquí viene el gran "BackDoor" del que os hablaba: :100019ED CMP DWORD PTR [100168F8],00 :100019F4 JNZ 10001A13 :100019FA CALL 100013AA :100019FF MOV EAX,[10014594] :10001A04 PUSH EAX :10001A05 CALL 1000337A <--- Muestra el LOGO :10001A0A ADD ESP,04 :10001A0D ADD [10016914],EAX :10001A13 CMP DWORD PTR [100168F8],01 :10001A1A JNZ 10001A3D :10001A20 CALL 100057E2 <--- Comprueba el CD :10001A25 ADD [10016914],EAX :10001A2B CMP DWORD PTR [100145AC],01 :10001A32 JNZ 10001A3D :10001A38 CALL 10001340 :10001A3D CMP DWORD PTR [100168F8],02 :10001A44 JNZ 10001A55 :10001A4A CALL 10008393 :10001A4F ADD [10016914],EAX :10001A55 CMP DWORD PTR [100168F8],02 :10001A5C JLE 10001A8E :10001A62 CMP DWORD PTR [100168F8],32 :10001A69 JGE 10001A8E :10001A6F CALL 100086A2 <--- Se asegura de que ha comprobado el CD :10001A74 ADD [10016914],EAX :10001A7A XOR EBX,EBX :10001A7C CALL 10008590 :10001A81 LEA EAX,[EAX*2+EAX] :10001A84 SUB EBX,EAX :10001A86 NEG EBX :10001A88 SUB [10016914],EBX :10001A8E CMP DWORD PTR [100168F8],32 :10001A95 JNZ 10001AD3 :10001A9B XOR EAX,EAX Comprobadlo vosotros mismos....pone el dword 100168F8 a 33h....pasáis en algún momento por las comprobaciones del CD??? :d Bien, por mi parte esto es todo.
MÉTODO Mr.Snow
Buenas a todos .... Como veis Mr.Ocean a explicado detalladamente esta protección, con lo que pensando un poco y recopilando datos podemos usar este método... Primero recopilemos datos: Como dice Mr.Ocean esta protección tiene crc´s y checkeos para saber si el código del programa ha sido alterado (por lo menos mientras dependa de la dll), aunque como veremos estos checkeos se puede decir que son INÚTILES .... Ya que todo depende de la forma en la que planteemos el problema. Así pues mi planteamiento es el siguiente: - Desviar el EP del programa a una rutina en memoria la cual se encargue de reconstruir y almacenar las apis. Pues entonces echemos un vistazo dentro de la rutina de laserlock: :10001D4B PUSH EAX :10001D4C PUSH EBP :10001D4D MOV EBP,ESP :10001D4F PUSH EAX :10001D50 PUSH EBX :10001D51 PUSH ECX :10001D52 PUSH EDX :10001D53 PUSH ESI :10001D54 PUSH EDI :10001D55 MOV EAX,SS:[EBP+08] :10001D59 PUSH EAX :10001D5A CALL 100018E1 <----------------------- (1) :10001D5F ADD SP,04 :10001D63 MOV EAX,DS:[EAX] :10001D66 CMP BYTE PTR [100168EC],01 <--- (2) (1) Es la rutina encargada de devolvernos la api correcta (mas bien el puntero a la api correcta) (2) Es la comprobación que indica si es JMP o CALL Bien pues con estos 2 datos, el inicio y tamaño de text tenemos mas que suficiente para reconstruir todas las llamadas a las apis. Este seria el código de reconstrucción: mov ecx,InicioDeTExt mov eax,TextFin xor edi,edi repite: cmp word ptr[ecx],15ffh jnz incre pushad add ecx,6 call Corrige popad add edi,5 inc ebx ; ebx se incrementa cada vez que corregimos incre: inc ecx cmp ecx,eax ; se comprueba si hemos llegado al final jnz repite Int 3 ; pararemos aquí si todo va bien. ;---------------------------------------------------------------------------- ; Procedimiento: Corrige ; Descripción: Obtiene la llamada correcta y la deposita en la zona de ; almacenamiento. jeje estamos de suerte y esta rutina nos ; devuelve la dirección de la api en .rdata ; Entrada: ECX = Offset a pushear para que devuelva el valor correcto ; EDI = Puntero A la zona donde guardamos la api corregida; ; Salida: ;---------------------------------------------------------------------------- Corrige: push edi ; guardamos edi y ebp ya ke la dll se push ebp ; lo ventila push ecx ; pusheamos el offset que hemos obtenido call dll_corrige(1) ; se llama a la rutina que corrige las apis pop ecx ; corregimos la pila porque se queda ; insertado el Push ECX pop ebp ; restauramos ebp pop edi ; restauramos edi mov ecx,Codigo_Mem_fin ; cargamos en ECX el puntero al final del ; código de memoria que será a partir de ; donde depositaremos las apis add ecx,edi ; lo sumamos en ecx y ecx será nuestro ; puntero a la zona de almacenamiento mov [ecx],eax ; guardamos en [ecx] el puntero a ; rdata hacia la api correcta. mov eax,SaltoCall ; cargamos en eax la variable que indica si ; es salto o call cmp byte ptr [eax],1 jz zero mov byte ptr[ecx+4],0 ; colocamos 0 si es call jmp fuera zero: mov byte ptr[ecx+4],1 ; o 1 si es JMP fuera: ret db "[APIS BOLCADAS]"Codigo_Mem_fin: (este código ha sido sacado de Delaserlock v1.0) Con este código ensamblado en la memoria que reserva Adump ,y simplemente desviando el EP del ejecutable a esta zona de memoria , tendremos una lista de las apis correctas. Con lo que solo nos queda volcar las apis reconstruidas a un fichero e inyectarlas en frío en el ejecutable protegido, para ello cuando paremos en la INT 3 solo tendremos que ver el numero de apis corregidas multiplicarlo por 5 (que es 1 dword para la api correcta + 1 byte para determinar si es salto o call) y volcar a partir de Codigo_Mem_fin con el resultado obtenido (/dump codigo_mem_fin ebx*5 c:\listaapis.bin) Ahora simplemente tenemos que inyectar las apis obtenidas, en el fichero original teniendo en cuenta que si el 4 byte de la apis recontruidas es 1 significa que hemos de cambiar el call por un salto el código sería algo así: Ecx= Inicio de text Ebx= rutina de laserlock xor edx,edx bucle: cmp word ptr[ecx],15ffh jnz inc_b cmp dword ptr[ecx+2],ebx jnz inc_b mov edi,Inicio_Apis_Recontruidas add edi,edx cmp byte ptr[edi+4],0 jz es_call mov byte ptr[ecx+1],25h es_call: mov edi,dword ptr[edi] mov dword ptr[ecx+2],edi add edx,5 inc conta inc_b: inc ecx cmp eax,ecx jnz bucle Pues como veis haciendo estas cositas también obtenemos un archivo reconstruido que no necesita del CD original para funcionar. Y ajustando un poco mas la rutina de reconstrucción teniendo en cuenta lo que dice Mr.Ocean, no habría problema para prescindir del cd original o incluso de este ultimo código encargado de inyectar las apis correctas en su sitio. Os preguntareis: ¿y porqué no lo has hecho así ....? pues mu sencillo , como he dicho el código de reconstrucción lo he sacado de Delaserlock v 1.0, que es una utilidad de cosecha propia J y ya que tengo el código a mano, ¿porque no usarlo? Jeje, además si os lo damos todo hecho no tendríais el placer de experimentar y disfrutar. ¿se anima alguien ha hacer un Delaserlock? Es muy sencillito y no necesitareis el Cd original .... Enga Salu2 y Punk Not Dead ....
REMATANDO LA FAENA
Nuestro crack funciona perfectamente, pero nosotros, que somos unos tíos elegantes, no vemos necesidad que nuestro crack dependa de la DLL de LaserLock. Así que vamos a eliminar esta dependencia de la IAT. Con PEditor 1.7 vemos lo siguiente: Vemos que la siguiente librería a la del Laser es 'DINPUT.dll'. Fácil. Abrimos el fichero con el HexWorkShop y buscamos la cadena 'DINPUT.dll'. La encontramos en dos offsets....aplicando un poco de ZEN deducimos que es la segunda (alrededor de la primera hay strings ;) que está en el offset 000E7CF6h. Invertimos los bytes de ese offset ( F67C0E00) y buscamos esos valores. De nuevo nos salen dos offsets, cualquiera de los dos nos vale. Yo para explicároslo cogeré el del offset 000E799Ch Conociendo la estructura IMAGE IMPORT DESCRIPTOR sabemos que:
-1- | -2- | -3- | -4- | -5-
Original First Thunk | TimeDateStamp | ForwardChain | Name | First Thunk
607A 0E00 | 0000 0000 | 0000 0000 | F67C 0E00 | 1C70 0E00
Pues ya lo tenemos: el 000E7990h es el RVA de nuestra tabla de importación. Lo corregimos con PEditor, ejecutamos y.......FUNCIONA!!! ;)
PALABRAS FINALES
El continuo retraso de la salida de este tutorial ha propiciado que saliese una nueva versión de LaserLock. Sabiendo crackear ésta no deberíais tener excesivos problemas con la nueva. De todas formas ya sabéis dónde encontrarnos: en el canal #cdcracking del IRC Hispano ;) Esto es todo, un saludito para los amigos de K-FOR, [WkT!], EiThel y MYTH Mr.Ocean & Mr.Snow [WkT!]
[ Entrada | Documentos Genéricos | WkT! Web Site ]
[ Todo el ECD | x Tipo de Protección | x Fecha de Publicación | x Orden Alfabético ]
(c) Whiskey Kon Tekila [WkT!] - The Original Spanish Reversers. Si necesitas contactar con nosotros , lee esto antes e infórmate de cómo puedes ayudarnos