android逆向工程加密,Android CrackMe[0]:TEA加密算法的逆向

有空找些CrackMe来让脑子转一转还是挺有意思的。

这道题据说是一道Android逆向工程师的面试题。给的APK是一个服务端程序,要求写一个客户端来解密来自服务端的数据。

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

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

image.png

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

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

image.png

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

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

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

image.png

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

创建socket,并绑定监听本地8080端口(代码中没有指定,默认是8080,可在adb shell

中使用netstat -ano 命令进行确认);

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

image.png

读取客户端发来的数据,将读取到的数据通过xxx()函数进行加密;

将加密后的数据发送给客户端。

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

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

image.png

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

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

image.png

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

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

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

image.png

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

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

image.png

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

9bc534e59cbf?tdsourcetag=s_pcqq_aiomsg

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);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值