简单Python逆向

笔记

getPycMagicNumber.py

1
2
3
4
rd=input('Input Magic Number (ex. 3413):')
MAGIC_NUMBER = (int(rd)).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little')
print(hex(_RAW_MAGIC_NUMBER))

做题

[NISACTF 2022]ezpython

python逆向

1
pyinstxtractor *.exe

出现 *.exe_extracted文件夹,找到src和struct,添加.pyc后缀。

用struct.pyc的Magic Number修复src.pyc($\mathrm{E3}$之前$12$个字节)

1
uncompyle6 src.pyc>src.py

解密得flag

[HUBUCTF 2022 新生赛]ezPython

uncompyle6

1
2
3
from Crypto.Util.number import *
tmp=long_to_bytes(22385992650816784030032474165)
print(tmp)

md5 16位小写

[SWPUCTF 2022 新生赛]py2

简单python逆向

[广东省大学生攻防大赛 2022]pyre

95年编译的?????exp:

1
2
3
4
5
6
c=[144,163,158,177,121,39,58,58,91,111,25,158,72,53,152,78,171,12,53,105,45,12,12,53,12,171,111,91,53,152,105,45,152,144,39,171,45,91,78,45,158,8]
for i in range(len(c)):
while c[i]%33!=0:
c[i]+=179
c[i]//=33
print(chr(c[i]),end='')

[LitCTF 2023]snake

python反编译,pygame贪吃蛇,发现玩不到1000,把flag输出代码分离得flag。

1
2
3
4
flag=[30,196,52,252,49,220,7,243,3,241,24,224,40,230,25,251,28,233,40,237,4,225,4,215,40,231,22,237,14,251,10,169]
for i in range(0,len(flag),2):
flag[i],flag[i+1]=flag[i+1]^136,flag[i]^119
print(bytes(flag).decode())

[HNCTF 2022 Week1]你知道什么是Py嘛?

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <cstdio>
using namespace std;
int arr[]={29,0,16,23,18,61,43,41,13,28,88,94,49,110,66,44,43,28,91,108,61,7,22,7,43,51,44,46,9,18,20,6,2,24},flag[35];
int main(void){
flag[0]=78;
for(register int i=0;i<34;i++)
for(register int j=1;j<=127;j++)
if((flag[i]^j)==arr[i]){
flag[i+1]=j;
break;
};
for(register int i=0;i<35;i++)
printf("%c",flag[i]);
return 0;
};

[MoeCTF 2021]Realezpy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enc=[119,121,111,109,100,112,123,74,105,100,114,48,120,95,49,99,95,99,121,48,121,48,121,48,121,48,95,111,107,99,105,125]
alb1=[chr(i) for i in range(ord('A'),ord('Z')+1)]
alb2=[chr(i) for i in range(ord('a'),ord('z')+1)]
for i in range(len(enc)):
if ord('A')<=enc[i]<=ord('Z'):
for j in alb1:
tmp=(ord(j)+514-ord('A'))%26+ord('A')
if tmp==enc[i]:
print(j,end='')
break
if ord('a')<=enc[i]<=ord('z'):
for j in alb2:
tmp=(ord(j)+114-ord('a'))%26+ord('a')
if tmp==enc[i]:
print(j,end='')
break
if (not(ord('A')<=enc[i]<=ord('Z')))and(not(ord('a')<=enc[i]<=ord('z'))):
print(chr(enc[i]),end='')

[MoeCTF 2021]midpython

1
2
3
4
key=[69,70,79,72,88,75,85,127,89,85,74,19,74,122,107,103,75,77,9,73,29,28,67]
for i in range(len(key)):
key[i]^=i^14^45^11
print(chr(key[i]),end='')

[广东强网杯 2021 个人组]goodpy

Python字节码,对Python字节码的机制有进一步了解。

逆向得源代码:

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
import os
flag=input()
for i in range(len(flag)):
a+=1
if a!=32:
print("error")
exit()
if(flag[0]!='f')or(flag[1]!='l')or(flag[2]!='a')or(flag[3]!='g')or(flag[4]!='{')or(flag[31]!='}'):
print("error")
exit()
tmp=[]
for i in range(a):
tmp.append(flag[i])
for i in range(a):
tmp[i]=ord(tmp[i])-9
for i in range(a):
tmp[i]^=51
for i in range(a):
tmp[i]+=8
tmp1=tmp[a-3]
tmp2=tmp[a-2]
tmp3=tmp[a-1]
for i in range(a-3):
tmp[a-1-i]=tmp[a-1-i-3]
tmp[0]=tmp3
tmp[1]=tmp2
tmp[2]=tmp1
for i in range(a):
if i%7==1:
continue
else:
tmp[i]^=119
f=open('out',"w")
f.write(str(tmp))

exp:

1
2
3
4
5
6
7
8
enc=[56,92,6,1,47,4,2,62,129,84,97,100,5,100,87,89,60,11,84,87,244,103,118,247,47,96,47,244,98,127,81,102]
for i in range(len(enc)):
if i%7!=1:
enc[i]^=119
enc=enc[3:]+enc[:3][::-1]
for i in range(len(enc)):
enc[i]=((enc[i]-8)^51)+9
print(chr(enc[i]),end='')

[HZNUCTF 2023 preliminary]Bytecode

Python字节码逆向。

1
2
3
4
5
6
c=[25,108,108,176,18,108,110,177,64,29,134,29,187,103,32,139,144,179,134,177,32,24,144,25,111,14,111,14]
for i in range(28):
for j in range(32,128):
if(j*39%196==c[i]):
print(chr(j),end='')
break

[FSCTF 2023]ez_pycxor

1
2
3
4
5
6
7
8
9
ciphertxt=[168,169,185,170,160,157,197,132,226,134,134,145,255,242,130,139,234,140,180,229,179,246,243,181,183,182,249,163,254,189,246,166]
key='FUTURESTARS'
for i in range(len(ciphertxt)):
if i % 2 == 0:
ciphertxt[i] ^= ord(key[i % 11])
if i % 2 == 1:
ciphertxt[i] ^= ord(key[i % 11])
for i in range(len(ciphertxt)):
print(chr((ciphertxt[i]-i)^168),end='')

[FSCTF 2023]ezcode

ROT_TWO用法:

1
s_box[i],s_box[j]=s_box[j],s_box[i]

字节码:

1
2
3
4
5
6
7
8
9
10
11
12
13
19          52 LOAD_FAST                1 (box)
54 LOAD_FAST 5 (j)
56 BINARY_SUBSCR
58 LOAD_FAST 1 (box)
60 LOAD_FAST 4 (i)
62 BINARY_SUBSCR
64 ROT_TWO
66 LOAD_FAST 1 (box)
68 LOAD_FAST 4 (i)
70 STORE_SUBSCR
72 LOAD_FAST 1 (box)
74 LOAD_FAST 5 (j)
76 STORE_SUBSCR

BUILD_SLICE数组切片方法:

1
temp_list = bin_str[:3]

字节码:

1
2
3
4
5
6
36     >>   70 LOAD_FAST                2 (bin_str)
72 LOAD_CONST 0 (None)
74 LOAD_CONST 6 (3)
76 BUILD_SLICE 2
78 BINARY_SUBSCR
80 STORE_FAST 7 (temp_list)

append另一种写法:

1
temp_list.append('00000000')

字节码:

1
2
3
4
5
40     >>  118 LOAD_FAST                7 (temp_list)
120 LOAD_CONST 7 ('00000000')
122 BUILD_LIST 1
124 INPLACE_ADD
126 STORE_FAST 7 (temp_list)

INPLACE_ADD为追加,与二进制算术加法区分开。

还原后代码长这样:

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
def func2(key):
s_box = list(range(256))
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
return s_box


def func3(plain, box):
res = []
y = 'FSCTF'
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr(ord(s) ^ k ^ ord(y[i % len(y)])))
cipher = ''.join(res)
return cipher


def func1(key, message):
s_box = func2(key)
crypt = str(func3(message, s_box))
return crypt


def encode(c):
s = 'vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/'
bin_str = []
for i in c:
x = str(bin(ord(i))).replace('0b', '')
bin_str.append('{:0>8}'.format(x))
outputs = ''
nums = 0
while bin_str:
temp_list = bin_str[:3]
if len(temp_list) != 3:
nums = 3 - len(temp_list)
while len(temp_list) < 3:
temp_list.append('00000000')
temp_str = ''.join(temp_list)
temp_str_list = []
for i in range(4):
temp_str_list.append(int(temp_str[i * 6:(i + 1) * 6], 2))
if nums:
temp_str_list = temp_str_list[:4 - nums]
for i in temp_str_list:
outputs += s[i]
bin_str = bin_str[3:]
outputs += nums * '='
return outputs


print('Please input your flag:')
m = input()
k = 'XFFTnT'
c = func1(k, m)
outputs = encode(c)
if outputs == 'ADkopgjJFP+28RYgXUxU2Oej':
print('Success!!')
else:
print('think again')

即为先RC4再换表Base64,Base64换表用赛博厨子,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import re
import base64
def rc4_decrypt(ciphertext,key):
S=list(range(256))
j=0
res=[]
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
y='FSCTF'
for char in ciphertext:
i=(i+1)%256
j=(j+S[i])%256
S[i],S[j]=S[j],S[i]
res.append(char^S[(S[i]+S[j])%256]^ord(y[i%len(y)]))
return bytes(res)
data_list=[0x3d,0x2e,0x07,0x23,0x4d,0xd8,0x51,0xef,0x9d,0xf2,0x0c,0x74,0xc2,0xd0,0xad,0x76,0x7c,0xb7]
key='XFFTnT'
key_list=[ord(c) for c in key]
flag=rc4_decrypt(data_list,key_list)
print(flag)

[SWPUCTF 2023 秋季新生赛]蟒蛇中文破解绿色版

1
2
3
4
5
6
enc=['39/1','83/3','83/2','67/3','42/1','70/3','123/2','116/3','52/1','49/3','115/2','95/3','49/2','115/3','95/2','112/3','121/2','95/3','119/2','37/1','57/1','36/1','50/1','11/1','95/2','35/1','58/1','35/1','115/2','39/1','26/1','34/1','97/2','36/1','33/2','125/3']
for i in range(len(enc)):
if i%2==0:
print(chr(int(eval(enc[i])*2)),end='')
else:
print(chr(int(eval(enc[i])*3)),end='')

[AFCTF 2018]JPython

就是字节码的单字节替换,找一下,有不少重复的:

1
2
3
4
5
6
7
8
9
f1 = open('./hash.pyc','rb').read()
f2 = open('./Jhash.pyc','rb').read()
print('{')
for i in range(len(f1)):
if f1[i] != f2[i]:
t1 = f1[i]
t2 = f2[i]
print(hex(t1),':',hex(t2),',')
print('}')

没去重的:

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
{
0x64 : 0x94 ,
0x64 : 0x94 ,
0x6c : 0x75 ,
0x5a : 0x45 ,
0x64 : 0x94 ,
0x5a : 0x45 ,
0x64 : 0x94 ,
0x5a : 0x45 ,
0x64 : 0x94 ,
0x5a : 0x45 ,
0x65 : 0x95 ,
0x64 : 0x94 ,
0x64 : 0x94 ,
0x5a : 0x45 ,
0x65 : 0x95 ,
0x64 : 0x94 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x18 : 0x27 ,
0x14 : 0x23 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x17 : 0x26 ,
0x14 : 0x23 ,
0x17 : 0x26 ,
0x5a : 0x45 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x17 : 0x26 ,
0x14 : 0x23 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x18 : 0x27 ,
0x14 : 0x23 ,
0x17 : 0x26 ,
0x5a : 0x45 ,
0x65 : 0x95 ,
0x5a : 0x45 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x65 : 0x95 ,
0x64 : 0x94 ,
}

找个脚本替换:

1
2
3
4
x={148:100,117:108,69:90,149:101,39:24,35:20,38:23}
f3=open('Jflag.pyc','rb').read().decode('ISO-8859-1')
t3=f3.translate(x).encode('ISO-8859-1')
open('flag.pyc','wb').write(t3)

用uncompyle6发现还是反编译不成功,但报错信息足够多了:

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
# file flag.pyc
# --- This code section failed: ---

L. 1 0 JUMP_ABSOLUTE 12 'to 12'
3 LOAD_CONST None
6 IMPORT_NAME 0 'time'
9 STORE_NAME 0 'time'

L. 2 12 LOAD_CONST -1
15 LOAD_CONST None
18 IMPORT_NAME 1 'base64'
21 STORE_NAME 1 'base64'

L. 3 24 LOAD_CONST -1
27 LOAD_CONST None
30 IMPORT_NAME 2 'sys'
33 STORE_NAME 2 'sys'

L. 6 36 LOAD_NAME 2 'sys'
39 LOAD_ATTR 3 'argv'
42 LOAD_CONST 1
45 BINARY_SUBSCR
46 STORE_NAME 4 'flag'

L. 7 49 LOAD_CONST 'jd'
52 STORE_NAME 5 'jd'

L. 8 55 LOAD_NAME 6 'len'
58 LOAD_NAME 4 'flag'
61 CALL_FUNCTION_1 1 None
64 LOAD_CONST 30
67 COMPARE_OP 2 ==
70 POP_JUMP_IF_FALSE 210 'to 210'

L. 9 73 LOAD_NAME 1 'base64'
76 LOAD_ATTR 7 'b64encode'
79 LOAD_NAME 4 'flag'
82 LOAD_CONST '+1s+1s+1s'
85 BINARY_ADD
86 LOAD_NAME 5 'jd'
89 LOAD_CONST 2
92 BINARY_MULTIPLY
93 BINARY_ADD
94 CALL_FUNCTION_1 1 None
97 STORE_NAME 8 'base64_str'

L. 10 100 LOAD_CONST ''
103 STORE_NAME 9 'b'

L. 11 106 SETUP_LOOP 73 'to 182'
109 LOAD_NAME 10 'range'
112 LOAD_CONST 0
115 LOAD_CONST 44
118 CALL_FUNCTION_2 2 None
121 GET_ITER
122 FOR_ITER 56 'to 181'
125 STORE_NAME 11 'i'

L. 12 128 LOAD_NAME 12 'ord'
131 LOAD_NAME 8 'base64_str'
134 LOAD_NAME 11 'i'
137 BINARY_SUBSCR
138 CALL_FUNCTION_1 1 None
141 LOAD_CONST 10
144 <36>
145 STORE_NAME 13 'head'

L. 13 148 LOAD_NAME 9 'b'
151 LOAD_NAME 14 'chr'
154 LOAD_NAME 12 'ord'
157 LOAD_NAME 8 'base64_str'
160 LOAD_NAME 11 'i'
163 BINARY_SUBSCR
164 CALL_FUNCTION_1 1 None
167 LOAD_CONST 7
170 BINARY_XOR
171 CALL_FUNCTION_1 1 None
174 INPLACE_ADD
175 STORE_NAME 9 'b'
178 JUMP_BACK 122 'to 122'
181 POP_BLOCK
182_0 COME_FROM 106 '106'

L. 14 182 LOAD_NAME 9 'b'
185 LOAD_CONST '^P]mc@]0emZ7VOZ2_}A}VBwpbQ?5e5>lN4UwSSM>L}A}'
188 COMPARE_OP 2 ==
191 POP_JUMP_IF_FALSE 202 'to 202'

L. 15 194 LOAD_CONST 'Congratllations!Yol Get Flag'
197 PRINT_ITEM
198 PRINT_NEWLINE_CONT
199 JUMP_ABSOLUTE 210 'to 210'

L. 17 202 LOAD_CONST 'Wrong!'
205 PRINT_ITEM
206 PRINT_NEWLINE_CONT
207 JUMP_FORWARD 0 'to 210'
210_0 COME_FROM 207 '207'

Parse error at or near `None' instruction at offset -1

写个脚本解密:

1
2
3
4
5
from base64 import b64decode
s = b"^P]mc@]0emZ7VOZ2_}A}VBwpbQ?5e5>lN4UwSSM>L}A}"
s = [(_ ^ 7) for _ in s]
s = ''.join([chr(x) for x in s])
print(s)

发现s中出现了俩非法字符“]”,那就挨个试,试到“B”,Base64解密即可。