0%

常见算法逆向

常见算法逆向

Base64

Base64加解密

1
2
3
4
1. 将要加密的字符串变成二进制
2. 二进制重新分组
3. 分组后二进制转十进制
4. 根据十进制查base64编码表
image-20240925101744872

加密步骤

image-20240925101933086

空缺时会有一个补零操作

image-20240925103600426

魔改

base64加密时一般会改base64编码表

比如本来的编码表:

1
origin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

更改后的编码表:

1
base = 'aUiBDdopSQVOFlfLnTNrv4sj+MJW3g2Cy5IZk6APYt9RGwqm/8H7eKcx1EzbX0hu'

使用更改的编码表进行解码:

1
2
3
4
5
6
7
8
9
10
import base64

origin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
base = 'aUiBDdopSQVOFlfLnTNrv4sj+MJW3g2Cy5IZk6APYt9RGwqm/8H7eKcx1EzbX0hu'
c = 'r6lrnKTo27T5FsDxfB+ElZMIlBQZMAS/MB6AlB4klBnelZFeFrTACn=='

table = str.maketrans(base, origin)
m = str(base64.b64decode(c.translate(table)), encoding='utf-8')

print(m)

表一般会在程序运行的时候改掉,比如下面的例子

题目 easybase.exe

点击下载easybase.exe

#### IDA 分析 image-20240925104938767

找到main函数,分析程序逻辑

1
2
// 打印提示信息
printf("plz_input_your_flag: ");
1
2
// 可能跟调试相关
debug()
1
2
//给字符串Str1加密
Str1 = (char *)base64_encode(v4, 1);
1
2
3
4
5
// 判断输入的字符串加密后是否与已知加密字符串相同
if ( !strcmp(Str1, "r6lrnKTo27T5FsDxfB+ElZMIlBQZMAS/MB6AlB4klBnelZFeFrTACn==") )
printf("Right!");
else
printf("try_again!");
image-20240925105452256

直接拿去解码是乱码,所以判断应该改了表

先进到base64_encode函数中查看加密逻辑:

image-20240925105540632 image-20240925105633185

在这里没有发现有什么改表操作,是一个常规的base64加密方法

那问题应该在debug()函数

image-20240925105753443

debug()函数中,最后返回的时候有一个random_shuffle()函数,参数传进了base64编码表,应该是使用随机数种子打乱了编码表。如果程序正常执行不调试,那么随机数种子为0,如果程序在调试模式中运行,那么随机数种子会被设置成114514

所以动态调试的时候跳过IsDebuggerPresent()函数即可,使用随机数种子0获取到打乱后的表,在进行解密就可以了

x64dbg动态调试

感觉x64dbg动态比IDA好用

image-20240925110900782

可以使用IDA快速定位到debug()函数,(主要分析在右侧注释)

在第一个断点处,将ZF位标位1,跳过Seed = 114514,这样就得到了非调试模式下的base64表,表就应该在0x40F020这段内存中

image-20240925110954237

image-20240925111048524

这一段就是修改后的base64编码表,编写一下一段py进行解码

1
2
3
4
5
6
7
8
9
10
import base64

origin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
base = 'aUiBDdopSQVOFlfLnTNrv4sj+MJW3g2Cy5IZk6APYt9RGwqm/8H7eKcx1EzbX0hu'
c = 'r6lrnKTo27T5FsDxfB+ElZMIlBQZMAS/MB6AlB4klBnelZFeFrTACn=='

table = str.maketrans(base, origin)
m = str(base64.b64decode(c.translate(table)), encoding='utf-8')

print(m)

解出:image-20240925111238482

RC4

RC4加解密

RC4是对称加密算法,加解密使用同一套秘钥

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
#pragma once

#include <windows.h>
#include <iostream>
#include <stdio.h>
#pragma warning(disable:6273)

using namespace std;
typedef struct _RC4_KEY {
unsigned char S_Box[256];
unsigned char T_Box[256];
} RC4_KEY, * PRC4_KEY;

void RC4Init(PRC4_KEY Context, unsigned char* Key, int KeyLength)
{
int i = 0, j = 0;
unsigned char v1;
if (Context == NULL)
{
return;
}
for (i = 0; i < 256; i++)
{
Context->S_Box[i] = i;
Context->T_Box[i] = Key[i % KeyLength];
}
for (i = 0; i < 256; i++)
{
j = (j + Context->S_Box[i] + Context->T_Box[i]) % 256;
v1 = Context->S_Box[i];
Context->S_Box[i] = Context->S_Box[j];
Context->S_Box[j] = v1;
}
}

void RC4Crypt(unsigned char* Input, unsigned int InputLength, unsigned char* Key, unsigned int KeyLength)
{
int v1 = 0, i = 0, j = 0, t = 0;
unsigned char v2;

RC4_KEY Context;
RC4Init(&Context, Key, KeyLength);

// 核心算法
for (v1 = 0; v1 < InputLength; v1++)
{
i = (i + 1) % 256;
j = (j + Context.S_Box[i]) % 256;

// 交换S盒
v2 = Context.S_Box[i];
Context.S_Box[i] = Context.S_Box[j];
Context.S_Box[j] = v2;

t = (Context.S_Box[i] + Context.S_Box[j]) % 256;
Input[v1] ^= Context.S_Box[t];
}
}

void RC4UpdateString(unsigned char Input[], unsigned int InputLength)
{
unsigned char Key[] = "Key";
RC4Crypt(Input, InputLength, Key, strlen((const char*)Key));
}

int main()
{
char v1[] = "Hello World!";
printf("Before encryption: %s\n", v1);
RC4UpdateString((unsigned char*)v1, strlen(v1));
printf("After encryption: %x\n", v1);
RC4UpdateString((unsigned char*)v1, strlen(v1));
printf("After decryption: %s\n", v1);
return 0;
}

题目TCR0.exe

image-20241020102114533

查一下壳

image-20241020102202897

IDA看一下主要逻辑

image-20241020102242918

直接动调拿出解密之后的flag

调试的时候发现没法调试,应该是有反调试,找main函数里面没有IsDebuggerPresent,猜测可能是使用TlsCallback,

image-20241020102422249

在最下面跳转的语句下一个断点,运行到这里先修改一下ZF标志位

分析一下主函数,v10应该是用户输入的flag,大小为40字节,所以先给input一个40位的输入,过后直接改这40位就好了

v8数组应该是秘钥,这里的密钥加载到内存中,使用小端存储,所以排列起来应该是从右到左,一个字节一个字节来(先转成Hex)

密钥:

1
2
3
71 0b 02 49 73 b4 e1 a3 2f 58 71 85 16 67 b3 ab
1a cf 62 4f 1e 7d 3b 6c 55 58 15 b9 69 36 7e c0
b8 f4 02 42 03 c2 7f 56

image-20241020102723472

main函数的断点下在加解密之前,第24行

image-20241020103305806

查看v10里面存储了40个'a'

image-20241020103345676

使用patch修改这一块的密钥,注意最后一段不要全覆盖掉,只改DWORD64大小的数据

image-20241020103528109

程序过到加密之后,再查看v10

image-20241020103700579

按A键转成字符串就出来了

image-20241020103733505

Flag:NSSCTF{75025d7f6c9f2fbcf746228b4f3d623a}

题目TCR.exe

这个题目没法运行exe,会报错,安装了dll还是会报错,所以直接先查壳再仿写

主要逻辑大概是这样

image-20241020204433963

image-20241020204620373

image-20241020204639565

主要就是上面三段逻辑,密钥、init初始化和加密算法

这是一个魔改的RC4算法,程序有点问题没法动调,所以仿写一下

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
#include <windows.h>
#include <iostream>
#include <stdio.h>
#pragma warning(disable:6273)

using namespace std;
typedef struct _RC4_KEY {
unsigned char S_Box[64];
unsigned char T_Box[64];
} RC4_KEY, * PRC4_KEY;


void RC4Init(PRC4_KEY Context, unsigned char* Key, int KeyLength)
{
int i = 0, j = 0;
unsigned char v1;
if (Context == NULL)
{
return;
}
for (i = 0; i < 64; i++)
{
Context->S_Box[i] = i;
Context->T_Box[i] = Key[i % KeyLength];
}
for (i = 0; i < 64; i++)
{
j = (j + Context->S_Box[i] + Context->T_Box[i]) % 64;
v1 = Context->S_Box[i];
Context->S_Box[i] = Context->S_Box[j];
Context->S_Box[j] = v1;
}
}

void RC4Crypt(unsigned char* Input, unsigned int InputLength, unsigned char* Key, unsigned int KeyLength)
{
int v1 = 0, i = 0, j = 0, t = 0;
unsigned char v2;

RC4_KEY Context;
RC4Init(&Context, Key, KeyLength);

for (v1 = 0; v1 < InputLength; v1++)
{
i = (i + 1) % 64;
j = (j + Context.S_Box[i]) % 64;

v2 = Context.S_Box[i];
Context.S_Box[i] = Context.S_Box[j];
Context.S_Box[j] = v2;

t = (Context.S_Box[i] + Context.S_Box[j]) % 64;
Input[v1] ^= Context.S_Box[t] + Context.S_Box[i];
}
}

void RC4UpdateString(unsigned char Input[], unsigned int InputLength)
{
unsigned char Key[] = "r4nd0m_k5y";
RC4Crypt(Input, InputLength, Key, strlen((const char*)Key));
}

void main()
{
char v1[41] = {
0x18, 0x35, 0x08, 0x59, 0x01, 0x79,
0x2D, 0x7D, 0x3D, 0x4C, 0x17, 0x0A,
0x09, 0x48, 0x6D, 0x18, 0x29, 0x04,
0x53, 0x25, 0x77, 0x29, 0x39, 0x6B,
0x4E, 0x66, 0x60, 0x65, 0x4A, 0x41,
0x6F, 0x27, 0x61, 0x7A, 0x16, 0x14,
0x4c, 0x5a, 0x57, 0x69, 0x00
};
RC4UpdateString((unsigned char*)v1, strlen(v1));
for (int i = 0; i < strlen(v1); i++) {
printf("%c", v1[i]);
}
}

看起来没问题,但是跑不出结果 image-20241020210828563

看一下上面有一个sub_A325

里面有IsDebuggerPresent()

里面有一个对密钥修改的操作:

1
2
ascii('4') xor 0x55		0x34 ^ 0x55 = 0x61 => DEC 97  => 'a'
ascii('0') xor 0x5F 0x30 ^ 0x5F = 0x6F => DEC 111 => '0'

所以,key应该是random_k5y

image-20241020212455435

果然算出了答案,但是发现少了一小段,发现少了四个字符的密钥

image-20241020213530431

发现这一串也是密钥的一部分

加上之后,终于算出了正确答案NSSCTF{4af695c7065c5cf6053271d3e3151a2d}

TEA

TEA(Tiny Encryption Algorithm)是一种对称密钥加密算法,由David Wheeler和Roger Needham在1994年提出。TEA的设计目标是简单、高效,并且在实现上尽量减少代码的复杂性。

TEA加解密

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
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#pragma warning(disable:26450)

#define UI32 uint32_t
#define DELTA 0x9E3779B9
#define ROUND 32

void decrypt(UI32* v, UI32* k)
{
UI32 v0 = v[0], v1 = v[1], sum = DELTA * ROUND, i;
UI32 k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++) {
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= DELTA;
}
v[0] = v0; v[1] = v1;
}

void encrypt(UI32* v, UI32* k)
{
UI32 v0 = v[0], v1 = v[1], sum = 0;
for (int i = 0; i < 32; i++) {
sum += DELTA;
v0 += ((v1 << 4) + k[2]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
v1 += ((v0 << 4) + k[3]) ^ (v0 + sum) ^ ((v0 >> 5) + k[0]);
}
v[0] = v0; v[1] = v1;
}

int main()
{
UI32 k[ y ] = { ...... }; // Key
UI32 v[ x ] = { ...... }; // Data
for (int i = 0; i < (x / 2); i++) // i要小于v[x]的x/2
{
UI32 f[2] = { v[2 * i], v[2 * i + 1] };
decrypt(f, k);
for (int j = 0; j < 2; j++)
{
printf("%c", f[j] & 0xff);
printf("%c", (f[j] >> 8) & 0xff);
printf("%c", (f[j] >> 16) & 0xff);
printf("%c", (f[j] >> 24) & 0xff);
}
}
return 0;
}

题目 SEA0.txt

题目:

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
hint1 = {11,22,33,44} 
hint2 = {0x7c12e17f,0x43c2b691,0xa8d8d6ce,0x6280ecc8,0xe6e18c6e,0x20d6dfa3,0xf2dd40c,0xb819b77e,0xb9258436,0x5d3e88b6}
.text:0000000000401550 ; unsigned int *__fastcall encrypt(unsigned int *, unsigned int *)
.text:0000000000401550 public _Z7encryptPjS_
.text:0000000000401550 _Z7encryptPjS_ proc near ; CODE XREF: main+FC↓p
.text:0000000000401550 ; DATA XREF: .pdata:000000000040A06C↓o
.text:0000000000401550
.text:0000000000401550 var_14= dword ptr -14h
.text:0000000000401550 var_10= dword ptr -10h
.text:0000000000401550 var_C= dword ptr -0Ch
.text:0000000000401550 var_8= dword ptr -8
.text:0000000000401550 var_4= dword ptr -4
.text:0000000000401550 arg_0= qword ptr 10h
.text:0000000000401550 arg_8= qword ptr 18h
.text:0000000000401550
.text:0000000000401550 55 push rbp
.text:0000000000401551 48 89 E5 mov rbp, rsp
.text:0000000000401554 48 83 EC 20 sub rsp, 20h
.text:0000000000401558 48 89 4D 10 mov [rbp+arg_0], rcx
.text:000000000040155C 48 89 55 18 mov [rbp+arg_8], rdx
.text:0000000000401560 C7 45 EC B9 79 37 9E mov [rbp+var_14], 9E3779B9h
.text:0000000000401567 48 8B 45 10 mov rax, [rbp+arg_0]
.text:000000000040156B 8B 00 mov eax, [rax]
.text:000000000040156D 89 45 FC mov [rbp+var_4], eax
.text:0000000000401570 48 8B 45 10 mov rax, [rbp+arg_0]
.text:0000000000401574 8B 40 04 mov eax, [rax+4]
.text:0000000000401577 89 45 F8 mov [rbp+var_8], eax
.text:000000000040157A C7 45 F4 00 00 00 00 mov [rbp+var_C], 0
.text:0000000000401581 C7 45 F0 00 00 00 00 mov [rbp+var_10], 0
.text:0000000000401581
.text:0000000000401588
.text:0000000000401588 loc_401588: ; CODE XREF: encrypt(uint *,uint *)+B2↓j
.text:0000000000401588 83 7D F0 1F cmp [rbp+var_10], 1Fh // DEC = 19
.text:000000000040158C 7F 76 jg short loc_401604
.text:000000000040158C
.text:000000000040158E 8B 45 EC mov eax, [rbp+var_14]
.text:0000000000401591 01 45 F4 add [rbp+var_C], eax
.text:0000000000401594 8B 45 F8 mov eax, [rbp+var_8]
.text:0000000000401597 C1 E0 04 shl eax, 4
.text:000000000040159A 89 C2 mov edx, eax
.text:000000000040159C 48 8B 45 18 mov rax, [rbp+arg_8]
.text:00000000004015A0 8B 00 mov eax, [rax]
.text:00000000004015A2 01 C2 add edx, eax
.text:00000000004015A4 8B 4D F8 mov ecx, [rbp+var_8]
.text:00000000004015A7 8B 45 F4 mov eax, [rbp+var_C]
.text:00000000004015AA 01 C8 add eax, ecx
.text:00000000004015AC 31 C2 xor edx, eax
.text:00000000004015AE 8B 45 F8 mov eax, [rbp+var_8]
.text:00000000004015B1 C1 E8 05 shr eax, 5
.text:00000000004015B4 89 C1 mov ecx, eax
.text:00000000004015B6 48 8B 45 18 mov rax, [rbp+arg_8]
.text:00000000004015BA 48 83 C0 04 add rax, 4
.text:00000000004015BE 8B 00 mov eax, [rax]
.text:00000000004015C0 01 C8 add eax, ecx
.text:00000000004015C2 31 D0 xor eax, edx
.text:00000000004015C4 01 45 FC add [rbp+var_4], eax
.text:00000000004015C7 8B 45 FC mov eax, [rbp+var_4]
.text:00000000004015CA C1 E0 04 shl eax, 4
.text:00000000004015CD 89 C2 mov edx, eax
.text:00000000004015CF 48 8B 45 18 mov rax, [rbp+arg_8]
.text:00000000004015D3 48 83 C0 08 add rax, 8
.text:00000000004015D7 8B 00 mov eax, [rax]
.text:00000000004015D9 01 C2 add edx, eax
.text:00000000004015DB 8B 4D FC mov ecx, [rbp+var_4]
.text:00000000004015DE 8B 45 F4 mov eax, [rbp+var_C]
.text:00000000004015E1 01 C8 add eax, ecx
.text:00000000004015E3 31 C2 xor edx, eax
.text:00000000004015E5 8B 45 FC mov eax, [rbp+var_4]
.text:00000000004015E8 C1 E8 05 shr eax, 5
.text:00000000004015EB 89 C1 mov ecx, eax
.text:00000000004015ED 48 8B 45 18 mov rax, [rbp+arg_8]
.text:00000000004015F1 48 83 C0 0C add rax, 0Ch
.text:00000000004015F5 8B 00 mov eax, [rax]
.text:00000000004015F7 01 C8 add eax, ecx
.text:00000000004015F9 31 D0 xor eax, edx
.text:00000000004015FB 01 45 F8 add [rbp+var_8], eax
.text:00000000004015FE 83 45 F0 01 add [rbp+var_10], 1
.text:0000000000401602 EB 84 jmp short loc_401588
.text:0000000000401602
.text:0000000000401604 ; ---------------------------------------------------------------------------
.text:0000000000401604
.text:0000000000401604 loc_401604: ; CODE XREF: encrypt(uint *,uint *)+3C↑j
.text:0000000000401604 48 8B 45 10 mov rax, [rbp+arg_0]
.text:0000000000401608 8B 55 FC mov edx, [rbp+var_4]
.text:000000000040160B 89 10 mov [rax], edx
.text:000000000040160D 48 8B 45 10 mov rax, [rbp+arg_0]
.text:0000000000401611 48 83 C0 04 add rax, 4
.text:0000000000401615 8B 55 F8 mov edx, [rbp+var_8]
.text:0000000000401618 89 10 mov [rax], edx
.text:000000000040161A 90 nop
.text:000000000040161B 48 83 C4 20 add rsp, 20h
.text:000000000040161F 5D pop rbp
.text:0000000000401620 C3 retn
.text:0000000000401620
.text:0000000000401620 _Z7encryptPjS_ endp

这是一段IDA反编译的汇编代码,Key的长度是4位的一般会想到TEA家族(TEA,XTEA,XXTEA)的加密 再看hint2,是4*10=40位,大概是flag的长度,所以hint2可能是密文,hint1是Key

分析汇编代码:

这是一段完整的TEA加密,发现了mov [rbp+var_14], 9E3779B9h,这里的delta,sub rsp, 20h,这里的20h应该是round = 32

汇编中还有shl eax, 4shr eax, 5,左移4和右移5,判断应该是标准TEA加密。

所以这一段汇编对应的C代码应该就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define UI32 uint32_t
#define DELTA 0x9E3779B9
#define ROUND 32

void decrypt(UI32* v, UI32* k)
{
UI32 v0 = v[0], v1 = v[1], sum = DELTA * ROUND, i;
UI32 k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++) {
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= DELTA;
}
v[0] = v0; v[1] = v1;
}

完整解密:

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 <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#pragma warning(disable:26450)

#define UI32 uint32_t
#define DELTA 0x9E3779B9
#define ROUND 32

void decrypt(UI32* v, UI32* k)
{
UI32 v0 = v[0], v1 = v[1], sum = DELTA * ROUND, i;
UI32 k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++) {
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= DELTA;
}
v[0] = v0; v[1] = v1;
}

int main()
{
UI32 k[4] = { 11,22,33,44 };
UI32 v[12] = { 0x7c12e17f,0x43c2b691,0xa8d8d6ce,0x6280ecc8,
0xe6e18c6e,0x20d6dfa3,0x0f2dd40c,0xb819b77e,
0xb9258436,0x5d3e88b6 };
for (int i = 0; i < 5; i++)
{
UI32 f[2] = { v[2 * i], v[2 * i + 1] };
decrypt(f, k);
for (int j = 0; j < 2; j++)
{
printf("%c", f[j] & 0xff);
printf("%c", (f[j] >> 8) & 0xff);
printf("%c", (f[j] >> 16) & 0xff);
printf("%c", (f[j] >> 24) & 0xff);
}
}
return 0;
}

解密之后的答案为:NSSCTF{5b84a51236d7043fe2480d69d24b37a3}

XTEA

XTEA加解密

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
#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;
}

XTEA是TEA的升级版,增加了更多的密钥表,移位和异或操作等,TEA 算法被发现存在缺陷,作为回应,设计者提出了一个 TEA 的升级版本——XTEA(有时也被称为”tean”)。XTEA 跟 TEA 使用了相同的简单运算,但它采用了截然不同的顺序,为了阻止密钥表攻击,四个子密钥(在加密过程中,原 128 位的密钥被拆分为 4 个 32 位的子密钥)采用了一种不太正规的方式进行混合,但速度更慢了。

XXTEA

特点:Corrected Block TEA,原字符串长度可以不是4的倍数了

XXTEA加解密

XXTEA与TEA类似,都是块加密算法,以下是常规的加密和解密方法

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
#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;
}

题目 sec_cython.so

main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
import sec_cython
flag = input('input your flag:')
m = sec_cython.encrypt(flag)
if m != 'wrong':
m = [hex(x.value) for x in m]
m = [int(x, 16) for x in m]
if m != sec_cython.enc:
print('Wrong!')
else:
print('Correct!')
else:
print('something wrong!')

看来主要的加密是在sec_cython里面的

so文件是Linux相当于windows的dll动态链接库,所以放到kali里面

kali里面写一个python文件

1
2
import sec_cython
help(sec_cython)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Help on module sec_cython:

NAME
sec_cython

FUNCTIONS
encrypt(v)

sea(v)

string_to_uint32_bytes(string)

DATA
DELTA = 2654435769
__test__ = {}
enc = [235096951, 4274635695, 1983592480, 2346500962, 4098093858, 1778...
k = [1935954450, 825721906, 305287507, 875914545]

FILE
/root/桌面/sec_cython.so
1
2
3
4
import sec_cython
print(sec_cython.enc)
print(sec_cython.k)
print(hex(sec_cython.DELTA))
1
2
3
4
5
[235096951, 4274635695, 1983592480, 2346500962, 4098093858, 1778866619, 2372400532, 839025380, 4012226876, 1286419149]

[1935954450, 825721906, 305287507, 875914545]

0x9e3779b9

看到了DELTA = 0x9e3779b9,所以应该也是TEA家族中的一个,第一个是加密字符,第二个是key

IDA分析一下sec_cython.so

在函数表里面直接搜一下之前help中出来的encrypt:

image-20241022095726217

encrypt前面这一大部分是编译so文件时编译上去的,只看结尾这个encrypt就行

找到下面这个东西,名字看起来是SEA,所以进到这个函数里面查看一下

image-20241022095938602

看到这里有些熟悉,v5 = 10,为密钥的长度;v6 = 52,v7 = Divide(v6, v5),这里应该算的就是轮数

image-20241022153144973

image-20241022153621796
image-20241022153652118

以上两段也有非常明显的左移5右移2异或,左移3右移4异或的操作

解密代码:

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
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9E3779B9

void decrypt(uint32_t* v, int n, uint32_t* k) {
int rounds = 6 + (52 / n);
uint32_t sum = rounds * DELTA;
uint32_t y = v[0];
while (rounds > 0) {
uint32_t e = (sum >> 2) & 3;
int p = n - 1;
while (p > 0) {
uint32_t z = v[p - 1];
v[p] -= (((z >> 5 ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z)));
y = v[p];
p--;
}
uint32_t z = v[n - 1];
v[0] -= (((z >> 5 ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z)));
y = v[0];
sum -= DELTA;
rounds--;
}
}

int main() {
uint32_t k[4] = { 0x73645212, 0x31378432, 0x12325153, 0x34356531 };
uint32_t m[10] = { 0xe034b77, 0xfec9c3af, 0x763b3820, 0x8bdcc362,
0xf443f322, 0x6a0759bb, 0x8d67f594, 0x320282e4,
0xef25b93c, 0x4cad32cd };
decrypt(m, sizeof(m) / sizeof(m[0]), k);
for (int i = 0; i < sizeof(m) / sizeof(m[0]); i++) {
for (int j = 0; j < 4; j++) {
putchar(m[i] & 0xff);
m[i] >>= 8;
}
}
return 0;
}

AES

题目 wheel.exe

打开发现这个程序加了upx壳

image-20241102162417886

用upx -d脱一下壳

image-20241102162443548

IDA已经识别出这是一个aes加密

image-20241102162722005