Android CrackMe[0]:TEA加密算法的逆向

有空找些CrackMe来让脑子转一转还是挺有意思的。
这道题据说是一道Android逆向工程师的面试题。给的APK是一个服务端程序,要求写一个客户端来解密来自服务端的数据。

把APK拖到JEB,直接看MainActivity,如图:


image.png

MainActivity启动后,就会创建一个子线程去做一些事,而且这里加载了一个动态库,估计线程做的事情是在Native层实现的。


image.png

如上图,果然,子线程调用了MainActivity的Native函数init()。

用IDA打开so库,找到对应的jni函数Java_com_example_crack0_MainActivity_init() 的实现,如图:


image.png

该函数主要做的事情如下:

  1. 创建socket,并绑定监听本地8080端口(代码中没有指定,默认是8080,可在adb shell
    中使用netstat -ano 命令进行确认);


    image.png
  2. 读取客户端发来的数据,将读取到的数据通过xxx()函数进行加密;
  3. 将加密后的数据发送给客户端。

这里要注意一个地方,就是上图中xxx()函数调用的第三个参数,传入的是v6的这个整型变量的地址。另外,可以看到,v6、v7、v8和v9都是很有规律的数,就是一串连续的16进制数,但看起来xxx()函数只用到了v6,这貌似不太对,我猜想这4个整型变量其实是连续的...于是我将代码视图跳转到ARM汇编视图,发现:


image.png

双击跳转到unk_2364地址处查看,如图:


image.png

果然这串十六进制数是紧挨着的,也就是说,这16个字节的数都会参与到加密。我猜想这串数应该是用作加解密的密钥。

好,下面来分析xxx()函数的实现,如图:


image.png

可以看到,xxx()函数是先对原始数据缓冲区srcData的每个字节进行取反,然后再对原始数据的字节长度进行各种算术运算,从而得到加密后数据的长度,以及后续do-while循环所需要的循环次数,同时还将一个地址值(srcDataLen的地址)放到缓冲区srcData中,。后面这个do-while循环就是再次对经过初步处理的原始数据进行加密,真正关键的加密实现在xxxx()函数中,如图:


image.png

从xxxx()函数的实现中,可以发现TEA的显著特征,那就是0xC6EF3720,和 -0x61C88647(即0x9E3779B9)。搜索一下TEA的C语言实现,如下图所示【详见链接】:

image.png

仔细对比一下该图的encrypt()和上面题目中的xxxx()函数,就能确定这个xxxx()函数就是对传入的数据进行了TEA加密。

识别出算法后,就可以通过对比TEA加密和解密实现的不同,来写出对应的解密函数xxxx_decrypt()了:

#define _DWORD unsigned int
#define HIDWORD(x) (*((_DWORD *)&(x) + 1))
#define LODWORD(x) (*((_DWORD *)&(x)))

//key="\x67\x45\x23\x01\xEF\xCD\xAB\x89\x98\xBA\xDC\xFE\x10\x32\x54\x76"
//TEA解密算法
long long xxxx_decrypt(int a1, int a2)
{
    int v2;
    int v3;
    int v4;
    unsigned int v5;
    unsigned int v6;
    int v7;
    long long v9;

    v2 = *(int *)(a2 + 8); //v2=0xFEDCBA98
    v3 = *(int *)a2;       //v3=0x1234567
    v4 = *(int *)(a2 + 4); //v4=0x89ABCDEF
    v5 = *(int *)a1;
    v6 = *(int *)(a1 + 4);
    HIDWORD(v9) = *(int *)(a2 + 12);
    v7 = 0xC6EF3720;
    LODWORD(v9) = v2; //v9=0x76543210FEDCBA98
    do
    {
        v6 -= ((v5 >> 5) + HIDWORD(v9)) ^ (16 * v5 + v2) ^ (v5 + v7);
        v5 -= ((v6 >> 5) + v4) ^ (16 * v6 + v3) ^ (v6 + v7);
        v7 += 0x61C88647;
    } while (v7 != 0);
    *(int *)a1 = v5;
    *(int *)(a1 + 4) = v6;
    return v9;
}

再回头看xxx()函数再到xxxx()函数的这个过程,首先xxxx函数的参数1是待加密数据的缓冲区首地址,参数2是key的首地址。且xxxx()函数每一次的调用,参数1所传入的缓冲区首地址,是原始缓冲区首地址+(i*8)个字节的偏移,这里的 i 表示循环计数。而循环次数 = 加密后数据的字节数 / 8,这个可以通过分析xxx()函数那几行代码得知,很容易的。

最后我写了一个客户端APK来解密服务端的数据,关键代码如下:

#define _DWORD unsigned int
#define HIDWORD(x) (*((_DWORD *)&(x) + 1))
#define LODWORD(x) (*((_DWORD *)&(x)))

//key="\x67\x45\x23\x01\xEF\xCD\xAB\x89\x98\xBA\xDC\xFE\x10\x32\x54\x76"
//TEA解密算法
long long xxxx_decrypt(int a1, int a2)
{
    int v2;
    int v3;
    int v4;
    unsigned int v5;
    unsigned int v6;
    int v7;
    long long v9;

    v2 = *(int *)(a2 + 8); //v2=0xFEDCBA98
    v3 = *(int *)a2;       //v3=0x1234567
    v4 = *(int *)(a2 + 4); //v4=0x89ABCDEF
    v5 = *(int *)a1;
    v6 = *(int *)(a1 + 4);
    HIDWORD(v9) = *(int *)(a2 + 12);
    v7 = 0xC6EF3720;
    LODWORD(v9) = v2; //v9=0x76543210FEDCBA98
    do
    {
        v6 -= ((v5 >> 5) + HIDWORD(v9)) ^ (16 * v5 + v2) ^ (v5 + v7);
        v5 -= ((v6 >> 5) + v4) ^ (16 * v6 + v3) ^ (v6 + v7);
        v7 += 0x61C88647;
    } while (v7 != 0);
    *(int *)a1 = v5;
    *(int *)(a1 + 4) = v6;
    return v9;
}

JNIEXPORT void Java_me_falcon_exam02_1crack_MainActivity_decryptData(JNIEnv *env, jobject obj, jbyteArray dataArr)
{
    int dataBytesLen = (*env)->GetArrayLength(env, dataArr);
    char key[] = "\x67\x45\x23\x01\xEF\xCD\xAB\x89\x98\xBA\xDC\xFE\x10\x32\x54\x76";
    jbyte *dataBytes = (*env)->GetByteArrayElements(env, dataArr, NULL);

    int nCnt = dataBytesLen / 8;
    for (int i = 0; i < nCnt; i++) {
        xxxx_decrypt(dataBytes + i * 8, key);
    }

    char srcDataBuf[1024] = {};
    for (int i = 0; dataBytes[i] != 0; i++) {
        srcDataBuf[i] = ~dataBytes[i];
    }

    LOGD("[From server] After decrypt, srcData=%s, srcDataLen=%d", srcDataBuf, strlen(srcDataBuf));
    (*env)->ReleaseByteArrayElements(env, dataArr, dataBytes, 0);
}

相关的代码、APK等有兴趣可通过下面的地址获取:
链接:https://pan.baidu.com/s/1pufbdPb6y1fnEm3W6lnXRg 密码:9jqs

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355