Home > Crypto, Uncategorized > Finding the M.I.R.A.C.L.

Finding the M.I.R.A.C.L.

Hola a todos, puede parecer que voy a encarar un tema religioso en las proximas lineas pero a no equivocarse. Lo que sigue va a ser una serie de articulos dedicados a la criptografia en diferentes crackmes.

Lo primero que note es que cuando me meti con RSA todos me decian: “usa la funcion tatata de euler que sale” y me tiraban una funcion copy/pasteada del tipo

d=e^{-1}\mod{n}

esto me dio la pauta de que muy bien nadie sabe como funciona esto del crypto, y cada vez que preguntas algo te contestan con mas preguntas….. calculo que esa fue la primera piedra en el camino que me encontre, en unos dias le voy a dedicar un post enterito a Euler, pero por ahora vamos con otra cosa.

Si han leido algun tute por ahi sobre crackmes con criptografia RSA habran aunque sea oido de la libreria M.I.R.A.C.L. esta libreria nos ayuda a trabajar con numeros muy grandotes conocidos como BIGNUM, permitiendonos usar los numeros gigantes que manejan en este tipo de retos.

Si nos encontramos con algun programa que use esta libreria, veremos que tiene un monton de funciones inlineadas bastante complicadas de tracear, dichas funciones son de la libreria estatica miracl

basta con ir un rato a http://crackmes.de/ bajarse un reto con crypto echo en MASM y me juego la cabeza a que usaron miracl para algo

estas librerias que se añaden al ejecutable no tienen ningun nombre cuando las vemos con nuestros debuggers favoritos

miracl-2

un ejemplo seria ese CALL que tenemos ahi, si nosotros estariamos reverseando este programa para crackearlo, al ver esa funcion le mandamos F7 y estariamos tratando de entender una funcion de una libreria que ya conocemos. Yo me pregunto:

¿No seria mucho mas bonito que esas funciones tengan nombres y nosotros desligarnos de tener que reconocer esas funciones a mano?

Me parece que ganariamos mucho tiempo al no tener que reversear (y ni siquiera mirar) las 10 o 15 funciones que usa el programa

Nose ustedes pero yo prefiero verlo asi

miracl-3

ya con eso y la documentacion de MIRACL en la mano me parece que es un poquito mas facil, o por lo menos cambia un poco el panorama

Una primera opcion seria abrir el programa con IDA y usar FLIRT para obtener los nombres de las funciones, exportar el map y cargarlo en olly con algun plugin. Pero este acercamiento no me convencia por las siguientes causas:

  • si el programa esta packeado, lo tenemos que desempacar 😦
  • abrirlo con IDA y dejar que lo analize ya son 2 minutos de vida que se te van 😛
  • exportar el map y abrir el olly. ¡Tomatelas! yo estoy haciendo esto para ganar tiempo, no para perderlo

si esto fuera un reto de alguna empresa por algun tipo de premio no darian muchas ganas de perder tiempo. Es por eso que busque otras alternativas y me encontre con un texto olvidado que escribio black-eye en el 2004 sobre como reconocer las funciones de miracl

para ser sinceros el texto no dice nada que el manual no diga, de echo la introduccion es un vivo copy/paste de una parte de la documentacion. Pero me sirvio en su momento para ver que usos podria tener

por ultimo termina el texto diciendo que si alguien escribe un plugin para IDA que use la tecnica esa para reconocer los nombres que lo pase, pero mirando aca y alla se ve que nadie se puso a laburar en 7 años o nadie lo hizo publico

la tecnica se basa en lo siguiente, cuando nosotros llamamos a mirsys, uno de los trabajitos de esta funcion es levantar una especie de tracer que tiene el paquete, el cual en caso de algun error en alguna funcion te avisa en cual fue. Mucho no mire esta parte, pero segun dicen los entendidos en la materia hasta se puede activar una ventana en caso de error, si alguno sabe como que se ponga en contacto asi lo añadimos 😉

ahora llego el momento de la verdad, si no les gusta lo que ven, los invito a cerrar el navegador o irse para otro lado, pero por favor ¡no a los comentarios mala leche!

mirsys retorna un puntero a una estructura de datos que muestra diferentes valores que nos pueden llegar a interesar

miracl-1

a no asustarse, como no tengo muchas ganas de colgar 10 screenshots se los puse todo en uno, la historia es la siguiente:

  • puse un BP luego de la llamada a mirsys para mirar el puntero que retorna (valor de EAX), el cual dijimos que tiene la info que nos nutrira en nuestro arte
  • puse un “BP on Write Memory” en el 1D, este valor siempre va a estar, creanme…
  • corro y veo como para en el “MOV DWORD PTR[EDX+EAX*4+20], 17” para el despabilado se esta moviendo el 17 suplantando el 1D que habia

¿Pero que es este valor? eso valores son unos numeritos que miracl le asigna a cada funcion y lo usa para el tracer de errores en caso de que casquee. Miremos alguna parte del codigo fuente para entenderlo

void fpack(_MIPD_ big n,big d,flash x)
{ /* create floating-slash number x=n/d from *</em>* big integer numerator and denominator   */
mr_lentype s;
int i,ld,ln;
#ifdef MR_OS_THREADS
 miracl *mr_mip=get_mip();
#endif</em>
if (mr_mip->ERNUM) return;

MR_IN(31)

la linea marcada es la que nos interesa, cada funcion exportada por la libreria tiene un numero asociado, en este caso seria 31 para fpack. Gracias a andrew, Black-Eye and Bart XT tenemos la lista entera.

<pre>MIRACL MAGIC NUMBERS TABLE:</pre>
by bLaCk-eye from an original ideea by bF!^k23

innum				equ 01h
otnum				equ 02h
jack				equ 03h
normalise			equ 04h
multiply			equ 05h
divide				equ 06h
incr				equ 07h
decr				equ 08h
premult				equ 09h
subdiv				equ 0Ah
fdsize				equ 0Bh
egcd				equ 0Ch
cbase				equ 0Dh
cinnum				equ 0Eh
cotnum				equ 0Fh
nroot				equ 10h
power				equ 11h
powmod				equ 12h
bigdig				equ 13h
bigrand				equ 14h
nxprime				equ 15h
isprime				equ 16h
mirvar				equ 17h
mad				equ 18h
multi_inverse			equ 19h
putdig				equ 1Ah
add				equ 1Bh
subtract			equ 1Ch
mirsys				equ 1Dh
xgcd				equ 1Eh
fpack				equ 1Fh
dconv				equ 20h
mr_shift			equ 21h
mround				equ 22h
fmul				equ 23h
fdiv				equ 24h
fadd				equ 25h
fsub				equ 26h
fcomp				equ 27h
fconv				equ 28h
frecip				equ 29h
fpmul				equ 2Ah
fincr				equ 2Bh
ftrunc				equ 2Dh
frand				equ 2Eh
sftbit				equ 2Fh
build				equ 30h
logb2				equ 31h
expint				equ 32h
fpower				equ 33h
froot				equ 34h
fpi				equ 35h
fexp				equ 36h
flog				equ 37h
fpowf				equ 38h
ftan				equ 39h
fatan				equ 3Ah
fsin				equ 3Bh
fasin				equ 3Ch
fcos				equ 3Dh
facos				equ 3Eh
ftanh				equ 3Fh
fatanh				equ 40h
fsinh				equ 41h
fasinh				equ 42h
fcosh				equ 43h
facosh				equ 44h
flop				equ 45h
gprime				equ 46h
powltr				equ 47h
fft_mult			equ 48h
crt_init			equ 49h
crt				equ 4Ah
otstr				equ 4Bh
instr				equ 4Ch
cotstr				equ 4Dh
cinstr				equ 4Eh
powmod2				equ 4Fh
prepare_monty			equ 50h
nres				equ 51h
redc				equ 52h
nres_modmult			equ 53h
nres_powmod			equ 54h
nres_moddiv			equ 55h
nres_powltr			equ 56h
divisible			equ 57h
remain				equ 58h
fmodulo				equ 59h
nres_modadd			equ 5Ah
nres_modsub			equ 5Bh
nres_negate			equ 5Ch
ecurve_init			equ 5Dh
ecurve_add			equ 5Eh
ecurve_mult			equ 5Fh
epoint_init			equ 60h
epoint_set			equ 61h
epoint_get			equ 62h
nres_powmod2			equ 63h
nres_sqroot			equ 64h
sqroot				equ 65h
nres_premult			equ 66h
ecurve_mult2			equ 67h
ecurve_sub			equ 68h
trial_division			equ 69h
nxsafeprime			equ 6Ah
nres_lucas			equ 6Bh
lucas				equ 6Ch
brick_init			equ 6Dh
pow_brick			equ 6Eh
set_user_function		equ 6Fh
nres_powmodn			equ 70h
powmodn				equ 71h
ecurve_multn			equ 72h
ebrick_init			equ 73h
mul_brick			equ 74h
epoint_norm			equ 75h
nres_multi_inverse		equ 76h
nres_dotprod			equ 78h
epoint_negate			equ 79h
ecurve_multi_add		equ 7Ah
ecurve2_init			equ 7Bh
epoint2_init			equ 7Ch
epoint2_set			equ 7Dh
epoint2_norm			equ 7Eh
epoint2_get			equ 7Fh
epoint2_comp			equ 80h
ecurve2_add			equ 81h
epoint2_negate			equ 82h
ecurve2_sub			equ 83h
ecurve2_multi_add		equ 84h
ecurve2_mult			equ 85h
ecurve2_multn			equ 86h
ecurve2_mult2			equ 87h
ebrick2_init			equ 88h
mul2_brick			equ 89h
prepare_basis			equ 8Ah
strong_bigrand			equ 8Bh
bytes_to_big			equ 8Ch
big_to_bytes			equ 8Dh
set_io_buffer_size		equ 8Eh

Lo que yo hice es cumplir la peticion de black-eye, me hice un script que reconoce las funciones de miracl y le pone el nombre, esta echo en python para Immunity debugger (es un pyCommand), a lo primero trate de hacerlo con OllyScript pero tenia que hacer 1 compare y un salto por cada funcion de miracl, imaginense que son como 200 funciones asi que quedaba un poquito grande.

Solo comento lo que hace y lo cuelgo por si alguno lo necesita:

  • Busca todas las funciones.
  • Mira si tiene los opcodes “C7 44 ?? 86 ??” ese es el MOV que vimos
  • en caso afirmativo agarra el numerito magico y lo usa para indexar un array con los nombres, nos podemos dar ese lujo porque los numeros son consecutivos 😀
  • cambia el nombre de la funcion y te avisa a vos

tengan en cuenta que para buscar las funciones use imm.getAllFunctions, esta funcion solo encontrara algo si el codigo esta analizado “Click derecho–>Analisys–>Analize code”

veamos al script haciendo su trabajo

miracl-4

este es un crackme que me encontre por ahi, llegar a la zona caliente es una pavada (en algun otro post lo veremos), ahora carguemos nuestro pyCommand

miracl-5

donde le damos click ahi, se nos abre esta ventana

miracl-6

veamos el codigo ahora

miracl-7

ya tenemos reconocidas las funciones 😀 me parece que ahora esta mucho mas facil la cosa

aca les dejo el pyCommand, no lo cuelgo porque WP no me deja, pero un copy/paste y lo tiran en el pyCommands como cualquiercosa.py


#!/usr/bin/env python

__VERSION__ = '0.1'

import immlib
import re

DESC= "locate all function of miracl package"

def main(args):
  imm = immlib.Debugger()
  miracl = []

  miracl.extend(["innum","otnum","jack","normalise","multiply","divide","incr"])
  miracl.extend(["decr","premult","subdiv","fdsize","egcd","cbase","cinnum","cotnum"])
  miracl.extend(["nroot","power","powmod","bigdig","bigrand","nxprime","isprime"])
  miracl.extend(["mirvar","mad","multi_inverse","putdig","add","subtract","mirsys"])
  miracl.extend(["xgcd","fpack","dconv","mr_shift","mround","fmul","fdiv","fadd"])
  miracl.extend(["fsub","fcomp","fconv","frecip","fpmul","fincr",0,"ftrunc","frand"])
  miracl.extend(["sftbit","build","logb2","expint","fpower","froot","fpi","fexp","flog"])
  miracl.extend(["fpowf","ftan","fatan","fsin","fasin","fcos","facos","ftanh","fatanh"])
  miracl.extend(["fsinh","fasinh","fcosh","facosh","flop","gprime","powltr","fft_mult"])
  miracl.extend(["crt_init","crt","otstr","instr","cotstr","cinstr","powmod2"])
  miracl.extend(["prepare_monty","nres","redc","nres_modmult","nres_powmod","nres_moddiv"])
  miracl.extend(["nres_powltr","divisible","remain","fmodulo","nres_modadd","nres_modsub"])
  miracl.extend(["nres_negate","ecurve_init","ecurve_add","ecurve_mult","epoint_init"])
  miracl.extend(["epoint_set","epoint_get","nres_powmod2","nres_sqroot","sqroot"])
  miracl.extend(["nres_premult","ecurve_mult2","ecurve_sub","trial_division","nxsafeprime"])
  miracl.extend(["nres_lucas","lucas","brick_init","pow_brick","set_user_function"])
  miracl.extend(["nres_powmodn","powmodn","ecurve_multn","ebrick_init","mul_brick"])
  miracl.extend(["epoint_norm","nres_multi_inverse",";null entry","nres_dotprod"])
  miracl.extend(["epoint_negate","ecurve_multi_add","ecurve2_init","epoint2_init"])
  miracl.extend(["epoint2_set","epoint2_norm","epoint2_get","epoint2_comp","ecurve2_add"])
  miracl.extend(["epoint2_negate","ecurve2_sub","ecurve2_multi_add","ecurve2_mult"])
  miracl.extend(["ecurve2_multn","ecurve2_mult2","ebrick2_init","mul2_brick"])
  miracl.extend(["prepare_basis","strong_bigrand","bytes_to_big","big_to_bytes"])
  miracl.extend(["set_io_buffer_size"])

  target = imm.getDebuggedName()
  target_base = imm.getModule(target).getBase()

  lista = imm.getAllFunctions(target_base)

  imm.log("PASTAFR0LA FINDING THE MIRACL",highlight=1)
  imm.log("usage: only run and see the 'M.I.R.A.C.L.E. Functions' windows for information")

  table = imm.createTable('M.I.R.A.C.L. Functions', ['Address', 'Function'])
  n = 0

  for i in lista:
    funcion = imm.getFunction(i)
    ret = max(funcion.getFunctionEnd())

    # k = "U8\x90\xCC\x0B\xADf0o\x0D" <-- raw_data
    # l = ['79','90','CC','0B','AD','70','30','76','0D']
    # s = '79-90-CC-0B-AD-70-30-76-0D' <-- opcodes

    k = imm.readMemory(i, (ret - funcion.getStart()))
    l = map(lambda x: hex(ord(x)).replace('0x', ''), k)
    s = " ".join(l)
    s = s.upper()

    m = re.search('C7 44 [A-F\d]+ 2[04] ([A-F\d]+)', s)
    if m is None: continue

    # -1 porque el arreglo comienza en zero
    magicnumber = m.group(1)
    table.add(0,["%X"%(funcion.getStart()),"%s"%(miracl[ int(magicnumber, 16)-1 ])])

    imm.setLabel(funcion.getStart(), miracl[ int(magicnumber, 16)-1 ])

    n += 1

  imm.log("located %d functions of the miracl package"%(n))
  return "Done.."

esto es todo amigos, espero que les haya gustado y cualquier sugerencia o critica aqui estoy

  1. NCR
    March 16, 2011 at 9:48 pm

    Groso! no habia visto este post! buenisimo!.

  2. October 21, 2014 at 3:18 pm

    Muy útil. Buen trabajo Joe!.

  1. No trackbacks yet.

Leave a reply to deurus Cancel reply