Introducción al Reversing – 0xC Instrucciones SIMD

Muy buenas a todos. En esta sección veremos 3 instrucciones SIMD e investigar a bajo nivel su funcionamiento. Como no puede ser de otra manera en algunas secciones usaremos retos de CTF’s para aprender, y en otras ocasiones usaremos software real (muestras de malware, o software no malicioso). De momento solo hemos usado retos de CTF’s, pero el curso lleva pocas secciones así que ya llegaremos a ello.

Introducción

El software que analizaremos es un crackme que a priori simplemente pide un input y nos da un output de si hemos acertado o hemos fallado. Con angr podríamos resolver este reto, pero vamos a resolver el problema para así entender bien las instrucciones SIMD. Tenemos también la string CTF{ por tanto tenemos 4 caracteres que ya sabemos, de manera deductiva tenemos en cuenta esos bytes, para así ir descubriendo los demás bytes que nos faltaría.

Las instrucciones que desconocemos son SHUFFLE, ADD32 y XOR. Nuestro input se mueve dentro del registro xmm0 (que es un registro de 128 bits o 16 bytes):

movdqa xmm0,XMMWORD PTR [rsp]

Después usará las 3 instrucciones:

pshufb xmm0, XMMWORD PTR [rip+0x2fa9]       # 0x555555558070 <SHUFFLE>
paddd  xmm0, XMMWORD PTR [rip+0x2f91]        # 0x555555558060 <ADD32>
pxor   xmm0, XMMWORD PTR [rip+0x2f79]        # 0x555555558050 <XOR>

Finalmente usará nuestro input y lo comparara con el nuevo generado con esas 3 instrucciones, usando la función strncmp con un size de 16 bytes.

movaps XMMWORD PTR [rsp+0x10], xmm0
call   0x555555555030 <strncmp@plt>
test   eax, eax

Si la comparación es correcta se asegura la comparación con la string CTF{.

mov    rsi, QWORD PTR [rip+0x2f94] # 0x555555558080 <EXPECTED_PREFIX>
mov    edx, 0x4
mov    rdi, rbp
call   0x555555555030 <strncmp@plt>

SIMD reversing

Según la documentación la instrucción pshufb:

PSHUFB performs in-place shuffles of bytes in the destination operand (the first operand) according to the shuffle control mask in the source operand (the second operand).

Básicamente, hace shuffle de cada byte de nuestra entrada de acuerdo con la máscara <SHUFFLE> :

gef➤  x/10xg 0x555555558070
0x555555558070 <SHUFFLE> 0x0e090b0501070602	0x000d0c0a08040f03

Usaremos gdb para realizar debugging de la aplicación. Si pueden observar en el registro de 128 bits tenemos el input en little-endian.

Convirtiendo nuestro input como un array, denominado a[0],a[1]..a[15] le podemos aplicar la mascara <SHUFFLE>:

02 06 07 01 05 0b 09 0e 03 0f 04 08 0a 0c 0d 00

Entonces tenemos:

Input: 0    1   2  3  4  5  6  7  8  9  10  11  12  13  14  15
-->  a[2] a[6] ...
Input: C   T   F   { ...  
-->    F   X   X   T ...  

Ahora sabemos que cuando se ejecute la instrucción, el primer byte será la «F». Lo comprobamos con el debugger:

Efectivamente tenemos 0x46 como primer byte que corresponde al carácter «F».

Ahora simplemente con esta instrucción se ha «colocado» o «barajado» (shuffle) los caracteres en función de la máscara dada. Continuamos con la siguiente instrucción correspondiente a ADD32 que será en función también en la máscara dada:

<ADD>: ef be ad de ad de e1 fe 37 13 37 13 66 74 63 67

Nos interesa solo los index del input correspondientes a todos excepto CTF{.

Si cogemos de base a[3], sabemos que corresponde al index 08 (Esta fuera de CTF{). Hemos colocado el carácter «{» en la posición 08.

Sabiendo que el index 08 esta en una posición que necesitamos conocer ahora realizará la operación add y después de xor para encontrar el carácter del index 08:

0x7b + add[8] = 0x7b + 0x37 = 0xb2
0xb2 ^ xor[8] = 0xb2 ^ 0xd4 = 0x66 => 'f'

Entonces en el index 08 esta el carácter «f». Si usamos ltrace podemos corroborar la información previa. Usaremos el binario con estos input:

CTF{AAAAAAAAAA} CTF{BBBBBBBBBB}

Podemos observar que tres caracteres se repiten «Df0». Con esto confirmamos el carácter «f», además de obtener el carácter del index 07 y 09. Esto implica que si conocemos el valor correcto de un byte del input, podríamos conocer el byte correcto de otro byte. Por lo tanto, la constante EXPECTED_PREFIX (CTF{) se puede usar para obtener la password del crackme.

CTF{AAADf0AAAA}  CTF{BBBDf0BBBB}

Obtenemos dos caracteres más.

Si usamos este procedimiento (mucho más rápida) obtenemos la password del crackme:

Si quieren el crackme o consultarme cualquier cuestión, pueden contactarme por telegram @naivenom. Un saludo y hasta la próxima sección del curso de ingeniería inversa (Follow The White Rabbit).