Skip to content

Commit 00e2bae

Browse files
author
Your Name
committed
0 parents  commit 00e2bae

18 files changed

+5682
-0
lines changed

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# History
2+
py-kms is a port of node-kms by [markedsword](http://forums.mydigitallife.info/members/183074-markedsword), which is a port of either the C#, C++, or .NET implementations of KMSEmulator, of which the original version was written by [CODYQX4](http://forums.mydigitallife.info/members/89933-CODYQX4) and is derived from the reverse-engineered code of Microsoft's official KMS.
3+
4+
# Features
5+
- Responds to V4, V5, and V6 KMS requests.
6+
- Supports activating Windows 7/8/8.1/2008R2/2012/2012R2 and Office 2010/2013.
7+
- It's written in Python.
8+
9+
# Dependencies
10+
- Python 2.7.x or Python 2.6.x with the "argparse" module installed.
11+
- If the "pytz" module is installed, the "Request Time" in the verbose output will be converted into local time. Otherwise, it will be in UTC.
12+
13+
# Usage
14+
- To start the server, execute `python server.py [listen_address] [port]`. The default listening address is `0.0.0.0` (all interfaces) and the default port is `1688`.
15+
- To run the client, use `python client.py server_address [port]`. The default port is `1688`.

aes.py

+697
Large diffs are not rendered by default.

client.py

+336
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
import argparse
2+
import binascii
3+
import datetime
4+
import random
5+
import socket
6+
import string
7+
import struct
8+
import sys
9+
import uuid
10+
import filetimes, rpcBind, rpcRequest
11+
12+
from dcerpc import MSRPCHeader, MSRPCBindNak
13+
from rpcBase import rpcBase
14+
15+
config = {}
16+
17+
def main():
18+
parser = argparse.ArgumentParser()
19+
parser.add_argument("ip", action="store", help="The IP address or hostname of the KMS host.", type=str)
20+
parser.add_argument("port", nargs="?", action="store", default=1688, help="The port the KMS service is listening on. The default is \"1688\".", type=int)
21+
parser.add_argument("-m", "--mode", dest="mode", choices=["WindowsVista","Windows7","Windows8","Windows81","Office2010","Office2013"], default="Windows7")
22+
parser.add_argument("-v", "--verbose", dest="verbose", action="store_const", const=True, default=False, help="Enable this flag to turn on verbose output.")
23+
parser.add_argument("-d", "--debug", dest="debug", action="store_const", const=True, default=False, help="Enable this flag to turn on debug output. Implies \"-v\".")
24+
config.update(vars(parser.parse_args()))
25+
config['call_id'] = 1
26+
if config['debug']:
27+
config['verbose'] = True
28+
updateConfig()
29+
s = socket.socket()
30+
print "Connecting to %s on port %d..." % (config['ip'], config['port'])
31+
s.connect((config['ip'], config['port']))
32+
if config['verbose']:
33+
print "Connection successful!"
34+
binder = rpcBind.bind('', config)
35+
RPC_Bind = str(binder.generateRequest())
36+
if config['verbose']:
37+
print "Sending RPC bind request..."
38+
s.send(RPC_Bind)
39+
try:
40+
bindResponse = s.recv(1024)
41+
except socket.error, e:
42+
if e[0] == 104:
43+
print "Error: Connection reset by peer. Exiting..."
44+
sys.exit()
45+
else:
46+
raise
47+
if bindResponse == '' or not bindResponse:
48+
print "No data received! Exiting..."
49+
sys.exit()
50+
packetType = MSRPCHeader(bindResponse)['type']
51+
if packetType == rpcBase.packetType['bindAck']:
52+
if config['verbose']:
53+
print "RPC bind acknowledged."
54+
#config['call_id'] += 1
55+
'''
56+
request = CreateRequest()
57+
requester = rpcRequest.request(request, config)
58+
s.send(request)
59+
response = s.recv(1024)
60+
if config['debug']:
61+
print "Response:", binascii.b2a_hex(response), len(response)
62+
parsed = ReadResponse(response)
63+
'''
64+
elif packetType == rpcBase.packetType['bindNak']:
65+
print MSRPCBindNak(bindResponse).dump()
66+
sys.exit()
67+
else:
68+
print "Something went wrong."
69+
sys.exit()
70+
71+
def updateConfig():
72+
if config['mode'] == 'WindowsVista':
73+
config['RequiredClientCount'] = 25
74+
config['KMSProtocolMajorVersion'] = 4
75+
config['KMSProtocolMinorVersion'] = 0
76+
config['KMSClientLicenseStatus'] = 2
77+
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
78+
config['KMSClientSkuID'] = "cfd8ff08-c0d7-452b-9f60-ef5c70c32094"
79+
config['KMSClientKMSCountedID'] = "212a64dc-43b1-4d3d-a30c-2fc69d2095c6"
80+
elif config['mode'] == 'Windows7':
81+
config['RequiredClientCount'] = 25
82+
config['KMSProtocolMajorVersion'] = 4
83+
config['KMSProtocolMinorVersion'] = 0
84+
config['KMSClientLicenseStatus'] = 2
85+
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
86+
config['KMSClientSkuID'] = "ae2ee509-1b34-41c0-acb7-6d4650168915"
87+
config['KMSClientKMSCountedID'] = "7fde5219-fbfa-484a-82c9-34d1ad53e856"
88+
elif config['mode'] == 'Windows8':
89+
config['RequiredClientCount'] = 25
90+
config['KMSProtocolMajorVersion'] = 5
91+
config['KMSProtocolMinorVersion'] = 0
92+
config['KMSClientLicenseStatus'] = 2
93+
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
94+
config['KMSClientSkuID'] = "458e1bec-837a-45f6-b9d5-925ed5d299de"
95+
config['KMSClientKMSCountedID'] = "3c40b358-5948-45af-923b-53d21fcc7e79"
96+
elif config['mode'] == 'Windows81':
97+
config['RequiredClientCount'] = 25
98+
config['KMSProtocolMajorVersion'] = 6
99+
config['KMSProtocolMinorVersion'] = 0
100+
config['KMSClientLicenseStatus'] = 2
101+
config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f"
102+
config['KMSClientSkuID'] = "81671aaf-79d1-4eb1-b004-8cbbe173afea"
103+
config['KMSClientKMSCountedID'] = "cb8fc780-2c05-495a-9710-85afffc904d7"
104+
elif config['mode'] == 'Office2010':
105+
config['RequiredClientCount'] = 5
106+
config['KMSProtocolMajorVersion'] = 4
107+
config['KMSProtocolMinorVersion'] = 0
108+
config['KMSClientLicenseStatus'] = 2
109+
config['KMSClientAppID'] = "59a52881-a989-479d-af46-f275c6370663"
110+
config['KMSClientSkuID'] = "6f327760-8c5c-417c-9b61-836a98287e0c"
111+
config['KMSClientKMSCountedID'] = "e85af946-2e25-47b7-83e1-bebcebeac611"
112+
elif config['mode'] == 'Office2013':
113+
config['RequiredClientCount'] = 5
114+
config['KMSProtocolMajorVersion'] = 5
115+
config['KMSProtocolMinorVersion'] = 0
116+
config['KMSClientLicenseStatus'] = 2
117+
config['KMSClientAppID'] = "0ff1ce15-a989-479d-af46-f275c6370663"
118+
config['KMSClientSkuID'] = "b322da9c-a2e2-4058-9e4e-f59a6970bd69"
119+
config['KMSClientKMSCountedID'] = "e6a6f1bf-9d40-40c3-aa9f-c77ba21578c0"
120+
121+
def CreateRequestBase():
122+
# Init requestDict
123+
requestDict = {}
124+
125+
# KMS Protocol Version
126+
requestDict['MajorVer'] = config['KMSProtocolMajorVersion']
127+
requestDict['MinorVer'] = config['KMSProtocolMinorVersion']
128+
129+
# KMS Client is NOT a VM
130+
requestDict['IsClientVM'] = 0
131+
132+
# License Status
133+
requestDict['LicenseStatus'] = config['KMSClientLicenseStatus']
134+
135+
# Grace Time
136+
requestDict['GraceTime'] = 43200
137+
138+
# Application ID
139+
requestDict['ApplicationId'] = uuid.UUID(config['KMSClientAppID'])
140+
141+
# SKU ID
142+
requestDict['SkuId'] = uuid.UUID(config['KMSClientSkuID'])
143+
144+
# KMS Counted ID
145+
requestDict['KmsCountedId'] = uuid.UUID(config['KMSClientKMSCountedID'])
146+
147+
# CMID
148+
requestDict['ClientMachineId'] = uuid.uuid4()
149+
150+
# Minimum Clients
151+
requestDict['RequiredClientCount'] = config['RequiredClientCount']
152+
153+
# Current Time
154+
requestDict['RequestTime'] = filetimes.dt_to_filetime(datetime.datetime.utcnow())
155+
156+
# Generate Random Machine Name (Up to 63 Characters)
157+
requestDict['MachineName'] = ''.join(random.choice(string.letters + string.digits) for i in range(32))
158+
159+
# Debug Stuff
160+
if config['debug']:
161+
print "Request Base Dictionary:", requestDict
162+
163+
request = str()
164+
request += struct.pack('<H', requestDict['MinorVer'])
165+
request += struct.pack('<H', requestDict['MajorVer'])
166+
request += struct.pack('<I', requestDict['IsClientVM'])
167+
request += struct.pack('<I', requestDict['LicenseStatus'])
168+
request += struct.pack('<I', requestDict['GraceTime'])
169+
request += requestDict['ApplicationId'].bytes_le
170+
request += requestDict['SkuId'].bytes_le
171+
request += requestDict['KmsCountedId'].bytes_le
172+
request += requestDict['ClientMachineId'].bytes_le
173+
request += struct.pack('<I', requestDict['RequiredClientCount'])
174+
request += struct.pack('>Q', requestDict['RequestTime'])
175+
request += requestDict['ClientMachineId'].bytes_le
176+
request += requestDict['MachineName'].encode('utf-16le')
177+
request += ('\0' * 32).encode('utf-16le')
178+
if config['debug']:
179+
print "Request Base:", binascii.b2a_hex(request), len(request)
180+
181+
return request
182+
183+
def CreateRequestV4():
184+
# Update the call ID
185+
config['call_id'] += 1
186+
187+
# Create KMS Client Request Base
188+
requestBase = CreateRequestBase()
189+
190+
# Create Hash
191+
hashed = str(kmsRequestV4.main(bytearray(requestBase)))
192+
193+
# Generate Request
194+
bodyLength = len(requestBase) + len(hashed)
195+
if bodyLength % 8 == 0:
196+
paddingLength = 0
197+
else:
198+
paddingLength = 8 - bodyLength % 8
199+
v4Data = {
200+
"BodyLength" : bodyLength,
201+
"BodyLength2" : bodyLength,
202+
"Hash" : hashed,
203+
"Padding" : str(bytearray(functions.arrayFill([], paddingLength, 0x00)))
204+
}
205+
if config['debug']:
206+
print "Request V4 Data:", v4Data
207+
request = str()
208+
request += struct.pack('<I',v4Data["BodyLength"])
209+
request += struct.pack('<I',v4Data["BodyLength2"])
210+
request += requestBase
211+
request += v4Data["Hash"]
212+
request += v4Data["Padding"]
213+
if config['debug']:
214+
print "Request V4:", binascii.b2a_hex(request), len(request)
215+
216+
return request
217+
218+
def CreateRequestV5():
219+
# Update the call ID
220+
config['call_id'] += 1
221+
222+
# Generate a Random Salt Key
223+
randomSalt = bytearray(random.getrandbits(8) for i in range(16))
224+
225+
# Set KMS Client Request Base
226+
requestBase = CreateRequestBase();
227+
228+
'''
229+
# AES-128 Encrypt
230+
ENC = 1
231+
DEC = 0
232+
key = bytearray([ 0xCD, 0x7E, 0x79, 0x6F, 0x2A, 0xB2, 0x5D, 0xCB, 0x55, 0xFF, 0xC8, 0xEF, 0x83, 0x64, 0xC4, 0x70 ])
233+
cipher = M2Crypto.EVP.Cipher(alg='aes_128_cbc', key=str(key), iv=str(randomSalt), op=ENC)
234+
crypted = cipher.update(requestBase)
235+
'''
236+
237+
# Generate Request
238+
bodyLength = 4 + len(randomSalt) + len(crypted)
239+
if bodyLength % 8 == 0:
240+
paddingLength = 0
241+
else:
242+
paddingLength = 8 - bodyLength % 8
243+
v5Data = {
244+
"Version" : 5,
245+
"BodyLength" : bodyLength,
246+
"BodyLength2" : bodyLength,
247+
"Encrypted" : crypted,
248+
"Padding" : str(bytearray(functions.arrayFill(bytearray(), paddingLength, 0x00)))
249+
}
250+
if config['debug']:
251+
print "Request V5 Data:", v5Data
252+
request = str()
253+
request += struct.pack('<I',v5Data["BodyLength"])
254+
request += struct.pack('<I',v5Data["BodyLength2"])
255+
request += struct.pack('<H',0)
256+
request += struct.pack('<H',v5Data["Version"])
257+
request += randomSalt
258+
request += crypted
259+
request += v5Data["Padding"]
260+
if config['debug']:
261+
print "Request V5:", binascii.b2a_hex(request), len(request)
262+
263+
# Return Request
264+
return request
265+
266+
def CreateRequest():
267+
# KMS Protocol Major Version
268+
if config['KMSProtocolMajorVersion'] == 4:
269+
request = CreateRequestV4()
270+
elif config['KMSProtocolMajorVersion'] == 5:
271+
request = CreateRequestV5()
272+
else:
273+
return None
274+
return RPCMessageWrapper(request)
275+
276+
def RPCMessageWrapper(request):
277+
# Create the dictionary of data
278+
wrapperDict = {}
279+
wrapperDict['Version'] = '\x05'
280+
wrapperDict['VersionMinor'] = '\x00'
281+
wrapperDict['PacketType'] = '\x00'
282+
wrapperDict['PacketFlags'] = '\x03'
283+
wrapperDict['DataRepresentation'] = struct.pack('<I', 0x10)
284+
wrapperDict['FragLength'] = struct.pack('<H', len(request) + 24)
285+
wrapperDict['AuthLength'] = struct.pack('<H', 0)
286+
wrapperDict['CallId'] = struct.pack('<I', config['call_id'])
287+
wrapperDict['AllocHint'] = struct.pack('<I', len(request))
288+
wrapperDict['ContextId'] = struct.pack('<H', 0)
289+
wrapperDict['Opnum'] = struct.pack('<H', 0)
290+
if config['debug']:
291+
print "RPC Wrapper Dictionary:", wrapperDict
292+
293+
wrapper = str()
294+
wrapper += wrapperDict['Version']
295+
wrapper += wrapperDict['VersionMinor']
296+
wrapper += wrapperDict['PacketType']
297+
wrapper += wrapperDict['PacketFlags']
298+
wrapper += wrapperDict['DataRepresentation']
299+
wrapper += wrapperDict['FragLength']
300+
wrapper += wrapperDict['AuthLength']
301+
wrapper += wrapperDict['CallId']
302+
wrapper += wrapperDict['AllocHint']
303+
wrapper += wrapperDict['ContextId']
304+
wrapper += wrapperDict['Opnum']
305+
wrapper += request
306+
if config['debug']:
307+
print "Wrapped Request:", binascii.b2a_hex(wrapper), len(wrapper)
308+
309+
# Return the wrapped request
310+
return wrapper
311+
312+
def ReadResponse(data):
313+
unknownDataSize = 8
314+
version1 = data[unknownDataSize + 2]
315+
version2 = data[unknownDataSize + 0]
316+
317+
if version1 == 4 and version2 == 0:
318+
print "Received V4 response"
319+
response = ReadResponseV4(data)
320+
elif version1 == 5 and version2 == 0:
321+
print "Received V5 response"
322+
response = ReadResponseV5(data)
323+
else:
324+
print "Unhandled response version", version1
325+
return response
326+
327+
def ReadResponseV4(data):
328+
responseDict = {}
329+
return responseDict
330+
331+
def ReadResponseV5(data):
332+
responseDict = {}
333+
return responseDict
334+
335+
if __name__ == "__main__":
336+
main()

0 commit comments

Comments
 (0)