前言

感觉有点尴尬,这个wp并不全,因为本人打算在比赛结束后写出此文,然后发现靶场的题全下完了,然后自己也没有备份,呃呃怎么办呢?只能凭印象了,想到哪道题就写哪道题罢,然后Ida的界面的东西也没有(因为题目被我删了)只剩解密脚本了凑合着看。。。

0x01 shiftjmp

5847d3c5041d4cc9bbe0c4965dbe7367

die查壳,发现是无壳的ELF64文件,直接拖入ida64

4f36ff36fa3d479ab15b23445ba8a9ca

按tab发现无法生存伪代码,发现这里有两处错误。

这两处错误是花指令,花指令详情见花指令 - CTF Wiki (ctf-wiki.org)

第一处是无意义跳转,第二处是jmp造成的脏字节。

把jz和imp都nop掉。

5a7233f2ea1a4f72a7e4741ecb515f8c

undefine (U)然后按 c生存代码 按p生存函数main

23badc126cdd4ee5b8ebe570748b8172

非常简单的异或加密,写个python脚本解密

1
2
3
4
5
enc='SXAxS6jd8doTxBQ{x"Ma'+chr(0x27)+r"csE-|El,o/{^\\"
flag=""
for i in range(34):
flag+=chr(ord(enc[i])^i^0)
print(flag)

flag:SYC{W3lc0me_tO_th3_r3veR5e_w0r1d~}

0x02 点击就送的逆向题

没什么好说的,Linux.s文件编译,打开虚拟机按照下面步骤手动编译就好了

gcc -c test.s -o test.o

gcc test.o -o test

这里补充一下c语言编译步骤:

1、预处理

gcc -E test.c -o test.i

2、用intel汇编代码来编译

gcc -s -masm=intel test.i -o test.s

3、汇编器将汇编代码转化为机器码

gcc -c test.s -o test.o

4、链接调用输出

gcc test.o -o test.out

之后拖到ida分析编译好的test文件,简单位移加密逆向后可以解出来。

0x03 AES!!AES?

简化版的AES加密,没有列混合和生成多个子密钥进行轮密钥加

【AES加密算法】| AES加密过程详解| 对称加密| Rijndael-128| 密码学| 信息安全_哔哩哔哩_ bilibili AES算法详细见这个视频)

照着ida里面的加密步骤逆出解密脚本就好了

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
S=[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, 0x6, 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表
key=[110,121,105,-125,121,
127,105,117,121,120,
-127,105,93,99,77,73]
for i in range(16):
key[i]-=10
if key[i]<0:
key[i]+=256
key[i] = key[i] & 0xff
#密钥:do_you_konw_SYC?
key1=[]
for i in range(16):
key1.append(key[i] ^ S[key[i]] & 0xff)
#密钥扩展
enc=[0xe0,0x05,0x6e,0xc2,0x6e,
0x99,0x68,0x45,0x7d,0x1f,
0x3f,0xf9,0x97,0x76,0x3b,
0x92,0x2f,0x44,0x06,0x67,
0xa8,0xeb,0xec,0x4a,0x6f,
0xe8,0x35,0xf9,0xac,0xa7,
0x8c,0x71]
#逆向行位移
def re_shiftrow(flag):#右移动
for i in range(0, 4):
for _ in range(i):
for k in range(3):
flag[4 * i + 3 - k], flag[4 * i + 3 - k - 1] = flag[4 * i + 3 - k - 1], flag[4 * i + 3 - k]
flag[16+4 * i + 3 - k], flag[16+4 * i + 3 - k - 1] = flag[16+4 * i + 3 - k - 1], flag[16+4 * i + 3 - k]
def re_tansform(flag):
for i in range(32):
flag[i] = S.index(flag[i])
v = []
for i in flag:
v.append(i)
n = 0
for j in range(4):
for k in range(4):
flag[n] = v[4 * k + j]
flag[n + 16] = v[16 + 4 * k + j]
n += 1
def byte_xor(flag,key):
for i in range(32):
flag[i] ^= key[i % 0x10]
#异或密钥
def byte_trans(flag):
for i in range(32):
flag[i] = S.index(flag[i])
#字节代换

#下面是主函数
byte_xor(enc,key1)
re_shiftrow(enc)
byte_trans(enc)
byte_xor(enc,key)
re_tansform(enc)
re_shiftrow(enc)
byte_trans(enc)
flag=''
for i in range(32):
if i==5:
enc[i]=46
if i==10:
enc[i]=108
if i==17:
enc[i]=48
flag += chr(enc[i])
print(flag)

(s表是手打的,当时不知道ida能快捷键Shift+E提取文本数据)

SYC{0.o_Thls_1s_n0t_A3s_(q^_^p)}

0x04 miku

这是一道golang逆向题,评价是完全看不懂,发现main函数里有一个叫rc4加密的函数,盲猜是rc4加密。传入的key通过静态分析很难找,直接Linux远程动调,运行程序后有一个输入初音未来色,经过百度查找是39c5bb,正确输入后经过动态调试发现生成了正确的key 0x43,手搓一个rc4对称解密脚本直接逆出flag

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
#rc4对称加密流程

enc=[0x25,0x6f,0x3d,0x6c,0xf9,0xe0,0xcf,0x3f,
0x2e,0x24,0xc6,0x7b,0x81,0xbf,0x55,0x4f,
0x0d,0x99,0x87,0x47,0x48,0xf7,0xb9,0x98,
0xfb,0x1b,0x22,0xec,0x84,0x23,0xfd,0xb2]
#key长度为18
#flag长度为32
key=[0x43]
#填充一个s_box,0到255
#key表待定
s=[]
k=[]
xor=[0]*32
for i in range(256):
s.append(i)
k.append(key[i%1])
#初始化s_box
j=0
for i in range(256):
j=(j+s[i]+k[i])%256

temp=s[i]
s[i]=s[j]
s[j]=temp
i=0
j=0
t=0
for k in range(32):
i=(i+1)%256
j=(j+s[i])%256

temp = s[i]
s[i] = s[j]
s[j] = temp

t=(s[i]+s[j])%256
xor[k]=s[t]



flag=''
for i in range(32):
flag+=chr(xor[i]^enc[i])
print(flag)

SYC{N0thing_1s_sEriOus_But_MIku}

0x05 幸运数字

感觉没啥好说的,题目说0到999有存在幸运数字,经过分析只要找出任意一个luck丢进逆向脚本就能出flag,由于幸运数字的范围小,直接爆破出来就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#前n项求和公式
def result(key):
if key!=0:
return result(key-1)+key
else:
return 0
enc=[0x0d,7,0x1d,0x25,0x1d,0x6e,0x30,0x39,0x2c,0x3f,0x2a,
0x2b,0x32,0x3f,0x2a,0x37,0x6e,0x30,0x30,0x30,0x30,0x2d,
1,7,0x31,0x2b,1,0x39,0x1f,0x3b,0x2d,0x2d,0x1b,0x3a,1,
0x0c,0x6f,0x39,0x36,0x2a,0x23]
key=[]
for i in range(999):
if result(i)%0xd3==94:
key.append(i)
for j in key:
flag=""
for i in range(41):
v4=enc[i]
v5=result(j)
flag+=chr(v4^(v5%0xd3))
print(flag)

SYC{C0ngratulati0nnnns_You_gAessEd_R1ght}

0x06 黄鸭

丢进die发现是PyInstaller封装的exe,py逆向,用pyinstxtractor 进行反编译

1
python pyinstxtractor.py test.exe

pyinstxtractor脚本主页

之后根据python的版本更改关键pyc文件的魔术头(PyObject_HEAD)魔术头的版本号可以通过查看struct文件了解。(不知道为什么我的pyin……弄出来后的pyc魔术头总是对不上号,这时可以用010editor去更改)

004bd73cd7694932bb6ce9353711dfd7

转自独奏の小屋

在线网站可以反编译pyc,python反编译 - 在线工具 (tool.lu)

uncompyle也可以,详情见uncompyle6

55efa10608db44838bb1f6968d1cf0b5找到关键的加密代码,看不懂是什么语法。。但也可以猜出来是一个向右移动13位的凯撒加密了,之后再将其他细节补全,逆出解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
enc = '~h|p4gs`gJdN`thPwR`jDn`te1w`2|RNH'
enc = enc[::-1]
flag = ''
for i in enc:
if i>='a' and i<='z':
i = chr(ord(i) - 2)
i = chr(ord(i)-13)
if ord(i) < 97:
i = chr(ord(i)+26)
flag += i
elif i>='A' and i<='Z':
i = chr(ord(i) - 2)
i=chr(ord(i)-13)
if ord(i) < 65:
i = chr(ord(i)+26)
flag += i
else:
flag += chr(ord(i)-1)
#手动将倒数第三个的 '}' 替换成 'm'
print(flag)

SYC{1_h0pe_yOu_ChAse_YoUr_dr3ams}

0x07 砍树

砍树是一道安卓逆向题,但本质感觉还是ida。考察了apk文件的本质是一个压缩包,将.apk改成.zip后解压就能发现里面的函数library,找到一个像颜文字一样的函数。这个函数在jadx静态分析界面里面是找不到的,是调用了某个外部库,然后是此题的关键加密函数。

找到这个函数后,拖到ida里面分析出来是个什么玩意,找到key=“Sycloverforerver”,逆出解密脚本

1
2
3
4
5
6
7
8
9
enc=[0,0x20,0x20,0x17,0x1b,0x36,0x0e,0x36,0x26,
0x17,4,0x2a,0x29,7,0x26,0x15,0x52,0x33,0x2d,
0x0f,0x3a,0x27,0x11,6,0x33,7,0x46,0x17,0x3d,
0x0a,0x3c,0x38,0x2e,0x22,0x18]
flag=""
key="Sycloverforerver"
for i in range(35):
flag+=chr(ord(key[i%7])^enc[i])
print(flag)

SYC{t@ke_thE_bul1_By_the_h0rns_TAT}

0x08 扎针游戏

此题是一个unity引擎的游戏,源自见缝插针。游戏引导玩家去扎30根针,可是扎完后发现还要再扎到40根。受不了这鸟气,在附带文件里面找到Assemble-Csharp.dll,丢进c#反汇编工具ILspy里面查看,查找相关字符串就可以定位到关键代码了,一个游戏流程控制代码,这里经过分析后会检测你鼠标点击的次数和扎针扎的分数,要达到100分。但是看不出解密方法。

问过大佬1k0ct后,他说用CE可以来修改score,改到100分。直接炫了一个Cheat Engine,找了一下修改方法将分数改到100,发现解出了错误flag。

后面分析发现前30次每次扎完针都会通过异或加密刷新一次密文,之后在score达到100时在游戏左上角显示出来。

之后手动扎30次针,再将score改到100,就出现了正确的flag。

0x09 rainbow

控制流平坦化,在ida里面导入了一个D-810的插件,可以将消去控制流平坦化,什么是控制流平坦化呢????利用符号执行去除控制流平坦化 - 博客 - 腾讯安全应急响应中心

自己看看吧,我也不知道。

1
2
3
4
5
6
7
8
9
10
11
12
13
enc = [0x65,0x58,0x41,0x8e,0x50,0x44,0x7b,0x62,
0x57,0x4a,0x7e,0x54,0x49,0x6c,0x7d,0x84,
0x4f,0x5b,0x95,0x60,0x60,0x64,0x77,0x48,
0x7d,0x4d,0x7b,0x9f,0x68,0x3c,0x2d,0x62]
for i in range(32):
enc[i] ^= i
for i in range(32):
if not i%3:
enc[i] -= 18
flag = ""
for i in range(32):
flag += chr(enc[i])
print(flag)

SYC{TAke_1t_3asy_Just_a_STart!!}

0x0A TEA

这是一道安卓逆向TEA,flag根据下标的奇数和偶数被分为两部分,偶数部分经过一次变种TEA加密后得到密文enc,奇数部分与enc异或得到密文enc1。

除了变种TEA外,本题的字符存储为int32位数据是是大端序存储的,注意这两点关键后就可以解出题目了(jadx里面的密文enc1被putExtra了,需要查找一个getExtra函数找到获取这个密文的代码文件,跳转过去后就能看到上面所说的enc1和异或加密操作了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enc2=[-107, -106, -95, -115, -119, 127, 26, 121, -62, -20, 86, 9]#奇数部分的密文
enc=[-91, -8, -110, -55, -49, 75, 115, 13, -76, -113, 102, 80]#偶数部分经过TEA后的密文
e=[]
#两者异或之后得到奇数部分的明文
for i in range(12):
enc2[i] &= 0xff
e.append(enc[i]^enc2[i])
arr=[]
for i in range(12):
if enc[i]<0:
enc[i]+=256
for i in range(0,12,4):
arr.append((enc[i]<<24)|(enc[i+1]<<16)|(enc[i+2]<<8)|(enc[i+3]))
for i in range(3):
print(hex(arr[i]))
for i in e:
if i<0:
i+=256
print(chr(i),end='')

得到密文enc的大端序存储数

0xa5f892c9
0xcf4b730d
0xb48f6650

这个是明文的奇数部分
0n3DF4itvc0Y

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
#include<iostream>
#include<string.h>
int arr[3] = { 0 };
unsigned char byte[12] = { 0 };
int main() {
int k0 = 2023708229, k1= -158607964, k2= -2120859654, k3 = 1167043672;
//密钥
int i;
int delta = 1640531527;
int i10= 0xa5f892c9, i11= 0xb48f6650, i12= 0xcf4b730d;
int sum = -64*delta ;
for (i = 0; i < 32; i++) {
i11 -= (((i12 << 4) + k2) ^ (i12 + sum)) ^ ((i12 >> 5) + k3);
i12 -= (((i11 << 4) + k0) ^ (i11 + sum)) ^ ((i11 >> 5) + k1);
sum += delta;
}
for (i = 0; i < 32; i++) {
i11 -= (((i10 << 4) + k2) ^ (i10 + sum)) ^ ((i10 >> 5) + k3);
i10 -= (((i11 << 4) + k0) ^ (i11 + sum)) ^ ((i11 >> 5) + k1);
sum += delta ;
}
arr[0] = i10;
arr[1] = i11;
arr[2] = i12;
int i4 = 0;
for (i = 0; i < 12; i += 4) {
byte[i] = (arr[i4] >> 24)&0xff;
byte[i + 1] = (arr[i4] >> 16) & 0xff;
byte[i + 2] = (arr[i4] >> 8) & 0xff;
byte[i + 3] = (arr[i4]) & 0xff;
i4++;
}
const char *n = "0n3DF4itvc0Y" ;
printf("SYC{");
for (i = 0; i < 12; i++) {
printf("%c", n[i]);
printf("%c", byte[i]);
}
printf("}");
return 0;

将上述密文enc和奇数部分明文输入脚本,再将密钥key啥的弄好,包裹一下SYC输出就好了

这里提一嘴int类型和unsigned int类型(简称uint),在做题时我把密文enc的数据类型设为了uint,导致后面输出的结果是错的后面查资料才发现,int类型和uint在按位运算时,前者是算术位移,后置是逻辑位移,举个例子:char a=1111 1111,>>2后是1111 1111,符号位不变

而uchar 1111 1111 ,>>2后是0011 1111,没有符号位,两者是有区别的,会影响到十六进制数的大小。

SYC{0Tn03VDtF343iTtnv0ci0tYr}

0x0B easymath

看起来就像是一个平平无奇的阅读ida代码然后写脚本的题,但是好难逆啊,结合提示整了一个z3库来约束一下条件,正向爆破出flag。

下载一个z3的库

1
pip install z3-solver

下载完后我打开pycharm然后发现 怎么都用不了,没办法,只能搞一个vscode来写

如何用vscode配置C语言环境? - 知乎 (zhihu.com)(这里放一个无关紧要的相关文章)

vscode写蟒蛇只要选个解释器就好了非常的简单。

082fdef6113444c8bb0faa58ccae7f41

ida里红色框框出的是核心的代码,需要爆破的关键。就是一个矩阵的乘法加法啥的,不用管当普通的加法乘法来算就好了。目的是求出last

26e619187dde4933a0f0d20efcb53ebf

这里有几个给定的值, 把这些提前约束好,应该是防止爆破出多解的。

6ec0376b9364473290b5d7469d53cf45

这几个函数大概的逻辑就是在flag的每一个字符,在table里面找到对应的值,并取下标然后储存到position里面,接着再用这个下标在number里面取值存到last里面,这几个步骤都是很好逆向的,所以只要求出last就好了。

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
from z3 import*
#定义一个last列表[x1,x2,x3......x24]
table="01234_asdzxcpoityumnbAOZWXGMY"
matrix=[18,29,16,19,27,
8,31,8,23,30,
29,3,28,10,21,
18,29,8,16,28,
11,30,7,20,7]
num=[1,2,3,4,5,6,7,8,9,10,
11,12,13,14,16,19,
22,26,27,28,29,31,32,
50,51,52,53,54,55,56]
#约束限制条件
flander_e=[1,0,0,0,0,
0,1,0,0,0,
0,0,1,0,0,
0,0,0,1,0,
0,0,0,0,1]
for i in range(5):
s=Solver()
last=IntVector('last',5)
if i==0:
s.add(last[1]==19)
if i==1:
s.add(last[2]==22)
if i==3:
s.add(last[2]==22)
#分个组

#for j in range(5):
#s.add(Or([last[j]==i for i in num]))
s.add(And([Or([last[j]==i for i in num])for j in range(5)]))
for j in range(5):
flander=IntVal(0)
for k in range(5):
flander=(flander+last[k]*matrix[5*k+j])%0x20
#%0x20和&0x1f等价的,但是这里的变量是int,不能按位运算
s.add(flander==flander_e[5*i+j])
result=s.check()
if result == sat:
m=s.model()
values=[m[last[w]].as_long() for w in range(5)]
for each in values:
print(table[num.index(each)],end='')
else:
print("no solution")

记得是之前听1k0ct说一次性爆出last会很慢很慢,我之后就改成了一次出last一行

i用来取行的序号,然后这里last的19,22,什么的是根据他给的t,y正推出来的,就根据那几个函数的步骤手动推出last 的已知解。然后就是不知道是什么的语法,这些查文档就好了吧(心虚)

flag已忘,望周知