逆向学习记录

RE

9月15日

[BJDCTF2020]JustRE

题目打开

有getflag选项,懒得看直接ida打开32位,字符串看下

发现了这个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
CHAR String[100]; // [esp+0h] [ebp-64h] BYREF

if ( a2 != 272 )
{
if ( a2 != 273 )
return 0;
if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
{
sprintf(String, Format, ++dword_4099F0);
if ( dword_4099F0 == 19999 )
{
sprintf(String, " BJD{%d%d2069a45792d233ac}", 19999, 0);
SetWindowTextA(hWnd, String);
return 0;
}
SetWindowTextA(hWnd, String);
return 0;
}
EndDialog(hWnd, (unsigned __int16)a3);
}
return 1;
}

直接出flag

BJD{1999902069a45792d233ac}

[GWCTF 2019]pyre

这个是一道pyre,我们可以采用工具

1
uncompyle6 -o attach.py attachment.pyc

生成反编译python去查看

可以可以先将flag输入然后确保他在128范围内也就是(input1[i] + i) % 128

接着进行了flag长度的异或操作,并且给出了加密结果。

逆向就非常简单了,进行倒序异或,刚才第一步加了多少的i就减去多少的i

再去除以128即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# uncompyle6 version 3.7.4
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.5.2 (default, Jan 26 2021, 13:30:48)
# [GCC 5.4.0 20160609]
# Embedded file name: encode.py
# Compiled at: 2019-08-19 06:01:57
print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l):
num = ((input1[i] + i) % 128 + 128) % 128
code += num

for i in range(l - 1):
code[i] = code[i] ^ code[(i + 1)]

print code
code = ['\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13']

exp

1
2
3
4
5
6
7
8
9
10
import os
code = ['\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13']
flag=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""]
print(len(code))
for i in range(len(code)-2,-1,-1):
code[i]=chr(ord(code[i])^ord(code[(i+1)]))
for i in range(len(code)):
flag[i]=(chr((ord(code[i])-i)%128))
for i in range(len(code)):
print(str(flag[i]),end='')

rsa

给了一个公钥,以及一个pcg。

我们通过网站

http://ctf.ssleye.com/pub_asys.html

可以得到

1
2
3
4
5
6
7
8
密钥类型RSA
密钥强度256
PN(e)65537
PN(n)
8693448229604811919066606200349480058890565601720302561721665405
8378322103517
DER格式
303c300d06092a864886f70d0101010500032b003028022100c0332c5c64ae47182f6c1c876d42336910545a58f7eefefc0bcaaf5af341ccdd0203010001

得到N可以再去网站

http://www.factordb.com

得到p q

最后exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import gmpy2
import rsa

e=65537
n=86934482296048119190666062003494800588905656017203025617216654058378322103517
p = 285960468890451637935629440372639283459
q = 304008741604601924494328155975272418463

phin=(q-1)*(p-1)
d=gmpy2.invert(e,phin)

key=rsa.PrivateKey(n,e,int(d),p,q)

with open("flag.enc","rb+") as f:
f=f.read()
flag=rsa.decrypt(f,key)
print(flag)

[ACTF新生赛2020]easyre

那到题目,我只能说IDA7.5非常不好用,改天换回ida7.0

ida7.5反汇编如下(加了UPX壳,直接去吾爱破解找upx3.6一键脱壳加壳神器)

就是因为这句qmemcpy(v4, “*F’"N,"(I?+@”, sizeof(v4));

卡了老半天,看别人ida7.0是非常明显的ASCII赋值,这样看就会非常明显有思路

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[12]; // [esp+12h] [ebp-2Eh] BYREF
int v5[3]; // [esp+1Eh] [ebp-22h]
char v6[5]; // [esp+2Ah] [ebp-16h] BYREF
int v7; // [esp+2Fh] [ebp-11h]
int v8; // [esp+33h] [ebp-Dh]
int v9; // [esp+37h] [ebp-9h]
char v10; // [esp+3Bh] [ebp-5h]
int i; // [esp+3Ch] [ebp-4h]

__main();
qmemcpy(v4, "*F'\"N,\"(I?+@", sizeof(v4));
printf("Please input:");
scanf("%s", v6);
if ( v6[0] != 0x41 || v6[1] != 67 || v6[2] != 84 || v6[3] != 70 || v6[4] != 123 || v10 != 125 )
return 0;
v5[0] = v7;
v5[1] = v8;
v5[2] = v9;
for ( i = 0; i <= 11; ++i )
{
if ( v4[i] != _data_start__[*((char *)v5 + i) - 1] )
return 0;
}
printf("You are correct!");
return 0;
}

这个加密方法挺简单的,就是根据你输入的字符串然后呢在去匹配加密密钥_data_start__

假设密钥是abcdefg,我明文是bg,密文就是27。同理给出了加密结果

qmemcpy(v4, “*F’"N,"(I?+@”, sizeof(v4));

只要用jio本匹配这些字符在的ascll在密钥中的位置所表示的字符再去输出出来就是明文了

exp如下

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
#coding=utf-8
key = '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !"' #'一定要加\
encrypt = [42,70,39,34,78,44,34,40,73,63,43,64]
x = []
flag = ''
for i in encrypt:
x.append(key.find(chr(i))+1)
for i in x:
flag += chr(i)
print(flag+'\n')
print(x)


CrackRTF

直接md5百度爆破花钱解决的事情,之前有人花钱过了,直接拿就行了非预期解免费网站(https://www.somd5.com/)

第二个md5=~!3a@0123321@DBApp

第一次密码123321

第二次密码~!3a@0

之后会生成dbapp.rtf

直接找这个文件

Flag{N0_M0re_Free_Bugs}

[2019红帽杯]easyRE

一道非常好的题目,题目不难,却胜在人性的弱点,说实话我一打开搜索字符串看见base64加密人就来劲了,

以为只要10次base64就能得到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
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
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
int result; // eax
unsigned __int64 v5; // rax
__int64 v6; // rax
int i; // [rsp+Ch] [rbp-114h]
__int64 v8; // [rsp+10h] [rbp-110h]
__int64 v9; // [rsp+18h] [rbp-108h]
__int64 v10; // [rsp+20h] [rbp-100h]
__int64 v11; // [rsp+28h] [rbp-F8h]
__int64 v12; // [rsp+30h] [rbp-F0h]
__int64 v13; // [rsp+38h] [rbp-E8h]
__int64 v14; // [rsp+40h] [rbp-E0h]
__int64 v15; // [rsp+48h] [rbp-D8h]
__int64 v16; // [rsp+50h] [rbp-D0h]
__int64 v17; // [rsp+58h] [rbp-C8h]
char v18[13]; // [rsp+60h] [rbp-C0h] BYREF
char v19[4]; // [rsp+6Dh] [rbp-B3h] BYREF
char v20[19]; // [rsp+71h] [rbp-AFh] BYREF
char v21[32]; // [rsp+90h] [rbp-90h] BYREF
int v22; // [rsp+B0h] [rbp-70h]
char v23; // [rsp+B4h] [rbp-6Ch]
__m128i v24[4]; // [rsp+C0h] [rbp-60h] BYREF
char v25; // [rsp+100h] [rbp-20h]
unsigned __int64 v26; // [rsp+108h] [rbp-18h]

v26 = __readfsqword(0x28u);
qmemcpy(v18, "Iodl>Qnb(ocy", 12);
v18[12] = 127;
qmemcpy(v19, "y.i", 3);
v19[3] = 127;
qmemcpy(v20, "d`3w}wek9{iy=~yL@EC", sizeof(v20));
memset(v21, 0, sizeof(v21));
v22 = 0;
v23 = 0;
sub_4406E0(0, v21, 0x25uLL);
v23 = 0;
LODWORD(v3) = sub_424BA0((const __m128i *)v21);
if ( v3 == 36 )
{
for ( i = 0; ; ++i )
{
LODWORD(v5) = sub_424BA0((const __m128i *)v21);
if ( i >= v5 )
break;
if ( (unsigned __int8)(v21[i] ^ i) != v18[i] )
{
result = 0xFFFFFFFE;
goto LABEL_13;
}
}
sub_410CC0("continue!");
memset(v24, 0, sizeof(v24));
v25 = 0;
sub_4406E0(0, (char *)v24, 0x40uLL);
v24[2].m128i_i8[7] = 0;
LODWORD(v6) = sub_424BA0(v24);
if ( v6 == 39 )
{
v8 = base64((__int64)v24);
v9 = base64(v8);
v10 = base64(v9);
v11 = base64(v10);
v12 = base64(v11);
v13 = base64(v12);
v14 = base64(v13);
v15 = base64(v14);
v16 = base64(v15);
v17 = base64(v16);
if ( !(unsigned int)sub_400360(v17, (__int64)off_6CC090) )
{
sub_410CC0("You found me!!!");
sub_410CC0("bye bye~");
}
result = 0;
}
else
{
result = -3;
}
}
else
{
result = -1;
}
LABEL_13:
if ( __readfsqword(0x28u) != v26 )
sub_444020();
return result;
}

如果是真正打比赛,成百上千函数无异于大海捞针,但是我们现在在练习的角度冷静思考,这是比赛的东西,那么就不会变态的为难

做题的人,我们可以根据人性的心理在主函数的上下函数去查找。结果发现main函数的下面是存在一个加密函数的。

先进行了判断输入的第一个字符和第四个字符是不是f,g,那么我们继续玩心理战,他判断的范围在1-4,常规flag开头就是flag

所以我们可以判断这个四个字符绝对就是flag,下面他就会对byte_6CC0A0中的字符进行异或操作加密

异或加密操作用到的密码我们可以根据

v1 ^ byte_6CC0A0[0]) == ‘f’

这段代码推测得知是明文的前4个字符与’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
unsigned __int64 jiami()
{
unsigned __int64 result; // rax
unsigned int v1; // [rsp+Ch] [rbp-24h]
int i; // [rsp+10h] [rbp-20h]
int j; // [rsp+14h] [rbp-1Ch]
unsigned int v4; // [rsp+24h] [rbp-Ch]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
v1 = sub_43FD20(0LL) - qword_6CEE38;
for ( i = 0; i <= 1233; ++i )
{
sub_40F790(v1);
sub_40FE60();
sub_40FE60();
v1 = sub_40FE60() ^ 0x98765432;
}
v4 = v1;
if ( ((unsigned __int8)v1 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v4) ^ (unsigned __int8)byte_6CC0A3) == 'g' )
{
for ( j = 0; j <= 24; ++j )
sub_410E90((unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v4 + j % 4)));
}
result = __readfsqword(0x28u) ^ v5;
if ( result )
sub_444020();
return result;
}

exp

1
2
3
4
5
6
7
8
9
10
11
12
s = [0x40, 0x35, 0x20, 0x56, 0x5D, 0x18, 0x22, 0x45, 0x17, 0x2F, 0x24, 0x6E, 0x62, 0x3C, 0x27, 0x54, 0x48, 0x6C, 0x24, 0x6E, 0x72, 0x3C, 0x32, 0x45, 0x5B]
s1 = 'flag'
flag = ''
key = ''

for i in range(4):
key += chr(s[i] ^ ord(s1[i]))

for i in range(len(s)):
flag += chr(s[i] ^ ord(key[i % 4]))

print(flag)

[ACTF新生赛2020]rome

main

ida7.5==-==打死我也不想用了

可以看见给了密文 strcpy(v12, “Qsw3sj_lz4_Ujw@l”);记得转成ASCII

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
int func()
{
int result; // eax
int v1[4]; // [esp+14h] [ebp-44h]
unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
unsigned __int8 v3; // [esp+25h] [ebp-33h]
unsigned __int8 v4; // [esp+26h] [ebp-32h]
unsigned __int8 v5; // [esp+27h] [ebp-31h]
unsigned __int8 v6; // [esp+28h] [ebp-30h]
int v7; // [esp+29h] [ebp-2Fh]
int v8; // [esp+2Dh] [ebp-2Bh]
int v9; // [esp+31h] [ebp-27h]
int v10; // [esp+35h] [ebp-23h]
unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF

strcpy(v12, "Qsw3sj_lz4_Ujw@l");
printf("Please input:");
scanf("%s", &v2);
result = v2;
if ( v2 == 'A' )
{
result = v3;
if ( v3 == 'C' )
{
result = v4;
if ( v4 == 'T' )
{
result = v5;
if ( v5 == 'F' )
{
result = v6;
if ( v6 == '{' )
{
result = v11;
if ( v11 == '}' )
{
v1[0] = v7;
v1[1] = v8;
v1[2] = v9;
v1[3] = v10;
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > '@' && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'Z' )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > '`' && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'z' )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
++*(_DWORD *)&v12[17];
}
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
return result;
++*(_DWORD *)&v12[17];
}
result = printf("You are correct!");
}
}
}
}
}
}
return result;
}

重点判断如下

1
2
3
4
5
6
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > '@' && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'Z' )//判断大写
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > '`' && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'z' )//判断小写
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
++*(_DWORD *)&v12[17];
}

这个都不算逆向的感觉。。。直接顺势思维把这段代码抄下来,因为数组是17下标是0-16,然后ASCII一共就128个

直接脚本一把梭

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#coding=utf-8
x = [81,115,119,51,115,106,95,108,122,52,95,85,106,119,64,108]
flag = ''
for k in range(0,16):
for i in range(0,127): #ASCII到目前为止共定义了128个字符,挨个儿试
z = i
if i > 64 and i <= 90:
i = (i-51)%26 + 65
if i > 96 and i <= 122:
i = (i-79)%26 + 97
if(i == x[k]):
flag += chr(z)

print(flag)

[FlareOn4]login

网页源代码如下

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

<!DOCTYPE Html />
<html>
<head>
<title>FLARE On 2017</title>
</head>
<body>
<input type="text" name="flag" id="flag" value="Enter the flag" />
<input type="button" id="prompt" value="Click to check the flag" />
<script type="text/javascript">
document.getElementById("prompt").onclick = function () {
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}
</script>
</body>
</html>

flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= “Z” ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});

重点在这,首先进行了字母大小写的判断c <= “Z” ? 90 : 122

其次对属于字母范围内的数据,进行加13,看大于不大于当前的Z(所谓当前的Z是因为第一步进行了大小写的判断,大写对于大Z,小写对应小z)

如果小于就保持不动,大于就减去26,相当于对明文进行了减去13

我们逆向的思维就是,判断大小写,给他-13看他小于当前的A不,小于就+13,不然就+13,不在字母范围内的就不管

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os
code = 'PyvragFvqrYbtvafNerRnfl@syner-ba.pbz'
flag=""
for i in code:
if ord(i) >=65 and ord(i) <=90:
if ord(i)-13<65:
flag+=chr(ord(i)+13)
else:
flag+=chr(ord(i)-13)
elif ord(i) >=97 and ord(i) <=122:
if ord(i)-13<97:
flag+=chr(ord(i)+13)
else:
flag+=chr(ord(i)-13)
else:
flag+=i
print(flag)

[GUET-CTF2019]re

无聊题,放到现在就是真无聊,直接UPX3.6一键脱壳,接着看见加密密钥就是乘上明文对比加密后的密文是否相等

除以就行了,然后这里没有a[6]直接爆破就行了

exp

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
# -*- coding:utf-8 -*-

a1 = chr(166163712 // 1629056)
a2 = chr(731332800 // 6771600)
a3 = chr(357245568 // 3682944)
a4 = chr(1074393000 // 10431000)
a5 = chr(489211344 // 3977328)
a6 = chr(518971936 // 5138336)
a8 = chr(406741500 // 7532250)
a9 = chr(294236496 // 5551632)
a10 = chr(177305856 // 3409728)
a11 = chr(650683500 // 13013670)
a12 = chr(298351053 // 6088797)
a13 = chr(386348487 // 7884663)
a14 = chr(438258597 // 8944053)
a15 = chr(249527520 // 5198490)
a16 = chr(445362764 // 4544518)
a17 = chr(981182160 // 10115280)
a18 = chr(174988800 // 3645600)
a19 = chr(493042704 // 9667504)
a20 = chr(257493600 // 5364450)
a21 = chr(767478780 // 13464540)
a22 = chr(312840624 // 5488432)
a23 = chr(1404511500 // 14479500)
a24 = chr(316139670 // 6451830)
a25 = chr(619005024 // 6252576)
a26 = chr(372641472 // 7763364)
a27 = chr(373693320 // 7327320)
a28 = chr(498266640 // 8741520)
a29 = chr(452465676 // 8871876)
a30 = chr(208422720 // 4086720)
a31 = chr(515592000 // 9374400)
a32 = chr(719890500 // 5759124)

print (a1,a2,a3,a4,a5,a6,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32)
1
2
flag{e165421110ba03099a1c039337}

[SUCTF2019]SignIn

挺好玩的一道题,就是有点依赖工具?

我们看下main函数

和师兄看过一些密码,一看就知道这个东西是rsa,这里借鉴下夏了茶糜

的rsa讲解

img

图中的C是密文,M是明文,E是公钥(E和 φ(N)互为质数),N是公共模数(质数 P 、Q相乘得到N),MOD就是模运算
解密算法:

img

由此呢我们现在知道的数据如下

1
2
3
C = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
N = 103461035900816914121390101299049044413950405173712170434161686539878160984549
E = 65537
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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char v4[16]; // [rsp+0h] [rbp-4A0h] BYREF
char v5[16]; // [rsp+10h] [rbp-490h] BYREF
char v6[16]; // [rsp+20h] [rbp-480h] BYREF
char v7[16]; // [rsp+30h] [rbp-470h] BYREF
char v8[112]; // [rsp+40h] [rbp-460h] BYREF
char v9[1000]; // [rsp+B0h] [rbp-3F0h] BYREF
unsigned __int64 v10; // [rsp+498h] [rbp-8h]

v10 = __readfsqword(0x28u);
puts("[sign in]");
printf("[input your flag]: ");
__isoc99_scanf("%99s", v8);
sub_96A(v8, v9);
__gmpz_init_set_str(v7, "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
__gmpz_init_set_str(v6, v9, 16LL);
__gmpz_init_set_str(v4, "103461035900816914121390101299049044413950405173712170434161686539878160984549", 10LL);
__gmpz_init_set_str(v5, "65537", 10LL);
__gmpz_powm(v6, v6, v5, v4);
if ( (unsigned int)__gmpz_cmp(v6, v7) )
puts("GG!");
else
puts("TTTTTTTTTTql!");
return 0LL;
}

使用https://nchc.dl.sourceforge.net/project/yafu/1.34/yafu-1.34.zip yafu工具去分解N得到p 和 q

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
C:\Users\20138\Desktop\yafu-1.34>yafu-x64.exe
factor(103461035900816914121390101299049044413950405173712170434161686539878160984549)


fac: factoring 103461035900816914121390101299049044413950405173712170434161686539878160984549
fac: using pretesting plan: normal
fac: no tune info: using qs/gnfs crossover of 95 digits
div: primes less than 10000
fmt: 1000000 iterations
rho: x^2 + 3, starting 1000 iterations on C78
rho: x^2 + 2, starting 1000 iterations on C78
rho: x^2 + 1, starting 1000 iterations on C78
pm1: starting B1 = 150K, B2 = gmp-ecm default on C78
ecm: 30/30 curves on C78, B1=2K, B2=gmp-ecm default
ecm: 74/74 curves on C78, B1=11K, B2=gmp-ecm default
ecm: 161/161 curves on C78, B1=50K, B2=gmp-ecm default, ETA: 0 sec

starting SIQS on c78: 103461035900816914121390101299049044413950405173712170434161686539878160984549

==== sieving in progress (1 thread): 36224 relations needed ====
==== Press ctrl-c to abort and save state ====
36319 rels found: 18931 full + 17388 from 186289 partial, (2607.49 rels/sec)

SIQS elapsed time = 79.8481 seconds.
Total factoring time = 93.8713 seconds


***factors found***

P39 = 366669102002966856876605669837014229419
P39 = 282164587459512124844245113950593348271

ans = 1

之后直接用Python的gmpy2模块解密就行了

现在我们有了P、Q和E,我们就可以计算出欧拉函数,然后我们就可以通过欧拉函数φ(N)和公钥E计算出私钥D。
使用python的gmpy2库计算私钥。(E * D % φ(N) = 1)

1
2
d = gmpy2.invert(e,(p-1)*(q-1))
d = 91646299298871237857836940212608056141193465208586711901499120163393577626813

计算出私钥d后我们就可以对密文C进行解密,解密算法是(密文C的私钥D次方对公共模数N取余)
使用python的gmpy2库计算明文

1
m = gmpy2.powmod(c,d,n)

得到

1
m = 185534734614696481020381637136165435809958101675798337848243069

把m转为字符串即可!
解密脚本

1
2
3
4
5
6
7
8
9
10
11
import gmpy2
import binascii

p = 282164587459512124844245113950593348271
q = 366669102002966856876605669837014229419
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
n = 103461035900816914121390101299049044413950405173712170434161686539878160984549
e = 65537
d = gmpy2.invert(e,(p-1)*(q-1))
m = gmpy2.powmod(c,d,n)
print(binascii.unhexlify(hex(m)[2:]).decode(encoding="utf-8"))
1
suctf{Pwn_@_hundred_years}

Youngter-drive

upx壳子直接老样子upx3.6解开,去看main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
HANDLE v4; // [esp+D0h] [ebp-14h]
HANDLE hObject; // [esp+DCh] [ebp-8h]

j_getflag();
::hObject = CreateMutexW(0, 0, 0);
j_strcpy(Destination, Source);
hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);//线程1
v4 = CreateThread(0, 0, j_fork2, 0, 0, 0);//线程2
CloseHandle(hObject);//close 1
CloseHandle(v4);//close 2
while ( dword_418008 != -1 )
;
j_result();
CloseHandle(::hObject);
return 0;
}

我们跟进线程1去康康

发现他会对数据进行加密操作,我们去康康怎么加密的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void __stdcall StartAddress_0(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( dword_418008 > -1 )
{
j_en((int)Source, dword_418008);
--dword_418008;
Sleep(0x64u);
}
ReleaseMutex(hObject);
}
}

如下,不难发现先做了次字母判断,然后判断大小写,大写字母的明文加密如下

a2 + a1代表的就是明文,off_418000是密钥

*(_BYTE )(a2 + a1) =off_418000[0][(char *)(a2 + a1) - 38];这部分加密的意思就是

将密钥中的[0][明文-38]对应的字符当成密文,小写字母同理。

那么逆向思维下就是大写字母加38小写字母加96算法简直是一模一样的=-=

但是坑爹的在后面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// positive sp value has been detected, the output may be wrong!
char *__cdecl en(int a1, int a2)
{
char *result; // eax
char v3; // [esp+D3h] [ebp-5h]

v3 = *(_BYTE *)(a2 + a1);
if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) )
exit(0);
if ( v3 < 97 || v3 > 122 )
{
result = off_418000[0];
*(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 38];
}
else
{
result = off_418000[0];
*(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 96];
}
return result;
}

我们刚才说了有两个线程

相当于主线程分开两部分指向,线程1结束执行线程2,我们还要去康康线程2干嘛了

并没对数据进行处理只是dword_418008-1,线程1也有。

1
2
3
4
5
6
7
8
9
10
11
12
13
void __stdcall fork2(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( dword_418008 > -1 )
{
Sleep(0x64u);
--dword_418008;
}
ReleaseMutex(hObject);
}
}

回到主函数看如果dword_418008减到0了就会结束整个程序。

意思很明确了,对于明文加密,明文当成数组,奇数位置的就加密,偶数位置不变,由此exp如下

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
flagpart = 'TOiZiZtOrYaToUwPnToBsOaOapsyS'
flagrange = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
flag = ''
for i in range(len(flagpart)):
if i % 2 == 0:
flag += flagpart[i]
else:
if flagpart[i].isupper():
flag += chr(flagrange.find(flagpart[i]) + 96)
else:
flag += chr(flagrange.find(flagpart[i]) + 38)

print(flag)

[WUSTCTF2020]level1

给了加密结果,加密方法在main

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+4h] [rbp-2Ch]
FILE *stream; // [rsp+8h] [rbp-28h]
char ptr[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v7; // [rsp+28h] [rbp-8h]

v7 = __readfsqword(0x28u);
stream = fopen("flag", "r");
fread(ptr, 1uLL, 0x14uLL, stream);
fclose(stream);
for ( i = 1; i <= 19; ++i )
{
if ( (i & 1) != 0 )
printf("%ld\n", (unsigned int)(ptr[i] << i));
else
printf("%ld\n", (unsigned int)(i * ptr[i]));
}
return 0;
}

直接把左移改右移,*改成/就行了

exp如下

exp

1
2
3
4
5
6
7
8
9
a = [198,232,816,200,1536,300,6144,984,51200,570,92160,1200,565248,756,1474560,800,6291456,1782,65536000]

for i in range(19):
if ((i+1) & 1):
print(chr(a[i] >> (i+1)),end="")
else:
print (chr(a[i] // (i+1)),end="")


[ACTF新生赛2020]usualCrypt

main

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // esi
int result; // eax
int v5[3]; // [esp+8h] [ebp-74h] BYREF
__int16 v6; // [esp+14h] [ebp-68h]
char v7; // [esp+16h] [ebp-66h]
char v8[100]; // [esp+18h] [ebp-64h] BYREF

sub_403CF8(&unk_40E140);
scanf("%s", v8);
v5[0] = 0;
v5[1] = 0;
v5[2] = 0;
v6 = 0;
v7 = 0;
magicbase64(v8, strlen(v8), v5);
v3 = 0;
while ( *((_BYTE *)v5 + v3) == byte_40E0E4[v3] )
{
if ( ++v3 > strlen((const char *)v5) )
goto LABEL_6;
}
sub_403CF8(aError);
LABEL_6:
if ( v3 - 1 == strlen(byte_40E0E4) )
result = sub_403CF8(aAreYouHappyYes);
else
result = sub_403CF8(aAreYouHappyNo);
return result;
}

魔改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
int __cdecl magicbase64(int a1, int a2, int a3)
{
int v3; // edi
int v4; // esi
int v5; // edx
int v6; // eax
int v7; // ecx
int v8; // esi
int v9; // esi
int v10; // esi
int v11; // esi
_BYTE *v12; // ecx
int v13; // esi
int v15; // [esp+18h] [ebp+8h]

v3 = 0;
v4 = 0;
magic();
v5 = a2 % 3;
v6 = a1;
v7 = a2 - a2 % 3;
v15 = a2 % 3;
if ( v7 > 0 )
{
do
{
LOBYTE(v5) = *(_BYTE *)(a1 + v3);
v3 += 3;
v8 = v4 + 1;
*(_BYTE *)(v8 + a3 - 1) = byte_40E0A0[(v5 >> 2) & 0x3F];
*(_BYTE *)(++v8 + a3 - 1) = byte_40E0A0[16 * (*(_BYTE *)(a1 + v3 - 3) & 3)
+ (((int)*(unsigned __int8 *)(a1 + v3 - 2) >> 4) & 0xF)];
*(_BYTE *)(++v8 + a3 - 1) = byte_40E0A0[4 * (*(_BYTE *)(a1 + v3 - 2) & 0xF)
+ (((int)*(unsigned __int8 *)(a1 + v3 - 1) >> 6) & 3)];
v5 = *(_BYTE *)(a1 + v3 - 1) & 0x3F;
v4 = v8 + 1;
*(_BYTE *)(v4 + a3 - 1) = byte_40E0A0[v5];
}
while ( v3 < v7 );
v5 = v15;
}
if ( v5 == 1 )
{
LOBYTE(v7) = *(_BYTE *)(v3 + a1);
v9 = v4 + 1;
*(_BYTE *)(v9 + a3 - 1) = byte_40E0A0[(v7 >> 2) & 0x3F];
v10 = v9 + 1;
*(_BYTE *)(v10 + a3 - 1) = byte_40E0A0[16 * (*(_BYTE *)(v3 + a1) & 3)];
*(_BYTE *)(v10 + a3) = 61;
LABEL_8:
v13 = v10 + 1;
*(_BYTE *)(v13 + a3) = 61;
v4 = v13 + 1;
goto LABEL_9;
}
if ( v5 == 2 )
{
v11 = v4 + 1;
*(_BYTE *)(v11 + a3 - 1) = byte_40E0A0[((int)*(unsigned __int8 *)(v3 + a1) >> 2) & 0x3F];
v12 = (_BYTE *)(v3 + a1 + 1);
LOBYTE(v6) = *v12;
v10 = v11 + 1;
*(_BYTE *)(v10 + a3 - 1) = byte_40E0A0[16 * (*(_BYTE *)(v3 + a1) & 3) + ((v6 >> 4) & 0xF)];
*(_BYTE *)(v10 + a3) = byte_40E0A0[4 * (*v12 & 0xF)];
goto LABEL_8;
}
LABEL_9:
*(_BYTE *)(v4 + a3) = 0;
return big_to_small((const char *)a3);
}

magic函数里面就是对base64置换表中从6-15进行的一个替换,从ida可以看出偏移是10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.data:0040E0A0 byte_40E0A0     db 41h                  ; DATA XREF: magic:loc_401005↑r
.data:0040E0A0 ; magic+17↑w ...
.data:0040E0A1 db 42h ; B
.data:0040E0A2 db 43h ; C
.data:0040E0A3 db 44h ; D
.data:0040E0A4 db 45h ; E
.data:0040E0A5 db 46h ; F
.data:0040E0A6 db 47h ; G
.data:0040E0A7 db 48h ; H
.data:0040E0A8 db 49h ; I
.data:0040E0A9 db 4Ah ; J
.data:0040E0AA ; char byte_40E0AA[]
.data:0040E0AA byte_40E0AA db 4Bh ; DATA XREF: magic+B↑r
.data:0040E0AA ; magic+11↑w
.data:0040E0AB aLmnopqrstuvwxy db 'LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',0

A0-AA刚好10个,然后最后的big_to_small就是个普通的大小写置换

其他中间一大串就是base64加密

我们捋下逆向思路,首先把加密结果给进行大小写转化,其次进行简单的temp操作得到魔改置换表,

接着通过魔改置换表和密文得到正常的base64,最后直接解base64

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#coding=utf-8
import base64
secret = 'zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9'.swapcase() #大小写转换
a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
dict = {}
offset = 10
flag = ''
for i in range(len(a)):
dict[a[i]]=a[i]
for i in range(6,15):
b=dict[a[i]]
dict[a[i]]=dict[a[i+offset]]
dict[a[i+offset]]=b
for i in range(len(secret)):
flag += dict[secret[i]]
flag = base64.b64decode(flag)
print(flag)

[MRCTF2020]Transform

简单题,直接一把梭哈,就是ida7.5毛病真的是字符串提取贼麻烦

main

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str[104]; // [rsp+20h] [rbp-70h] BYREF
int j; // [rsp+88h] [rbp-8h]
int i; // [rsp+8Ch] [rbp-4h]

sub_402230(argc, argv, envp);
printf("Give me your code:\n");
scanf("%s", Str);
if ( strlen(Str) != 33 )
{
printf("Wrong!\n");
system("pause");
exit(0);
}
for ( i = 0; i <= 32; ++i )
{
enstr[i] = Str[key[i]];
enstr[i] ^= LOBYTE(key[i]);
}
for ( j = 0; j <= 32; ++j )
{
if ( flag[j] != enstr[j] )
{
printf("Wrong!\n");
system("pause");
exit(0);
}
}
printf("Right!Good Job!\n");
printf("Here is your flag: %s\n", Str);
system("pause");
return 0;
}

第一步将加密结果保存为输入的字符串中密钥所表达的ASCII表代表的位置

第二步去和密钥异或,这样直接倒过来getflag

exp

1
2
3
4
5
6
7
8
9
key = [0x9, 0x0A, 0x0F, 0x17, 0x7, 0x18, 0x0C, 0x6, 0x1, 0x10, 0x3, 0x11, 0x20, 0x1D, 0x0B, 0x1E, 0x1B, 0x16, 0x4, 0x0D, 0x13, 0x14, 0x15, 0x2, 0x19, 0x5, 0x1F, 0x8, 0x12, 0x1A, 0x1C, 0x0E, 0]
enflag= [0x67, 0x79, 0x7B, 0x7F, 0x75, 0x2B, 0x3C, 0x52, 0x53, 0x79, 0x57, 0x5E, 0x5D, 0x42, 0x7B, 0x2D, 0x2A, 0x66, 0x42, 0x7E, 0x4C, 0x57, 0x79, 0x41, 0x6B, 0x7E, 0x65, 0x3C, 0x5C, 0x45, 0x6F, 0x62, 0x4D]
flag=[0]*33
for i in range(len(key)):
enflag[i]^=key[i]
for i in range(len(key)):
flag[key[i]]=enflag[i]
for i in range(len(key)):
print(chr(flag[i]),end="")

[GWCTF 2019]xxor

看见这个题说来惭愧,自己出了道re难度偏难包含魔改凯撒,魔改rc4,魔改xtea算法,看见这里有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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int i; // [rsp+8h] [rbp-68h]
int j; // [rsp+Ch] [rbp-64h]
__int64 v6[6]; // [rsp+10h] [rbp-60h] BYREF
__int64 v7[6]; // [rsp+40h] [rbp-30h] BYREF

v7[5] = __readfsqword(0x28u);
puts("Let us play a game?");
puts("you have six chances to input");
puts("Come on!");
v6[0] = 0LL;
v6[1] = 0LL;
v6[2] = 0LL;
v6[3] = 0LL;
v6[4] = 0LL;
for ( i = 0; i <= 5; ++i )
{
printf("%s", "input: ");
__isoc99_scanf("%d", (char *)v6 + 4 * i);
}
v7[0] = 0LL;
v7[1] = 0LL;
v7[2] = 0LL;
v7[3] = 0LL;
v7[4] = 0LL;
for ( j = 0; j <= 2; ++j )
{
dword_601078 = v6[j];
dword_60107C = HIDWORD(v6[j]);
tea((unsigned int *)&dword_601078, dword_601060);
LODWORD(v7[j]) = dword_601078;
HIDWORD(v7[j]) = dword_60107C;
}
if ( (unsigned int)confirm(v7) != 1 )
{
puts("NO NO NO~ ");
exit(0);
}
puts("Congratulation!\n");
puts("You seccess half\n");
puts("Do not forget to change input to hex and combine~\n");
puts("ByeBye");
return 0LL;
}

我已经patch好了变量名,一目了然该干嘛干嘛,其实这题挺简单的,

我们先顺着来,输入明文,进行两次加密,其中每一次加密过程如下

取明文的下标j进行加密,其中601060存放的是密钥。

由此推测被加密的数据是明文的1,2。我们去康康这个tea算法怎么玩的。

tea

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 __fastcall tea(unsigned int *a1, _DWORD *a2)
{
__int64 result; // rax
unsigned int v3; // [rsp+1Ch] [rbp-24h]
unsigned int v4; // [rsp+20h] [rbp-20h]
int v5; // [rsp+24h] [rbp-1Ch]
unsigned int i; // [rsp+28h] [rbp-18h]

v3 = *a1;
v4 = a1[1];
v5 = 0;
for ( i = 0; i <= 63; ++i )
{
v5 += 0x458BCD42;
v3 += (v4 + v5 + 11) ^ ((v4 << 6) + *a2) ^ ((v4 >> 9) + a2[1]) ^ 0x20;
v4 += (v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 0x10;
}
*a1 = v3;
result = v4;
a1[1] = v4;
return result;
}

关键点在for那,就是个tea算法。

我们去看confirm函数

confirm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__int64 __fastcall confirm(_DWORD *a1)
{
__int64 result; // rax

if ( a1[2] - a1[3] == 2225223423LL
&& a1[3] + a1[4] == 4201428739LL
&& a1[2] - a1[4] == 1121399208LL
&& *a1 == 3746099070
&& a1[5] == 2230518816
&& a1[1] == 550153460 )
{
puts("good!");
result = 1LL;
}
else
{
puts("Wrong!");
result = 0LL;
}
return result;
}

加密后的数据全在这里了。

那么现在开始逆向分析推导

第一步进行逆向tea算法

第二步输出

这里的关键会在脚本中标注

exp

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
#include <stdio.h>

int main()
{
unsigned int a1[]={0,0,0,0,0,0};
a1[0]=0xDF48EF7E;
a1[5]=0x84F30420;
a1[1]=0x20CAACF4;
a1[3]=(0x42D731A8-0x84A236FF+0xFA6CB703)/2;
a1[4]=0xFA6CB703-a1[3];
a1[2]=a1[3]+0x84A236FF;
int i = 0,j=0;
int temp[2] = {0};
int data[4] = { 2,2,3,4 };

for (i = 0; i < =2; ++1)
{
unsigned int v3 = a1[i];
unsigned int v4 = a1[i + 1];/*v3,v4的值为什么是这个v3 = *a1;
v4 = a1[1];从这两个在tea函数中的变量可知*/

long long v5 = 0x458BCD42 * 64;//总共加密了64次所以逆向的时候他其实是64倍
for ( j = 0; j < 64; j++)
{
v4 -= (v3 + v5 + 20) ^ ((v3 << 6) + 3) ^ ((v3 >> 9) + 4) ^ 0x10;
v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + 2) ^ ((v4 >> 9) + 2) ^ 0x20;
v5 -= 0x458BCD42;
}
a1[i] = v3;
a1[i + 1] = v4;//每次解密后让密文的值更改为明文。
}
for (i = 0; i < 6; i++)
printf("%c%c%c", *((char*)&a1[i]+2), *((char*)&a1[i] + 1), *(char*)&a1[i]);
}

[MRCTF2020]Xor

easy

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int i; // eax

printf((int)"Give Me Your Flag String:\n");
scanf("%s", byte_4212C0);
if ( strlen(byte_4212C0) != 27 )
{
LABEL_6:
printf((int)"Wrong!\n");
system("pause");
_loaddll(0);
__debugbreak();
}
for ( i = 0; i < 27; ++i )
{
if ( ((unsigned __int8)i ^ (unsigned __int8)byte_4212C0[i]) != byte_41EA08[i] )
goto LABEL_6;
}
printf((int)"Right!\n");
system("pause");
return 0;
}

直接异或搞定

exp

1
2
3
4
5
6
import os
en='MSAWB~FXZ:J:`tQJ"N@ bpdd}8g'
flag=''
for i in range(len(en)):
flag+=chr(i^ord(en[i]))
print((flag),end="")

[FlareOn4]IgniteMe

好题目我很喜欢。又增强了我的逆向思维,以及win下的初次动调(其实献给了IOT)

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void __noreturn start()
{
DWORD NumberOfBytesWritten; // [esp+0h] [ebp-4h] BYREF

NumberOfBytesWritten = 0;
hFile = GetStdHandle(0xFFFFFFF6);
dword_403074 = GetStdHandle(0xFFFFFFF5);
WriteFile(dword_403074, giveme, 0x13u, &NumberOfBytesWritten, 0);
sub_4010F0(NumberOfBytesWritten);
if ( en() )
WriteFile(dword_403074, aG00dJ0b, 0xAu, &NumberOfBytesWritten, 0);
else
WriteFile(dword_403074, aN0tT00H0tRWe7r, 0x24u, &NumberOfBytesWritten, 0);
ExitProcess(0);
}

没什么好说的直接单刀直入加密函数en

en

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int sub_401050()
{
int v1; // [esp+0h] [ebp-Ch]
int i; // [esp+4h] [ebp-8h]
unsigned int j; // [esp+4h] [ebp-8h]
char v4; // [esp+Bh] [ebp-1h]

v1 = sub_401020((int)byte_403078);
v4 = sub_401000();
for ( i = v1 - 1; i >= 0; --i )
{
byte_403180[i] = v4 ^ byte_403078[i];
v4 = byte_403078[i];
}
for ( j = 0; j < 39; ++j )
{
if ( byte_403180[j] != (unsigned __int8)byte_403000[j] )
return 0;
}
return 1;
}

403180作为我们的一个密文保存,403078是明文的输入保存,虽然从程序来看,v1好像是无限的其实我们看下面就知道

真实的值是39,继续看那个v4(大病,忘记了可以动态调试看内存,pwn玩的熟悉的东西反而没带过来,惭愧)

我们在汇编

1
.text:0040106B                 mov     [ebp+var_1], al

此处下断点,var_1就是v4不懂汇编的自己回去补去

结果发现是4。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding:utf-8 -*-

arr2 = [0x0D,0x26,0x49,0x45,0x2A,0x17,0x78,0x44,0x2B,0x6C,0x5D,0x5E,0x45,0x12,0x2F,0x17,
0x2B,0x44,0x6F,0x6E,0x56,0x09,0x5F,0x45,0x47,0x73,0x26,0x0A,0x0D,0x13,0x17,0x48,
0x42,0x01,0x40,0x4D,0x0C,0x02,0x69]

arr1 = []
v4 = 4
for i in range(len(arr2)-1,-1,-1):
arr1.append(arr2[i] ^ v4)
v4 = arr1[-1]
for i in range(len(arr2)-1,-1,-1):
print (chr(arr1[i]),end="")

[WUSTCTF2020]level3

base64置换表更改的

main

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v3; // rax
char v5; // [rsp+Fh] [rbp-41h]
char v6[56]; // [rsp+10h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+48h] [rbp-8h]

v7 = __readfsqword(0x28u);
printf("Try my base64 program?.....\n>");
__isoc99_scanf("%20s", v6);
v5 = time(0LL);
srand(v5);
if ( (rand() & 1) != 0 )
{
v3 = (const char *)base64_encode(v6);
puts(v3);
puts("Is there something wrong?");
}
else
{
puts("Sorry I think it's not prepared yet....");
puts("And I get a strange string from my program which is different from the standard base64:");
puts("d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD==");
puts("What's wrong??");
}
return 0;
}

可以看见密文在最后面,我们发了个骚东西O_OLookAtYou

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__int64 O_OLookAtYou()
{
__int64 result; // rax
char v1; // [rsp+1h] [rbp-5h]
int i; // [rsp+2h] [rbp-4h]

for ( i = 0; i <= 9; ++i )
{
v1 = base64_table[i];
base64_table[i] = base64_table[19 - i];
result = 19 - i;
base64_table[result] = v1;
}
return result;
}

这个函数就是对置换表进行魔改的,用人话表示就是这样的

table[i],table[19-i]=table[19-i],table[i]

置换得到正常的表后就可以进行骚操作

Python永远滴神吹爆

maketrans()方法的作用就是起到自定义字符串替换的效果,这样就省去了脚本寻找的代码编写时间

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
model = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=")
s = "d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD=="

for i in range(10):
model[i], model[19-i] = model[19-i], model[i]

model = ''.join(model)

table = str.maketrans(model, table)
print (base64.b64decode(s.translate(table)))

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2021-2023 H.greed
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信