Adventure with Android. Part 3.

You can download xxtea for your preferred language from here:

https://github.com/xxtea

Then write small program that reads encrypted file then skip first 7 bytes and call xxtea_decrypt. But when I wrote program that does this - function failed. I tried different languages thinking that there could be bug in one particular implementation but function still failing. This was my second big disappointment. For some time, I didn’t know what to do. In desperation I started looking at decrypt function and accidentally I found this:

0x96cc28fc:  movw    r1, #55894      ; 0xda56
0x96cc2900:  movw    r2, #31161      ; 0x79b9
0x96cc2904:  movt    r1, #46412      ; 0xb54c
0x96cc2908:  movt    r2, #40503      ; 0x9e37

It looks like two constants being used: 0x9e3779b9 and 0xb54cda56. After that I checked C version of xxtea but I only able only to find usage of first constant (0x9e3779b9):

#define DELTA 0x9e3779b9

I even checked release version of compiled code for x86_64 because C compiler sometimes doing really clever optimizations. And I still cannot see how second constant being used anywhere in that code. And after some thoughts and looking at disassembled function I came to conclusion that algorithm was modified. And now I will have to go thru disassembly and reconstruct algorithm in one of the languages I know. I don’t really want to do this because I’m not that familiar with ARM assembler and even with familiar architecture it will require a lot of work. And then Eureka moment hit me. I can just call this function from Android application. I started Visual Studio 2019, then went to File|New |Project, selected Android Native-Activity application. Because Android is actually Linux, I did quick search and found that I need to use dlopen and dlsym functions to load and find exported functions. At the end I have following code that I put at the beginning of main.cpp:
 

#include <dlfcn.h>
…
typedef void* (*xxtea_encrypt_func)(const void* data, size_t len, const void* key, size_t* out_len);
typedef void* (*xxtea_decrypt_func)(const void* data, size_t len, const void* key, size_t* out_len);

xxtea_encrypt_func xxtea_encrypt;
xxtea_encrypt_func xxtea_decrypt;

void Run()
{
    void* handle = dlopen("libgame.so", RTLD_NOW);

    if (handle == nullptr)
    {
        LOGW("DH: Failed to load libgame.so");
        return;
    }

    void* decryptAddr = dlsym(handle, "xxtea_decrypt");

    if (handle == nullptr)
    {
        LOGW("DH: Failed to resolve function");
        return;
    }

    void* encryptAddr = dlsym(handle, "xxtea_encrypt");

    if (handle == nullptr)
    {
        LOGW("DH: Failed to resolve function.");
        return;
    }

    xxtea_encrypt = (xxtea_encrypt_func)encryptAddr;
    xxtea_decrypt = (xxtea_encrypt_func)decryptAddr;
}

And I add following line as first line of android_main:

Run();

Then I placed .so files from game into Packaging\ARM\Debug\Package\libs\directory. For my project it was at MyAppp\Android2\Android2.Packaging\ARM\Debug\Package\libs. After that, these libraries will be deployed with my application and my application can load and use these libraries.

My next thought was to encrypt data by calling xxtea_encrypt and then call decrypt it to make sure that everything works well before trying to read file. Something like this:

    const char *text = "Hello World!";
    const char *key = "1234567890";
    size_t len;
    unsigned char *encrypt_data = xxtea_encrypt(text, strlen(text), key, &len);
    char *decrypt_data = xxtea_decrypt(encrypt_data, len, key, &len);

But to my surprise xxtea_encrypt immediately crash with segmentation fault. I checked all parameters and they were correct. I checked values in registers in debugger and they were correct. After spending quite some time I figured out two things. First: debugger didn’t stop exactly at the beginning of the functions and some registers already changed as you can see on this here:

   0x96fc77a8 <+0>:     stmdb   sp!, {r4, r5, r6, r7, r8, r9, r10, lr}
   0x96fc77ac <+4>:     sub     sp, #24
   0x96fc77ae <+6>:     mov     r9, r0
   0x96fc77b0 <+8>:     ldr     r0, [pc, #244]  ; (0x96fc78a8 <xxtea_decrypt+256>)
   0x96fc77b2 <+10>:    ldr.w   r10, [pc, #248] ; 0x96fc78ac <xxtea_decrypt+260>
   0x96fc77b6 <+14>:    mov     r5, r3
   0x96fc77b8 <+16>:    add     r0, pc
   0x96fc77ba <+18>:    ldr.w   r8, [sp, #56]   ; 0x38
   0x96fc77be <+22>:    mov     r7, r2
   0x96fc77c0 <+24>:    mov     r4, r1
   0x96fc77c2 <+26>:    ldr.w   r0, [r10, r0]
   0x96fc77c6 <+30>:    cmp     r5, #15
   0x96fc77c8 <+32>:    ldr     r0, [r0, #0]
   0x96fc77ca <+34>:    str     r0, [sp, #20]
   0x96fc77cc <+36>:    mov.w   r0, #0
=> 0x96fc77d0 <+40>:    str.w   r0, [r8]

Check for => on left side. It is where debugger stopped. You can confirm by printing pc register that contains address of intruction to execute:

(gdb) x $pc
0x96cc27d0 <xxtea_decrypt+40>:  0x0000f8c8

Second: game have different version of xxtea. The version available in github has 4 parameters for encrypt and decrypt functions: data, data length, key and output parameter that will receive length of returned data. But version that game has there is one extra parameter before output parameter: length of the key. You can clearly see it from this code:

0x96cc27a8 <+0>:     stmdb   sp!, {r4, r5, r6, r7, r8, r9, r10, lr}
…
0x96cc27ba <+18>:    ldr.w   r8, [sp, #56]   ; 0x38
…
0x96cc27cc <+36>:    mov.w   r0, #0
0x96cc27d0 <+40>:    str.w   r0, [r8]

As you can see that we store r4 on stack, later retrieve it and put it in r8, then load zero into r0 and then we save that zero to address that r8 has. So clearly there should be another parameter and after some fiddling, I figured out that it is should length of the key. As result I changed definitions to this:

typedef void* (*xxtea_encrypt_func)(const void* data, size_t len, const void* key, size_t keylen, size_t* out_len);
typedef void* (*xxtea_decrypt_func)(const void* data, size_t len, const void* key, size_t keylen, size_t* out_len);
…
    const char *text = "Hello World!";
    const char *key = "1234567890";
    size_t len;
    unsigned char *encrypt_data = xxtea_encrypt(text, strlen(text), key, strlen(key), &len);
    char *decrypt_data = xxtea_decrypt(encrypt_data, len, key, strlen(key), &len);

And after these changes I was able to successfully encrypt data. But decryption still failed. Another disappointment. Then I decided to check of xxtea_encrypt function and found that it does not use magical constant 0xb54cda56 from xxtea_decrypt. And after some time, I came to conclusion that only decrypt function was modified but encryption stayed pretty much unchanged.

And next step would be to load encrypted file from somewhere in Android emulator and pass it to decrypt function. But it will be in next post.

Comments

Post comment