最近新做了一种题型,感觉以后考的应该挺多的,然后干脆放一起总结一下吧,因为也算是re入门的一部分Y(^o^)Y

python解包

首先拿到一个exe文件它的外形长这样,基本就是用pyinsatller打包的exe文件

image-20250715202704937

用工具查一下有pyinsatller显示出来

image-20250715202704937

python逆向基础流程(纯小白教程)_python exe逆向-CSDN博客

解法也很简单下一个pyinstxtractor,放一个文件夹下解包就可以了,这个是正统的方法

像我这种不正经的人就会想歪门邪道用在线的,但是孩子们不要学歪哈(^_^)

解开之后拿到pyc文件,然后用一个库拿到py文件,当然也有快速的方法,但是太求速度也不好,尽量知道正常的怎么做

1
uncompyle6 文件名.pyc>文件名.py.(要在pyc文件所在目录)

但是如果python版本太高的话就只能转换成字节码了,什么你不知道什么是python字节码!!!

那快去学一下把ヽ(✿゚▽゚)ノ

pyc.encrypted

这一类也挺简单的没什么难度,只是在上面的基础上对要分析的pyc进行了加密,只要用加密脚本去掉加密正常分析就可以了

我爱看小品

下载附件是一个elf文件,我们把它放到kili里运行,遇到了像下面的报错

1
2
3
┌──(root㉿kali)-[~]
└─# '/root/桌面/something'
[223774] Failed to extract ld-linux-x86-64.so.2: decompression resulted in return code 0!

ai一下发现是打包过的文件

image-20250502144629265

在kali上对文件进行解包

1
python3 '/root/桌面/pyinstxtractor/pyinstxtractor-2025.02/pyinstxtractor.py' '/root/桌面/something' 

运行之后发现有一个文件出现在当前的文件夹下,把它移到桌面上打开会发现something.pyc

image-20250502145430406

用这个网站在线Python pyc文件编译与反编译

对pyc反编译得到下面的一个主函数

可以发现其它的函数都有定义,关键在于mypy文件里内容

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
# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information
# Version : Python 3.8

import mypy
import yourpy

def something():
print(' 打工奇遇')
print('宫室长悬陇水声')
print('廷陵刻此侈宠光')
print('玉池生肥咽不彻')
print('液枯自断仙无分')
print('酒醒玉山来映人')


def check():
your_input = input()
if your_input[:5] == 'ISCC{' and your_input[-1] == '}':
print("Come along, you'll find the answer!")
else:
print('Flag is wrong!')

if __name__ == '__main__':
mypy.myfun()
something()
print('Please enter flag:')
check()

在文件夹内发现/PYZ-00.pyz_extracted文件夹里有mypy.pyc但是被加密了有一个. encrypted的后缀,需要把它解出来,这里有一篇文章写着很详细做法

在Python中反编译加密的pyc文件 - 我也是个傻瓜 - 博客园

key在/root/桌面/something_extracted/的pyimod00_crypto_key文件中

image-20250502150323645

用脚本把文件解密,记得修改路径和key还有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 glob
import zlib
import tinyaes
from pathlib import Path

CRYPT_BLOCK_SIZE = 16

# key obtained from pyimod00_crypto_key
key = bytes('yibaibayibei1801', 'utf-8')

for p in Path("/root/桌面/something_extracted/PYZ00.pyz_extracted").glob("**/*.pyc.encrypted"):
inf = open(p, 'rb') # encrypted file input
outf = open(p.with_name(p.stem), 'wb') # output file

# Initialization vector
iv = inf.read(CRYPT_BLOCK_SIZE)

cipher = tinyaes.AES(key, iv)

# Decrypt and decompress
plaintext = zlib.decompress(cipher.CTR_xcrypt_buffer(inf.read()))

# Write pyc header
# The header below is for Python 3.8
outf.write(b'\x55\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0')

# Write decrypted data
outf.write(plaintext)

inf.close()
outf.close()

# Delete .pyc.encrypted file
p.unlink()

这个时候再打开发现都变成了 pyc文件,反汇编一下

image-20250502150449107

myfun函数长这样,把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
# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information
# Version : Python 3.8

import time

def myfun():
part1 = 'ISCC{'
part2 = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
part3 = '}'
tmp = ''
part2_1 = part2[:11]
part2_2 = part2[11:15]
part2_3 = part2[15:20]
part2_4 = part2[20:]
for i in range(len(part2_1)):
tmp += chr(ord(part2_1[i]) + 1)
for i in range(len(part2_2)):
if part2_2[i].isalpha():
tmp += chr(ord(part2_2[i]) ^ 32)
continue
tmp += chr(ord(part2_2[i]) + 0)
for i in range(len(part2_3)):
tmp += chr(ord(part2_3[i]) - 1)
for i in range(len(part2_4)):
tmp += chr(ord(part2_4[i]) + i % 2)
cipher = 'qzjotubmmfs_IS_udqx^iotfrfsuiog'
true_flag = part1 + part2 + part3
print(time.strftime('%Y-%m-%d %H:%M', time.localtime(time.time())))
return true_flag

if __name__ == '__main__':
ss = myfun()
print(ss)

写一下脚本

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
def decrypt():
cipher = 'qzjotubmmfs_IS_udqx^iotfrfsuiog'

# 第一段:每个字符 -1
part2_1 = ''.join([chr(ord(c) - 1) for c in cipher[0:11]])

# 第二段:如果是字母,就 ^32(大小写互换)
part2_2 = ''
for c in cipher[11:15]:
if c.isalpha():
part2_2 += chr(ord(c) ^ 32)
else:
part2_2 += c

# 第三段:每个字符 +1
part2_3 = ''.join([chr(ord(c) + 1) for c in cipher[15:20]])

# 第四段:每个字符 - (i % 2)
part2_4 = ''.join([
chr(ord(c) - (i % 2)) for i, c in enumerate(cipher[20:])
])

part2 = part2_1 + part2_2 + part2_3 + part2_4
return 'ISCC{' + part2 + '}'

print(decrypt())

Cython逆向

pyd文件相当于dll文件,也就是动态链接库的概念,它不能像pyc一样直接打出字节码,只能反汇编

如果可以的话试试自己写一个pyd,可以发现混淆挺严重的,逆向难度大大提升,我们只能使用朴素的方法直接ida

做题步骤:①先打印观察引用的库,导出函数

​ ②看python版本

​ ③ida打开,查看字符串,数据

​ ④跳到实现函数,动调或者看库函数分析逻辑

当然,还有用注入pyd的方法,看到里面的加密方法,

感觉要是利用好注入和动调的话,这种题简直就是易如反掌

但是主要是我现在知识浅薄还不会那么写脚本::>_<::

还是要多加努力✊

moon

拿到文件先看一下主函数,用了moon.pyd里面的check函数

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

print("I tried my best, to live an ordinary life.")
print("But I hope you can look up and see the moonlight through reverse engineering on the streets full of sixpence.")

user_input = input("Enter your flag: ").strip()

result, error = moon.check_flag(user_input)

if error:
print("Error.")
elif result:
print("I think you have found the right way.")
else:
print("You seem to be lost.")

因为pyd类似于dll,无法像pyc一样拿到源码所以我们用ida来逆向一下逻辑,为了看清里面的逻辑我们先借助python中的函数查看一下pyd里面的内容

image-20250709103729126

里面有两个函数一个是check一个是xor,大概能猜到是先经过异或然后检验一下flag是否正确。

用ida打开,试了一下发现用两种方法可以找到具体的实现函数。

image-20250709105857244

①如果你是用1这个地方引用的话,直接引用到data字段,字符串下面是另一个函数指针,指向实际实现check_flag功能的函数,然后点进去一般return的第一个函数就是你要的功能函数

image-20250709105524059

②如果是用2这个地方引用的话,第一行引用是这个函数的包装函数,第二行才是这函数的内部实现,但也有可能会有超过两行的情况,看一下偏移其实大差不差,点进去的函数,就是我们要分析的函数内部

image-20250709110442752

ok,首先让我们先找到 模块状态 来分析一下里面都有什么,就是这个东西,可以看出里面有seed和一个16进制的字符串,还有random,和我们利用python打印出的模块也差不多可能是random.seed去生成随机数。

这个off_18000B618就是模块全局字典,没太看懂里面是什么意思

image-20250709162510424

我们再去找一下我们打印出的check和xor函数

找到函数之后我们就可以开始分析了,很多东西都可以从红色的一些库函数来了解程序的走向,在check函数中我们可以看到比较的函数,剩下的就是获取对象之类的函数,因为是对比函数,所以只要我们输入的和python对象加载的flag一样就好了,所以我们跑到xor函数大概就知道要我们输入的是什么了

image-20250709155528161

image-20250709160057232

xor函数里面发现一个异或函数,找到这里发现他是异或完加到一个列表后面,这个v8在后面找一下会发现是在PyDict_GetItem_KnownHash这个函数这边对模块全局字典引用可以发现是一个种子

image-20250709221003115

这个种子就是刚刚用函数打印出来的1131796,大致就是用这个种子生成了随机数,然后16进制密文去异或随机数得到flag

image-20250709221703493

静态

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

random.seed(1131796)
en = "426b87abd0ceaa3c58761bbb0172606dd8ab064491a2a76af9a93e1ae56fa84206a2f7"
enbt = bytes.fromhex(en)

key = []
for i in range(len(enbt)):
key.append(random.randint(0, 255))

flag_bytes = []
for i in range(len(enbt)):
flag_bytes.append(enbt[i] ^ key[i])

flag = bytes(flag_bytes).decode()
print(flag)

动态

在PyObject_RichCompare和PyNumber_Xor上下断点,然后附加在python3.11exe上动调,这里我的输入是好多a,试了一下发现这里异或的数值都是不变的,然后直接f9到PyObject_RichCompare

回到PyNumber_Xor把异或后得到的数据提出来,然后异或回去得到随机数

image-20250710104041533

image-20250710105351690

最后得到flag

rand0m

首先拿到一个pyd

先用help和dir查看模块内的函数和类的用法

1
2
3
4
import rand0m

print(dir(rand0m))
print(help(rand0m))

image-20250708172451421

大致了解一下里面有rand0m和check函数

用ida打开分析一下,首先先看字符串,找到函数对应的字符串,找到函数实现部分

image-20250708172802264

然后结合动调对函数里面进行分析

image-20250714210114986

然后可以知道rand0m里面的进行了什么操作

1
2
3
4
def rand0m(x):
a = ((x^2654435769)>>11)**65537%4294967293
b = ((x<<4)&4198170623)+((x>>5)>>23)
return a, b

再对check函数进行动调可以发现,在4660函数部分进行了跳转,也就是先初始化了数组,然后进行rand0m里面的操作

image-20250715180135630

image-20250715180807680

到这里就差不多了

逆向与操作的通用方法

如果已知 A & B = C,并且知道 AB 中的一个,可以部分恢复另一个:

  • 如果 A 的某位是 0,则 B 的该位 无法确定(因为 0 & B = 0,无论 B0 还是 1)。
  • 如果 A 的某位是 1,则 B 的该位 必须等于 C 的对应位(因为 1 & B = B)。

直接爆破一下

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
'''
def rand0m(x):
a = ((x^2654435769)>>11)**65537%4294967293
b = ((x<<4)&4198170623)+((x>>5)>>23)
return a, b
'''

import gmpy2
def fun(i, y, n):
if n == 0:
return y & ~(1 << i)
else:
return y | (1 << i)


list = [26, 24, 23, 22, 18, 16]
en1 = [0x12287F38, 0x4a30f74d, 0x23a1268, 0x88108807]
en2 = [0x98d24b3a, 0xe0f1db77, 0xadf38403, 0xd8499bb6]
flag = []
for i in range(len(en1)):
x = en1[i]
c = x & 0xf
b = x - c

for one in range(2):
b = fun(list[0], b, one)
for two in range(2):
b = fun(list[1], b, two)
for three in range(2):
b = fun(list[2], b, three)
for four in range(2):
b = fun(list[3], b, four)
for five in range(2):
b = fun(list[4], b, five)
for six in range(2):
b = fun(list[5], b, six)
part = ((x << 28) | (b >> 4)) & 0xffffffff

p1 = (part ^ 0x9e3779b9) >> 0xb
res = gmpy2.powmod(p1, 0x10001, 0xfffffffd)
if res == en2[i]:
flag.append(part)
for i in flag:
t = hex(i)[2:]
print(t)

在别的地方看到了一个十分有意思的做法,用hook注入来看数据加密的过程

第十八届信息安全大赛 && 第二届长城杯 - re-先知社区

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
class Symbol:
def __init__(self, name):
self.name = name

def __eq__(self, other):
print(f"Comparing {self.name} and {other:08x}")
if isinstance(other, Symbol):
return self.name == other.name
return self.name == other

import rand0m
tempcheck = rand0m.check
temprand0m = rand0m.rand0m

def mycheck(flag):
print("Checking flag: ", flag, end=" ->\n")
result = tempcheck(flag)
print("Result: ", result)
return result

def myrand0m(x):
print("\tRandom: ", x, end=" -> ")
result = temprand0m(x)
print(f"\tResult: ({result[0]:08x}, {result[1]:08x})")
result = (Symbol(f"{result[0]:x}"), Symbol(f"{result[1]:x}"))
return result

rand0m.check = mycheck
rand0m.rand0m = myrand0m
flag = "1234567890abcdef1234567890abcdef"
print(rand0m.check(flag))

ヽ(✿゚▽゚)ノ

ez-cython

首先解包,可以看到主代码

image-20250714105910944

关键是运用了pyd里面的一个函数,先查看一下pyd里面有哪些类

1
2
3
4
import cy

print(dir(cy))
print(help(cy))

从输出可以看到有一个自定义类**QOOQOOQOOQOOOQ**,大概可以猜到是用来生成key的,然后有三个函数,一个用来检查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
['QOOQOOQOOQOOOQ', '__builtins__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__test__', 'sub14514', 'sub50520', 'sub50804']
Help on module cy:

NAME
cy

CLASSES
builtins.object
QOOQOOQOOQOOOQ

class QOOQOOQOOQOOOQ(builtins.object)
| Methods defined here:
|
| __init__(self)
|
| get_key(self)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)

FUNCTIONS
sub14514(nmnmnnnmnmmnmmnn)

sub50520(T7T77T77T7T7TT, key)

sub50804(a, b, c, d, e, f)

DATA
__test__ = {}

打开pyd文件先找到了14514函数,分析一下看到了PyObject_RichCompare对比函数

对50804函数进行分析可以看到都是异或和位移弄在一起,

对50520函数进行分析可以看到都是与运算。

反正就这么一步步看

image-20250715175027932

所以可以得到50804函数里面的东西是这个

其实按这个步骤再分析50520函数就行,但是我实在脑子要炸掉了

1
2
def shift(a, b, c, d, e, f):
return ((((a >> 3) ^ (b << 3)) + ((b >> 4) ^ (a << 2))) ^ ((b ^ c) + (d[(e & 2) ^ f] ^ a)))

这次直接采用直接注入的方式试一试,运行之后可以发现里面的加密过程,直接逆

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
import cy

class Symbol:
def __init__(self, name):
self.name = name

def __repr__(self):
return self.name

def __rshift__(self, other):
if isinstance(other, Symbol):
expression = Symbol(f"({self.name} >> {other.name})")
else:
expression = Symbol(f"({self.name} >> {other})")
return expression

def __lshift__(self, other):
if isinstance(other, Symbol):
expression = Symbol(f"({self.name} << {other.name})")
else:
expression = Symbol(f"({self.name} << {other})")
return expression

def __rxor__(self, other):
if isinstance(other, Symbol):
expression = Symbol(f"({self.name} ^ {other.name})")
else:
expression = Symbol(f"({self.name} ^ {other})")
return expression

def __xor__(self, other):
if isinstance(other, Symbol):
expression = Symbol(f"({self.name} ^ {other.name})")
else:
expression = Symbol(f"({self.name} ^ {other})")
return expression

def __add__(self, other):
if isinstance(other, Symbol):
expression = Symbol(f"({self.name} + {other.name})")
else:
expression = Symbol(f"({self.name} + {other})")
return expression

def __and__(self, other):
if isinstance(other, Symbol):
expression = Symbol(f"({self.name} & {other.name})")
else:
expression = Symbol(f"({self.name} & {other})")
return expression

class AList:
def __init__(self, nums):
self.nums = [Symbol(str(num)) for num in nums]

def __getitem__(self, key):
return self.nums[key]

def copy(self):
return AList(self.nums)

def __len__(self):
return len(self.nums)

def __setitem__(self, key, value):
print(f"new_{self.nums[key]} = {value}")
self.nums[key] = Symbol(f"new_{self.nums[key].name}")

def __eq__(self, other):
print(f"{self.nums} == {other}")
return self.nums == other

inp = AList([f"a[{1}]" for i in range(32)])
res = cy.sub14514(inp)

if __name__ == '__main__':
print(res)

中间其实还有很多的,我简略一些

1
2
3
4
5
6
7
8
9
10
new_a[1] = ((a[1] + ((((a[1] >> 3) ^ (a[1] << 3)) + ((a[1] >> 4) ^ (a[1] << 2))) ^ ((a[1] ^ 2654435769) + (a[1] ^ 49)))) & 4294967295)
new_a[1] = ((a[1] + ((((new_a[1] >> 3) ^ (a[1] << 3)) + ((a[1] >> 4) ^ (new_a[1] << 2))) ^ ((a[1] ^ 2654435769) + (new_a[1] ^ 49)))) & 4294967295)
new_a[1] = ((a[1] + ((((new_a[1] >> 3) ^ (a[1] << 3)) + ((a[1] >> 4) ^ (new_a[1] << 2))) ^ ((a[1] ^ 2654435769) + (new_a[1] ^ 121)))) & 4294967295)
new_a[1] = ((a[1] + ((((new_a[1] >> 3) ^ (a[1] << 3)) + ((a[1] >> 4) ^ (new_a[1] << 2))) ^ ((a[1] ^ 2654435769) + (new_a[1] ^ 121)))) & 4294967295)
new_a[1] = ((a[1] + ((((new_a[1] >> 3) ^ (a[1] << 3)) + ((a[1] >> 4) ^ (new_a[1] << 2))) ^ ((a[1] ^ 2654435769) + (new_a[1] ^ 49)))) & 4294967295)
。。。。。。。
new_new_new_new_new_a[1] = ((new_new_new_new_a[1] + ((((new_new_new_new_new_a[1] >> 3) ^ (new_new_new_new_a[1] << 3)) + ((new_new_new_new_a[1] >> 4) ^ (new_new_new_new_new_a[1] << 2))) ^ ((new_new_new_new_a[1] ^ 387276957) + (new_new_new_new_new_a[1] ^ 121)))) & 4294967295)
new_new_new_new_new_a[1] = ((new_new_new_new_a[1] + ((((new_new_new_new_new_a[1] >> 3) ^ (new_new_new_new_new_a[1] << 3)) + ((new_new_new_new_new_a[1] >> 4) ^ (new_new_new_new_new_a[1] << 2))) ^ ((new_new_new_new_new_a[1] ^ 387276957) + (new_new_new_new_new_a[1] ^ 121)))) & 4294967295)
[new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1], new_new_new_new_new_a[1]] == [4108944556, 3404732701, 1466956825, 788072761, 1482427973, 782926647, 1635740553, 4115935911, 2820454423, 3206473923, 1700989382, 2460803532, 2399057278, 968884411, 1298467094, 1786305447, 3953508515, 2466099443, 4105559714, 779131097, 288224004, 3322844775, 4122289132, 2089726849, 656452727, 3096682206, 2217255962, 680183044, 3394288893, 697481839, 1109578150, 2272036063]

然后用爆出来的直接写个逆过来的脚本

因为一共进行了5轮加密,每轮32次加密O(∩_∩)O~~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
en1 = [2654435769, 1013904242, 3668340011, 2027808484, 387276957]
en2 = [[49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121], [67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83], [121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49], [83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67, 83, 83, 67, 67], [49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121, 49, 49, 121, 121]]
en3 = [4108944556, 3404732701, 1466956825, 788072761, 1482427973, 782926647, 1635740553, 4115935911, 2820454423, 3206473923, 1700989382, 2460803532, 2399057278, 968884411, 1298467094, 1786305447, 3953508515, 2466099443, 4105559714, 779131097, 288224004, 3322844775, 4122289132, 2089726849, 656452727, 3096682206, 2217255962, 680183044, 3394288893, 697481839, 1109578150, 2272036063]
for j in list(range(5))[::-1]:
for i in list(range(32))[::-1]:
id = [i, i-1, i+1]
if i-1 < 0:
id[1] = 31
if i+1 > 31:
id[2] = 0
tt = (((en3[id[1]] >> 3) ^ (en3[id[2]] << 3)) + ((en3[id[2]] >> 4) ^ (en3[id[1]] << 2))) ^ ((en3[id[2]] ^ en1[j]) + (en3[id[1]] ^ en2[j][i]))
en3[id[0]] -= tt & 0xFFFFFFFF
if en3[id[0]] < 0:
en3[id[0]] += 0x100000000
print("".join([chr(x) for x in en3]))

总结

本意是想记录一下自己学cython的过程

确实挺麻烦的,要自己理逻辑分析,动调真的排上大用场了,感觉自己动调有一点长进吧,至少有的时候知道下在哪了

现在还不熟悉动调也没关系,多练练就好了b( ̄▽ ̄)d