这次来了解下Tea的改进版,xTea和xxTea
来自wiki的介绍
翻译过来就是:
在密码学中,XTEA(扩展TEA)是一种用于纠正TEA弱点的分组密码。该密码的设计师是大卫·惠勒和罗杰·尼达姆的的剑桥大学计算机实验室,该算法在未公开的技术报告,1997年(李约瑟和惠勒,1997)提出的。它不受任何专利的约束。[1]
与TEA一样,XTEA是一个64位块 Feistel密码,具有128位密钥和建议的64轮。与TEA的一些差异是显而易见的,包括更复杂的关键时间表以及轮班,异或和补充的重新安排。
/* 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;
}
说了那么多,反正我对密码学的东西一窍不通,只知道和Tea存在不同就是了,所以。盘它就完事了!
从代码上可以看到和tea相比多了一个参数num_rounds
,以及计算的方式发生了变化,其实我感觉和tea差不多,都是左移4位,右移5位。
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
题目代码都差不多,放在文末
我们直接开始盘它,在进encrypt
之前看一下参数
rsi
rdi
注意到参数有三,跟进、下断,运行,循环。
循环位置如下:
这次循环的位置就不是非常容易观察出来。
可以看到主要的操作,多了一些有意思的东西。
<<4
>>5
xor 3
<<4
>>5
>>11
xor 3
一轮循环之后,通过观察比对数据,可以总结出以下特征:
0x9E3779B9
解题脚本在文末
具体介绍参考Wiki
字有点多懒的复制了
#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);
}
}
将n个字编码或解码为单个块,其中n > 1
一些参数含义如下:
xxTea的加解密是通过参数来进行控制的,因此看代码时只需要关注其一半即可
基础的不说了直接跟进cyrpt
部分
先是n和1进行比较
rounds = 6 + 52/n;
根据n算出rounds,其中的mov ecx 34h和add ecx 6
都很明显
右移2位并且异或3
在汇编里看起来很明显,咱们F5试一下?
顿时一愣!还是看汇编清楚。
继续往下,可以看到和tea&xtea算法类似的操作:
>>5
<<2
>>3
<<4
xor 3
其中有两层循环一个是rounds
次,一个是n-1
次
所以总结特征如下:
1. key 128 bit
2. enc => 32*i(i=>2)
3. 特征量`0x9e3779b9`
4. 两层循环,通常记住最外层的循环为rounds=6+52/n
5. 5,2,3,4左右移操作
注意一点xxTea
的加密数据最少是64位,长度是可以任意的(32*i)。
这次学完了这个系列的算法,来总结一下,不要弄混淆了。
相同点:
0x9e3779b9
首先如果题目中出现常量0x9e3779b9
,那么肯定是Tea
相关算法了。
区分:
<<4,>>5,xor
,循环32轮<<4,>>5,>>11,xor
,循环次数不定,但通常也为32轮,需要传入3个参数>>5,<<2,>>3,<<4,xor
,循环次数为6+52/n
,enc长度大于64我想拿这三者结合起来,魔改一下,出个题,看看自己到底掌握了没有。
腾讯的加密算法是16轮的Tea算法 参考
说明:在上一篇的解题脚本中出现了一点错误,现修正如下,其实是格式化输出的问题。
printf("flag{%x-%x}\n",flagLong[0],flagLong[1]);
修改为
printf("flag{%X-%.08X}\n",flagLong[0],flagLong[1]);
xTea题目:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.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 getStr(char *buffer,int maxLen){
char c; // 读取到的一个字符
int len = 0; // 当前输入的字符串的长度
// 一次读取一个字符,保存到buffer
// 直到遇到换行符(\n),或者长度超过maxLen时,停止读取
while( (c=getchar()) != '\n' ){
buffer[len++]=c; // 将读取到的字符保存到buffer
if(len>=maxLen){
break;
}
}
buffer[len]='\0'; // 读取结束,在末尾手动添加字符串结束标志
fflush(stdin); // 刷新输入缓冲区
return len;
}
/*将大写字母转换成小写字母*/
int tolower(int c)
{
if (c >= 'A' && c <= 'Z')
{
return c + 'a' - 'A';
}
else
{
return c;
}
}
//将十六进制的字符串转换成整数
int htoi(char s[])
{
int i = 0;
int n = 0;
if (s[0] == '0' && (s[1]=='x' || s[1]=='X'))
{
i = 2;
}
else
{
i = 0;
}
for (; (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z');++i)
{
if (tolower(s[i]) > '9')
{
n = 16 * n + (10 + tolower(s[i]) - 'a');
}
else
{
n = 16 * n + (tolower(s[i]) - '0');
}
}
return n;
}
void reverse(char *s, int start, int end)
{
char t;
while(end>start){
t=s[start];
s[start]=s[end];
s[end]=t;
start++;
end--;
}
}
int main()
{
uint32_t v[2]={1,2},k[4]={2,2,3,4};
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
int flagLen = 0;
bool success = false;
char flag[33];
memset(flag, 0, sizeof(flag));//清空字符串
setbuf(stdin,0);
setbuf(stdout,0);
setbuf(stderr,0);
printf("Please input you flag:");
flagLen = getStr(flag,32);
//check formant
uint8_t vv[5] = {0};
strncpy(vv,flag,4);
uint8_t five = 123;
uint8_t last = 125;
uint8_t *v1,*v2;
if(((uint8_t)flag[5] - five)>0){
printf("five error!");
return -1;
}
if(((uint8_t)flag[flagLen-1] + last) == 250){
;
}else{
printf("last error!");
return -1;
}
if(strcmp(vv,"flag")){
printf("header error!");
return -1;
}
int mallocSize = flagLen - 6;
char *tokstr = (char *)malloc(sizeof(char)*mallocSize+1);
memset(tokstr, 0, sizeof(tokstr));//清空字符串
strncpy(tokstr,flag+5,mallocSize);
v1 = strtok(tokstr,"-");
v2 = strtok(NULL,"-");
//exchange scale
uint32_t flagLong[2];
flagLong[0] = (uint32_t)htoi((char *)v1);
flagLong[1] = (uint32_t)htoi((char *)v2);
// printf("%d",sizeof(int)); 4 byte == 32 bit
// printf("加密前原始数据:%x %x\n",flagLong[0],flagLong[1]);
encipher(32,flagLong, k);
// printf("加密后的数据:%x %x\n",flagLong[0],flagLong[1]);
// check flag
uint8_t check_enc[4];
uint8_t check_index[4] = {3,1,0,2};
uint8_t i=0;
check_enc[0] = 0x8c;
check_enc[1] = 0xa2;
check_enc[2] = 0x26;
check_enc[3] = 0x46;
for(i=0;i<4;i++){
uint8_t t = (uint8_t)(flagLong[0]>>(8*i));
// printf("%x\t",t);
if(check_enc[i]!=flagLong[check_index[i]]){
success = false;
}
}
char check_enc_last[9] = "61ba69e3";
// snprintf(check_enc_last,9,"%x",flagLong[1]);//3e96ab16
reverse(check_enc_last,0,7);
uint32_t enc_hex = htoi(check_enc_last);
// printf("%x",enc_hex);
if(flagLong[1] == enc_hex){
success = true;
}
if(!success){
printf("You Lost!\n");
}else{
printf("You Win!\n");
}
return 0;
}
解题代码:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
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 k[4]={2,2,3,4};
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
//exchange scale
uint32_t flagLong[2];
flagLong[0] = 0x26a2468c;
//enc 26a2468c 3e96ab16
flagLong[1] = 0x3e96ab16;
decipher(32,flagLong,k);
printf("flag{%X-%.08X}\n",flagLong[0],flagLong[1]);
return 0;
}
xxTea题目:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.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 getStr(char *buffer,int maxLen){
char c; // 读取到的一个字符
int len = 0; // 当前输入的字符串的长度
// 一次读取一个字符,保存到buffer
// 直到遇到换行符(\n),或者长度超过maxLen时,停止读取
while( (c=getchar()) != '\n' ){
buffer[len++]=c; // 将读取到的字符保存到buffer
if(len>=maxLen){
break;
}
}
buffer[len]='\0'; // 读取结束,在末尾手动添加字符串结束标志
fflush(stdin); // 刷新输入缓冲区
return len;
}
/*将大写字母转换成小写字母*/
int tolower(int c)
{
if (c >= 'A' && c <= 'Z')
{
return c + 'a' - 'A';
}
else
{
return c;
}
}
//将十六进制的字符串转换成整数
int htoi(char s[])
{
int i = 0;
int n = 0;
if (s[0] == '0' && (s[1]=='x' || s[1]=='X'))
{
i = 2;
}
else
{
i = 0;
}
for (; (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z');++i)
{
if (tolower(s[i]) > '9')
{
n = 16 * n + (10 + tolower(s[i]) - 'a');
}
else
{
n = 16 * n + (tolower(s[i]) - '0');
}
}
return n;
}
void reverse(char *s, int start, int end)
{
char t;
while(end>start){
t=s[start];
s[start]=s[end];
s[end]=t;
start++;
end--;
}
}
int main()
{
uint32_t v[2]={1,2},k[4]={2,2,3,4};
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
int flagLen = 0;
bool success = false;
char flag[33];
memset(flag, 0, sizeof(flag));//清空字符串
setbuf(stdin,0);
setbuf(stdout,0);
setbuf(stderr,0);
printf("Please input you flag:");
flagLen = getStr(flag,32);
//check formant
uint8_t vv[5] = {0};
strncpy(vv,flag,4);
uint8_t five = 123;
uint8_t last = 125;
uint8_t *v1,*v2;
if(((uint8_t)flag[5] - five)>0){
printf("five error!");
return -1;
}
if(((uint8_t)flag[flagLen-1] + last) == 250){
;
}else{
printf("last error!");
return -1;
}
if(strcmp(vv,"flag")){
printf("header error!");
return -1;
}
int mallocSize = flagLen - 6;
char *tokstr = (char *)malloc(sizeof(char)*mallocSize+1);
memset(tokstr, 0, sizeof(tokstr));//清空字符串
strncpy(tokstr,flag+5,mallocSize);
v1 = strtok(tokstr,"-");
v2 = strtok(NULL,"-");
//exchange scale
uint32_t flagLong[2];
flagLong[0] = (uint32_t)htoi((char *)v1);
flagLong[1] = (uint32_t)htoi((char *)v2);
// printf("%d",sizeof(int)); 4 byte == 32 bit
// printf("加密前原始数据:%x %x\n",flagLong[0],flagLong[1]);
btea(flagLong,2, k);
// printf("加密后的数据:%x %x\n",flagLong[0],flagLong[1]);
// check flag
uint8_t check_enc[4];
uint8_t check_index[4] = {3,1,0,2};
uint8_t i=0;
check_enc[0] = 0x57;
check_enc[1] = 0x8b;
check_enc[2] = 0x36;
check_enc[3] = 0x9b;
for(i=0;i<4;i++){
uint8_t t = (uint8_t)(flagLong[0]>>(8*i));
// printf("%x\t",t);
if(check_enc[i]!=flagLong[check_index[i]]){
success = false;
}
}
char check_enc_last[9] = "6b45a63b";
// snprintf(check_enc_last,9,"%x",flagLong[1]);//b36a54b6
reverse(check_enc_last,0,7);
uint32_t enc_hex = htoi(check_enc_last);
// printf("%x",enc_hex);
if(flagLong[1] == enc_hex){
success = true;
}
if(!success){
printf("You Lost!\n");
}else{
printf("You Win!\n");
}
return 0;
}
解题代码:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.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 k[4]={2,2,3,4};
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
//exchange scale
uint32_t flagLong[2];
flagLong[0] = 0x368b9b57;
flagLong[1] = 0xb36a54b6;
btea(flagLong,-2,k);
printf("flag{%X-%.08X}\n",flagLong[0],flagLong[1]);
return 0;
}
完。