0%

OpenVPN Access Server 2.9.x 破解在线人数限制

后记: 由于 2.9.x 以上版本由于使用了 Python3 编写, 生成的库文件格式已变化, 本补丁无法处理, 本补丁目前最高支持 2.8.8 版本! 现已支持2.9.x版本~!

前言

本人家里使用了运行了一台服务器, 为了方便远程管理, 最近在 Docker 中用 OpenVPN Access Server 跑了一个 OpenVPN 服务器, 但是想试试这个东西的最大负载, 怎奈免费版本的 OpenVPN Access Server 只允许两个连接, 本着孜孜不倦的精神为了能够深入研究一下于是上网冲浪一番.
在网上博览群文之后发现流行的爆破补丁都是针对 2.5.0 版本的, 直接拿来用肯定是不行的, 经过分析发现 OpenVPN Access Server 的作者对盗版这个事情并不是很在意, 从 2.5.02.9.x, 对于授权这款的代码基本是没改过的, 所以在结合网络上面其他人的博文搞了一番并且成功了之后, 特别在这里记录一下.

截止到我写这篇文字的时候最新的版本已经是 2.9.4, 但是由于我家里的机器用 Docker 部署 2.8.7 的时候会跑不起来所以用的 2.8.0, 我这也是采用的最新版 2.9.4.

跑不起来的原因是: https://github.com/linuxserver/docker-openvpn-as/issues/108

我更新这个文章的时候已经是自己构建 Docker 镜像, 架梯子下载的, 不会出现下载不下来的情况了!

过程

环境准备

名称 版本 必要
OpenVPN Access Server >=2.5(太旧的我没测试) YES
Python Runtime 3.7 YES

由于 2.9.x 使用的 Python 运行时是 3.x, 所以操作 2.9.0 以下的版本需要 Python 2.8, 2.9.0 及以上版本需要 Python 3.x, 不能太新, 会出问题! 最新的 2.9.4 已经使用的是 python 3.7!

实操阶段

  • 主要操作的文件是一个名叫 pyovpn-2.0-pyx.x.egg 的文件, 以我了解的情况来看, 从 2.5.02.9.x 文件名一直都是这个, 只是不同版本里面的内容不一样.
  • 这个文件有点类似 Java 当中的 jar 库文件, 也是一个 zip 压缩文件, 里面包含了一些 Python 的字节码文件.
  • 破解的原理大概是在 Python 中采用类似 Java 动态代理的技术, 将原本读取用户属性的调用返回值拦截, 修改用户限制数量再返回.

2.9.0 以下版本破解的目标文件是 /pyovpn/lic/uprop.pyo, 2.9.0 及以上是 /pyovpn/lic/uprop.pyc; 按照网上流行的破解方法, 把这个文件解压出来并改名为 uprop2.pyouprop2.pyc, 然后新建一个 uprop.py 文件, 内容如下:

2.9.0 以下版本内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import uprop2
old_figure = None

def new_figure(self, licdict):
ret = old_figure(self, licdict)
ret['concurrent_connections'] = 1024
return ret


for x in dir(uprop2):
if x[:2] == '__':
continue
if x == 'UsageProperties':
exec('old_figure = uprop2.UsageProperties.figure')
exec('uprop2.UsageProperties.figure = new_figure')
exec('%s = uprop2.%s' % (x, x))

2.9.0 及以上版本内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pyovpn.lic import uprop2
old_figure = None

def new_figure(self, licdict):
ret = old_figure(self, licdict)
ret['concurrent_connections'] = 1024
return ret


for x in dir(uprop2):
if x[:2] == '__':
continue
if x == 'UsageProperties':
exec('old_figure = uprop2.UsageProperties.figure')
exec('uprop2.UsageProperties.figure = new_figure')
exec('%s = uprop2.%s' % (x, x))

再将上面的 uprop.py 编译为库文件 uprop.pyouprop.pyc:

1
2
3
4
# <2.9.0
python2 -O -m compileall uprop.py
# >=2.9.0
python3 -O -m compileall uprop.py && mv __pycache__/uprop.cpython-37.opt-1.pyc uprop.pyc

注意 uprop.cpython-37.opt-1.pyc 文件名会随着 python 版本变化而变化.

现在我们得到了一个改文件名的文件 uprop2.pyouprop2.pyc, 和一个编译出来的 uprop.pyouprop.pyc; 把这两个文件压缩到 pyovpn-2.0-pyx.x.egg/pyovpn/lic/ 目录下, 然后去服务器替换目标文件, 重启服务就OK了.

对原理感兴趣的可以使用 python-uncompyle6 反编译原本的 uprop 字节码文件. 举个例子:

1
2
3
4
# 安装 uncompyle6
pip install uncompyle6
# 反编译 uprop.pyc, 成功之后就会在当前目录得到一个文件 uprop.py
uncompyle6 -o ./ uprop.pyc

我这也贴一个我反编译的源文件:

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
# uncompyle6 version 3.7.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.11 (default, Aug 17 2021, 16:15:07)
# [GCC 10.2.1 20210110]
# Embedded file name: build/bdist.linux-x86_64/egg/pyovpn/lic/uprop.py
# Compiled at: 2021-08-17 12:27:35
# Size of source mod 2**32: 3315 bytes
from pyovpn.util.date import YYYYMMDD
from pyovpn.util.error import Passthru
from pyovpn.lic.prop import LicenseProperties
from pyovpn.util.env import get_env_debug
from pyovpn.pki.sign import rsa_verify_complex
from pyovpn.aws.info import AWSInfo
DEBUG = get_env_debug('DEBUG_UPROP')

class UsageProperties(object):

def figure(self, licdict):
proplist = set(('concurrent_connections', ))
good = set()
ret = None
if licdict:
for key, props in list(licdict.items()):
if 'quota_properties' not in props:
print('License Manager: key %s is missing usage properties' % key)
continue
proplist.update(props['quota_properties'].split(','))
good.add(key)

for prop in proplist:
v_agg = 0
v_nonagg = 0
if licdict:
for key, props in list(licdict.items()):
if key in good and prop in props:
try:
nonagg = int(props[prop])
except:
raise Passthru('license property %s (%s)' % (prop, props.get(prop).__repr__()))

v_nonagg = max(v_nonagg, nonagg)
prop_agg = '%s_aggregated' % prop
agg = 0
if prop_agg in props:
try:
agg = int(props[prop_agg])
except:
raise Passthru('aggregated license property %s (%s)' % (
prop_agg, props.get(prop_agg).__repr__()))

v_agg += agg
if DEBUG:
print('PROP=%s KEY=%s agg=%d(%d) nonagg=%d(%d)' % (
prop, key, agg, v_agg, nonagg, v_nonagg))

apc = self._apc()
v_agg += apc
if ret == None:
ret = {}
ret[prop] = max(v_agg + v_nonagg, bool('v_agg') + bool('v_nonagg'))
ret['apc'] = bool(apc)
if DEBUG:
print("ret['%s'] = v_agg(%d) + v_nonagg(%d)" % (prop, v_agg, v_nonagg))

return ret

def _apc(self):
try:
pcs = AWSInfo.get_product_code()
if pcs:
return pcs['snoitcennoCtnerrucnoc'[::-1]]
except:
if DEBUG:
print(Passthru('UsageProperties._apc'))

return 0

@staticmethod
def _expired(today, props):
if 'expiry_date' in props:
exp = YYYYMMDD.validate(props['expiry_date'])
return today > exp
return False


class UsagePropertiesValidate(object):
proplist = ('concurrent_connections', 'client_certificates')

def validate(self, usage_properties):
lp = LicenseProperties(usage_properties)
lp.aggregated_post()
lp['quota_properties'] = ','.join([p for p in self.proplist if p in lp])
return lp

结果

屏幕截图 2021-08-29 020224.png

引用

本文章提到的内容只允许做个人学习研究之用,不得用于商业用途!否则后果自负! 若资金允许,请购买正版,谢谢!

  • 本文作者: 6x
  • 本文链接: https://6xyun.cn/article/112
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-ND 许可协议。转载请注明出处!