ghctf

题目:Lockesercet

点进主函数,发现逻辑很简单

img

这个init是一个初始化的地方,然后这个decode就是加密的地方了,点进去

img

发现加密逻辑,这里还先给密钥异或了一下,动调查看dword_C543D8的值,然后写出还原密钥的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <bits/stdc++.h>
using namespace std;

int main() {
unsigned char dword_C543D8[8] = { 0x64, 0x96, 0x50, 0x16, 0x69, 0xff, 0xbe, 0x60 };
unsigned char src[] = "IamTheKeyYouKnow";
unsigned char key[18];
for (int i = 0; i < 16; i++) {
key[i] = src[i] ^ dword_C543D8[i % 8];
}
for (int i = 0; i < 16; i++) {
printf("%02X ", key[i]);
}
cout << endl;

return 0;
}

然后这个加密算法当时看了好久,没有想到是什么,然后当我把,这些变量名改了一下时,发现这好像是个类似的tea,当时又不是普通的,然后这里面还有两个嵌套的,所以相当于进行了8轮,写出解密代码(当时逆了好久来着)

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <iostream>
#include <cstring>
using namespace std;

//void decrypt(uint32_t *enc, uint32_t *key) {
// unsigned int v1; // ecx
// unsigned int v2; // eax
// unsigned int v4; // [esp+4h] [ebp-78h]
// unsigned int v5; // [esp+14h] [ebp-68h]
// unsigned int v6; // [esp+18h] [ebp-64h]
// unsigned int v7; // [esp+1Ch] [ebp-60h]
// unsigned int v8; // [esp+20h] [ebp-5Ch]
// unsigned int v9; // [esp+24h] [ebp-58h]
// unsigned int v10; // [esp+28h] [ebp-54h]
// unsigned int v11; // [esp+30h] [ebp-4Ch]
// unsigned int v12; // [esp+38h] [ebp-44h]
// unsigned int v13; // [esp+3Ch] [ebp-40h]
// unsigned int v14; // [esp+40h] [ebp-3Ch]
// unsigned int v15; // [esp+44h] [ebp-38h]
// unsigned int v0; // [esp+48h] [ebp-34h]
// v0 = enc[1];
// v15 = enc[0]+ ((key[1] + (v0 >> 5)) ^ (v0 + 0x5E2377FF) ^ (key[0] + 16 * v0));
// v14 = v0 + ((key[3] + (v15 >> 5)) ^ (v15 + 0x5E2377FF) ^ (key[2] + 16 * v15));
// v13 = v15 + ((key[1] + (v14 >> 5)) ^ (v14 - 0x43B91002) ^ (key[0] + 16 * v14));
// v12 = v14 + ((key[3] + (v13 >> 5)) ^ (v13 - 0x43B91002) ^ (key[2] + 16 * v13));
// v11 = v12+ ((key[3] + ((v13 + ((key[1] + (v12 >> 5)) ^ (v12 + 0x1A6A67FD) ^ (key[0] + 16 * v12))) >> 5)) ^ (v13 + ((key[1] + (v12 >> 5)) ^ (v12 + 0x1A6A67FD) ^ (key[0] + 16 * v12)) + 0x1A6A67FD) ^ (key[2] + 16 * (v13 + ((key[1] + (v12 >> 5)) ^ (v12 + 0x1A6A67FD) ^ (key[0] + 16 * v12)))));
// v0 = v13 + tea1();
// v11 =v12 - ((key[3] + (v13+ (tea1(v12,0x1A6A67FD) >>5)^v ((tea1(v12,0x1A6A67FD))+ 0x1A6A67FD) ^(key[2] + tea1(v13,0x1A6A67FD));
// v1 = v13+ ((key[1] + (v12 >> 5)) ^ (v12 + 0x1A6A67FD) ^ (key[0] + 16 * v12))+ ((key[1] + (v11 >> 5)) ^ (v11 + 0x788DDFFC) ^ (key[0] + 16 * v11));
// v1 = v0+ ((key[1] + (v11 >> 5)) ^ (v11 + 0x788DDFFC) ^ (key[0] + 16 * v11));
// v10 = v11 + ((key[3] + (v1 >> 5)) ^ (v1 + 0x788DDFFC) ^ (key[2] + 16 * v1));
// v9 = v1 + ((key[1] + (v10 >> 5)) ^ (v10 - 0x294EA805) ^ (key[0] + 16 * v10));
// v8 = v10 + ((key[3] + (v9 >> 5)) ^ (v9 - 0x294EA805) ^ (key[2] + 16 * v9));
// v7 = v9 + ((key[1] + (v8 >> 5)) ^ (v8 + 0x34D4CFFA) ^ (key[0] + 16 * v8));
// v6 = v8 + ((key[3] + (v7 >> 5)) ^ (v7 + 0x34D4CFFA) ^ (key[2] + 16 * v7));
// v5 = v7 + ((key[1] + (v6 >> 5)) ^ (v6 - 0x6D07B807) ^ (key[0] + 16 * v6));
// v2 = v6 + ((key[3] + (v5 >> 5)) ^ (v5 - 0x6D07B807) ^ (key[2] + 16 * v5));
// v4 = v2 + ((key[3] + ((v5 + ((key[1] + (v2 >> 5)) ^ (v2 - 0xEE44008) ^ (key[0] + 16 * v2))) >> 5)) ^ (v5 + ((key[1] + (v2 >> 5)) ^ (v2 - 0xEE44008) ^ (key[0] + 16 * v2)) - 0xEE44008) ^ (key[2] + 16 * (v5 + ((key[1] + (v2 >> 5)) ^ (v2 - 0xEE44008) ^ (key[0] + 16 * v2)))));
// enc[0] = (v5 + ((key[1] + (v2 >> 5)) ^ (v2 - 0xEE44008) ^ (key[0] + 16 * v2))) ^ 0xF;
// enc[1] = v4 ^0xf;
//}
uint32_t k[4] = {0x423df72d, 0x05f59a01, 0x633fcf1d, 0x77d19122};
uint32_t k0 = k[0],k1 = k[1],k2 = k[2],k3 = k[3];

int tea1(uint32_t m,uint32_t sum){
m &= 0xffffffff;
return ((k[1] + (m >> 5))) ^ ((m + sum)) ^((k[0] + 16 * m) );

}
int tea2(uint32_t m,uint32_t sum){
m &= 0xffffffff;
return ((k[3] + (m >> 5))) ^ ((m + sum)) ^((k[2] + 16 * m) );

}
void decrpyt(uint32_t *enc){
unsigned int v1; // ecx
unsigned int v2; // eax
unsigned int v4; // [esp+4h] [ebp-78h]
unsigned int v5; // [esp+14h] [ebp-68h]
unsigned int v6; // [esp+18h] [ebp-64h]
unsigned int v7; // [esp+1Ch] [ebp-60h]
unsigned int v8; // [esp+20h] [ebp-5Ch]
unsigned int v9; // [esp+24h] [ebp-58h]
unsigned int v10; // [esp+28h] [ebp-54h]
unsigned int v11; // [esp+30h] [ebp-4Ch]
unsigned int v12; // [esp+38h] [ebp-44h]
unsigned int v13; // [esp+3Ch] [ebp-40h]
unsigned int v14; // [esp+40h] [ebp-3Ch]
unsigned int v15; // [esp+44h] [ebp-38h]
unsigned int v0; // [esp+48h] [ebp-34h]
unsigned int v01;
for(int i=0;i<4;i++){
enc[2*i]^=0xf;
enc[2*i+1]^=0xf;
// v15 = (enc[0] + tea1(enc[0],1579382783))&0xffffffff;
// v14 = (enc[1] + tea2(v15,1579382783))&0xffffffff;
//
// v13 = (v15 + tea1(v14,- 1136201730))&0xffffffff;
// v12 = (v14 + tea2(v13,- 1136201730))&0xffffffff;
// v0 = (v13 + tea1(v12,443181053))&0xffffffff;
// v11 = (v12 + tea2(v0, 443181053))&0xffffffff;
// v1 = (v0 + tea1(v11,2022563836))&0xffffffff;
// v10 = (v11 + tea2(v1,2022563836))&0xffffffff;
// v9 = (v1 + tea1(v10,- 693020677))&0xffffffff;
// v8 = (v10 + tea2(v9,- 693020677))&0xffffffff;
// v7 = (v9 + tea1(v8,886362106))&0xffffffff;
// v6 = (v8 + tea2(v7,886362106))&0xffffffff;
// v5 = (v7 + tea1(v6,- 1829222407))&0xffffffff;
// v2 = (v6 +tea2(v5,- 1829222407))&0xffffffff;
// v01 = (v5 + tea1(v2,- 249839624))&0xffffffff;
// v4 = (v2 + tea2(v01,- 249839624))&0xffffffff;
// enc[0] = v01;
// enc[1] = v4;
v2 = (enc[2*i+1] - tea2(enc[2*i],- 249839624))&0xffffffff;
v5 = (enc[2*i] -tea1(v2,- 249839624))&0xffffffff;
v6 = (v2 - tea2(v5,- 1829222407))&0xffffffff;
v7 =( v5 - tea1(v6,- 1829222407))&0xffffffff;
v8 = (v6 - tea2(v7,886362106))&0xffffffff;
v9 = (v7 - tea1(v8,886362106))&0xffffffff;
v10 =(v8 - tea2(v9,- 693020677))&0xffffffff;
v1 = (v9 - tea1(v10,- 693020677))&0xffffffff;
v11 = (v10 - tea2(v1,2022563836))&0xffffffff;
v0 = (v1 - tea1(v11,2022563836))&0xffffffff;
v12 = (v11 - tea2(v0, 443181053))&0xffffffff;
v13 =( v0 - tea1(v12,443181053))&0xffffffff;
v14 = (v12 - tea2(v13,- 1136201730))&0xffffffff;
v15 = (v13 - tea1(v14,- 1136201730))&0xffffffff;
v01= (v14 - tea2(v15,1579382783))&0xffffffff;
enc[2*i] = (v15 - tea1(v01,1579382783))&0xffffffff;
enc[i*2+1] = v01;
}


}

int main() {
uint32_t enc[10] = {0x031e45dc, 0x2776e989, 0x01234847, 0x64ced270, 0x33467fda, 0xa34903b1,
0x2cd10027, 0x75bdb337};
decrpyt(enc);
for (int i = 0; i <= 6; i++) {
cout << enc[i] << " ";
}
cout << endl;
for (int i = 0; i < 8; i++) {
cout << (char)(enc[i] & 0xFF) << (char)((enc[i] >> 8) & 0xFF) << (char)((enc[i] >> 16) & 0xFF) << (char)((enc[i] >> 24) & 0xFF);
}
return 0;
}

得到flagNSSCTF{!!!Y0u_g3t_th3_s3cr3t!!!}

题目:Mio?Ryo?Soyo?

下载出来发现是py打包的,当时不知道为啥py解包解不出来,然后就去找了个在线网站在线去解包

https://pyinstxtractor-web.netlify.app/

img

找到一样的名字的program的pyc文件在线反编译

1
2
3
4
5
6
7
8
9
10
from Secret import *
if __name__ == '__main__':
print('输入:', '', **('end',))
aaaaaaaaaaaaa = input()
wwwwwwwwwww = l(aaaaaaaaaaaaa)
if sssssssssssss == wwwwwwwwwww.encode():
print('哦,对的。')
else:
print('哎,并非。')
input()

发现有个secret,于是再去库里面找到secret文件去反编译

img

得到

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
from SecretEncrypt import *
sssssssssssss = bytes([
57,
118,
33,
114,
68,
56,
117,
115,
34,
52,
52,
95,
78,
40,
49,
59,
95,
85,
63,
122,
54,
33,
77,
110,
49,
54,
34,
109,
106,
122,
60,
92,
108,
91,
61,
51,
42,
62,
35,
38,
52,
67,
62,
122,
116,
48,
76,
50,
67,
51,
59,
41,
122,
45,
45,
51,
90])

def l(_ = None):
return SSSooooyyooo(MMMMiiiiiio.MMMMiiooooooo(SSSooooyyooo(RRRRyyooo.RRRRRRRyyyyooooo(_.encode()), 7).encode()), 9)

于是类似再去找到secretencrpyt加密的文件

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
import math

class MMMMiiiiiio:
MMiiiiiiooo = ''.join((lambda .0: [ chr(Miiooooooooo) for Miiooooooooo in .0 ])(range(33, 118)))


def MMMMiiooooooo(MMMMMMMMMiiiooo = None):
MMMMiiiiioooooooooo = ''
MMMMMMMiiiiioo = (4 - len(MMMMMMMMMiiiooo) % 4) % 4
MMMMMMMMMiiiooo += b'\x00' * MMMMMMMiiiiioo
for MMMMMMiiiiiio in range(0, len(MMMMMMMMMiiiooo), 4):
MMMMiiiiiiooooo = MMMMMMMMMiiiooo[MMMMMMiiiiiio:MMMMMMiiiiiio + 4]
MMMMMMiiioooooo = int.from_bytes(MMMMiiiiiiooooo, 'big')
MMMMMMMiiooooooooo = ''
for _ in range(5):
MMMMMMMiiooooooooo = MMMMiiiiiio.MMiiiiiiooo[MMMMMMiiioooooo % 85] + MMMMMMMiiooooooooo
MMMMMMiiioooooo //= 85
MMMMiiiiioooooooooo += MMMMMMMiiooooooooo
if MMMMMMMiiiiioo:
MMMMiiiiioooooooooo = MMMMiiiiioooooooooo[:-MMMMMMMiiiiioo]
return MMMMiiiiioooooooooo

MMMMiiooooooo = None(MMMMiiooooooo)


class RRRRyyooo:
RRRRyooooooo = ''.join((lambda .0: [ chr(RRRRRRRyyyyyoooo) for RRRRRRRyyyyyoooo in .0 ])(range(48, 93)))


def RRRRRRRyyyyooooo(RRRRRRyyyoooooo = None):
RRRRyyyyyooo = []
RRyyyyyyyyyoooooo = 0
if RRyyyyyyyyyoooooo < len(RRRRRRyyyoooooo):
if RRyyyyyyyyyoooooo + 1 < len(RRRRRRyyyoooooo):
RRRRRRRRRyyo = RRRRRRyyyoooooo[RRyyyyyyyyyoooooo] << 8 | RRRRRRyyyoooooo[RRyyyyyyyyyoooooo + 1]
RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45])
RRRRRRRRRyyo //= 45
RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45])
RRRRRRRRRyyo //= 45
RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo])
RRyyyyyyyyyoooooo += 2
continue
RRRRRRRRRyyo = RRRRRRyyyoooooo[RRyyyyyyyyyoooooo]
RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45])
RRRRRRRRRyyo //= 45
RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo])
RRyyyyyyyyyoooooo += 1
continue
return ''.join(RRRRyyyyyooo)

RRRRRRRyyyyooooo = None(RRRRRRRyyyyooooo)

def SSSooooyyooo(SSSSooyoooooo, SSSSSoyyooooo):
SSoooooyyyyyyoo = []
for SSSSSSSSSoyooo in SSSSooyoooooo:
if SSSSSSSSSoyooo <= SSSSSSSSSoyooo or SSSSSSSSSoyooo <= 'z':
pass
else:
'a'
SSSSoooyooooooo = ((ord(SSSSSSSSSoyooo) - ord('a')) + SSSSSoyyooooo) % 26
SSoooooyyyyyyoo.append(chr(ord('a') + SSSSoooyooooooo))
continue
if SSSSSSSSSoyooo <= SSSSSSSSSoyooo or SSSSSSSSSoyooo <= '9':
pass
else:
'0'
SSSSoooyooooooo = (ord(SSSSSSSSSoyooo) - ord('0') - SSSSSoyyooooo) % 10
SSoooooyyyyyyoo.append(chr(ord('0') + SSSSoooyooooooo))
continue
SSoooooyyyyyyoo.append(SSSSSSSSSoyooo)
continue
return ''.join(SSoooooyyyyyyoo)

这里应该就是真正加密逻辑的地方了,根据secret的文件发现加密逻辑是先执行自定义的base45,再去执行凯撒偏移量为7的加密,再去base85编码,然后再执行凯撒偏移量为9的加密,于是得到4个加密逻辑

先进行凯撒解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def kaisa(s, shift):
result = []
for c in s:
if "a" <= c <= "z":
ccc = chr((ord(c) - ord("a") - shift) % 26 + ord("a"))
elif "0" <= c <= "9":
ccc = chr((ord(c) - ord("0") + shift) % 10 + ord("0"))
else:
ccc = c
result.append(ccc)
return "".join(result)
encrypted_date = "9v!rD8us\"44_N(1;_U?z6!Mn16\"mjz<\\l[=3*>#&4C>zt0L2C3;)z--3Z"
shift = 9
decrypted_date = kaisa(encrypted_date, shift)
print(decrypted_date)

再拿去base85解码

img

然后再拿去凯撒进行偏移量为7的解码得到

JX9NG:CM:KJ?S7=:>?NC>K2<V96Z2<Y:6C=;LA8RQ6G:4

发现base45是自定义的,据ai分析是讲三个字节分成啥啥来着的

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
class RRRRyyooo:
RRRRyooooooo = ''.join(chr(i) for i in range(48, 93))

@staticmethod
def base45_decode(encoded_str):
n = len(encoded_str)
decoded_bytes = []
i = 0
while i < n:
if i + 3 <= n:
c0, c1, c2 = encoded_str[i: i + 3]
value = (
RRRRyyooo.RRRRyooooooo.index(c0)
+ RRRRyyooo.RRRRyooooooo.index(c1) * 45
+ RRRRyyooo.RRRRyooooooo.index(c2) * 45 * 45
)
decoded_bytes.extend(value.to_bytes(2, "big"))
i += 3
elif i + 2 <= n:
c0, c1 = encoded_str[i: i + 2]
value = RRRRyyooo.RRRRyooooooo.index(c0) + RRRRyyooo.RRRRyooooooo.index(c1) * 45
decoded_bytes.extend(value.to_bytes(1, "big"))
i += 2
else:
raise ValueError("Invalid encoded string length")
return bytes(decoded_bytes)

# 给定的密文
encoded_str = 'JX9NG:CM:KJ?S7=:>?NC>K2<V96Z2<Y:6C=;LA8RQ6G:4'

# 使用 RRRRyyooo.base45_decode 进行解码
decoded_bytes = RRRRyyooo.base45_decode(encoded_str)

# 将字节转换为字符串
try:
decoded_str = decoded_bytes.decode("utf-8")
except UnicodeDecodeError:
decoded_str = decoded_bytes.decode("latin-1")

print(decoded_str)

得到flagNSSCTF{Th3y’r3_a11_p1aY_Ba5e!}

题目:TimeSpaceTravle

img

打开主函数,发现有两个重要的函数调用,先点进去第一个看看

img

第一个函数并不是将我们输入的input调用进去,而是创立了一个src并初始化为0,然后再传进去,发现这里有一堆反调的东西,然后还有个获取时间的api函数,然后再后面有个return 39,就是极其不正常,然后追踪过去

img

发现这种 就是push ebp 然后一堆加减数操作的,然后pop ebp ,然后具体了解就是,这里不是有个cmp比较的指令嘛,这里比较的是q ,你再去计算一下会发现,这里第一次是不可能成立的,但是ida又不会2次分析,所以这里的jz 跳转就不会跳到正确地址,解决办法就是,把push 和pop 这一段代码nop掉就可以了,下面还有两个差不多的花,然后再去动调一下就得到了下面修正后的函数

img

然后这个tm是个结构体,具体实现如下

img

1
2
3
4
5
6
7
8
9
10
11
12
struct tm
{
int tm_sec; /*秒,正常范围0-59, 但允许至61*/
int tm_min; /*分钟,0-59*/
int tm_hour; /*小时, 0-23*/
int tm_mday; /*日,即一个月中的第几天,1-31*/
int tm_mon; /*月, 从一月算起,0-11*/
int tm_year; /*年, 从1900至今已经多少年*/
int tm_wday; /*星期,一周中的第几天, 从星期日算起,0-6*/
int tm_yday; /*从今年1月1日到目前的天数,范围0-365*/
int tm_isdst;/*日光节约时间的旗标*/
};

就是将结构体中的年月日赋值给src,然后src再去md5加密,加密之后再去与0x14和0x11异或得到key

img

找到第2个函数发现是个aes,不过魔改了一下,但是还好幸好不是魔改的字节替换这种的,只是将密文和密钥修改了一下,所以我们得到逻辑就是,key被md和异或完之后来当作aes的密钥用来加密,但是这里不能动调,也得不到当时的年月日,所以就只能爆破,然后根据题目提示是从是从2024年到2025年,GitHub上面趴了md5的加密函数,和aes的加解密函数下来,然后看着wp勉强写出代码

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
#include <iostream>    
using namespace std;
#include <bits/stdc++.h>
// Constants are the integer part of the sines of integers (in radians) * 2^32.
const uint32_t k[64] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee ,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 ,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be ,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 ,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa ,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 ,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed ,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a ,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c ,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 ,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 ,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 ,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 ,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 ,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 ,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 };

// r specifies the per-round shift amounts
const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};

// leftrotate function definition
#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c))))

void to_bytes(uint32_t val, uint8_t *bytes)
{
bytes[0] = (uint8_t) val;
bytes[1] = (uint8_t) (val >> 8);
bytes[2] = (uint8_t) (val >> 16);
bytes[3] = (uint8_t) (val >> 24);
}

uint32_t to_int32(const uint8_t *bytes)
{
return (uint32_t) bytes[0]
| ((uint32_t) bytes[1] << 8)
| ((uint32_t) bytes[2] << 16)
| ((uint32_t) bytes[3] << 24);
}

void md5(const uint8_t *initial_msg, size_t initial_len, uint8_t *digest) {

// These vars will contain the hash
uint32_t h0, h1, h2, h3;

// Message (to prepare)
uint8_t *msg = NULL;

size_t new_len, offset;
uint32_t w[16];
uint32_t a, b, c, d, i, f, g, temp;

// Initialize variables - simple count in nibbles:
h0 = 0x67452301;
h1 = 0xefcdab89;
h2 = 0x98badcfe;
h3 = 0x10325476;

//Pre-processing:
//append "1" bit to message
//append "0" bits until message length in bits ≡ 448 (mod 512)
//append length mod (2^64) to message

for (new_len = initial_len + 1; new_len % (512/8) != 448/8; new_len++)
;

msg = (uint8_t*)malloc(new_len + 8);
memcpy(msg, initial_msg, initial_len);
msg[initial_len] = 0x80; // append the "1" bit; most significant bit is "first"
for (offset = initial_len + 1; offset < new_len; offset++)
msg[offset] = 0; // append "0" bits

// append the len in bits at the end of the buffer.
to_bytes(initial_len*8, msg + new_len);
// initial_len>>29 == initial_len*8>>32, but avoids overflow.
to_bytes(initial_len>>29, msg + new_len + 4);

// Process the message in successive 512-bit chunks:
//for each 512-bit chunk of message:
for(offset=0; offset<new_len; offset += (512/8)) {

// break chunk into sixteen 32-bit words w[j], 0 ≤ j ≤ 15
for (i = 0; i < 16; i++)
w[i] = to_int32(msg + offset + i*4);

// Initialize hash value for this chunk:
a = h0;
b = h1;
c = h2;
d = h3;

// Main loop:
for(i = 0; i<64; i++) {

if (i < 16) {
f = (b & c) | ((~b) & d);
g = i;
} else if (i < 32) {
f = (d & b) | ((~d) & c);
g = (5*i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
g = (3*i + 5) % 16;
} else {
f = c ^ (b | (~d));
g = (7*i) % 16;
}

temp = d;
d = c;
c = b;
b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]);
a = temp;

}

// Add this chunk's hash to result so far:
h0 += a;
h1 += b;
h2 += c;
h3 += d;

}

// cleanup
free(msg);

//var char digest[16] := h0 append h1 append h2 append h3 //(Output is in little-endian)
to_bytes(h0, digest);
to_bytes(h1, digest + 4);
to_bytes(h2, digest + 8);
to_bytes(h3, digest + 12);
}


typedef struct{
uint32_t eK[44], dK[44]; // encKey, decKey
int Nr; // 10 rounds
}AesKey;

#define BLOCKSIZE 16 //AES-128分组长度为16字节

// uint8_t y[4] -> uint32_t x
#define LOAD32H(x, y) \
do { (x) = ((uint32_t)((y)[0] & 0xff)<<24) | ((uint32_t)((y)[1] & 0xff)<<16) | \
((uint32_t)((y)[2] & 0xff)<<8) | ((uint32_t)((y)[3] & 0xff));} while(0)

// uint32_t x -> uint8_t y[4]
#define STORE32H(x, y) \
do { (y)[0] = (uint8_t)(((x)>>24) & 0xff); (y)[1] = (uint8_t)(((x)>>16) & 0xff); \
(y)[2] = (uint8_t)(((x)>>8) & 0xff); (y)[3] = (uint8_t)((x) & 0xff); } while(0)

// 从uint32_t x中提取从低位开始的第n个字节
#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)

/* used for keyExpansion */
// 字节替换然后循环左移1位
#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^ \
((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))

// uint32_t x循环左移n位
#define ROF32(x, n) (((x) << (n)) | ((x) >> (32-(n))))
// uint32_t x循环右移n位
#define ROR32(x, n) (((x) >> (n)) | ((x) << (32-(n))))

/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
// AES-128轮常量
static const uint32_t rcon[10] = {
0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};
// S盒
unsigned char S[256] = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

//逆S盒
unsigned char inv_S[256] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};

/* copy in[16] to state[4][4] */
int loadStateArray(uint8_t (*state)[4], const uint8_t *in) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[j][i] = *in++;
}
}
return 0;
}

/* copy state[4][4] to out[16] */
int storeStateArray(uint8_t (*state)[4], uint8_t *out) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
*out++ = state[j][i];
}
}
return 0;
}
//秘钥扩展
int keyExpansion(const uint8_t *key, uint32_t keyLen, AesKey *aesKey) {

if (NULL == key || NULL == aesKey){
printf("keyExpansion param is NULL\n");
return -1;
}

if (keyLen != 16){
printf("keyExpansion keyLen = %d, Not support.\n", keyLen);
return -1;
}

uint32_t *w = aesKey->eK; //加密秘钥
uint32_t *v = aesKey->dK; //解密秘钥

/* keyLen is 16 Bytes, generate uint32_t W[44]. */

/* W[0-3] */
for (int i = 0; i < 4; ++i) {
LOAD32H(w[i], key + 4*i);
}

/* W[4-43] */
for (int i = 0; i < 10; ++i) {
w[4] = w[0] ^ MIX(w[3]) ^ rcon[i];
w[5] = w[1] ^ w[4];
w[6] = w[2] ^ w[5];
w[7] = w[3] ^ w[6];
w += 4;
}

w = aesKey->eK+44 - 4;
//解密秘钥矩阵为加密秘钥矩阵的倒序,方便使用,把ek的11个矩阵倒序排列分配给dk作为解密秘钥
//即dk[0-3]=ek[41-44], dk[4-7]=ek[37-40]... dk[41-44]=ek[0-3]
for (int j = 0; j < 11; ++j) {

for (int i = 0; i < 4; ++i) {
v[i] = w[i];
}
w -= 4;
v += 4;
}

return 0;
}

// 轮秘钥加
int addRoundKey(uint8_t (*state)[4], const uint32_t *key) {
uint8_t k[4][4];

/* i: row, j: col */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
k[i][j] = (uint8_t) BYTE(key[j], 3 - i); /* 把 uint32 key[4] 先转换为矩阵 uint8 k[4][4] */
state[i][j] ^= k[i][j];
}
}

return 0;
}

//字节替换
int subBytes(uint8_t (*state)[4]) {
/* i: row, j: col */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[i][j] = S[state[i][j]]; //直接使用原始字节作为S盒数据下标
}
}

return 0;
}

//逆字节替换
int invSubBytes(uint8_t (*state)[4]) {
/* i: row, j: col */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[i][j] = inv_S[state[i][j]];
}
}
return 0;
}

//行移位
int shiftRows(uint8_t (*state)[4]) {
uint32_t block[4] = {0};

/* i: row */
for (int i = 0; i < 4; ++i) {
//便于行循环移位,先把一行4字节拼成uint_32结构,移位后再转成独立的4个字节uint8_t
LOAD32H(block[i], state[i]);
block[i] = ROF32(block[i], 8*i);
STORE32H(block[i], state[i]);
}

return 0;
}

//逆行移位
int invShiftRows(uint8_t (*state)[4]) {
uint32_t block[4] = {0};

/* i: row */
for (int i = 0; i < 4; ++i) {
LOAD32H(block[i], state[i]);
block[i] = ROR32(block[i], 8*i);
STORE32H(block[i], state[i]);
}

return 0;
}

/* Galois Field (256) Multiplication of two Bytes */
// 两字节的伽罗华域乘法运算
uint8_t GMul(uint8_t u, uint8_t v) {
uint8_t p = 0;

for (int i = 0; i < 8; ++i) {
if (u & 0x01) { //
p ^= v;
}

int flag = (v & 0x80);
v <<= 1;
if (flag) {
v ^= 0x1B; /* x^8 + x^4 + x^3 + x + 1 */
}

u >>= 1;
}

return p;
}

// 列混合
int mixColumns(uint8_t (*state)[4]) {
uint8_t tmp[4][4];
uint8_t M[4][4] = {{0x02, 0x03, 0x01, 0x01},
{0x01, 0x02, 0x03, 0x01},
{0x01, 0x01, 0x02, 0x03},
{0x03, 0x01, 0x01, 0x02}};

/* copy state[4][4] to tmp[4][4] */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j){
tmp[i][j] = state[i][j];
}
}

for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) { //伽罗华域加法和乘法
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
}
}

return 0;
}

// 逆列混合
int invMixColumns(uint8_t (*state)[4]) {
uint8_t tmp[4][4];
uint8_t M[4][4] = {{0x0E, 0x0B, 0x0D, 0x09},
{0x09, 0x0E, 0x0B, 0x0D},
{0x0D, 0x09, 0x0E, 0x0B},
{0x0B, 0x0D, 0x09, 0x0E}}; //使用列混合矩阵的逆矩阵

/* copy state[4][4] to tmp[4][4] */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j){
tmp[i][j] = state[i][j];
}
}

for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
}
}

return 0;
}
// AES-128加密接口,输入key应为16字节长度,输入长度应该是16字节整倍数,
// 这样输出长度与输入长度相同,函数调用外部为输出数据分配内存
void key_and_enc(uint8_t*enc){
for (unsigned int i = 0; i < 0x10; i += 2) {
unsigned char temp = enc[i]^0x5;
enc[i] = enc[i + 1]^0x5;
enc[i + 1] = temp;
}
}
void reverse_sub_291030(uint8_t* flag) {
unsigned int i; // [esp+0h] [ebp-Ch]
unsigned int i_1; // [esp+4h] [ebp-8h]
char v3; // [esp+Bh] [ebp-1h]

i_1 = 0;
for ( i = 15; i_1 < i; --i )
{
v3 = flag[i_1] ^ 0xF;
flag[i_1] = flag[i] ^ 0xF;
flag[i] = v3;
++i_1;
}
}
int aesEncrypt(const uint8_t *key, uint32_t keyLen, uint8_t *pt, uint8_t *ct, uint32_t len) {

AesKey aesKey;
uint8_t *pos = ct;
const uint32_t *rk = aesKey.eK; //解密秘钥指针
uint8_t out[BLOCKSIZE] = {0};
uint8_t actualKey[16] = {0};
uint8_t state[4][4] = {0};

if (NULL == key || NULL == pt || NULL == ct){
printf("param err.\n");
return -1;
}

if (keyLen > 16){
printf("keyLen must be 16.\n");
return -1;
}

if (len % BLOCKSIZE){
printf("inLen is invalid.\n");
return -1;
}
for (int j = 0; j < 16; j++) {
printf("%X ", pt[j]);
}
printf("\n");
memcpy(actualKey, key, keyLen);

keyExpansion(actualKey, 16, &aesKey); // 秘钥扩展

// 使用ECB模式循环加密多个分组长度的数据
for (int i = 0; i < len; i += BLOCKSIZE) {
// 把16字节的明文转换为4x4状态矩阵来进行处理


for (int j = 0; j < 16; j++) {
printf("%X ", pt[j]);
}
printf("\n");
loadStateArray(state, pt);
// 轮秘钥加
addRoundKey(state, rk);

for (int j = 1; j < 10; ++j) {
rk += 4;
subBytes(state); // 字节替换
shiftRows(state); // 行移位
mixColumns(state); // 列混合
addRoundKey(state, rk); // 轮秘钥加
}



subBytes(state); // 字节替换

shiftRows(state); // 行移位
// 此处不进行列混合

addRoundKey(state, rk+4); // 轮秘钥加

// 把4x4状态矩阵转换为uint8_t一维数组输出保存
storeStateArray(state, pos);

pos += BLOCKSIZE; // 加密数据内存指针移动到下一个分组
pt += BLOCKSIZE; // 明文数据指针移动到下一个分组
rk = aesKey.eK; // 恢复rk指针到秘钥初始位置
}
return 0;
}

// AES128解密, 参数要求同加密
int aesDecrypt(const uint8_t *key, uint32_t keyLen, uint8_t *ct, uint8_t *pt, uint32_t len) {
AesKey aesKey;
uint8_t *pos = pt;
const uint32_t *rk = aesKey.dK; //解密秘钥指针
uint8_t out[BLOCKSIZE] = {0};
uint8_t actualKey[16] = {0};
uint8_t state[4][4] = {0};

if (NULL == key || NULL == ct || NULL == pt){
printf("param err.\n");
return -1;
}

if (keyLen > 16){
printf("keyLen must be 16.\n");
return -1;
}

if (len % BLOCKSIZE){
printf("inLen is invalid.\n");
return -1;
}

memcpy(actualKey, key, keyLen);
key_and_enc(actualKey);
keyExpansion(actualKey, 16, &aesKey); //秘钥扩展,同加密

for (int i = 0; i < len; i += BLOCKSIZE) {
key_and_enc(ct);
// 把16字节的密文转换为4x4状态矩阵来进行处理
loadStateArray(state, ct);
// 轮秘钥加,同加密
addRoundKey(state, rk);

for (int j = 1; j < 10; ++j) {
rk += 4;
invShiftRows(state); // 逆行移位
invSubBytes(state); // 逆字节替换,这两步顺序可以颠倒
addRoundKey(state, rk); // 轮秘钥加,同加密
invMixColumns(state); // 逆列混合
}

invSubBytes(state); // 逆字节替换
invShiftRows(state); // 逆行移位
// 此处没有逆列混合
addRoundKey(state, rk+4); // 轮秘钥加,同加密


storeStateArray(state, pos); // 保存明文数据
reverse_sub_291030(pos);
pos += BLOCKSIZE; // 输出数据内存指针移位分组长度
ct += BLOCKSIZE; // 输入数据内存指针移位分组长度
rk = aesKey.dK; // 恢复rk指针到秘钥初始位置
}
return 0;
}
int main()
{
uint8_t enc[35]={0xCD, 0x16, 0xDB, 0xB5, 0xD1, 0x02, 0xA4, 0x82, 0x8E, 0x59,
0x73, 0x9E, 0x96, 0x26, 0x56, 0xF2, 0x16, 0x8E, 0x46, 0xF2,
0x55, 0x7B, 0x92, 0x31, 0x30, 0xDC, 0xAA, 0x8A, 0xF3, 0x1C,
0xA0, 0xAA};
unsigned char c[32];
for(int month=0;month<12;month++){
for(int day=1;day<=31;day++){
uint8_t key[16]={0};
int timeData[]{day,month,2024-1900};
md5((uint8_t*)timeData,12,key);
for(int i=0;i<16;i++){
key[i]=(key[i]^0x14)%256;
}
for(int i=0;i<16;i++){
key[i]=(key[i]^0x11)%256;
}
aesDecrypt(key,16,enc,c,32);//这个c是解出来的明文
// for(int i=0;i<32;i++){
// printf("%c",*((char *)c+i)&0xff);
// }
// cout<<endl;
if (c[0] == 'N'
&& c[1] == 'S'
&& c[2] == 'S')
{
printf("%.32s\n", c);
break;
}
}
}
return 0;
}
//NSSCTF{W0w_Y0u're_@n_AE5_M@5t3r}

题目:Room 0

看这题之前先来了解一下这个异常的基础知识

异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。

异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw

  • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
  • catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
  • try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。

如果有一个块抛出一个异常,捕获异常的方法会使用 trycatch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
try
{
// 保护代码
}catch( ExceptionName e1 )
{
// catch 块
}catch( ExceptionName e2 )
{
// catch 块
}catch( ExceptionName eN )
{
// catch 块
}

如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常。

抛出异常

您可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。

捕获异常

catch 块跟在 try 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。

上面的代码会捕获一个类型为 ExceptionName 的异常。如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 …

实例

下面是一个实例,抛出一个除以零的异常,并在 catch 块中捕获该异常。

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
#include <iostream>
using namespace std;

double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}

int main ()
{
int x = 50;
int y = 0;
double z = 0;

try {
z = division(x, y);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}

return 0;
}

当程序运行时,会捕获到除0异常,从而抛出Division by zero condition!

好那么现在在返回这道题,当时比赛时是没看这道题的,后来是给了提示有除0异常和smc,了解题目考点之后,来看这道题

img

第一眼看到这个题emmm,密钥在判断的时候有了,然后密文也有了,又是个rc4,去写脚本果然得到一个fake,既然题目说了提示是div除0异常和smc了,但是这里有没有smc,如果又能反编译,那一定是藏在某个地方了

然后动调打断点,进入汇编代码

img

1
2
loc_40286A:
; __except(loc_402864) // owned by 402709

表示 结构化异常处理 (SEH, Structured Exception Handling),也就是 Windows 下用于异常捕获的机制。

__except(loc_402864) 指明了异常处理函数loc_402864 处。

// owned by 402709 说明该异常处理代码属于某个函数/代码块(地址 0x402709****)

知道catch代码的地方了,但是为啥在反汇编里面没有显示出来呢,于是再一步分析

img

看见loc_402864这里

1
2
3
4
loc_402864:                             ; DATA XREF: .rdata:stru_404668↓o
.text:00402864 ; __except filter // owned by 402709
.text:00402864 mov eax, 1
.text:00402869 retn

这个代码段是一个异常过滤器(exception filter),用于**__except 结构中的过滤子句**(filter expression)。在 Windows 的结构化异常处理(SEH)中,__except 语句用于捕获异常并进行相应的处理。

而且这个异常过滤器的作用是总是返回 1,这意味着当 __try 代码块中抛出异常时,异常总是会被捕获并进入 __except 代码块执行异常处理

也就是说明进入异常处理时,总会进入loc_402864 catch代码这里,而不会进入下面那段代码,于是我们把这里nop掉

但是看到这个汇编第2个call指令,loc_402410,它是有花的,所以我们进入到这个函数

img

img

跟之前一样的花,nop掉就好

img

处理完之后就会发现下面有三个函数,第一个就是将16进制转化为整数的函数

img

第2个就是smc的加密段,就是将这些地址与key异或,是将.enc段加密,这个key既然不是判断的这个0x11451419,那是什么呢,这就要说到smc特性了,由于我们已经知道这段加密方式,就是每4个一个循环,去异或,于是

img

由于开头基本上都是push ebp mov ebp esp ,对应的操作数55 8b ec,

img

所以我们可以去异或一下,可以得到前三个密钥,这样爆破范围就大大减小了

img

得到前三个密钥,由于只有触发了除0异常,才会进入catch段代码,然后调用那个函数对enc段当作密钥传进去,来进行加密,因此我们可以根据这个来写爆破代码

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
#include <iostream>    
using namespace std;
#include <bits/stdc++.h>
#define BYTE1(dw) ((uint8_t)(((dw) >> 8) & 0xFF))
#define BYTE2(dw) ((uint8_t)(((dw) >> 16) & 0xFF))
#define HIBYTE(dw) ((uint8_t)(((dw) >> 24) & 0xFF))
int __cdecl sub_402000(int v2)
{
// int v2; // [esp+0h] [ebp-1Ch]
int i; // [esp+4h] [ebp-18h]
int v4; // [esp+8h] [ebp-14h]
int v5; // [esp+Ch] [ebp-10h]
int v6; // [esp+10h] [ebp-Ch]
int v7; // [esp+10h] [ebp-Ch]
unsigned int v8; // [esp+14h] [ebp-8h]
int v9; // [esp+18h] [ebp-4h]

// v2 = sub_402590(Str);
// if ( !v2 )
// return 0;
v6 = 0;
v9 = v2;
v8 = HIBYTE(v2);
v5 = BYTE2(v2);
v4 = BYTE1(v2);
for ( i = 0; i < 32; ++i )
{
v7 = v6 * (v8 + 1415881080) * (v9 - 1467486175) * ((v8 - v9) ^ (v8 >> 4));
v5 = (v9 + v5) ^ (8 * v4);
v4 = (v9 + v8) ^ (8 * v5);
v8 = (v9 + v4) ^ (8 * v5);
v9 -= v4 + v5 + v8;
if (v9 == 1415881080)
{
cout<<"key:"<<hex<<v2;
return 0;
}
v6 = v7 + (v8 + 1467486175) * (((v8 - v9) ^ (v8 >> 4)) / (v9 - 1415881080));
}
return v9 ^ v6;
}
int main(){
for(int i=0x755ff000;i<0x755ff0ff;i++){
sub_402000(i);
}
return 0;
}

得到密钥755ff0d3

这时候就可以来动调了,然后来解这个smc,你可以用idapython,也可以取动调,我就采用了动调

img

然后断点,步入之后

img

如果只运行到打的断点这里是看不见密钥的,得到了那个判断函数才找到密钥

img

然后动调得到flag

img

这个时候脚本我也去写了

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
#include <iostream>    
using namespace std;
#include <bits/stdc++.h>

void encrypt_init(unsigned char *key, int key_len, unsigned char *S) {
unsigned char tmp;
int v6 = 0;
unsigned char BianKey[256];
for (int i = 0; i < 256; i++) {
S[i] = i;
}
for (int k = 0; k < 256; ++k )
BianKey[k] = *(key + k % 8);
for (int i = 0; i < 256; i++) {
v6 = ( BianKey[i] + S[i] + v6) % 256;
tmp = S[i];
S[i] = S[v6];
S[v6] = tmp;
}
}
void encrypt_decrypt(unsigned char *data, int data_len, unsigned char *S,unsigned char key[]) {
unsigned char K;
int i = 0;
int j = 0;

for (int k = 0; k < data_len; k++) {
i = (i + 1) % 256;
j = (j + S[i]) % 256;
unsigned char tmp = S[i];
S[i] = S[j];
S[j] = tmp;
K = S[(unsigned char)(S[i] + S[j])%256];
data[k]^= K^ key[i % 8];
}
}
int main() {

uint8_t key[] = {0xD4, 0x35, 0x6D, 0xF8, 0xF8, 0x6D, 0x35, 0xD4, 0x00};

int key_len = strlen((const char *)key);
unsigned char encrypted_data[] = {0x22, 0xC4, 0xA0, 0x5A, 0xDE, 0xED, 0x62, 0x5E, 0x25, 0xE2,
0x6D, 0xA6, 0x05, 0xA7, 0x20, 0x8D, 0x7D, 0x99, 0x52, 0x3E,
0x8C, 0xA7, 0x7F, 0xFA, 0x09, 0xD8, 0x62, 0xDB, 0x00, 0x80,
0xC2, 0xA9};
// int data_len =strlen((const char *)encrypted_data);

int data_len =32;
unsigned char S[256];

encrypt_init(key, key_len, S);

encrypt_decrypt(encrypted_data, data_len, S,key);

cout << "Decrypted data: ";
for (int i = 0; i < data_len; i++) {
cout << encrypted_data[i];
}
cout << endl;
return 0;
}
//NSSCTF{Int3r3st1ng_5MC_Pr0gr@m?}

题目:Canon

打开主函数

img

img

这么来看就是vm了,不过嵌套了两层,且把flag分成了三份

点进去decode也是更逆天

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
switch ( a3 )
{
case 1: // 凯撒
for ( i = 0; i < enc_len; ++i )
{
v22 = old[i % key_len];
if ( Challenge[i] < 65 || Challenge[i] > 90 )
{
if ( Challenge[i] < 97 || Challenge[i] > 122 )
{
if ( Challenge[i] >= 48 && Challenge[i] <= 57 )
Challenge[i] = (Challenge[i] + v22 - '0') % 10 + '0';
}
else
{
Challenge[i] = (Challenge[i] + v22 - 'a') % 26 + 97;
}
}
else
{
Challenge[i] = (Challenge[i] + v22 - 'A') % 26 + 65;
}
}
break;
case 2: // opcode里面没有这个
v32[0] = 1;
v32[1] = 3;
v32[2] = 5;
v32[3] = 7;
v32[4] = 9;
v32[5] = 11;
v32[6] = 15;
v32[7] = 17;
v32[8] = 19;
v32[9] = 21;
v32[10] = 23;
v32[11] = 25;
for ( j = 0; j < enc_len; ++j )
{
if ( Challenge[j] < 65 || Challenge[j] > 90 )
{
if ( Challenge[j] >= 97 && Challenge[j] <= 122 )
Challenge[j] = (old[j % key_len] + v32[j % 12] * (Challenge[j] - 97)) % 26 + 'a';
}
else
{
Challenge[j] = (old[j % key_len] + v32[j % 12] * (Challenge[j] - 65)) % 26 + 'A';
}
}
break;
case 3: // 列置换加密
v10 = *old % 10 + 2;
v33 = v10;
Block = malloc(saturated_mul(v10, 8ui64));
for ( k = 0; k < v10; ++k )
{
Block[k] = malloc(enc_len + 1);
memset(Block[k], 0, enc_len + 1);
}
for ( m = 0; v10 * m < enc_len; ++m )
{
for ( n = 0; n < v10 && n + v10 * m < enc_len; ++n )
*(Block[n] + m) = Challenge[n + v10 * m];
}
v18 = 0;
for ( ii = 0; ii < v10; ++ii )
{
for ( jj = 0; jj < m; ++jj )
{
if ( *(Block[ii] + jj) && v18 < enc_len )
Challenge[v18++] = *(Block[ii] + jj);
}
}
Challenge[v18] = 0;
for ( kk = 0; kk < v10; ++kk )
free(Block[kk]);
free(Block);
break;
case 4: // 右旋加密
v25 = *old % 10 + 2;
for ( mm = 0; mm < v25; ++mm )
{
v11 = Challenge[enc_len - 1];
for ( nn = enc_len - 1; nn > 0; --nn )
Challenge[nn] = Challenge[nn - 1];
*Challenge = v11;
}
break;
case 5:
v34 = enc_len;
v26 = malloc(saturated_mul(enc_len, 4ui64));
for ( i1 = 0; i1 < enc_len; ++i1 )
v26[i1] = (old[i1 % key_len] + '9') ^ Challenge[i1];
Source = base64(v26, enc_len);
strcpy(Challenge, Source);
free(v26);
free(Source);
break;
case 6: // rc4_1
v3 = saturated_mul(strlen(Challenge), 4ui64);
v27 = malloc(v3);
rc4(Challenge, old, v27);
v4 = strlen(Challenge);
v30 = base64(v27, v4);
strcpy(Challenge, v30);
free(v27);
free(v30);
break;
case 7: // rc4_2 opcode里面没有这个
v5 = saturated_mul(strlen(Challenge), 4ui64);
v28 = malloc(v5);
sub_7FF6A7E11250(Challenge, old, v28);
v6 = strlen(Challenge);
v31 = base64(v28, v6);
strcpy(Challenge, v31);
free(v28);
free(v31);
break;
}

不过还好有两个case没有出现在opcode里面,往常的opcode都是一些类似于指令之类的,这个是直接是加密,

真的难崩了,不过我们先去获取执行流

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
#include<stdio.h>
#include <stdlib.h>
int main(){
__int64 v3; // rdx
__int64 v4; // r8
__int64 v6; // rdx
__int64 v7; // r8
int j; // [rsp+20h] [rbp-1F8h]
int i; // [rsp+24h] [rbp-1F4h]
int v10; // [rsp+28h] [rbp-1F0h]
int v11[4]; // [rsp+30h] [rbp-1E8h]
__int64 v12; // [rsp+40h] [rbp-1D8h]
__int64 v13; // [rsp+48h] [rbp-1D0h]
__int64 v14; // [rsp+50h] [rbp-1C8h]
int v15[8]; // [rsp+58h] [rbp-1C0h]
int v16[4]; // [rsp+78h] [rbp-1A0h]
char Str[12]; // [rsp+88h] [rbp-190h] BYREF
char Source[12]; // [rsp+94h] [rbp-184h] BYREF
char v19[16]; // [rsp+A0h] [rbp-178h] BYREF
char Destination[112]; // [rsp+B0h] [rbp-168h] BYREF
char Str1[112]; // [rsp+120h] [rbp-F8h] BYREF
char v22[112]; // [rsp+190h] [rbp-88h] BYREF

v15[0] = 1;
v15[1] = 5;
v15[2] = 6;
v15[3] = 3;
v15[4] = 4;
v15[5] = 1;
v15[6] = 4;
v15[7] = 5;
v16[0] = 0;
v16[1] = 1;
v16[2] = 2;
v11[0] = 0;
v11[1] = 0;
v11[2] = 0;
for ( i = 0; i < 8; ++i )
{
for ( j = 0; j < 3; ++j )
{
if ( i >= v16[j] )
{
v10 = v11[j];
if ( v10 < 8 )
{
if ( j )
{
if ( j == 1 )
{
printf("fun1 input2, input3, %d\n",v15[v10]);
}
else if ( j == 2 )
{
printf("fun2 input3, input1, %d\n",v15[v10]);
}
}
else
{
printf("fun3 input1, input2, %d\n",v15[v10]);
}
++v11[j];
}
}
}
}
}

img

得到执行流之后对每个写解密代码,不过那个列置换的解密代码我写不出来

然后照着wp打了一遍,(以后也得学习py了啊)

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import base64
import math

from ghcanon1 import rc4_decrypt

# ord 是转化为ascll字符 chr 是转化为字符
new_table = "stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr"
old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

def Rc4_decrypt(cipher,key):
key=key.encode() #将key编码为字节串bytes
S = list(range(256)) #转化为0~256的列表,相当于S盒
j=0
#初始化s盒
for i in range(256):
j=(j+S[i]+key[i%len(key)])%256
S[i], S[j]=S[j],S[i]
i=j=0
plaintext=[]
for byte in cipher: #cipher 是一个字节串(以 b 开头)。循环会依次将每个字节(实际上是对应的 ASCII 值)赋值给 byte
i=(i+1)%256
j=(j+S[i])%256
S[i], S[j] = S[j], S[i]
k=S[(S[i]+S[j])%256]
plaintext.append(((byte-0x39)^k)%256)
return bytes(plaintext)
def replay(violin, bass, mode):
if mode == 1:
res = "" # 定义一个空字符串
for i, char in enumerate(violin):
offest = ord(bass[i % len(bass)])
if "a" <= char <= "z":
res += chr((ord(char) - ord("a") - offest) % 26 + ord("a"))
elif "A" <= char <= "Z":
res += chr((ord(char) - ord("A") - offest) % 26 + ord("A"))
elif "0" <= char <= "9":
res += chr((ord(char) - ord("0") - offest) % 10 + ord("0"))
else:
res += char # 其他字符不变
return res
elif mode == 3: #
Ek = ord(bass[0]) % 10 + 2
Dk = int(len(violin) / Ek)
res = ''
yushu = len(violin) % Ek
steps = []

if len(violin) % Ek == 0: #情况 1:密文长度刚好能被 Ek 整除
step = Dk
for i in range(Ek):
steps.append(step)

else: #情况 2:密文长度不能被 Ek 整除
big_step = math.ceil(len(violin) / Ek)
small_step = int(len(violin) / Ek)
for p in range(yushu):
steps.append(big_step)
for q in range(Ek - yushu):
steps.append(small_step)

n_column = 0
while n_column < math.ceil(len(violin) / Ek):
count_steps = 0
for one_step in steps:
if len(res) == len(violin):
break
else:
res += violin[n_column + count_steps]
count_steps += one_step
n_column += 1
return res

elif mode == 4: #加密是右旋加密 所以解密就用左旋
# 计算 v25
v25 = ord(bass[0]) % 10 + 2 # 这里修正

# 计算加密数据的长度
enc_len = len(violin)

# 将字符串转换为列表以便修改
challenge = list(violin)

# 进行 v25 次左旋
for _ in range(v25):
v11 = challenge[0]
for nn in range(enc_len - 1):
challenge[nn] = challenge[nn + 1]
challenge[-1] = v11

res = ''.join(challenge)
return res

elif mode==5:
violin_decoded = base64.b64decode(violin.translate(str.maketrans(new_table, old_table))) #str.maketrans 创建了一个字符映射表,将 new_table 中的字符映射为 old_table 中的对应字符 translate 使用该映射表替换 violin 字符串中的字符。
# 如果 new_table = "xyz",old_table = "abc",那么 violin 中所有 x 变 a,y 变 b,z 变 c。
res = ''
for i,char in enumerate(violin_decoded):
res+=chr(char^ord(bass[i%len(bass)])+0x39)
return res

elif mode==6:
violin_decoded = base64.b64decode(violin.translate(str.maketrans(new_table, old_table)))
res = Rc4_decrypt(violin_decoded,bass)
return res.decode() #将 res 从字节数据(bytes)转换为字符串(str),然后返回。

def main():
violin = ["WgvDmssEvcY326bHo3nNro3vXvvfmgrz", "gX+Ri9PG=bt5=00B6hscPQOL", "T6bHgUPL2gXUd=xT=FNHtPzV"]
v = [3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 2, 1, 1]
chord = [1, 4, 5, 4, 1, 4, 3, 4, 1, 6, 3, 4, 5, 6, 3, 1, 5, 6, 1, 5, 1]
for i in range(len(v)):
tmp = replay(violin[v[i] - 1], violin[v[i] % 3], chord[i])
violin[v[i] - 1] = tmp
print(''.join(violin))


if __name__ == '__main__':
main()