在做逆向的时候,我们难免会遇到各种各样的加密算法
这次来介绍几种常见的加密算法

RC4

RC4加密算法简介

RC4是一种对称的加密算法,所谓的对称加密算法是指加密和解密使用相同密钥的加密算法,数据发送方利用密钥将明文经过特殊加密算法处理后,使其变成复杂的加密密文发送出去,接收方收到密文后,若想解读原文,则需要使用加密用过的密钥以及相同的加密算法对明文进行解密,才能使其恢复成可读的明文。
RC4算法简单,运行速度快,而且密钥长度是可变的,可变范围为1-256字节(8-2048比特),生成的密钥流的长度和铭文的长度是对应的。在现在技术支持的前提下,当密钥长度为128比特是,用暴力法搜索密钥可能已经不太可行,所以能够预见RC4的密钥范围仍然能够在今后相当长的时间里低于暴力搜索哟密钥的攻击。实际上,现在也没有找到对于128bit密钥长度的RC4加密算法的有效的攻击方法。
RC4主要包括了初始化算法(KSA)和加密算法两大部分。

加密(解密)原理

RC4由伪随机数生成器和异或运算组成。RC4的密钥长度可变,范围是[1,255]。RC4一个字节一个字节地加解密。给定一个密钥,伪随机数生成器接受密钥并产生一个S盒。S盒用来加密数据,而且在加密过程中S盒会变化。

逆向特征

有很多取模操作

有很多256

有多个次数为256的循环

最后操作为异或

代码通常的结构

初始化S和T:

1
2
3
for i=0 to do
S[i]=i;
T[i]=K[i mod keylen];

初始排列S:

1
2
3
4
j=0;
for i=0 to 255 do
j = (j+S[i]+T[i]mod256;
swap(S[i],S[j];

生成密钥流,利用密钥流和明文进行加密:

1
2
3
4
5
6
7
8
i,j=0;
for r=0 to len do //r为明文长度,r字节
i=(i+1)mod 256;
j=(j+S[i])mod 256;
swap(S[i],S[j]);
t=(S[i]+S[j])mod 256;
K[r]=S[t];
data[r]^=K[r];

例题

附件

首先我们查找字符串交叉引用到main函数

然后按Tab或F5进行反编译

然后发现反编译失败,根据报错窗口跳转到对应地址处

1
2
3
4
int __cdecl main(int argc, const char **argv, const char **envp)
{
JUMPOUT(0x40105D);
}

发现了花指令

1
2
3
4
5
6
7
8
9
.text:00401055 57                            push    edi                             ; ArgList
.text:00401056 33 C0 xor eax, eax
.text:00401058 74 03 jz short near ptr loc_40105C+1 <--
.text:00401058
.text:0040105A 11 22 adc [edx], esp
.text:0040105C
.text:0040105C loc_40105C: ; CODE XREF: _main+18↑j
.text:0040105C 33 66 B8 xor esp, [esi-48h]
.text:0040105F 08 00 or [eax], al

将其修改为(先d转换数据,然后使用ctrl+alt+k使用keypatch将多余的那个字节修改成0x90即可,然后再按C转换为指令)(注:ida上方菜单栏的小绿点为自动反汇编功能,点击取消自动反汇编)

1
2
3
4
5
6
7
8
9
10
.text:00401058
.text:0040105A 11 22 adc [edx], esp
.text:0040105A
.text:0040105A ; ---------------------------------------------------------------------------
.text:0040105C 33 db 33h
.text:0040105D ; ---------------------------------------------------------------------------
.text:0040105D
.text:0040105D loc_40105D: ; CODE XREF: _main+18↑j
.text:0040105D 66 B8 08 00 mov ax, 8
.text:00401061 66 83 F0 07 xor ax, 7

即可反编译

加密分析

输入flag以及长度计算

1
2
3
4
5
6
memset(flag, 0, sizeof(flag));
gets(flag);
v3 = strlen(flag);
v4 = strlen(v23);
memset(v22, 0, sizeof(v22));

关键加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
do
{
v8 = v27[v6];
v7 = (v7 + v25[v6] + v8) % 256;
v27[v6++] = v27[v7];
v27[v7] = v8 ^ 0x37;
}
while ( v6 < 256 );
sub_401010("\n\n", v17);
v9 = 0;
v23 = 0;
v10 = 0;
if ( v3 )
{
do
{
v9 = (v9 + 1) % 256;
v11 = v27[v9];
v10 = (v11 + v10) % 256;
v27[v9] = v27[v10];
v27[v10] = v11;
v12 = v23;
v24[v23] ^= v27[(unsigned __int8)(v11 + v27[v9])];
v23 = v12 + 1;
}
while ( v12 + 1 < v3 );
v10 = 0;
}
for ( j = 0; j < v3; ++j )
v10 = v24[j] == *((_BYTE *)v19 + j);
v14 = (char *)&unk_402184;
if ( v10 == 1 )
v14 = aGood;

解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdio.h>
#include <string.h>

void rc4_init(unsigned char*s,unsigned char*key,unsigned long len)
{
int i=0;
int j=0;
unsigned char k[256]={};
unsigned char temp = 0;
for(i=0;i<256;i++)
{
s[i]=i;
k[i]=key[i%len];
}
for(i=0;i<256;i++)
{
j=(j+s[i]+k[i])%256;
temp=s[i];
s[i]=s[j];
s[j]=temp ^ 0x37;
}
}

void rc4_crypt(unsigned char*s,unsigned char*data,unsigned long len)
{
int i=0,j=0,t=0;
unsigned long k=0;
unsigned char temp;
for(k=0;k<len;k++)
{
i=(i+1)%256;
j=(j+s[i])%256;
temp=s[i];
s[i]=s[j];
s[j]=temp;
t=(s[i]+s[j])%256;
data[k]^=s[t];
}
}

int main()
{
unsigned char s[256] = {0};
char key[256] = {0x74, 0x61, 0x6C, 0x6C, 0x6D, 0x65, 0x77, 0x68, 0x79};
unsigned char data[512]= {0xf5, 0x8c, 0x8d, 0xe4, 0x9f, 0xa5, 0x28, 0x65, 0x30, 0xf4, 0xeb, 0xd3, 0x24, 0xa9, 0x91, 0x1a, 0x6f, 0xd4, 0x6a, 0xd7, 0xb, 0x8d, 0xe8, 0xb8, 0x83, 0x4a, 0x5a, 0x6e, 0xbe, 0xcb, 0xf4, 0x4b, 0x99, 0xd6, 0xe6, 0x54, 0x7a, 0x4f, 0x50, 0x14, 0xe5, 0xec};
unsigned long key_len = strlen(key);
rc4_init(s,(unsigned char*)key,key_len);//初始化得到s
rc4_crypt(s,(unsigned char*)data,42);//解密
for(int i=0;i<42;i++)
{
printf("%c",(data[i])&0xff);
}
return 0;
}

得到flag:flag{c5e0f5f6-f79e-5b9b-988f-28f046117802}

TEA

推荐阅读

在安全学领域,TEA(Tiny Encryption Algorithm)是一种分组加密算法,它的实现非常简单,通常只需要很精短的几行代码。TEA 算法最初是由剑桥计算机实验室的 David Wheeler 和 Roger Needham 在 1994 年设计的。

TEA系列算法在目前较为基础的reserve题目中会较常出现,但是随着CTF的参赛人员质量的提高,以往一些在逆向较为常见的算法

也逐渐运用到pwn里面形成了新的题型RePwn,所以做为pwn手,对于逆向的学习是很有必要的。

目前,tea算法总共有3大类(魔改除外),tea,xtea,xxtea。

TEA算法的原理简单阐述就是每次加密2个元素,加密方式通过利用key schedule constant(关键时刻表常数)以及4位密钥

进行指定轮数的加减、位移以及异或操作,通常的轮数为32轮。

例题

附件

拖入IDA中进行分析,同样可以通过函数名搜索或者字符串查找来定位到我们的main函数

发现处理我们的输入数据的就只有enc函数

Untitled

反编译之后根据特征分析可以得知就是TEA加密算法

并且魔改过

delta的值被修改为了0x73637466

Untitled

在最后赋值回去前添加了异或

Untitled

异或的值是delta的值

根据这个加密算法,我们先写出正向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>

uint32_t d = 0x73637466;

void enc(char *data, unsigned char * out) //unsigned char * data, unsigned char * out
{
uint32_t inp[4] = {*((uint32_t*)&data[0] + 0), *((uint32_t*)&data[0] + 1), *((uint32_t*)&data[0] + 2), *((uint32_t*)&data[0] + 3)};
uint32_t v0=inp[0], v1=inp[1];
uint32_t v2=inp[2], v3=inp[3];
uint32_t s1 = 0;
uint32_t s2 = 0;
uint32_t k[4] = {9, 7, 8, 6};
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
uint32_t dst[4] = {0, 0, 0, 0};
for(int i=0;i<32;i++)
{
s1 += d;
s2 += d;
v0 += ((v1<<4) + k2) ^ (v1 + s1) ^ ((v1>>5) + k3) ^ (s1 + i);
v1 += ((v0<<4) + k0) ^ (v0 + s1) ^ ((v0>>5) + k1) ^ (s1 + i);

v2 += ((v3<<4) + k2) ^ (v3 + s2) ^ ((v3>>5) + k3) ^ (s2 + i);
v3 += ((v2<<4) + k0) ^ (v2 + s2) ^ ((v2>>5) + k1) ^ (s2 + i);
}
dst[0] = v0 ^ (*((char*)&d+3) & 0xff);
dst[1] = v1 ^ (*((char*)&d+2) & 0xff);
dst[2] = v2 ^ (*((char*)&d+1) & 0xff);
dst[3] = v3 ^ (*((char*)&d+0) & 0xff);

//printf("%#x, %#x, %#x, %#x\n", dst[0], dst[1], dst[2], dst[3]);
int m = 0;
for(int j=0;j<4;j++)
{
for(int k=0; k<4 ;k++)
{
out[m] = *((char*)&dst[j] + k) & 0xff;
m+=1;
}
}
}

int main()
{
char inp[0x21] = {};//这里输入我们的测试数据
unsigned char out[32] = {0};
enc(inp, out);
enc(inp+16, out+16);
for(int i=0;i<32;i++)
{
printf("%#x, ", out[i]);
}
return 0;
}

最后我们根据加密写出解密即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

uint32_t delta = 0x73637466;

void solve(unsigned char *enc)
{
uint32_t tmp[4] = {*((uint32_t*)&enc[0] + 0), *((uint32_t*)&enc[0] + 1), *((uint32_t*)&enc[0] + 2), *((uint32_t*)&enc[0] + 3)};
uint32_t v0=tmp[0], v1=tmp[1];
uint32_t v2=tmp[2], v3=tmp[3];
//printf("%#x, %#x, %#x, %#x\n", v0, v1, v2, v3);
uint32_t k[4] = {9, 7, 8, 6};
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
v0 = v0 ^ (*((char*)&delta+3) & 0xff);
v1 = v1 ^ (*((char*)&delta+2) & 0xff);
v2 = v2 ^ (*((char*)&delta+1) & 0xff);
v3 = v3 ^ (*((char*)&delta+0) & 0xff);
//printf("%#x, %#x, %#x, %#x\n", v0, v1, v2, v3);
uint32_t sum = delta*32;
for(int i=0; i<32; i++)
{

v1 -= ((v0<<4) + k0) ^ (v0 + sum) ^ ((v0>>5) + k1) ^ (sum + (31-i));
v0 -= ((v1<<4) + k2) ^ (v1 + sum) ^ ((v1>>5) + k3) ^ (sum + (31-i));

v3 -= ((v2<<4) + k0) ^ (v2 + sum) ^ ((v2>>5) + k1) ^ (sum + (31-i));
v2 -= ((v3<<4) + k2) ^ (v3 + sum) ^ ((v3>>5) + k3) ^ (sum + (31-i));

sum -= delta;
}
printf("%c%c%c%c%c%c%c%c",*((char*)&v0+0),*((char*)&v0+1),*((char*)&v0+2),*((char*)&v0+3),*((char*)&v1+0),*((char*)&v1+1),*((char*)&v1+2),*((char*)&v1+3));
printf("%c%c%c%c%c%c%c%c",*((char*)&v2+0),*((char*)&v2+1),*((char*)&v2+2),*((char*)&v2+3),*((char*)&v3+0),*((char*)&v3+1),*((char*)&v3+2),*((char*)&v3+3));
}

int main()
{
unsigned char cmp[32] = {0x55, 0x37, 0xac, 0x72, 0x6a, 0xa1, 0xe, 0x9c, 0x57, 0x15, 0x1c, 0x8b, 0x6c, 0x90, 0xc5, 0xe5, 0x87, 0, 0xb8, 0x91, 0x2e, 0xa9, 0x5b, 0x4f, 0x93, 0x56, 0x34, 0xb5, 0xaa, 0x80, 0xd6, 0x70};
solve(cmp);
solve(cmp+16);
return 0;
}

得到flag:c5e0f5f6f79e5b9b988f8f04611a7802

Base64