Banner 1

Decodificando cadenas ofuscadas del bytecode Dalvik de What’s App

Un análisis de David Kaplan (2of1) de los opcodes Dalvik de algunas funciones de What's App demuestra que utiliza una ofuscación muy sencilla.
Echemos un vistazo a una de esas funciones: 
0010EFB8   Method 6552 (0x1998):_
0010EFB8    static void
0010EFB8   com.whatsapp.n0.()
0010EFB8   const/16                        v2, 0x5B
0010EFBC   const/16                        v3, 0x39
0010EFC0   const/16                        v1, 0x19
0010EFC4   const/16                        v4, 9
0010EFC8   const/4                         v6, 0
0010EFCA   const/4                         v0, 2
0010EFCC   new-array                       v9, v0, 
0010EFD0   const-string                    v0, aK_20 # "k>^"
0010EFD4   invoke-virtual                  {v0}, 
0010EFDA   move-result-object              v0
0010EFDC   array-length                    v5, v0
0010EFDE   move                            v7, v5
0010EFE0   move                            v8, v6
0010EFE2   move-object                     v5, v0
0010EFE4
0010EFE4 loc_10EFE4:                             # CODE XREF: n0__clinit_@V+9A j
0010EFE4   if-gt                           v7, v8, loc_10F034
0010EFE8   new-instance                    v0, 
0010EFEC   invoke-direct                   {v0, v5}, (ref) imp. @ unk_2EB58>
0010EFF2   invoke-virtual                  {v0}, 
0010EFF8   move-result-object              v0
0010EFFA   aput-object                     v0, v9, v6
0010EFFE   const/4                         v8, 1
0010F000   const-string                    v0, aE_10 # "E"
0010F004   invoke-virtual                  {v0}, 
0010F00A   move-result-object              v0
0010F00C   array-length                    v5, v0
0010F00E   move                            v7, v6
0010F010   move                            v6, v5
0010F012   move-object                     v5, v0
0010F014
0010F014 loc_10F014:                             # CODE XREF: n0__clinit_@V+CC j
0010F014   if-gt                           v6, v7, loc_10F066
0010F018   new-instance                    v0, 
0010F01C   invoke-direct                   {v0, v5}, (ref) imp. @ unk_2EB58>
0010F022   invoke-virtual                  {v0}, 
0010F028   move-result-object              v0
0010F02A   aput-object                     v0, v9, v8
0010F02E   sput-object                     v9, n0_z
0010F032
0010F032 locret:
0010F032   return-void
0010F034 # ---------------------------------------------------------------------------
0010F034
0010F034 loc_10F034:                             # CODE XREF: n0__clinit_@V:loc_10EFE4 j
0010F034   aget-char                       v10, v5, v8
0010F038   rem-int/lit8                    v0, v8, 5
0010F03C   packed-switch                   v0, switchdata_10F098
0010F042 # ---------------------------------------------------------------------------
0010F042
0010F042 loc_10F042:                             # CODE XREF: n0__clinit_@V+84 j
0010F042   move                            v0, v4 # default:
0010F044
0010F044 loc_10F044:                             # CODE XREF: n0__clinit_@V+9E j
0010F044                                         # n0__clinit_@V+A2 j ...
0010F044   xor-int/2addr                   v0, v10
0010F046   int-to-char                     v0, v0
0010F048   aput-char                       v0, v5, v8
0010F04C   add-int/lit8                    v0, v8, 1
0010F050   move                            v8, v0
0010F052   goto                            loc_10EFE4
0010F054 # ---------------------------------------------------------------------------
0010F054
0010F054 loc_10F054:                             # CODE XREF: n0__clinit_@V+84 j
0010F054   move                            v0, v1 # case 0: // (0x0)
0010F056   goto                            loc_10F044
0010F058 # ---------------------------------------------------------------------------
0010F058
0010F058 loc_10F058:                             # CODE XREF: n0__clinit_@V+84 j
0010F058   move                            v0, v2 # case 1: // (0x1)
0010F05A   goto                            loc_10F044
0010F05C # ---------------------------------------------------------------------------
0010F05C
0010F05C loc_10F05C:                             # CODE XREF: n0__clinit_@V+84 j
0010F05C   move                            v0, v3 # case 2: // (0x2)
0010F05E   goto                            loc_10F044
0010F060 # ---------------------------------------------------------------------------
0010F060
0010F060 loc_10F060:                             # CODE XREF: n0__clinit_@V+84 j
0010F060   const/16                        v0, 0x75 # case 3: // (0x3)
0010F064   goto                            loc_10F044
0010F066 # ---------------------------------------------------------------------------
0010F066
0010F066 loc_10F066:                             # CODE XREF: n0__clinit_@V:loc_10F014 j
0010F066   aget-char                       v10, v5, v7
0010F06A   rem-int/lit8                    v0, v7, 5
0010F06E   packed-switch                   v0, switchdata_10F0B0
0010F074 # ---------------------------------------------------------------------------
0010F074
0010F074 loc_10F074:                             # CODE XREF: n0__clinit_@V+B6 j
0010F074   move                            v0, v4 # default:
0010F076
0010F076 loc_10F076:                             # CODE XREF: n0__clinit_@V+D0 j
0010F076                                         # n0__clinit_@V+D4 j ...
0010F076   xor-int/2addr                   v0, v10
0010F078   int-to-char                     v0, v0
0010F07A   aput-char                       v0, v5, v7
0010F07E   add-int/lit8                    v0, v7, 1
0010F082   move                            v7, v0
0010F084   goto                            loc_10F014
0010F086 # ---------------------------------------------------------------------------
0010F086
0010F086 loc_10F086:                             # CODE XREF: n0__clinit_@V+B6 j
0010F086   move                            v0, v1 # case 0: // (0x0)
0010F088   goto                            loc_10F076
0010F08A # ---------------------------------------------------------------------------
0010F08A
0010F08A loc_10F08A:                             # CODE XREF: n0__clinit_@V+B6 j
0010F08A   move                            v0, v2 # case 1: // (0x1)
0010F08C   goto                            loc_10F076
0010F08E # ---------------------------------------------------------------------------
0010F08E
0010F08E loc_10F08E:                             # CODE XREF: n0__clinit_@V+B6 j
0010F08E   move                            v0, v3 # case 2: // (0x2)
0010F090   goto                            loc_10F076
0010F092 # ---------------------------------------------------------------------------
0010F092
0010F092 loc_10F092:                             # CODE XREF: n0__clinit_@V+B6 j
0010F092   const/16                        v0, 0x75 # case 3: // (0x3)
0010F096   goto                            loc_10F076
0010F096 # ---------------------------------------------------------------------------
0010F098 switchdata_10F098:                      # DATA XREF: n0__clinit_@V+84 r
0010F098   .short 0x100
0010F09A   .short 4
0010F09C   .int 0
0010F0A0   .int 0xC, 0xE, 0x10, 0x12
0010F0B0 switchdata_10F0B0:                      # DATA XREF: n0__clinit_@V+B6 r
0010F0B0   .short 0x100
0010F0B2   .short 4
0010F0B4   .int 0
0010F0B8   .int 0xC, 0xE, 0x10, 0x12
0010F0B8   Method End
Lo primero que notamos es que la función asigna algunos valores fijos a los registros v1-v4. Estos valores parecen mantenerse sin cambios a través del código de decodificación.


Moviéndonos a lo largo en el código, podemos ver también que se está asignando una especie de cadena ofuscada o mangled @ 0×0010EFD0.
Obteniendo los datos hasta su terminación NULL produce lo siguiente:
6b3e5e1c7a6d3e4b5a7971345710267a344c1b7d6b224e147d7a335c0726783d4d107b6d3e41016a713a57126c7d7b551a66722e4936666c354d07705a345d10297f295618295a344c1b7d6b22691d66773e701b6f767b5f1460753e5d
La función ahora realizará un bucle a través de cada carácter de la cadena para descifrarla. La transformación es un simple XOR.
¿Recuerdas los 4 registros fijos que comentábamos al principio? Son los valores utilizados para el XOR. De hecho, en realidad hay 5 valores en esa función, el quinto se utiliza simplemente como un valor literal directo en lugar de haber sido colocado en un registro.

La instrucción ‘packed-switch’ decide qué valor XOR se va a usar basándose en el índice del carácter actual dentro de la cadena MOD 5. Esto le permite recorrerla repetidamente a través de las 'claves' del XOR.

A continuación, una función Python que realiza el descifrado:
def decode_string(encoded_str, key):
    encoded_str = bytearray(encoded_str)
    decoded_str = ''
 
    for i in range(len(encoded_str)):
        decoded_str += chr(encoded_str[i] ^ key[i % 5])
 
    return decoded_str
Y el uso:
import binascii
 
key = [0x19, 0x5b, 0x39, 0x75, 0x09]
encoded_str = binascii.unhexlify("6b3e5e1c7a6d3e4b5a7971345710267a344c1b7d6b224e147d7a335c0726783d4d107b6d3e41016a713a57126c7d7b551a66722e4936666c354d07705a345d10297f295618295a344c1b7d6b22691d66773e701b6f767b5f1460753e5d")
 
print decode_string(encoded_str, key)
Que en este caso devuelve:
register/phone/countrywatcher/aftertextchanged lookupCountryCode from CountryPhoneInfo failed

KlassMaster?

Fuente:http://www.hackplayers.com/2012/08/decodificando-cadenas-ofuscadas-del.html

No hay comentarios:

Powered by Bad Robot
Helped by Blackubay