mirror of
https://github.com/SystemRage/py-kms.git
synced 2024-11-25 17:55:37 +01:00
127 lines
6.0 KiB
Python
127 lines
6.0 KiB
Python
|
import argparse
|
||
|
import binascii
|
||
|
import hashlib
|
||
|
import random
|
||
|
import re
|
||
|
import socket
|
||
|
import SocketServer
|
||
|
import struct
|
||
|
import uuid
|
||
|
import rpcBind, rpcRequest
|
||
|
from dcerpc import MSRPCHeader
|
||
|
from rpcBase import rpcBase
|
||
|
import logging
|
||
|
import os
|
||
|
|
||
|
config = {}
|
||
|
|
||
|
def main():
|
||
|
parser = argparse.ArgumentParser(description='py-kms: KMS Server Emulator', epilog="version: py-kms_2017-06-01")
|
||
|
parser.add_argument("ip", nargs="?", action="store", default="0.0.0.0",
|
||
|
help='The IP address to listen on. The default is \"0.0.0.0\" (all interfaces).', type=str)
|
||
|
parser.add_argument("port", nargs="?", action="store", default=1688,
|
||
|
help='The network port to listen on. The default is \"1688\".', type=int)
|
||
|
parser.add_argument("-e", "--epid", dest="epid", default=None,
|
||
|
help='Use this flag to manually specify an ePID to use. If no ePID is specified, a random ePID will be generated.', type=str)
|
||
|
parser.add_argument("-l", "--lcid", dest="lcid", default=1033,
|
||
|
help='Use this flag to manually specify an LCID for use with randomly generated ePIDs. If an ePID is manually specified,\
|
||
|
this setting is ignored.', type=int)
|
||
|
parser.add_argument("-c", "--client-count", dest="CurrentClientCount", default=26,
|
||
|
help='Use this flag to specify the current client count. Default is 26. A number >25 is required to enable activation.', type=int)
|
||
|
parser.add_argument("-a", "--activation-interval", dest="VLActivationInterval", default=120,
|
||
|
help='Use this flag to specify the activation interval (in minutes). Default is 120 minutes (2 hours).', type=int)
|
||
|
parser.add_argument("-r", "--renewal-interval", dest="VLRenewalInterval", default=1440 * 7,
|
||
|
help='Use this flag to specify the renewal interval (in minutes). Default is 10080 minutes (7 days).', type=int)
|
||
|
parser.add_argument("-s", "--sqlite", dest="sqlite", action="store_const", const=True, default=False,
|
||
|
help='Use this flag to store request information from unique clients in an SQLite database.')
|
||
|
parser.add_argument("-w", "--hwid", dest="hwid", action="store", default='364F463A8863D35F',
|
||
|
help='Use this flag to specify a HWID. The HWID must be an 16-character string of hex characters. \
|
||
|
The default is \"364F463A8863D35F\" or type \"random\" to auto generate the HWID.', type=str)
|
||
|
parser.add_argument("-v", "--loglevel", dest="loglevel", action="store", default="ERROR", choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
|
||
|
help='Use this flag to set a Loglevel. The default is \"ERROR\".', type=str)
|
||
|
parser.add_argument("-f", "--logfile", dest="logfile", action="store", default=os.path.dirname(os.path.abspath( __file__ )) + "/pykms_server.log",
|
||
|
help='Use this flag to set an output Logfile. The default is \"pykms_server.log\".', type=str)
|
||
|
|
||
|
config.update(vars(parser.parse_args()))
|
||
|
#Random HWID
|
||
|
if config['hwid'] == "random":
|
||
|
randomhwid = uuid.uuid4().hex
|
||
|
config['hwid'] = randomhwid[:16]
|
||
|
|
||
|
# Sanitize HWID
|
||
|
try:
|
||
|
config['hwid'] = binascii.a2b_hex(re.sub(r'[^0-9a-fA-F]', '', config['hwid'].strip('0x')))
|
||
|
if len(binascii.b2a_hex(config['hwid'])) < 16:
|
||
|
logging.error("HWID \"%s\" is invalid. Hex string is too short." % binascii.b2a_hex(config['hwid']).upper())
|
||
|
return
|
||
|
elif len(binascii.b2a_hex(config['hwid'])) > 16:
|
||
|
logging.error("HWID \"%s\" is invalid. Hex string is too long." % binascii.b2a_hex(config['hwid']).upper())
|
||
|
return
|
||
|
except TypeError:
|
||
|
logging.error("HWID \"%s\" is invalid. Odd-length hex string." % binascii.b2a_hex(config['hwid']).upper())
|
||
|
return
|
||
|
|
||
|
logging.basicConfig(level=config['loglevel'], format='%(asctime)s %(levelname)-8s %(message)s',
|
||
|
datefmt='%a, %d %b %Y %H:%M:%S', filename=config['logfile'], filemode='w')
|
||
|
|
||
|
try:
|
||
|
import sqlite3
|
||
|
config['dbSupport'] = True
|
||
|
except:
|
||
|
logging.warning("Module \"sqlite3\" is not installed, database support disabled.")
|
||
|
config['dbSupport'] = False
|
||
|
server = SocketServer.TCPServer((config['ip'], config['port']), kmsServer)
|
||
|
server.timeout = 5
|
||
|
logging.info("TCP server listening at %s on port %d." % (config['ip'], config['port']))
|
||
|
logging.info("HWID: %s" % binascii.b2a_hex(config['hwid']).upper())
|
||
|
server.serve_forever()
|
||
|
|
||
|
class kmsServer(SocketServer.BaseRequestHandler):
|
||
|
def setup(self):
|
||
|
self.connection = self.request
|
||
|
logging.info("Connection accepted: %s:%d" % (self.client_address[0], self.client_address[1]))
|
||
|
|
||
|
def handle(self):
|
||
|
while True:
|
||
|
# self.request is the TCP socket connected to the client
|
||
|
try:
|
||
|
self.data = self.connection.recv(1024)
|
||
|
except socket.error, e:
|
||
|
if e[0] == 104:
|
||
|
logging.error("Connection reset by peer.")
|
||
|
break
|
||
|
else:
|
||
|
raise
|
||
|
if self.data == '' or not self.data:
|
||
|
logging.warning("No data received !")
|
||
|
break
|
||
|
# self.data = bytearray(self.data.strip())
|
||
|
# logging.debug(binascii.b2a_hex(str(self.data)))
|
||
|
packetType = MSRPCHeader(self.data)['type']
|
||
|
if packetType == rpcBase.packetType['bindReq']:
|
||
|
logging.info("RPC bind request received.")
|
||
|
handler = rpcBind.handler(self.data, config)
|
||
|
elif packetType == rpcBase.packetType['request']:
|
||
|
logging.info("Received activation request.")
|
||
|
handler = rpcRequest.handler(self.data, config)
|
||
|
else:
|
||
|
logging.error("Invalid RPC request type ", packetType)
|
||
|
break
|
||
|
|
||
|
handler.populate()
|
||
|
res = str(handler.getResponse())
|
||
|
self.connection.send(res)
|
||
|
|
||
|
if packetType == rpcBase.packetType['bindReq']:
|
||
|
logging.info("RPC bind acknowledged.")
|
||
|
elif packetType == rpcBase.packetType['request']:
|
||
|
logging.info("Responded to activation request.")
|
||
|
break
|
||
|
|
||
|
def finish(self):
|
||
|
self.connection.close()
|
||
|
logging.info("Connection closed: %s:%d" % (self.client_address[0], self.client_address[1]))
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|