pyarmor.cli 9.2.6__4-py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyarmor/cli/docker.py ADDED
@@ -0,0 +1,192 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ #############################################################
5
+ # #
6
+ # Copyright @ 2023 - Dashingsoft corp. #
7
+ # All rights reserved. #
8
+ # #
9
+ # Pyarmor #
10
+ # #
11
+ # Version: 8.3.0 - #
12
+ # #
13
+ #############################################################
14
+ #
15
+ #
16
+ # @File: cli/docker.py
17
+ #
18
+ # @Author: Jondy Zhao (pyarmor@163.com)
19
+ #
20
+ # @Create Date: Tue Aug 1 09:18:12 CST 2023
21
+ #
22
+ import argparse
23
+ import logging
24
+ import os
25
+ import socketserver
26
+ import struct
27
+ import sys
28
+
29
+ from .context import Context
30
+ from .generate import Pytransform3
31
+ from .register import Register, MACHFLAGS
32
+
33
+
34
+ CONFIG = {
35
+ 'host': '0.0.0.0',
36
+ 'port': 29092,
37
+ 'home': os.path.expanduser(os.path.join('~', '.pyarmor', 'docker')),
38
+ 'machid': None,
39
+ 'ctx': None,
40
+ }
41
+
42
+
43
+ class DockerAuthHandler(socketserver.BaseRequestHandler):
44
+
45
+ def handle(self):
46
+ data = self.request.recv(64)
47
+ logging.info('receive request from %s', self.client_address)
48
+ try:
49
+ logging.debug('request data (%d): %s', len(data), data)
50
+ response = self.process(data)
51
+ logging.info('send auth result to %s', self.client_address)
52
+ logging.debug('response data (%d): %s', len(response), response)
53
+ except Exception as e:
54
+ logging.error('%s', str(e))
55
+ msg = 'verification failed, please check host console'.encode()
56
+ msg += b'\00'
57
+ self.request.send(struct.pack('!HH', 1, len(msg)) + msg)
58
+
59
+ def process(self, packet):
60
+ cmd = packet[:4]
61
+ if cmd == b'PADH':
62
+ response = b'\n'.join(CONFIG['machid']) + b'\x00'
63
+ self.request.send(response)
64
+ elif cmd == b'PADI':
65
+ userdata = packet[4:].decode('utf-8')
66
+ rtkey = self.generate_outer_key(userdata, outer=False)
67
+ magic = b'DockerRuntimeKey'
68
+ response = magic + struct.pack('!HH', 0, len(rtkey)) + rtkey
69
+ self.request.send(response)
70
+ elif cmd == b'PADK':
71
+ userdata = self.parse_packet(packet).decode('utf-8')
72
+ rtkey = self.generate_runtime_key(userdata)
73
+ response = struct.pack('!HH', 0, len(rtkey)) + rtkey
74
+ self.request.send(response)
75
+ else:
76
+ raise RuntimeError('unknown packet %r' % packet)
77
+ return response
78
+
79
+ def parse_packet(self, packet):
80
+ if len(packet) == 32 and packet[:4] == b'PADK':
81
+ return packet[12:]
82
+ raise RuntimeError('invalid auth request: %r' % packet)
83
+
84
+ def generate_runtime_key(self, userdata):
85
+ ctx = CONFIG['ctx']
86
+ ctx.cmd_options['user_data'] = userdata
87
+ ctx.cmd_options['expired'] = '.3'
88
+ ctx.cmd_options['outer'] = 0
89
+ Pytransform3._pytransform3.init_ctx(ctx)
90
+ return Pytransform3.generate_runtime_key(ctx)
91
+
92
+ def generate_outer_key(self, userdata, outer=True):
93
+ ctx = CONFIG['ctx']
94
+ ctx.cmd_options['user_data'] = userdata
95
+ ctx.cmd_options['expired'] = '.3'
96
+ ctx.cmd_options['outer'] = 1
97
+ Pytransform3._pytransform3.init_ctx(ctx)
98
+ return Pytransform3.generate_runtime_key(ctx, outer=outer)
99
+
100
+
101
+ def register_pyarmor(ctx, regfile):
102
+ reg = Register(ctx)
103
+ logging.info('register "%s"', regfile)
104
+ reg.register_regfile(regfile)
105
+ if reg.license_info['features'] < 15:
106
+ raise RuntimeError('this feature is only for group license')
107
+ Pytransform3.init(ctx)
108
+
109
+
110
+ def main_entry():
111
+ parser = argparse.ArgumentParser()
112
+ parser.add_argument('-d', '--debug', action='store_true',
113
+ help='Enable debug mode')
114
+ parser.add_argument('--host', type=str, default=CONFIG['host'])
115
+ parser.add_argument('-p', '--port', type=int, default=CONFIG['port'],
116
+ help=argparse.SUPPRESS)
117
+ parser.add_argument('-s', '--sock', default='/var/run/docker.sock',
118
+ help=argparse.SUPPRESS)
119
+ parser.add_argument('--home', help=argparse.SUPPRESS)
120
+ parser.add_argument('regfile', nargs=1,
121
+ help='group device registration file for this machine')
122
+ args = parser.parse_args(sys.argv[1:])
123
+
124
+ if args.debug:
125
+ logging.getLogger().setLevel(logging.DEBUG)
126
+
127
+ if args.home:
128
+ CONFIG['home'] = os.path.expandvars(os.path.expanduser(args.home))
129
+ home = CONFIG['home']
130
+ logging.info('work path: %s', home)
131
+
132
+ # lpath should be non-existent or empty
133
+ ctx = Context(home=home, lpath=os.path.join(home, 'non-existent'))
134
+ register_pyarmor(ctx, args.regfile[0])
135
+ CONFIG['ctx'] = ctx
136
+
137
+ CONFIG['machid'] = [Pytransform3.get_hd_info(x) for x in MACHFLAGS]
138
+ logging.debug('machine id: %s', CONFIG['machid'])
139
+
140
+ host, port = args.host, args.port
141
+ with socketserver.TCPServer((host, port), DockerAuthHandler) as server:
142
+ logging.info('listen container auth request on %s:%s', host, port)
143
+ server.serve_forever()
144
+
145
+
146
+ def main():
147
+ logging.basicConfig(
148
+ level=logging.INFO,
149
+ format='%(asctime)s: %(message)s',
150
+ )
151
+ main_entry()
152
+
153
+
154
+ #
155
+ # Deprecrated functions
156
+ #
157
+ def get_docker_gateway(client):
158
+ filters = {
159
+ 'driver': 'bridge',
160
+ 'type': 'builtin'
161
+ }
162
+ networks = client.networks.list(filters=filters)
163
+ return networks[0].attrs['IPAM']['Config'][0]['Gateway']
164
+
165
+
166
+ def get_container(client, ipaddr):
167
+ filters = {
168
+ 'driver': 'bridge',
169
+ 'type': 'builtin'
170
+ }
171
+ networks = client.networks.list(filters=filters, greedy=True)
172
+ containers = networks[0].attrs.get('Containers')
173
+ if containers:
174
+ marker = ipaddr + '/'
175
+ for dockid, netattr in containers.items():
176
+ if netattr.get('IPv4Address').startswith(marker):
177
+ return client.containers.get(dockid)
178
+ raise RuntimeError('no found countainer with IPv4 %s' % ipaddr)
179
+
180
+
181
+ def copy_file_into_docker(containerid, filename):
182
+ from subprocess import check_call
183
+ check_call(['docker', 'cp', filename, '%s:/' % containerid])
184
+
185
+
186
+ def get_container_info(client, shortid):
187
+ container = client.container.get(shortid)
188
+ return container.attrs
189
+
190
+
191
+ if __name__ == '__main__':
192
+ main()
@@ -0,0 +1,256 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ #############################################################
5
+ # #
6
+ # Copyright @ 2023 - Dashingsoft corp. #
7
+ # All rights reserved. #
8
+ # #
9
+ # Pyarmor #
10
+ # #
11
+ # Version: 8.0.1 - #
12
+ # #
13
+ #############################################################
14
+ #
15
+ #
16
+ # @File: cli/generate.py
17
+ #
18
+ # @Author: Jondy Zhao (pyarmor@163.com)
19
+ #
20
+ # @Create Date: 2022-12-06
21
+ #
22
+ import os
23
+ import shutil
24
+
25
+ from pyarmor.cli.core import Pytransform3
26
+
27
+ from . import logger, CliError
28
+ from .resource import FileResource, PathResource
29
+
30
+
31
+ class Finder(object):
32
+
33
+ def __init__(self, ctx):
34
+ self.ctx = ctx
35
+
36
+ def _build_resource(self, pathlist):
37
+ resources = []
38
+ for path in pathlist:
39
+ if not os.path.exists(path):
40
+ raise CliError('argument "%s" doesn\'t exists' % path)
41
+ if os.path.isfile(path):
42
+ logger.info('find script %s', path)
43
+ res = FileResource(path)
44
+ resources.append(res)
45
+ else:
46
+ logger.info('find package at %s', path)
47
+ res = PathResource(path)
48
+ resources.append(res)
49
+ options = self.ctx.get_res_options(res.fullname)
50
+ res.rebuild(**options)
51
+ return resources
52
+
53
+ def prepare(self, input_paths):
54
+ self.ctx.resources = self._build_resource(input_paths)
55
+
56
+ def process_extra(self, contents):
57
+ extra_paths = [x for x in contents if x.endswith('.pyc')]
58
+ for pyz in [x for x in contents if x.endswith('.pyz_extracted')]:
59
+ extra_paths.extend([os.path.join(pyz, x) for x in os.listdir(pyz)])
60
+ resnames = [x.pkgname for x in self.ctx.resources]
61
+ for res in self._build_resource(extra_paths):
62
+ if res.pkgname not in resnames:
63
+ self.ctx.obfuscated_modules.add(res.pkgname)
64
+ self.ctx.extra_resources.append(res)
65
+
66
+ def append(self, resources):
67
+ resnames = [x.pkgname for x in self.ctx.resources]
68
+ for res in self._build_resource(resources):
69
+ if res.pkgname not in resnames:
70
+ self.ctx.obfuscated_modules.add(res.pkgname)
71
+ self.ctx.resources.append(res)
72
+
73
+ def process(self):
74
+ logger.info('search inputs ...')
75
+ self.prepare(self.ctx.input_paths)
76
+ logger.info('find %d top resources', len(self.ctx.resources))
77
+
78
+ modules = [x.fullname for res in self.ctx.resources for x in res
79
+ if x.is_script()]
80
+ self.ctx.obfuscated_modules.update(modules)
81
+
82
+
83
+ class Builder(object):
84
+
85
+ def __init__(self, ctx):
86
+ self.ctx = ctx
87
+
88
+ def format_output(self, outputs, count=0):
89
+ try:
90
+ output = outputs[count]
91
+ except IndexError:
92
+ output = self.ctx.alias_suffix.format(outputs[0], count)
93
+ return output
94
+
95
+ def generate_runtime_key(self, outer=None):
96
+ return Pytransform3.generate_runtime_key(self.ctx, outer)
97
+
98
+ def generate_runtime_package(self, output):
99
+ if self.ctx.runtime_key is None:
100
+ self.ctx.runtime_key = self.generate_runtime_key()
101
+ Pytransform3.generate_runtime_package(self.ctx, output)
102
+
103
+ def _generate_obfuscated_script(self, res):
104
+ try:
105
+ return Pytransform3.generate_obfuscated_script(self.ctx, res)
106
+ except RuntimeError as e:
107
+ if str(e) != 'out of license':
108
+ raise
109
+
110
+ def _copy_script(self, path, res):
111
+ logger.warning('%s is not obfuscated because out of license', res)
112
+ fullpath = os.path.join(path, res.output_filename)
113
+ output = os.path.dirname(fullpath)
114
+ os.makedirs(output, exist_ok=True)
115
+ logger.info('write %s (not obfuscated)', fullpath)
116
+ shutil.copy2(res.fullpath, output)
117
+
118
+ def _obfuscate_scripts(self):
119
+ rev = self.ctx.version_info()
120
+ template = self.ctx.bootstrap_template
121
+ relative = self.ctx.import_prefix
122
+ pkgname = self.ctx.runtime_package_name
123
+ bootpath = self.ctx.cfg.get('builder', 'bootstrap_file')
124
+ encoding = self.ctx.cfg.get('builder', 'encoding')
125
+
126
+ plugins = [x for x in self.ctx.plugins if hasattr(x, 'post_script')]
127
+
128
+ namelist = []
129
+ for res in self.ctx.resources + self.ctx.extra_resources:
130
+ logger.info('process resource "%s"', res.fullname)
131
+ name = res.name
132
+ path = self.format_output(self.ctx.outputs, namelist.count(name))
133
+ namelist.append(name)
134
+ os.makedirs(path, exist_ok=True)
135
+
136
+ for r in res:
137
+ if not r.is_script():
138
+ logger.info('copy data file %s', r.fullpath)
139
+ data_path = os.path.join(path, r.output_path)
140
+ os.makedirs(data_path, exist_ok=True)
141
+ shutil.copy2(r.fullpath, data_path)
142
+ continue
143
+
144
+ logger.info('obfuscating %s', r)
145
+ code = Pytransform3.generate_obfuscated_script(self.ctx, r)
146
+ source = r.generate_output(
147
+ template, code, relative=relative, pkgname=pkgname,
148
+ bootpath=bootpath, rev=rev
149
+ )
150
+
151
+ fullpath = os.path.join(path, r.output_filename)
152
+ os.makedirs(os.path.dirname(fullpath), exist_ok=True)
153
+
154
+ for plugin in plugins:
155
+ patched_source = plugin.post_script(self.ctx, r, source)
156
+ if patched_source:
157
+ source = patched_source
158
+
159
+ logger.info('write %s', fullpath)
160
+ with open(fullpath, 'w', encoding=encoding) as f:
161
+ f.write(source)
162
+
163
+ def process(self, options, packer=None):
164
+ for opt in options['inputs']:
165
+ if not os.path.exists(opt):
166
+ raise CliError('no found input "%s"' % opt)
167
+ self.ctx.input_paths = options['inputs']
168
+
169
+ output = options.get('output', 'dist')
170
+ self.ctx.outputs = output.split(',')
171
+
172
+ finder = Finder(self.ctx)
173
+ finder.process()
174
+
175
+ if packer and hasattr(packer, 'analysis'):
176
+ auto_resources = packer.analysis()
177
+ logger.info('find extra resources: %s', auto_resources)
178
+ finder.append(auto_resources)
179
+
180
+ Pytransform3.pre_build(self.ctx)
181
+
182
+ self.ctx.runtime_key = self.generate_runtime_key()
183
+ if not options.get('no_runtime'):
184
+ logger.info('start to generate runtime files')
185
+ self.generate_runtime_package(self.ctx.outputs[0])
186
+ logger.info('generate runtime files OK')
187
+
188
+ logger.info('start to obfuscate scripts')
189
+ n = self.ctx.cfg.getint('builder', 'max_workers')
190
+ async_obfuscate_scripts(self, n) if n else self._obfuscate_scripts()
191
+ logger.info('obfuscate scripts OK')
192
+
193
+ Pytransform3.post_build(self.ctx)
194
+
195
+
196
+ def async_obfuscate_scripts(builder, max_workers):
197
+ from concurrent.futures import ProcessPoolExecutor, wait
198
+
199
+ ctx = builder.ctx
200
+ rev = ctx.version_info()
201
+ template = ctx.bootstrap_template
202
+ relative = ctx.import_prefix
203
+ pkgname = ctx.runtime_package_name
204
+ bootpath = ctx.cfg.get('builder', 'bootstrap_file')
205
+ encoding = ctx.cfg.get('builder', 'encoding')
206
+ max_workers = None if max_workers < 2 else max_workers
207
+
208
+ plugins = [x for x in ctx.plugins if hasattr(x, 'post_script')]
209
+
210
+ namelist = []
211
+ futures = []
212
+
213
+ with ProcessPoolExecutor(max_workers=max_workers) as executor:
214
+ for res in ctx.resources + ctx.extra_resources:
215
+ logger.info('process resource "%s"', res.fullname)
216
+ name = res.name
217
+ path = builder.format_output(ctx.outputs, namelist.count(name))
218
+ namelist.append(name)
219
+ os.makedirs(path, exist_ok=True)
220
+
221
+ for r in res:
222
+ if not r.is_script():
223
+ logger.info('copy data file %s', r.fullpath)
224
+ data_path = os.path.join(path, r.output_path)
225
+ os.makedirs(data_path, exist_ok=True)
226
+ shutil.copy2(r.fullpath, data_path)
227
+ continue
228
+
229
+ logger.info('obfuscating %s', r)
230
+ futures.append(executor.submit(aysnc_generate_script, *(
231
+ r, path, ctx, template, relative, pkgname, bootpath,
232
+ rev, plugins, encoding)))
233
+
234
+ wait(futures)
235
+ for x in futures:
236
+ x.result()
237
+
238
+
239
+ def aysnc_generate_script(r, path, ctx, template, relative,
240
+ pkgname, bootpath, rev, plugins, encoding):
241
+ code = Pytransform3.generate_obfuscated_script(ctx, r)
242
+ source = r.generate_output(
243
+ template, code, relative=relative, pkgname=pkgname,
244
+ bootpath=bootpath, rev=rev
245
+ )
246
+
247
+ fullpath = os.path.join(path, r.output_filename)
248
+ os.makedirs(os.path.dirname(fullpath), exist_ok=True)
249
+
250
+ for plugin in plugins:
251
+ patched_source = plugin.post_script(ctx, r, source)
252
+ if patched_source:
253
+ source = patched_source
254
+
255
+ with open(fullpath, 'w', encoding=encoding) as f:
256
+ f.write(source)
pyarmor/cli/hdinfo.py ADDED
@@ -0,0 +1,81 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ #############################################################
5
+ # #
6
+ # Copyright @ 2023 - Dashingsoft corp. #
7
+ # All rights reserved. #
8
+ # #
9
+ # Pyarmor #
10
+ # #
11
+ # Version: 8.4.6 - #
12
+ # #
13
+ #############################################################
14
+ #
15
+ #
16
+ # @File: cli/hdinfo.py
17
+ #
18
+ # @Author: Jondy Zhao (pyarmor@163.com)
19
+ #
20
+ # @Create Date: Fri Dec 15 09:27:30 CST 2023
21
+ #
22
+ import argparse
23
+ import sys
24
+
25
+ from pyarmor.cli.core import Pytransform3
26
+
27
+
28
+ GROUP_LICENSE_MACHINE_FLAG = 22
29
+ HT_HARDDISK, HT_IFMAC, HT_IPV4, HT_IPV6, HT_DOMAIN = range(5)
30
+
31
+
32
+ def get_hd_info(hdtype, name=None):
33
+ try:
34
+ return repr(Pytransform3.get_hd_info(hdtype, name))
35
+ except Exception as e:
36
+ return str(e)
37
+
38
+
39
+ def get_all_ifmac():
40
+ try:
41
+ buf = Pytransform3.get_hd_info(HT_IFMAC, name='*')
42
+ i = 0
43
+ n = len(buf)
44
+ rlist = []
45
+ while i < n and buf[i]:
46
+ j = i + 1 + buf[i]
47
+ rlist.append(':'.join(['%02x' % x for x in buf[i+1:j]]))
48
+ i = j
49
+ except Exception as e:
50
+ return str(e)
51
+
52
+ return '<%s>' % ','.join(set(rlist))
53
+
54
+
55
+ def main(argv):
56
+ parser = argparse.ArgumentParser()
57
+ parser.add_argument('devname', nargs='*', help=(
58
+ 'In Linux get netcard name like "eth0" '
59
+ 'or harddisk name like "/dev/sda"'))
60
+ args = parser.parse_args(argv)
61
+
62
+ print('Machine ID: %s' % get_hd_info(GROUP_LICENSE_MACHINE_FLAG)[1:])
63
+
64
+ if not args.devname:
65
+ print('Default Harddisk Serial Number: %s' % get_hd_info(HT_HARDDISK))
66
+ print('Default Mac address: %s' % get_hd_info(HT_IFMAC))
67
+ print('Default IPv4 address: %s' % get_hd_info(HT_IPV4))
68
+ print('Multiple Mac addresses: %s' % get_all_ifmac())
69
+ print('Domain: %s' % get_hd_info(HT_DOMAIN))
70
+
71
+ for name in args.devname:
72
+ if name.startswith('/'):
73
+ print('Query Harddisk "%s" Serial Number: %s' %
74
+ (name, get_hd_info(HT_HARDDISK, name=name)))
75
+ else:
76
+ print('Query Netcard "/dev/%s" Mac Address: %s' %
77
+ (name, get_hd_info(HT_IFMAC, name=name)))
78
+
79
+
80
+ if __name__ == '__main__':
81
+ main(sys.argv[1:])