2021 Geek and HECTF re wp#

最近比赛超多,有点顶不住了,湖湘杯的题质量还是很好的,值得花时间去复现,就是py太多了,也不知道有些队伍没wp都敢交flag是怎么想的,掉钱眼子里面去了。

本文章记录一个月的极客大挑战,和2两天的HECTF。

2021 Geek re

做完了逆向,题目质量有高有低,一些适合新生,一些又不适合新生,写wp给新生看看,毕竟大家都是这么过来的。

Re0

签到题 ida打开,shift + f12查看字符串就行。
SYC{Welcome_to_Geek_challenge2021}

顺便也将几个常用的快捷键讲讲吧

f5 反编译出类似于c代码的窗口

h键 10进制和16进制互换

d键 将IDA view-RIP解面的单个字节进行转换,可转换为2个,3个,4个,甚至更多个字节。

u键 将多个字节数据,甚至是汇编字节码进行拆分为单个字节的。

c键 重新分析为汇编代码

p键 重新生成函数

/键 在汇编解密也可以看到f5的伪代码,方便在汇编界面调试,前提是代码能f5

r键 转换为字符串类型的样子。

n键 修改变量 函数名称

y键 修改变量类型,使代码更加好看

m键 查看一些api的参数名称

还有些set ip呀,重新定义数组呀之类操作。

还有些好用的ida插件也都说说。

lazyida 可以快速dump出想要类型的数据,word,dword,等等。

findcrypto3 可以用来查看是否有某些常见加密算法的特征值,从而快速定位到关键位置。

keypatch 用来打补丁的,个人用的较少。

bindiff 恢复符号表的好东西,他可以匹配代码的相似度来达到恢复符号表的功能,特别是elf文件动调时,经常遇到符号表不见了的情况,就可以使用bindiff来恢复。

刘壮桌面美化大师

好像是一个桌面控件,弄一下就可以出。但是我感觉是签到题,就直接用jeb去看了看字符串,然后就在里面。

也是一个安卓题常用到的知识点吧,就是字符一般都会在jeb左上角的这个目录下。

Resource/values/strings.xml

当然也可以用apktool来解包。

SYC{We1c0m3_t0_4ndRo1d_ReV3rse!}

买Activity

这个题,还是需要了解点安卓,好像是启动一个活动就可以得到flag,但是本人没具体搞过安卓,所以做的方式可能比较奇怪。

先是用jeb看了看decode函数,发现好像是个自解密,先从native层得到一串字符然后去异或。

所以我们只需要得到native层的函数就行,先解压apk,分析so文件。

函数Java_com_sorrowrain_buyactivity_Decode_stringFromNative里面有两串字符串,后面一堆混淆,解题后推测应该是个栅栏自解密。

1
2
3
4
5
6
7

s1='CSD!Os!yiyO#|iU`bu1'
s2='Ikxc$dFdOCBq!Oh dtm'
for i in range(len(s1)):
print(chr(ord(s1[i])^16),end='')
print(chr(ord(s2[i]) ^ 16),end='')
#SYC{Th1s_4ct1Vity_iS_R3al1y_Exp0rted!}

Re1

发现就是一个base64+一个异或

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

#include<stdio.h>

int main()
{
int v9[60];
v9[0] = 21;
v9[1] = 113;
v9[2] = 44;
v9[3] = 4;
v9[4] = 37;
v9[5] = 113;
v9[6] = 40;
v9[7] = 16;
v9[8] = 21;
v9[9] = 44;
v9[10] = 121;
v9[11] = 40;
v9[12] = 34;
v9[13] = 45;
v9[14] = 18;
v9[15] = 38;
v9[16] = 25;
v9[17] = 45;
v9[18] = 6;
v9[19] = 58;
v9[20] = 26;
v9[21] = 20;
v9[22] = 25;
v9[23] = 112;
v9[24] = 24;
v9[25] = 114;
v9[26] = 6;
v9[27] = 57;
v9[28] = 26;
v9[29] = 22;
v9[30] = 121;
v9[31] = 112;
v9[32] = 33;
v9[33] = 7;
v9[34] = 22;
v9[35] = 38;
v9[36] = 25;
v9[37] = 45;
v9[38] = 6;
v9[39] = 58;
v9[40] = 33;
v9[41] = 24;
v9[42] = 14;
v9[43] = 38;
v9[44] = 34;
v9[45] = 114;
v9[46] = 26;
v9[47] = 38;
v9[48] = 35;
v9[49] = 45;
v9[50] = 22;
v9[51] = 114;
v9[52] = 26;
v9[53] = 24;
v9[54] = 10;
v9[55] = 58;
v9[56] = 26;
v9[57] = 24;
v9[58] = 112;
v9[59] = 125;

int i;
for(i=0;i<60;i++)
{
printf("%c",v9[i]^0x40);
}
}
//U1lDe1hPUl9hbmRfYmFzZTY0X2FyZV90aGVfYmFzaXNfb2ZfcmV2ZXJzZX0=

然后base64解密
SYC{XOR_and_base64_are_the_basis_of_reverse}

调试

考点就是ida动调elf文件,和简单的汇编识别能力,需要知道直接调一遍的流程是正确的,需要我们修改流程,而且该题也是个自解密。

但是实际上静态可以做,只需要简单改点汇编,去个花就可以看了。

正常情况我们是无法f5看完整代码的,原因如下。

是在11c9处u键,p键

修改后就可以看伪代码了。

flag:SYC{C0ngr@tuIatlOns_thls_1s_th3_r!gHt_f!ag}

珍惜生命

pyc文件,利用uncompyle6还原为py代码,然后分析py代码。

简单分析后发现考点是z3约束器解方程式。

先用z3求出key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

from z3 import *

s = Solver()
key = [BitVec('key[%d]' % i, 16) for i in range(8)]
KEY_cmp = 'Syclover'

key_cmp = [0]*8

key_cmp[0] = (key[1] * key[2] - key[5] * 72 - key[4] * 3 - key[3] ^ key[1] + (key[3] << 2) + key[2] * 6 - key[7] & key[6] - 1000) - 14
key_cmp[1] = (key[5] * 7 + key[3] * 3 + key[2] + key[6] - (key[2] >> 2) - key[1] ^ key[0] + key[7] + (key[4] ^ key[1]) + (key[4] | key[7])) - 801
key_cmp[2] = (key[6] * 5 + key[2] * 6 - key[3] * 7 + key[4] | key[5] + key[4] * 10 + key[0] ^ key[1] * 3 - key[7] + key[0] + key[1]) - 924
key_cmp[3] = key[1] * 3 + key[5] * 9 + key[0] + key[2] * 2 + key[3] * 5 - key[4] * (key[6] ^ key[7]) + 321 - 16
key_cmp[4] = (key[5] * 12 - key[0] ^ key[6] - key[3] * 23 + key[4] * 3 + key[2] * 8 + key[1] - key[7] * 2 + key[6] * 4 + 1324) + 1
key_cmp[5] = key[3] * 54 - key[1] * 3 + key[2] * 3 + key[4] * 11 - key[5] * 2 + key[0] + key[7] * 3 - key[6] - 6298 + 40
key_cmp[6] = key[7] - key[6] * key[3] + key[2] * key[2] - key[4] * 32 + key[5] * (key[0] >> 2) - key[1] * key[1] - 6689 + 41
key_cmp[7] = (key[5] - key[3] * 41 + key[6] * 41 + key[5] ^ (key[4] & key[6] | key[0]) - (key[7] * 24 | key[2]) + key[1] - 589) - 36

for i in range(8):
s.add(key_cmp[i]==ord(KEY_cmp[i]))

print(s.check())
m=s.model()
print(m)

然后异或

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

key=[0]*8
key[6] = 54
key[1] = 38
key[3] = 99
key[0] = 83
key[7] = 46
key[2] = 121
key[5] = 45
key[4] = 64
key_cmp = ''

key_cmp += chr((key[1] * key[2] - key[5] * 72 - key[4] * 3 - key[3] ^ key[1] + (key[3] << 2) + key[2] * 6 - key[7] & key[6] - 1000) - 14)
key_cmp += chr((key[5] * 7 + key[3] * 3 + key[2] + key[6] - (key[2] >> 2) - key[1] ^ key[0] + key[7] + (key[4] ^ key[1]) + (key[4] | key[7])) - 801)
key_cmp += chr((key[6] * 5 + key[2] * 6 - key[3] * 7 + key[4] | key[5] + key[4] * 10 + key[0] ^ key[1] * 3 - key[7] + key[0] + key[1]) - 924)
key_cmp += chr(key[1] * 3 + key[5] * 9 + key[0] + key[2] * 2 + key[3] * 5 - key[4] * (key[6] ^ key[7]) + 321 - 16)
key_cmp += chr((key[5] * 12 - key[0] ^ key[6] - key[3] * 23 + key[4] * 3 + key[2] * 8 + key[1] - key[7] * 2 + key[6] * 4 + 1324) + 1)
key_cmp += chr(key[3] * 54 - key[1] * 3 + key[2] * 3 + key[4] * 11 - key[5] * 2 + key[0] + key[7] * 3 - key[6] - 6298 + 40)
key_cmp += chr(key[7] - key[6] * key[3] + key[2] * key[2] - key[4] * 32 + key[5] * (key[0] >> 2) - key[1] * key[1] - 6689 + 41)
key_cmp += chr((key[5] - key[3] * 41 + key[6] * 41 + key[5] ^ (key[4] & key[6] | key[0]) - (key[7] * 24 | key[2]) + key[1] - 589) - 36)

print(bytes(key))
flag = [113, 74, 71, 35, 29, 91, 29, 12, 114, 73, 60, 52, 69, 5, 113, 35, 95, 38, 20, 112, 95, 7, 74, 12, 102, 23, 7, 31, 87, 5, 113, 98, 85, 38, 16, 112, 29, 6, 30, 12, 65, 73, 83, 36, 12, 23]

for i in range(46):
print(chr(flag[i] ^ key[((i + 1) % len(key))]),end='')
#W3$c0m3_T0_th3_py_w0r1d_@nd_z3_1s_s0000_g00d!!

new_language

考察net逆向,也就是c#语言,使用dnspy就行,甚至还可以调试。

简单分析后,发现是aes的换盒,也没啥好讲的。实际上就是求box的index。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

box=[99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,118,202,130,201,125,250,89,71,240,173,212,162,
175,156,164,114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,
154,7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,0,237,32,
252,177,91,106,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,
64,143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,
25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,36,92,194,211,172,98,
145,149,228,121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,
232,221,116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,
217,142,148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,66,104,65,153,45,15,176,84,187,22]

enc=[64,249,133,69,146,253,253,207,182,4,157,207,251,4,60,81,59,77,146,77,207,26,38,207,64,77,177,77,64,195,77,253,253]
for i in enc:
print(chr(box.index(i)),end='')
#right!!_y0u_c0mpIete_C#_reVer3e!!

easypyc

文件名称为,easypyc.exe,应该是py打包的exe,对于这种题型我们需要先用pyinstxtractor.py解包exe,然后将生成文件夹中的pyc文件利用uncompyle6还原为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

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)]
# Embedded file name: easypyc.py
whatbox = [
0] * 256

def aaaaaaa(a, b):
k = [
0] * 256
t = 0
for m in range(256):
whatbox[m] = m
k[m] = ord(a[(m % b)])
else:
for i in range(256):
t = (t + whatbox[i] + k[i]) % 256
temp = whatbox[i]
whatbox[i] = whatbox[t]
whatbox[t] = temp


def bbbbbbbbbb(a, b):
q = 0
w = 0
e = 0
for k in range(b):
q = (q + 1) % 256
w = (w + whatbox[q]) % 256
temp = whatbox[q]
whatbox[q] = whatbox[w]
whatbox[w] = temp
e = (whatbox[q] + whatbox[w]) % 256
a[k] = a[k] ^ whatbox[e] ^ 102
print(whatbox[e] ^ 102,end=',')

def ccccccccc(a, b):
for i in range(b):
a[i] ^= a[((i + 1) % b)]
else:
for j in range(1, b):
a[j] ^= a[(j - 1)]


if __name__ == '__main__':
kkkkkkk = 'Geek2021'
tttttt = [117, 62, 240, 152, 195, 117, 103, 74, 240, 151, 173, 162, 17, 75, 141, 165, 136, 117, 113, 33, 98, 151, 174, 4, 48, 25, 254, 101, 185, 127, 131, 87]
ssss = input('Please input your flag:')
inp = [0] * len(ssss)
if len(ssss) != 32:
print('Length Error!!!!')
exit(0)
for i in range(len(ssss)):
inp[i] = ord(ssss[i])
else:
aaaaaaa(kkkkkkk, len(kkkkkkk))
bbbbbbbbbb(inp, 32)
ccccccccc(inp, 32)
for m in range(32):
if tttttt[m] != inp[m]:
raise Exception('sorry your flag is wrong')
print('success!!!!!!')
print('your flag is {}'.format(ssss))

分析下就发现,是个魔改的rc4,然后加了点数组内异或操作,数组异或我就没自己逆了,直接用z3他不香吗。

先用z3解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

enc = [117, 62, 240, 152, 195, 117, 103, 74, 240, 151, 173, 162, 17, 75, 141, 165, 136, 117, 113, 33, 98, 151, 174, 4, 48, 25, 254, 101, 185, 127, 131, 87]

def ccccccccc(a, b):
for i in range(b):
a[i] ^= a[((i + 1) % b)]
else:
for j in range(1, b):
a[j] ^= a[(j - 1)]

from z3 import *

s = Solver()
flag = [BitVec('flag[%d]' % i, 8) for i in range(32)]

ccccccccc(flag,32)
for i in range(32):
s.add(flag[i]==enc[i]);


print(s.check())
m=s.model()
print(m)

再随便输入合适长度的flag,调试或打印出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

flag=[0]*32
flag[8] = 104
flag[3] = 210
flag[16] = 135
flag[15] = 175
flag[26] = 59
flag[11] = 143
flag[18] = 87
flag[9] = 210
flag[6] = 87
flag[24] = 38
flag[22] = 181
flag[30] = 93
flag[14] = 105
flag[0] = 34
flag[25] = 18
flag[28] = 71
flag[19] = 83
flag[10] = 181
flag[20] = 3
flag[23] = 140
flag[21] = 64
flag[7] = 69
flag[17] = 170
flag[4] = 186
flag[29] = 155
flag[2] = 28
flag[5] = 225
flag[12] = 128
flag[13] = 51
flag[27] = 220
flag[31] = 161
flag[1] = 87

xor=[113,14,95,169,240,148,36,49,55,179,234,202,229,86,12,202,226,203,36,42,92,18,214,184,121,96,82,187,47,239,98,220]
for i in range(32):
print(chr(xor[i]^flag[i]),end='')
#SYC{Just_a_Eeeeeeasy_Rc4_right?}

Brute_force

go语言逆向,现在ida7.6已经很好的解决了go语言符号表恢复的问题,主要是从main_main开始分析,做这道题主要是考察动调的能力,通过分析可知,使用的命令行传参。

如何命令行传参呢。

开始分析。

接下来分析加密函数。

爆破脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

import hashlib

table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/_"
list1=['957a3926d4ff16d0d3bac4ed3044537b','d7bf94ada03842f20934c5605728fbc5','5781f597ce91fb5f66057f35846bcb78','ae4b9b5b2468a3d15b6b8690b761e160','4ad50c4af2c5beda7aca217afbac9bd6', 'f40b339be4c5898741b8a0d2a8007bd5']

for j in list1:
for a in range(30,128):
for b in range(30,128):
for c in range(30,128):
for d in range(30,128):
str = chr(a)+chr(b)+chr(c)+chr(d)
flag = hashlib.md5(str.encode('utf-8')).hexdigest()
if flag == j.casefold():
print(str, end='')
#7h3_g0_pr0g@rm_13_slgned

Win32

upx脱壳,后发现经典的win32窗口,如果了解过一些win32知识,做起肯定是简单的,如果没了解过,也没关系,因为这个题因为出题人的小失误,变成了签到题。

对于这种题型,首先需要去找到,消息处理函数,其实我们对窗口做一些操作,比如说关闭窗口,点击窗口,输入东西,都会记录在消息列队里面,有些消息是由系统自己处理,有些就是由出题人自己写的处理方法处理,这些方法就在消息处理函数中。

这个函数就在winmain中的sub_1400011EC,学习下windows窗口的创建就可以知道了。

先创建了个子窗口

然后是0x111

WM_COMMAND

0x0111

选择窗口菜单项或某个控件发送一条消息给它的父窗口或按下一个快捷键时产生此消息

这意味着我们按下试试呢,就会进入这个case语句。

所以这道题解个base64就行。

flag

SYC{y0u_g3t_A_f1ag_by_cyberloafing_auth0r}

wasm

这个题就是一种题型,可以先看看这两篇文章,介绍了这种题怎么出的,和怎么解。

https://www.anquanke.com/post/id/179556
https://blog.csdn.net/TurkeyCock/article/details/83317935

所以需要安装wabt(https://github.com/WebAssembly/wabt),linux下安装就行。

$ mkdir build
$ cd build
$ cmake .. 
$ cmake --build .

根据个人情况,先安装camke,第3条指令会报错,需要加上报错语句里面的最后一句话当参数,off什么来着,方可成功。

之后就可以跟着文章做,得到o文件。

分析o文件,发现init_memory导入了很多数据。

然后看main,发现了下面的语句

if ( i32_load(&w2c_memory, v5 + 88, ((unsigned __int64)(unsigned int)v5 + 88) >> 32) != 22 )

i32_store8(&w2c_memory, v4, 0, v3 ^ 0x66);

所以推测就是长度为22的数据,然后和0x66异或,实际上爆破都行,题目文件也给了xor。

1
2
3
4
5
6
7
8

enc=[0x35, 0x3F, 0x25, 0x1D, 0x11, 0x07, 0x15, 0x0B, 0x39, 0x2F,
0x15, 0x39, 0x35, 0x56, 0x39, 0x21, 0x09, 0x56, 0x02, 0x47,
0x47, 0x1B]

for i in enc:
print(chr(i^0x66),end='')
#SYC{wasm_Is_S0_Go0d!!}

猜拳

也是一个win32程序,估计加了保护器,所有代码都被加密了。没事,动调就行,在动调的过程中直接f9,还发现了反调试,所以就可以根据反调试来找到main函数了,一种是慢慢调试,找出弹出反调试的messagebox的窗口在哪,第二种就是调试过程中,找到isdebugprevent函数,打个断点,然后断下来。

winmain

仔细分析下,消息处理函数,发现有个函数里面还有switch case,并且有个case在rc4解密,然后调用messagebox,估计就是打印flag了。
然后在7FF6E4761567处直接set ip,然后f8调试,就会弹出一个有flag的窗口了。

SYC{gR3@t1_Y0U_ch3@t_@nd_w!n_gam3}

have a tea

一个elf文件,也是一个类似于加壳的文件,静态看不到代码,动调就行。

动调执行完最下面的jmp指令,就可以看到和elf文件常见的start函数了,开始分析,发现是利用fork创建了个子进程,然后就不能调试了,一种类似于CreateProcess的反调试方法,只不过可以用set ip来强行调试那个关键函数。

set ip 调试sub_5576384552BD函数,发现加密方式就是将字符串变为unsigned int类型,然后两个为一组,先异或,然后进行tea加密,异或的值是前一组加密后的值,第一组是和key的1,2个进行异或。

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

//try_hardgive_you_cup_tea
//
//SYC{abcdefghijklmnopqrstuvwxyz1234567}

//0x65766967, 0x756F795F, 0x7075635F, 0x6165745F

#include<stdio.h>

void encrypt(unsigned int *flag , unsigned int *key)
{
unsigned int v0,v1,sum=0,i;
unsigned int delta=0x61C88647;
v0=flag[0];
v1=flag[1];
for(i=0;i<32;i++)
{
sum-=delta;
v0+=( (v1<<4)+key[0] ) ^ ( v1+sum ) ^((v1>>5)+key[1]);
v1+=( (v0<<4)+key[2] ) ^ ( v0+sum ) ^((v0>>5)+key[3]);
}
flag[0]=v0;
flag[1]=v1;
}
void decrypt(unsigned int *code , unsigned int *key)
{
unsigned int delta=0x61C88647;
unsigned int v0,v1,sum=0xC6EF3720,i;// sum=0xC6EF3720

v0=code[0];
v1=code[1];
for(i=0;i<32;i++)
{
v1-=( (v0<<4)+key[2] ) ^ (v0+sum) ^ ( (v0>>5)+key[3] );
v0-=( (v1<<4)+key[0] ) ^ (v1+sum) ^ ( (v1>>5)+key[1] );
sum+=delta;
}
code[0]=v0;
code[1]=v1;
}

//0x7b435953, 0x79615379 0x446e6153 0x5f6e6169 0x6e30685a 0x5f614c5f 0x5f6e3179 0x5f616843 0x6e614978 0x0202007d
int main()
{
unsigned int key[6]={0x5F797274,0x64726168,0x65766967, 0x756F795F, 0x7075635F, 0x6165745F};
unsigned int code[10]={0xC9FA3B95, 0x7CFD0735, 0x958C7C9F, 0xC143B59E, 0x61741E89, 0xF47DCDC4, 0xD6E2A1F2, 0x6A38E9AD,
0xC2C16FEB, 0x8C0EE999};
unsigned int flag[10]={0x7b435953, 0x79615379 ,0x446e6153 ,0x5f6e6169 ,0x6e30685a ,0x5f614c5f ,0x5f6e3179 ,0x5f616843 ,0x6e614978 ,0x0202007d};
int i;

for(i=0;i<1;i++)
{

decrypt(&code[0],&key[2]);
code[0]^=key[0];
code[1]^=key[1];
printf("%08x %08x\n",code[0],code[1]);

}
for(i=0;i<10;i++)
{
//printf("%08x:",code[i]);
printf("%c%c%c%c",*((char*)&flag[i]+0),*((char*)&flag[i]+1),*((char*)&flag[i]+2),*((char*)&flag[i]+3));
}
//printf("%08x %08x",code[0],code[1]);
}
//SYC{ySaySanDian_Zh0n_La_y1n_Cha_xIan}

HECTF

排名还行,听说还有线下,比赛友好,几乎没有恶臭的py交易,题目质量也不错,有许多坑,需要细心,比赛时在打湖湘杯,所有没时间做完,后面补了几题,只剩道安卓,安卓硬伤呀,然后去看了看BinaryNoob 师傅的wp,发现思路没问题,可能我自己弄的jpg文件头有问题吧。

hard

shift 12看字符串就行。

HECTF{HElLo_RRRRe}

Baby_upx

esp定律手脱壳后,发现就是个异或加密,产生的异或值是确定的。

1
2
3
4
5
6
7
8
9
10
11

s=[0x17, 0x2B, 0x2A, 0x7D, 0x1A, 0x21, 0x2A, 0x07, 0x7D, 0x19, 0x10, 0x10, 0x15, 0x1E, 0x0B, 0x04, 0x29, 0x24, 0x04]

flag='aaaaaaaaaaaaaaaaaaa'
x=[ 0x22, 0x22, 0x22, 0x29, 0x24, 0x29, 0x22, 0x35, 0x29, 0x27,
0x24, 0x24, 0x24, 0x27, 0x35, 0x29, 0x29, 0x29, 0x24]


for i in range(len(s)):
print(chr(s[i]^x[i]^ord(flag[i])),end='')
#Thi5_iiS5_UUPX_LalA

Baby_maze

去掉花指令,发现是一个金字塔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
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

#include<stdio.h>
//sddddHssaSasHHsdHdwHdsH
//o....^oo0~0o^^o.^.O^.o^
//HECTF{o....^oo0~0o^^o.^.O^.o^}
int main()
{
int i;
int j;

int map1[6][6]={0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

int map2[5][5]={0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00};

int map3[4][4]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00};

int map4[4][3]={ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00};

int map5[4][2]={ 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};

int map6[3][1]={0x02, 0x00, 0x00};

for(i=0;i<6;i++)
{
for(j=0;j<6;j++)
{
printf("%d ",map1[i][j]);

}
printf("\n");
}

printf("\n");


for(i=0;i<5;i++)
{
for(j=0;j<5;j++)
{
printf("%d ",map2[i][j]);

}
printf("\n");
}

printf("\n");

for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
{
printf("%d ",map3[i][j]);

}
printf("\n");
}

printf("\n");


for(i=0;i<4;i++)
{
for(j=0;j<3;j++)
{
printf("%d ",map4[i][j]);

}
printf("\n");
}

printf("\n");


for(i=0;i<4;i++)
{
for(j=0;j<2;j++)
{
printf("%d ",map5[i][j]);

}
printf("\n");
}

printf("\n");


for(i=0;i<3;i++)
{
for(j=0;j<1;j++)
{
printf("%d ",map6[i][j]);

}
printf("\n");
}

printf("\n");
}

走的时候注意是右下角对齐。
1 0 0 0 0 0
1 1 1 1 1 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 0 0 0
0 0 0 0 0 0

0 0 0 1 0
0 0 0 1 0
0 0 1 1 0
0 1 0 0 0
0 0 0 0 0

0 0 0 0
0 0 0 0
1 0 0 0
1 1 0 0

0 0 0
0 1 0
1 1 0
0 0 0

1 1
0 1
0 0
0 0

2
0
0

flag

HECTF{o....^oo0~0o^^o.^.O^.o^}

Baby_anti

去掉花指令,发现限定了输入范围,并根据输入不同然后,进行了不同的数据的处理,最后合并为16进制,然后和密文比较。

爆破就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# enc='48656c31305f614e37695f5265'
# for i in enc:
# print(int(i,16),end=',')

s='Hel10_aN7i_Re'
for i in s:
print(hex(ord(i)).replace("0x",''),end='')
print('\n')
enc=[4,8,6,5,6,12,3,1,3,0,5,15,6,1,4,14,3,7,6,9,5,15,5,2,6,5]
table="1234567890abcdef"
for j in range(len(enc)):
for k in table:
if((ord(k)-48)>9):
if((ord(k)-97)<=5):
if(enc[j]==(ord(k)-j-37)%6+10):
print(k,end='')
break
else:
if(enc[j]==(j+ord(k)+52)%10):
print(k,end='')
break

#HECTF{47422b74515e480b70805c3920}

crypt

赛后才发现,tea加密后,还对全局变量进行了处理。。。伪代码看不到,还是通过自己写数据发现的。

动调发现tea加密后,还进行了下面的操作

1
2
3
4
5
6
7
8
9
10
11

__int64 sub_400807()
{
__int64 result; // rax

srand(0x2710u);
dword_602108 ^= rand() % 1000;
result = dword_60210C ^ (unsigned int)(rand() % 1000);
dword_60210C = result;
return result;
}

发现就是异或伪随机数。

之后就是变表base64加密了。

所以解法就是,先进行base64解密,转换为DWORD,然后和伪随机数进行异或,然后tea解密。

变表base64

1
2
3
4
5
6
7
8
9
10
11

import base64

str1 ='7id81DpecpDeES8frwU4Qa=='
string1 = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/'
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
s=base64.b64decode(str1.translate(str.maketrans(string1, string2)))
for i in s:
print(hex(i),end=',')
#0x84,0x80,0xe2,0x6e,0x73,0xc4,0x8,0xf9,0xc4,0xa3,0x68,0x85,0x45,0x6e,0x1e,0xd0,

异或+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
66
67
68
69

#include<stdio.h>
//HECTF{aaaa-bbbb-cccc-dddd}

void encrypt(unsigned int r ,unsigned int *flag ,unsigned int *key)
{
unsigned int v0,v1,sum=0,i;
unsigned int delta=0x779EFF7A;
v0=flag[0];
v1=flag[1];
for(i=0;i<r;i++)
{
v0+=( ((v1<<4) ^ (v1>>5)) +v1 ) ^ ( sum + key[sum&3] );
sum-=delta;
v1+=( ((v0<<4) ^(v0>>5)) +v0 ) ^ ( sum + key[ (sum>>11)&3 ]);
}
flag[0]=v0;
flag[1]=v1;


}

void decrypt(unsigned int r ,unsigned int *code ,unsigned int *key)
{
unsigned int v0,v1,i,delta=0x779EFF7A;
unsigned int sum=0-delta*32;

v0=code[0];
v1=code[1];
for(i=0;i<r;i++)
{
v1-=( ((v0<<4) ^(v0>>5)) +v0 ) ^ ( sum + key[ (sum>>11)&3 ]);
sum+=delta;
v0-=( ((v1<<4) ^ (v1>>5)) +v1 ) ^ ( sum + key[sum&3] );
}
code[0]=v0;
code[1]=v1;
}

#include<stdio.h>
// 0x84,0x80,0xe2,0x6e,0x73,0xc4,0x8,0xf9, 0xc4,0xa3,0x68,0x85, 0x45,0x6e,0x1e,0xd0,
int main()
{
unsigned int key[4]={ 0x64636261, 0x68676665, 0x6C6B6A69, 0x706F6E6D};
unsigned int r=32,i;
unsigned int code[4]={0x6ee28084,0xf908c473,0x8568a3c4,0xd01e6e45};
unsigned int flag[2]={0x61616161,0x62626262};

unsigned int Xor[4]={0x0237D6C33,0x1E3156EE,0x0237D6C33,0x01E3156EE};
for(i=0;i<4;i++)
{
code[i]^=Xor[i]%1000;
}

// encrypt(r,flag,key);
// printf("%08x %08x\n",flag[0],flag[1]);

for(i=0;i<2;i++)
{
decrypt(r,&code[i*2],key);
printf("%08x %08x\n",code[0],code[1]);
}

for(i=0;i<4;i++)
{
printf("%c%c%c%c ",*((char*)&code[i]+0),*((char*)&code[i]+1),*((char*)&code[i]+2),*((char*)&code[i]+3));
}
}
//HECTF{1024-2048-abcd-hhhh}

flag

go语言,一个魔改tea加密,有结合了点xxtea的特征,照着写个加密,然后反解就行。

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

#include<stdio.h>

//HECTF{abcdefghabcdefghabcdefg}
void encrypt(unsigned int *flag )
{
unsigned int index,i,j;
int dealt=0;

for(i=0;i<32;i++)
{
for(j=0;j<30;j++)
{
index=(j + 1)%30;

flag[j]+=((dealt+flag[index]+0xEAE5AD5) ^ ( 16 *flag[index] +0x64B71598) ^ ((flag[index] >> 5) - 0x2A04C413));

flag[index]+= (dealt + flag[j] + 0xEAE5AD5) ^ (16 * flag[j] + 0x1185971C) ^ ((flag[j] >> 5) + 0x6F61074);
}
dealt+=0xEAE5AD5;
}
}

void decrypt(unsigned int *flag )
{
unsigned int i,index;
int dealt=0xEAE5AD5*32;
int j;


for(i=0;i<32;i++)
{
dealt-=0xEAE5AD5;

for(j=29;j>=0;j--)
{
index=(j+1)%30;

flag[index]-= (dealt + flag[j] + 0xEAE5AD5) ^ (16 * flag[j] + 0x1185971C) ^ ((flag[j] >> 5) + 0x6F61074);

flag[j]-=((dealt+flag[index]+0xEAE5AD5) ^ ( 16 *flag[index] +0x64B71598) ^ ((flag[index] >> 5) - 0x2A04C413));

}

}
}


int main()
{
unsigned int i;
unsigned int code[30]={0x48,0x45,0x43,0x54,0x46,0x7b,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x7d};
unsigned int flag[30]={0x67185a11,0xe98651dc,0x354a7497,0x104a33b2,0x4b0d80a6,0x7b7dad7a,0xeeab3824,0xfc48315e,0x31aaf1c5,0x577ee036,0x65938f6c,0xdb52c493,0xa20eace0,0xcc54c27d,0x54ecc3c1,0xf7f51fa6,0xbf187da8,0x4f723fb1,0x0f936a37,0x466e0d5d,0x42bba543,0x707d816c,0xea694ed4,0xa4c6d06f,0x266e4f35,0x3246607f,0xd91b6bfd,0x7933b9a4,0x1185eed8,0xe2e4347d};

encrypt(code);
for(i=0;i<30;i++)
{
printf("%08x ",code[i]);
}
printf("\n");


decrypt(flag);
for(i=0;i<30;i++)
{
printf("%c",flag[i]);
}

}
//HECTF{Lets_g0_t0_dr1nk_tea!!!}

Baby_pp

开始看到易语言图标,以为易语言写的,然后调试了一波,看到利用了CreateProcess创建子进程反调试,以为多难呢,后来发现就是个python打包的exe,直接常规操作得到py文件。

然后开逆,发现就是伪随机数异或+栅栏+云隐密码(hint有)

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

import random

# def encode(s, nuum):
# step = len(s) // nuum
# ens = ''
# for i in range(step):
# ens += s[i::step]
# else:
# return ens
# print(encode('abcdefghijklmnopqr',6))
#adgjmpbehknqcfilor

enc = '742641edefb6770733ab5932325106b3a5fa75222791d09e451161c46f15504402b32737362443d4df7d136145cd970b54116669c230'
s=''
for j in range(6):
for i in range(18):
s+=enc[i * 6 + j]
flag=bytearray.fromhex(s)

random.seed(10085)
t = ''
for i in range(len(flag)):
flag[i] = flag[i] ^ random.randint(0, 127)
print(chr(flag[i]),end='')

#HECTF{decrypt(80410840840842108808881088408084210842)}

def de_code(c):
dic = [chr(i) for i in range(ord("A"), ord("Z") + 1)]
flag = ''
c2 = [i for i in c.split("0")]
for i in c2:
c3 = 0
for j in i:
c3 += int(j)
flag+=(dic[c3 - 1])
return flag
print(de_code('80410840840842108808881088408084210842'))
#HELLOPYTHON

Baby_and

安卓题,感觉就是用key异或一个jpg图片,看了下当时的脚本,发现是key没弄对,key的最后一个字符错了,原因是因为用的jpg的标准头文件不止一种,实际上也可以猜的,可以看到jpg里面有很多,q7Z2,和0异或就是本身,所以可以猜测key=’q7Z2’。

对安卓的了解还是太少了,安卓题是硬伤呀。。。只能通过jeb里面的代码来猜测。对整体的流程还是不清楚。

1
2
3
4
5
6
7
8
9
10
11
12
13

f=open("JFIF.jpg",'rb')
data=f.read()[:-1]
array=list(data)
key='q7Z2'
flag=[]

flag_file=open('flag.jpg','wb')
for i in range(len(array)):
flag.append(array[i]^ord(key[i%4]))
flag_file.write(bytes(flag))
#HECTF{He1l0_Android^_^}