江苏工匠杯bad_python
Python逆向
拿到附件是一个pyc文件,按照我们常规的python逆向思路,肯定是先试一试能不能uncompyle6反编译.
发现无法反编译,我们用010editor查看一下pyc文件的文件头.可以看到文件头是以0D33结尾的
33 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 08 00 00 00 40 00 00
00 73 1C 01 00 00 64 00 64 01 6C 00 54 00 64 00
64 02 6C 01 6D 02 5A 02 01 00 64 00 64 03 6C 01
6D 03 5A 03 01 00 64 04 64 05 84 00 5A 04 65 05
64 06 6B 02 90 01 72 18 65 06 64 07 83 01 5A 07
64 08 64 09 64 0A 64 0B 67 04 5A 08 65 09 65 07
83 01 64 0C 6B 03 72 62 65 0A 64 0D 83 01 01 00
65 0B 64 1C 83 01 01 00 67 00 5A 0C 78 5E 65 0D
64 00 64 0C 64 0F 83 03 44 00 5D 4E 5A 0E 65 02
65 0F 65 07 65 0E 65 0E 64 10 17 00 85 02 19 00
64 11 83 02 83 01 5A 10 65 02 65 0F 65 07 65 0E
64 10 17 00 65 0E 64 0F 17 00 85 02 19 00 64 11
83 02 83 01 5A 11 65 0C 65 04 65 10 65 11 67 02
65 08 83 02 37 00 5A 0C 71 74 57 00 64 12 64 13
64 14 64 15 64 16 64 17 64 18 64 19 67 08 5A 12
78 30 65 0D 64 0F 83 01 44 00 5D 24 5A 0E 65 12
65 0E 19 00 65 0C 65 0E 19 00 6B 03 72 E4 65 0A
64 0D 83 01 01 00 65 0B 64 1D 83 01 01 00 71 E4
57 00 65 0A 64 1A 65 07 16 00 83 01 01 00 64 1B
53 00 29 1E E9 00 00 00 00 29 01 DA 01 2A 29 01
DA 0D 62 79 74 65 73 5F 74 6F 5F 6C 6F 6E 67 29
01 DA 0D 6C 6F 6E 67 5F 74 6F 5F 62 79 74 65 73
63 02 00 00 00 00 00 00 00 07 00 00 00 08 00 00
00 43 00 00 00 73 C4 00 00 00 74 00 7C 00 64 01
19 00 83 01 7D 02 74 00 7C 00 64 02 19 00 83 01
7D 03 74 00 64 01 83 01 7D 04 64 03 7D 05 78 92
74 01 64 04 83 01 44 00 5D 86 7D 06 7C 02 04 00
6A 02 7C 03 6A 02 64 05 3E 00 7C 03 6A 02 64 06
3F 00 41 00 7C 03 6A 02 17 00 7C 04 6A 02 7C 01
7C 04 6A 02 64 07 40 00 19 00 17 00 41 00 37 00
02 00 5F 02 7C 04 04 00 6A 02 7C 05 37 00 02 00
5F 02 7C 03 04 00 6A 02 7C 02 6A 02 64 05 3E 00
7C 02 6A 02 64 06 3F 00 41 00 7C 02 6A 02 17 00
7C 04 6A 02 7C 01 7C 04 6A 02 64 08 3F 00 64 07
40 00 19 00 17 00 41 00 37 00 02 00 5F 02 71 2E
57 00 7C 02 6A 02 7C 03 6A 02 66 02 53 00 29 09
4E 72 01 00 00 00 E9 01 00 00 00 69 EF BE AD 0B
E9 20 00 00 00 E9 04 00 00 00 E9 07 00 00 00 E9
03 00 00 00 E9 09 00 00 00 29 03 5A 08 63 5F 75
69 6E 74 33 32 DA 05 72 61 6E 67 65 DA 05 76 61
6C 75 65 29 07 DA 01 76 DA 01 6B 5A 02 76 30 DA
02 76 31 5A 04 73 75 6D 31 5A 05 64 65 6C 74 61
DA 01 69 A9 00 72 11 00 00 00 FA 07 70 79 72 65
2E 70 79 DA 07 65 6E 63 72 79 70 74 06 00 00 00
73 12 00 00 00 00 01 0C 01 0C 01 08 01 04 01 0E
01 38 01 0E 01 40 01 72 13 00 00 00 DA 08 5F 5F
6D 61 69 6E 5F 5F 7A 17 70 6C 65 61 73 65 20 69
6E 70 75 74 20 79 6F 75 72 20 66 6C 61 67 3A E9
FF 00 00 00 E9 BB 00 00 00 E9 33 00 00 00 E9 44
00 00 00 72 06 00 00 00 7A 06 77 72 6F 6E 67 21
72 05 00 00 00 E9 08 00 00 00 72 07 00 00 00 DA
05 61 73 63 69 69 6C 03 00 00 00 02 54 8F 5D 03
00 6C 03 00 00 00 3F 36 D2 33 02 00 6C 03 00 00
00 61 5E 77 0A 02 00 69 97 04 45 21 6C 03 00 00
00 B0 14 1F 21 02 00 69 02 F6 3A 66 69 6F 7A CA
3A 69 BA 82 25 74 7A 10 66 6C 61 67 20 69 73 20
66 6C 61 67 7B 25 73 7D 4E E9 FF FF FF FF 72 1B
00 00 00 29 13 5A 06 63 74 79 70 65 73 5A 12 43
72 79 70 74 6F 2E 55 74 69 6C 2E 6E 75 6D 62 65
72 72 03 00 00 00 72 04 00 00 00 72 13 00 00 00
DA 08 5F 5F 6E 61 6D 65 5F 5F DA 05 69 6E 70 75
74 DA 04 66 6C 61 67 72 0E 00 00 00 DA 03 6C 65
6E DA 05 70 72 69 6E 74 DA 04 65 78 69 74 DA 01
61 72 0B 00 00 00 72 10 00 00 00 DA 05 62 79 74
65 73 72 0F 00 00 00 5A 02 76 32 DA 03 65 6E 63
72 11 00 00 00 72 11 00 00 00 72 11 00 00 00 72
12 00 00 00 DA 08 3C 6D 6F 64 75 6C 65 3E 02 00
00 00 73 28 00 00 00 08 01 0C 01 0C 02 08 19 0A
01 08 01 0C 01 0C 01 08 01 08 01 04 01 12 01 1A
01 1E 01 16 0E 14 01 0E 01 10 01 08 01 0C 01
结合这个Pyc文件的名字,猜想这个可能是以python3.6版本进行编译的。通过pyc魔术版本我们用脚本跑一下,看一下330D是哪个3.6版本的头部.
for i in range(3360,3381):
MAGIC_NUMBER = (i).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
print(f'i = {i},opcode = {hex(_RAW_MAGIC_NUMBER)}')
i = 3379,opcode = 0xa0d0d33
不过这里是要按照小端序填充的,即33 0d 0d 0a,填充后,发现仍然无法进行pyc反编译.说明多半是别的文件头出问题了。
首先我们需要一个python3.6的环境,我们可以用conda来实现,然后我们需要在python环境中利用Python -m 1.py来生成.pyc文件。
我们先用notepdd写一个简单python脚本,然后在conda环境中,用python -m xxx.py生成对应的pyc文件
print("Hello CTFer!")
print("Welcome new world")
运行之后,如果不指定目录的话,会在当前目录下生成一个名字叫做__pycache__的文件夹,里面会有一个对应的pyc,我们用010editor查看一下对应的文件头,将其复制到题目中破坏的pyc文件上去.
反编译成功!
uncompyle6.exe pyre.cpython-36.pyc
# uncompyle6 version 3.7.4
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.8.9 (tags/v3.8.9:a743f81, Apr 2 2021, 10:59:45) [MSC v.1928 32 bit (Intel)]
# Embedded file name: pyre.py
# Compiled at: 2023-06-19 15:29:22
# Size of source mod 2**32: 49 bytes
from ctypes import *
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
def encrypt(v, k):
v0 = c_uint32(v[0])
v1 = c_uint32(v[1])
sum1 = c_uint32(0)
delta = 195935983
for i in range(32):
v0.value += (v1.value << 4 ^ v1.value >> 7) + v1.value ^ sum1.value + k[(sum1.value & 3)]
sum1.value += delta
v1.value += (v0.value << 4 ^ v0.value >> 7) + v0.value ^ sum1.value + k[(sum1.value >> 9 & 3)]
return (
v0.value, v1.value)
if __name__ == '__main__':
flag = input('please input your flag:')
k = [255, 187, 51, 68]
if len(flag) != 32:
print('wrong!')
exit(-1)
a = []
for i in range(0, 32, 8):
v1 = bytes_to_long(bytes(flag[i:i + 4], 'ascii'))
v2 = bytes_to_long(bytes(flag[i + 4:i + 8], 'ascii'))
a += encrypt([v1, v2], k)
enc = [
4006073346, 2582197823, 2235293281, 558171287, 2425328816, 1715140098, 986348143, 1948615354]
for i in range(8):
if enc[i] != a[i]:
print('wrong!')
exit(-1)
print('flag is flag{%s}' % flag)
# okay decompiling pyre.cpython-36.pyc
py文件算法分析
- k = [255,187,51,68],flag长度必须为32
- 首先是对我们的flag,每8位中,前四位作为v1,后四位作为v2,以及k,传入到encrypt函数中进行加密.
encrypt函数
def encrypt(v, k):
v0 = c_uint32(v[0])
v1 = c_uint32(v[1])
sum1 = c_uint32(0)
delta = 195935983
for i in range(32):
v0.value += (v1.value << 4 ^ v1.value >> 7) + v1.value ^ sum1.value + k[(sum1.value & 3)]
sum1.value += delta
v1.value += (v0.value << 4 ^ v0.value >> 7) + v0.value ^ sum1.value + k[(sum1.value >> 9 & 3)]
return (
v0.value, v1.value)
整个函数逻辑就是通过移位加上异或来进行运算,类似于Tea算法,这里的delta可以看作Tea算法中的常量。
解密脚本:
#include<stdio.h>
#define u_int unsigned int
int main()
{
u_int enc[] = { 4006073346, 2582197823, 2235293281, 558171287,
2425328816, 1715140098, 986348143, 1948615354 };
u_int k[] = { 255, 187, 51, 68 };
for (int i = 0; i < 8; i += 2)
{
u_int delta = 195935983;
u_int s1 = delta * 32;
for (int j = 0; j < 32; j++)
{
enc[i + 1] -= (enc[i] << 4 ^ enc[i] >> 7) + enc[i] ^ s1 + k[s1 >> 9 & 3];
s1 -= delta;
enc[i] -= (enc[i + 1] << 4 ^ enc[i + 1] >> 7) + enc[i + 1] ^ s1 + k[s1 & 3];
}
}
int j;
for (j = 0; j < 8;j++)
{
printf("enc = %lld\n", enc[j]);
}
char flag[32] = { 0 };
for (int i = 0; i < 8; i++)
{
flag[4 * i] = enc[i] >> 24;
flag[4 * i + 1] = (enc[i] >> 16) & 0xFF;
flag[4 * i + 2] = (enc[i] >> 8) & 0xFF;
flag[4 * i + 3] = enc[i] & 0xFF;
}
for (int i = 0; i < 32; i++)
{
printf("%c", flag[i]);
}
}
/*enc = 63912563639333235
enc = 63912563820295007
enc = 63912563319981409
enc = 63912564160552784
enc = 63912564260890672
enc = 63912564074962770
enc = 63912563086615890
enc = 63912564159242032
Th1s_1s_A_Easy_Pyth0n__R3veRse_0*/
这里我没有写出Python的脚本来,这里的话因为数据宽度的原因,对于Python来说没有内置的无符号整型的函数,如果要用python写的话,还是只能先通过C把解密后的数据提出来,然后分组移位+与运算即可求出flag.脚本如下:
enc = [63912563639333235,63912563820295007,63912563319981409,63912564160552784,63912564260890672,63912564074962770,63912563086615890,63912564159242032]
flag = bytearray(32)
for i in range(8):
flag[4 * i] = (enc[i] >> 24) & 0xFFFFFFFF & 0xFF
flag[4 * i + 1] = (enc[i] >> 16) & 0xFFFFFFFF & 0xFF
flag[4 * i + 2] = (enc[i] >> 8) & 0xFFFFFFFF & 0xFF
flag[4 * i + 3] = (enc[i] & 0xFFFFFFFF & 0xFF)
print(flag)
#bytearray(b'Th1s_1s_A_Easy_Pyth0n__R3veRse_0')
解释一下这个Python脚本为什么要先与0xFFFFFFFF,然后再与0xFF.在题目中实际上是调用了cbytes这个库的,也就是处理的数据都是无符号32位整型(u_int32),但是Python内部不支持32位整型的函数,所以在处理数据的时候,要先通过与运算取得其32位的整体值,然后再去取其中的最低位字节.还是建议用C写脚本比较好:)
Over!!!
标签:02,00,01,python,工匠,bad,64,65,72 From: https://www.cnblogs.com/qsons/p/17491327.html