CTF逆向常见算法

Posted by Humb1e on 2023-02-12
Estimated Reading Time 4 Minutes
Words 1.1k In Total
Viewed Times

很多题目ida伪代码是具有相当的辨识度的,RC4,base系列的大佬们基本上是一眼破的

所以想总结一个逆向常见的算法,还有原理和特征

base系列

base64的原理比较简单

一个字节有8位,这是计算机的编码方式

但是base64先构造了一张由64个可打印字符组成的表

把明文以6位一组的方式读入,因为2^6=64

所以每一组读入的数据在base64表中对应着一个密文

比如说读入一个3字节的数据就会返回4字节的密文

但是这种编码方式会在位数不是6的倍数的时候出现长度不齐的现象

所以在缺4位的时候会补上4个0,再在密文的末尾补上两个==

同理缺两位的情况下会补上2个0,在在密文的末尾补上一个=

其他base系列的加密方法都是类似的

这类加密方法具有的显著特征有:

1.密文后可能常见‘“=”

2.可能有base表存在,并且base表的长度为2^n

3.加密代码中常见移位取余之类的截取操作

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <stdio.h>
#include "string.h"
#include "stdlib.h"

const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
static char find_pos(char ch);
char *base64_encode(const char* data, int data_len,int *len);
/***找到ch在base中的位置***/
static char find_pos(char ch)
{
//the last position (the only) in base[]
char *ptr = (char*)strrchr(base, ch);
return (ptr - base);
}
/** *BASE64编码***/
char *base64_encode(const char* data, int data_len,int *len)
{ //Length change to prior 3/4
int prepare = 0;
int ret_len;
*len=0;
int temp = 0;
char *ret = NULL;
char *f = NULL;
int tmp = 0;
char changed[4];
int i = 0;
ret_len = data_len / 3; //确定最后的 =
temp = data_len % 3; //
if (temp > 0)
{
ret_len += 1;
}
//最后一位以''结束
ret_len = ret_len*4 + 1; //加上‘\0’
ret = (char *)malloc(ret_len); //确定编码后的长度
if ( ret == NULL)
{
printf("No enough memory.n");
exit(0);
}
memset(ret, 0, ret_len);
f = ret;
//tmp记录data中移动位置
while (tmp < data_len)
{
temp = 0;
prepare = 0;
memset(changed, 0, 4);
while (temp < 3) //每次读取三位
{
if (tmp >= data_len)
{
break;
}
//将data前8*3位移入prepare的低24位
prepare = ((prepare << 8) | (data[tmp] & 0xFF));//作用是变为二进制
tmp++;
temp++; //作为计数器
}
//将有效数据移到以prepare的第24位起始位置
prepare = (prepare<<((3-temp)*8));
for (i = 0; i < 4 ;i++ )
{
//最后一位或两位
if (temp < i)
{
changed[i] = 0x40;
}
else
{
//24位数据
changed[i] = (prepare>>((3-i)*6)) & 0x3F;
}
*f = base[ changed[ i ] ];
f++;
(*len)++;
}
}
strcpy(f,"");
return ret;
}

RC4

RC4是流密码的一种,所谓流密码就是逐字节加密的加密方式

RC4在电子领域是很常见的

RC4是需要接收密钥的

解密的时候也需要密钥

在CTF题目中密钥一般都会给出

当然也有可能需要爆破密钥,因为之前已经提到了,RC4是逐字节加密的,所以爆破不会很麻烦

加密过程比较多:

1.初始化状态向量s[256](一般都是256)

常见的就是是s[i]=i

2.输入密钥key[x]

在这里如果x<256的话会进行一个轮转操作,就是用key填满256位,被填满的数组就叫他k[256]

3.对状态向量s进行打乱

1
2
3
4
5
j=0;
for (int i=0;i<256;i++){
j=(j+s[i]+k[i]) % 256;
swap(s[i],s[j]);
}

这样处理完的话s基本上就被打乱了

4.最后就是密钥流的生成

假如说需要加密的明文长度(字节数)是lenth

那么加密轮数就是lenth

1
2
3
4
5
6
7
8
9
10
i=0;
j=0;
while(lenth--){
   i = (i + 1) % 256;
    j = (j + S[i]) % 256;
    swap(S[i] , S[j]);
    t = (S[i] + S[j])% 256;
    k = S[t];//实际上最后就得到了一个k的值并且这个值是在s中的,所以0<k<255
//我们就可以用这个k对每一位的明文进行加密操作
}

假如说我们用k对每一位密文异或了

那么实际上密钥不变化的话我们再次输入密文得到的结果就是明文了,毕竟异或两次就回去了

但是如果我们用的是+ - *的操作,那样话可能要自己搓一个脚本跑一下了


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !