前言:自己有時間做了幾道攻防世界的幾道逆向題目,發現TEA系列的算法是很常見的,這里的話就想順便學習下
TEA算法
TEA算法介紹
"TEA" 的全稱為"Tiny Encryption Algorithm" 是1994年由英國劍橋大學的David j.wheeler發明的。
TEA算法也算是一種微型加密算法的。
在安全學領域,TEA(Tiny Encryption Algorithm)是一種分組加密算法,它的實現非常簡單,通常只需要很精短的幾行代碼。
TEA算法使用64位的明文分組和128位的密鑰,它使用Feistel分組加密框架,需要進行64輪迭代,但是作者認為32輪已經足夠了,所以32輪迭代加密后最后得到的密文就是64位。
簡單的說就是,TEA加密解密是以原文以8字節(64位bit)為一組,密鑰16字節(128位bit)為一組,(char為1字節,int為4字節,double為8字節),該算法加密輪次可變,作者建議為32輪,因為被加密的明文為64位,所以最終加密的結果也是64位。
該算法使用了一個神秘常數δ作為倍數,它來源於黃金比率,以保證每一輪加密都不相同。但δ的精確值似乎並不重要,這里TEA把它定義為 δ=「(√5 - 1)231」
,這個δ對應的數指就是0×9E3779B9,所以這個值在TEA加密或者解密中會有用到。
TEA算法圖解分析
TEA算法的圖解如下圖所示
從圖解中可以看到運算有加法運算,位運算,異或運算。
流程1:
1、首先TEA加密解密是以原文以8字節,所以從兩邊各自傳入四個字節
2、右邊傳入的4個字節,這里將這4個字節稱呼為M,M進行了三個部分的操作,M左移4位與密鑰[0]相加,M右移5位與密鑰[1]相加,M與δ相加,最后這三個算出的值再異或
3、左邊傳入的4個字節,這里將這4個字節稱呼為N,N=N+M
流程2:
接着就到了下面這個部分,這里的話M和N交換了位置,
2、右邊傳入的4個字節,N進行了三個部分的操作,N左移4位與密鑰[2]相加,N右移5位與密鑰[3]相加,N與δ相加,最后這三個算出的值再異或
3、左邊傳入的4個字節,M=M+N
4、此時拿到的M和N就是加密過后的M和N
TEA算法的實現
#include <stdio.h>
#include <stdint.h>
//加密函數
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i < 32; i++) { /* basic cycle start */
sum += delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
} /* end cycle */
v[0]=v0; v[1]=v1;
}
//解密函數
void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<32; i++) { /* basic cycle start */
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t v[2]={1,2},k[4]={2,2,3,4};
// v為要加密的數據是兩個32位無符號整數
// k為加密解密密鑰,為4個32位無符號整數,即密鑰長度為128位
printf("加密前原始數據:%u %u\n",v[0],v[1]);
encrypt(v, k);
printf("加密后的數據:%u %u\n",v[0],v[1]);
decrypt(v, k);
printf("解密后的數據:%u %u\n",v[0],v[1]);
return 0;
}
TEA算法逆向
1、tea算法的特征就是DELTA值和十六個字節的密鑰(也就是128位)。
2、在逆向程序的時候,可以利用ida的插件findcypto識別tea算法
3、x-=0x61c88647和x+=0x9e3779b9,這兩個值是等價的,可能會在反匯編中看到
這里將上面測試的代碼編譯好拖入IDA中進行觀察
可以看到δ就被反編譯為v4 -= 1640531527;
但是反匯編中就是0x9e3779b9,如下圖所示
我們這里用findcypto插件來進行識別測試,0x9e3779b9特征碼被識別,如下圖所示
TEA算法的變化技巧
前面有提到關於"但δ的精確值似乎並不重要",所以δ的值不一定需要0x9E3779B9,所以這里的話在加密解密的時候可以改變這個δ的值,改了之后就會導致部分識別加密的工具失效來達到算法魔改的目的。
測試代碼,改變其中的δ的值
//加密函數
void encrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum = 0, i; /* set up */
uint32_t delta = 0x12345678; /* a key schedule constant */
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++) { /* basic cycle start */
sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
} /* end cycle */
v[0] = v0; v[1] = v1;
}
//解密函數
void decrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum = 0x468acf00, i; /* set up */
uint32_t delta = 0x12345678; /* a key schedule constant */
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i<32; i++) { /* basic cycle start */
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
} /* end cycle */
v[0] = v0; v[1] = v1;
}
這里將上面測試的代碼編譯好拖入IDA中進行觀察,可以看到這個插件就無法識別出TEA算法了,因為改了0x9E3779B9特征碼
騰訊TEA算法分析
每次處理64位數據,使用128位密鑰。數據和密鑰都分割成32為無符號整型處理。
不同點:TEA標准中使用的32輪加密,而騰訊只用了16輪。
可以看到其他都一樣的,就是迭代次數為16次
#include <stdint.h>
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i < 16; i++) { /* basic cycle start */
sum += delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
} /* end cycle */
v[0]=v0; v[1]=v1;
}
void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t sum = delta << 4;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<16; i++) { /* basic cycle start */
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}
XTEA算法
XTEA算法介紹
XTEA算法也被稱作為Corrected Block TEA
XTEA是TEA的升級版,增加了更多的密鑰表,移位和異或操作等等,設計者是Roger Needham, David Wheeler
之后 TEA 算法被發現存在缺陷,作為回應,設計者提出了一個 TEA 的升級版本——XTEA(有時也被稱為"tean")。XTEA 跟 TEA 使用了相同的簡單運算,但它采用了截然不同的順序,為了阻止密鑰表攻擊,四個子密鑰(在加密過程中,原 128 位的密鑰被拆分為 4 個 32 位的子密鑰)采用了一種不太正規的方式進行混合,但速度更慢了。
XTEA算法圖解
XTEA算法實現
#include <stdio.h>
#include <stdint.h>
/* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
for (i=0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t v[2]={1,2};
uint32_t const k[4]={2,2,3,4};
unsigned int r=32;//num_rounds建議取值為32
// v為要加密的數據是兩個32位無符號整數
// k為加密解密密鑰,為4個32位無符號整數,即密鑰長度為128位
printf("加密前原始數據:%u %u\n",v[0],v[1]);
encipher(r, v, k);
printf("加密后的數據:%u %u\n",v[0],v[1]);
decipher(r, v, k);
printf("解密后的數據:%u %u\n",v[0],v[1]);
return 0;
}
XXTEA算法
XXTEA算法介紹
XXTEA就被稱作為Corrected Block TEA。
特點:原字符串長度可以不是4的倍數了
XXTEA算法圖解
XXTEA算法實現
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}
int main()
{
uint32_t v[2]= {1,2};
uint32_t const k[4]= {2,2,3,4};
int n= 2; //n的絕對值表示v的長度,取正表示加密,取負表示解密
// v為要加密的數據是兩個32位無符號整數
// k為加密解密密鑰,為4個32位無符號整數,即密鑰長度為128位
printf("加密前原始數據:%u %u\n",v[0],v[1]);
btea(v, n, k);
printf("加密后的數據:%u %u\n",v[0],v[1]);
btea(v, -n, k);
printf("解密后的數據:%u %u\n",v[0],v[1]);
return 0;
}