起因

看到坛友求助的一个python破解问题:

大佬们帮忙看下这个软件汇编的思路 - 吾爱破解 - 52pojie.cn

首先说明,我不熟悉pyInstaller,这是一次尝试,期间也查了很多资料和教程,如果有更好的方法还请诸位大佬赐教!

简单分析

查壳

使用Die查壳,结果如下:

image ​可以看到,程序是使用python编写、PyInstaller打包的

解包

既然是使用PyInstaller打包的,那么我们可以尝试解包它,看看能不能反编译出python代码

解包网站:PyInstaller Extractor WEB

上传AML.exe解包,网站会返回一个压缩包

image

解压压缩包,我们会得到一堆文件

image

找到AML.pyc,通过网站反编译为py代码

image

注意选择uncompyle6引擎,因为相较于pycdc,反编译的代码更完整。

下面是反编译AML.pyc的代码(代码较多,只放出关键部分):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import importlib, os, sys, threading, time   
from time import sleep
from tkinter import \*
from tkinter import messagebox
from tkinter.ttk import \*
from BootUSB import BootUSB
from pyamlboot.aml\_verifysn\_s905lsb\_ import isVerify, reg #导入pyamlboot
from pyamlboot.rkamlboot import RKAmlogicSoC
from usb\_manager import USBManager
from six.moves.http\_cookies import SimpleCookie
num = 0
while isVerify() == False: #授权验证
if num > 0:
sys.exit()
else:
reg()
num += 1

if \_\_name\_\_ == "\_\_main\_\_":
win = WinGUI()
win.mainloop()

如果isVerify()返回False,说明验证失败,程序会尝试注册一次(num从0开始,第一次调用reg(),然后num变为1,下次循环时num>0就会退出)

可以发现授权验证部分在pyamlboot​包中,找到对应的pyamlboot文件夹

image

反编译aml_verifysn_s905lsb_.pyc​,关键代码如下(可以使用ai分析):

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import time   
import tkinter
import winreg
from tkinter import END
import M2Crypto
from pyamlboot import sn
REG\_KEY = '\_aml\_s905l3\_sb' # 注册表键名
PEM = '2D2D2D2D2D424547494E205055424C4943204B45592D2D2D2D2D0A4D4947664D413047435371475349623344514542415155414134474E4144434269514B42675143564D6C396E635659395A616259496D3862583268756F504F620A4B617950334165704A47355774765A544F6C416F3859304737665A6541693941636A4745696A4A63547A465932773977347033454A5676767939464977744D4A0A3455353157613334647653524F683673356C59574B4F59716A2F74414868624A507650705873612F58416139467A376A7569735769772B52725458564A4A744C0A6C7652514F50444434616B44592B34796F514944415141420A2D2D2D2D2D454E44205055424C4943204B45592D2D2D2D2D' #十六进制编码的RSA公钥
key\_path = 'SOFTWARE\\\\Software' # 注册表位置

def decrypt(msg):
key = bytes.fromhex(PEM) # 将十六进制公钥转字节
bio = M2Crypto.BIO.MemoryBuffer(key)
rsa\_pub = M2Crypto.RSA.load\_pub\_key\_bio(bio) # 加载公钥
return rsa\_pub.public\_decrypt(msg, M2Crypto.RSA.pkcs1\_padding) # 公钥解密


def verify\_reg(code):
try:
if not code: return False
\# 解密注册码(HEX转字节->解密->解码)
plain = decrypt(bytes.fromhex(code)).decode()


\# 格式校验(需包含冒号分隔符)
if ':' not in plain: return False
sn\_part, date\_part = plain.split(':',1)


\# 硬件校验(比对磁盘序列号)
if sn.get\_disk\_info() != sn\_part: return False


\# 有效期校验(日期格式YYYYMMDD)
expire\_date = int(date\_part.replace('-',''))
current\_date = int(time.strftime('%Y%m%d'))
return expire\_date > current\_date


except Exception:
return False

def reg():
import tkinter as tk
ScrolledText = ScrolledText
import tkinter.scrolledtext
root = tk.Tk()
curWidth = 400
curHight = 320
(scn\_w, scn\_h) = root.maxsize()
cen\_x = (scn\_w - curWidth) / 2
cen\_y = (scn\_h - curHight) / 2
size\_xy = '%dx%d+%d+%d' % (curWidth, curHight, cen\_x, cen\_y)
root.geometry(size\_xy)
root.resizable(0, 0)
root.title('软件注册')
label\_frame = tk.LabelFrame(root, '机器码', \*\*('text',))
label\_frame.grid(0, 0, 10, 10, \*\*('row', 'column', 'padx', 'pady'))
\_sn = sn.get\_disk\_info()
isVerify = verify\_reg(verify\_register\_info(REG\_KEY))
machine\_code = tk.StringVar(root, \_sn, \*\*('value',))
entryMachineCode = tk.Entry(label\_frame, machine\_code, 30, \*\*('textvariable', 'width'))
if isVerify:
entryMachineCode.configure('disabled', \*\*('state',))
entryMachineCode.grid(0, 0, \*\*('row', 'column'))
result\_frame = tk.LabelFrame(root, '注册码', \*\*('text',))
result\_frame.grid(1, 0, 10, 10, \*\*('row', 'column', 'padx', 'pady'))
text\_widget = ScrolledText(result\_frame, 50, 10, \*\*('width', 'height'))
if isVerify:
text\_widget.insert(END, '软件已注册')
text\_widget.configure('disable', \*\*('state',))
text\_widget.pack(True, 'both', \*\*('expand', 'fill'))


def callbackMachineCode(event = None):
entryMachineCode.event\_generate('<<Copy>>')

menuPackage = tk.Menu(root, False, \*\*('tearoff',))
menuPackage.add\_command('复制', callbackMachineCode, \*\*('label', 'command'))


def popupMachineCode(event = None):
menuPackage.post(event.x\_root, event.y\_root)

entryMachineCode.bind('<Button-3>', popupMachineCode)


def register():
code\_input = text\_widget.get('1.0', 'end-1c')
isVerify = verify\_reg(code\_input)
if isVerify:
write\_register\_info(REG\_KEY, code\_input) # 写入注册表
tk.messagebox.showinfo('成功', '恭喜', \*\*('message',))
root.destroy()
return True
None.messagebox.showinfo('警告!', '不是合法的注册码', \*\*('message',))
return False

button = tk.Button(root, '注册', register, 'disabled' if isVerify else 'active', \*\*('text', 'command', 'state'))
button.grid(2, 0, \*\*('row', 'column'))
root.mainloop()


def isVerify():
if not verify\_reg(verify\_register\_info(REG\_KEY)):
del\_winreg() # 验证失败时删除注册表项
return False
return True


def delete\_key\_value():
pass
\# WARNING: Decompyle incomplete


def del\_winreg():
delete\_key\_value()


def write\_register\_info(key, value):
pass
\# WARNING: Decompyle incomplete


def verify\_register\_info(key):
registry\_key = winreg.ConnectRegistry(None, winreg.HKEY\_CURRENT\_USER)
\# WARNING: Decompyle incomplete

有一部分没反编译成功,但不影响分析

经分析,程序通过公钥解密注册码,将解密后的数据与校验码对比,如果一致就注册成功

程序的校验码应该为:”机器码:到期时间”

程序使用了RSA算法,没有私钥没法写注册机,所以只能另辟蹊径

修改内存

既然公钥是直接写在代码中的,那么我们可以将内置的公钥替换为我们自己的公钥

image

随便生成一对公私钥,注意密钥长度为1024(这里对比程序内置的公钥可以发现)

将公钥转为hex,记下来

image

打开Cheat Engine,选择AML.exe(一般是下面一个,是后出现的)

image

image

扫描的值就是程序内置的公钥:

2D2D2D2D2D424547494E205055424C4943204B45592D2D2D2D2D0A4D4947664D413047435371475349623344514542415155414134474E4144434269514B42675143564D6C396E635659395A616259496D3862583268756F504F620A4B617950334165704A47355774765A544F6C416F3859304737665A6541693941636A4745696A4A63547A465932773977347033454A5676767939464977744D4A0A3455353157613334647653524F683673356C59574B4F59716A2F74414868624A507650705873612F58416139467A376A7569735769772B52725458564A4A744C0A6C7652514F50444434616B44592B34796F514944415141420A2D2D2D2D2D454E44205055424C4943204B45592D2D2D2D2D

image ​点击确定后就修改成功了

好了,接下来我们就可以用私钥 加密 校验码 生成 注册码了

机器码就是程序上显示的:

image

校验码需要加上到期时间,随便构造一个:1136987320:20251213​

用rsa加密校验码:

image

注意选择私钥加密

输入到程序里,注册成功

image


本站由 一花一树叶 使用 Stellar 1.33.1 主题创建。

萌国备案号 萌ICP备20240560号
联系我 gdjx2020@qq.com